目录
一、简介
二、电路原理图
三、程序源代码
四、程序分析
1.头文件、宏定义
2.管脚定义,变量声明
3.按键防抖函数
4.开关函数
5.延迟1s函数
6.流水灯实现程序
7.主函数
一、简介
本实验基于STC15开发板上实施,硬件电路包括STC单片机与PC机USB接口的通信线路,以及LED4、LED7、LED8、LED9、LED10等LED灯,对应STC的P2.7,P1.7,P1.6,P4.7,P4.6。基于Proteus中的STC15W4K32S4编写的程序,实验要求:要求LED4(P2.7)、LED10(P4.6)、LED9(P4.7)、LED8(P1.6)、LED7(P1.7)按顺序每隔1S依次闪烁,按下SW17(接P3.2)时,灯停止闪烁。
本系列文章共有三篇,区别为按键和延时实现的方式(查询or中断)。文章发布从逻辑上由简至难,本篇为用查询实现的按键功能和用查询实现的延时功能。
二、电路原理图
三、程序源代码
#include
#include
#define uint unsigned int
#define uchar unsigned char
sbit LED4=P2^7;
sbit LED10=P4^6;
sbit LED9=P4^7;
sbit LED8=P1^6;
sbit LED7=P1^7;
sbit SW17=P3^2;
uint b=1;
uint flag=1;
void delayms(uint n)
{
while(n--);
}
void key()
{
if(SW17==0)
{
flag=SW17;
delayms(100);
if(SW17==0)
{
LED4=LED10=LED9=LED8=LED7=1;
b=0;
}
}
}
void delay() //1s延时
{
uchar i,j,k,m;
_nop_();
_nop_();
for(i=5;i>0;i--)
for(j=68;j>0;j--)
for(k=22;k>0;k--)
for(m=94;m>0;m--)
key();
}
void LED()
{
if(flag==1)
{
switch(b)
{
case 1: LED4=0;break;
case 2: LED4=1;LED10=0;break;
case 3: LED10=1;LED9=0;break;
case 4: LED9=1;LED8=0;break;
case 5: LED8=1;LED7=0;break;
default:LED7=1;LED4=0;b=1;break;
}
delay();
b++;
}
}
void main()
{
P0M0=0; //定义I/O口工作模式
P0M1=0;
P1M0=0;
P1M1=0;
P2M0=0;
P2M1=0;
P3M0=0;
P3M1=0;
P4M0=0;
P4M1=0;
while(1)
{
LED();
}
}
四、程序分析
1.头文件、宏定义
1.头文件、宏定义
本实验代码段未用到
#include#include #define uint unsigned int #define uchar unsigned char
2.管脚定义,变量声明
sbit LED4=P2^7;
sbit LED10=P4^6;
sbit LED9=P4^7;
sbit LED8=P1^6;
sbit LED7=P1^7;
sbit SW17=P3^2;
uint b=1;
uint flag=1;
3.按键防抖函数
因为本实验开发板用的为按钮式开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上就稳定地接通,在断开时也不会一下子彻底断开,而是在闭合和断开的瞬间伴随了一连串的抖动按键 。抖动时间是由按键的机械特性决定的,一般都会在10ms以下,为了确保程序对按键的一次闭合或者一次断开只响应 一次, 必须进行按键的消抖处理。
本实验使用软件消抖。软件消抖就是当检测到按键状态变化后,先等待一个10ms左右的延时,让抖动消失后再进行一次按键状态检测, 如果与刚才检测到的状态相同,就可以确认按键已经稳定地动作了。这个10ms的延时就是通过让单片机执行函数中的一段程序循环实现,接下来的1s延时也是同理。
void delayms(uint n)
{
while(n--);
}
4.开关函数
当运行到开关函数时,先用if查询一次开关状态,如果闭合(SW17==0),则清零标志位,并延时防抖,当抖动结束后再查询一次,如果闭合,则令所有灯置1(灭)。b=0意义为流水灯循环从头开始,因为本实验开关只起到了关的作用,无影响,可不写。
void key()
{
if(SW17==0)
{
flag=SW17;
delayms(100);
if(SW17==0)
{
LED4=LED10=LED9=LED8=LED7=1;
b=0;
}
}
}
5.延迟1s函数
本地方着重讲解1s实现原理,因为要求1s精确到0.999999,所以必须将c语言反汇编成汇编语言从而精确计算1s。本地方也是笔者实验中遇到的问题所在,经查阅资料慢慢解决。
转为汇编语言如下:
C:0x0120 00 NOP //两条空指令,每条空指令占1个系统时钟数,为了凑够刚好1s
C:0x0121 00 NOP
C:0x0122 790D MOV R1,#0x05 //这四条MOV指令每条占2个系统时钟数
C:0x0124 7D1D MOV R5,#0x44
C:0x0126 7B18 MOV R3,#0x16
C:0x0128 7A6E MOV R2,#0x5E
C:0x012A 1200E0 LCALL key(C:00E0) //子程序调用指令,占4个系统时钟数,请注意,调用子程序后,子程序中还有一条JB指令(5)和子程序返回RET指令(4),所以本行程序共占13个系统时钟数
C:0x012D DAFB DJNZ R2,C:012A //这四条DJNZ指令每条占4个系统时钟数
C:0x012F DBF7 DJNZ R3,C:0128
C:0x0131 DDF3 DJNZ R5,C:0126
C:0x0133 D9EF DJNZ R1,C:0124
C:0x0135 22 RET
内层循环:子程序调用+DJNZ*循环次数
二层循环:(内层循环+DJNZ+MOV)*循环次数
三层循环:[(内层循环+DJNZ+MOV)*循环次数+6]*循环次数
......
总系统时钟数为两条空指令加循环循体时钟数加上延时程序调用及返回时钟数
总系统时钟数=2+4+{[ ( ( (4+5+4)+4)*m+6)k+6]j+6}i+4=12,000,000
因为本实验单片机晶振为12MHz,所以时钟周期是 1/12M,则为1s。
void delay() //1s延时
{
uchar i,j,k,m;
_nop_();
_nop_();
for(i=5;i>0;i--)
for(j=68;j>0;j--)
for(k=22;k>0;k--)
for(m=94;m>0;m--)
key();
}
6.流水灯实现程序
flag==1判断按键是否有按下,如果按下flag在按键程序被清零,从而无法进入流水灯循环程序,实现流水灯真正的停止。
用Switch函数实现循环流水,b初值为1,先执行case 2,然后break,延时后加1,再次回到函数时执行case 3......以此类推
void LED()
{
if(flag==1)
{
switch(b)
{
case 1: LED4=0;break;
case 2: LED4=1;LED10=0;break;
case 3: LED10=1;LED9=0;break;
case 4: LED9=1;LED8=0;break;
case 5: LED8=1;LED7=0;break;
default:LED7=1;LED4=0;b=1;break;
}
delay();
b++;
}
}
7.主函数
因为使用的是15单片机,定义I/O工作模式为准双向口。while(1)使程序处于流水灯循环不停止。
void main()
{
P0M0=0; //定义I/O口工作模式
P0M1=0;
P1M0=0;
P1M1=0;
P2M0=0;
P2M1=0;
P3M0=0;
P3M1=0;
P4M0=0;
P4M1=0;
while(1)
{
LED();
}
}
至此,本实验讲解完毕,实验很简单,笔者也是刚入门,写的东西对于单片机有所经验的人来说有所赘余,但对刚接触单片机的同学肯定有所帮助!一次肝完的,有些地方也未深入讲解,不懂的同学可以留言。希望大家能点个赞!
下次文章为按键如何用中断实现,祝大家一切顺利!



