LED流水灯堆叠设计,且按键不占用CPU时间,上电显示第1功能,单个按键的单击显示第2种功能,单按第5次返回第1功能,任意功能长按2S以上返回第2功能.程序注释全。附件包含程序源码和PROTUSE仿真图。
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
单片机源程序如下:
- /*****************************************************************************
- *名称:头文件,预定义。
- *功能:包含头文件,预定义,端口定义
- ******************************************************************************/
- #include<reg51.h> //包含 51 单片机寄存器定义的头文件
- #define uchar unsigned char //定义uchar可在函数中当unsign char使用,取值不超过255。
- #define uint unsigned int //定义uint可在函数中当unsign int使用,取值不超过65535。
- sbit KEY=P1^0; //定义按键KEY为P1.0
- #define LED P2 //定义LED为P2口
- #define KEY_STATE_0 0 //按键初始状态
- #define KEY_STATE_1 1 //按键消抖
- #define KEY_STATE_2 2 //按键按下功能种类是单击,双击还是长按
- #define KEY_STATE_3 3 //按键弹起
- #define SINGLE_KEY_TIME 3 //SINGLE_KEY_TIME*10MS = 30MS?判定单击的时间长度,软件消抖
- #define KEY_INTERVAL 30 //KEY_INTERVAL*10MS?= 300MS 判定双击的时间间隔
- #define LONG_KEY_TIME 200 //LONG_KEY_TIME*10MS? ?= 2S? ?判定长按的时间长度
- #define N_KEY 0 //按键没动作
- #define S_KEY 1 //单击
- #define L_KEY 2 //长按
- #define D_KEY 3 //双击
- uchar code Zuoyi[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; //左流水数组,低电平导通
- uchar code Youyi[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe}; //右流水数组,低电平导通
- uchar keySu=0; // 定义按键功能状态变量
- uchar key_val; // 按键值
- uchar time_10ms_ok=0; //10MS定时标志
- //define LongCount 100; //延时长按变量100X10MS=1S
- /******************************************************************************
- ***函数名称: void delay_ms(uint x)
- ***函数功能: MS延时(按下按键时,可以跳出循环体)
- ***输入: 无
- ***输出: 无
- ******************************************************************************/
- /*******/
- void delay_ms(uint x) // 延时函数,延时x MS,
- {
- uint i; //定义无符号字符型变量j值域为0-65536.
- uchar j; //定义无符号字符型变量i值域为0-255.
- if(KEY) //如果按键为1时,则执行延时循环
- {
- for(i=0;i<x;i++) // 循环x遍
- {
- for(j=0;j<112;j++)
- ; //空语句等待一个机器周期
- }
- }
- else //如果按键按下则跳出循环
- return;
- }
- /******************************************************************************
- ***函数名称: void move_l(void)
- ***函数功能: 左流水(P2口低位向高位流动跑马)
- ***输入: 无
- ***输出: 无
- ******************************************************************************/
- /******/
- void move_l(void) //1.左流水函数
- {
- uchar b; //循环变量b
- while(1) //死循环
- {
- for(b=0;b<8;b++) // 循环8遍
- {
- LED=Zuoyi[b]; //调用左流水数组
- delay_ms(500); //延时500MS
- if(KEY==0) //如果有按键按下
- return; //退出循环
- }
- //if(KEY==0) //此两语句可以不用
- //return;
- }
- }
- /******************************************************************************
- ***函数名称: void move_r(void)
- ***函数功能: 右流水(P2口高位向低位流动跑马)
- ***输入: 无
- ***输出: 无
- ******************************************************************************/
- /******/
- void move_r(void) //2.右流水函数
- {
- uchar b; //循环变量b
- while(1) //无限循环
- {
- for(b=0;b<8;b++) // 循环8遍
- {
- LED=Youyi[b]; //调用右流水数组
- delay_ms(500); //延时500MS
- if(KEY==0) //如果有按键按下则退出循环
- return; //退出循环
- }
- //if(KEY==0) //此两语句可以不用
- //return;
- }
- }
- /******************************************************************************
- ***函数名称: void flash(void)
- ***函数功能: 闪烁
- ***输入: 无
- ***输出: 无
- ******************************************************************************/
- /******/
- void flash(void) //3.闪烁
- {
- while(1) //无限循环
- {
- LED=0x00; //亮
- delay_ms(500); //延时500MS
- LED=0xff; //灭
- delay_ms(500); //延时500MS(毫秒)
- if(KEY==0) //如果有按键按下则退出循环
- return; //退出循环
- }
- }
- /******************************************************************************
- ***函数名称: void Di_zeng1 (void)
- ***函数功能:.左递增右递减,,右递增左递减
- ***输入: 无
- ***输出: 无
- ******************************************************************************/
- /******/
- void Di_zeng1 (void) //4.左递增右递减,,右递增左递减
- {
- while(1)
- {
- uchar m; //循环变量
- uchar n=0; //中间变量
- uchar n1=0; //中间变量
- uchar n2=0; //中间变量
- uchar n3=0; //中间变量
- for(m=0;m<8;m++) //左递增,循环8位
- {
- n +=(0x01<<m); // 1左移m位再赋值给n
- LED=~n; //n取反后点亮LED灯
- delay_ms(500); //延时500毫秒
- if(KEY==0) //如果按键按下
- return; //退出循环
- }
-
- for(m=0;m<8;m++) //右递减,循环8位
- {
- n1 +=(0x80>>m); // 1右移m位再赋值给n1
- LED=n1; //n1取反后点亮LED灯
- delay_ms(500); //延时500毫秒
- if(KEY==0) //如果按键按下
- return; //退出循环
- }
-
- for(m=0;m<8;m++) //右递增
- {
- n2 +=(0x80>>m);
- LED=~n2;
- delay_ms(500);
- if(KEY==0)
- return;
- }
- for(m=0;m<8;m++) //左递减
- {
- n3=n3+(0x01<<m);
- LED=n3;
- delay_ms(500);
- if(KEY==0)
- return;
- }
- //if(KEY==0) //此两语句可以不用
- //return;
- }
- }
- /******************************************************************************
- ***函数名称: void Di_zeng2 (void)
- ***函数功能:左递增左递减,,右递增右递减
- ***输入: 无
- ***输出: 无
- ******************************************************************************/
- /******/
- void Di_zeng2 (void) //5.左递增左递减,,右递增右递减
- {
- while(1)
- {
- uchar m,n=0;
- uchar n1=0;
- uchar n2=0;
- uchar n3=0;
- for(m=0;m<8;m++) //左递增
- {
- n=n+(0x01<<m);
- LED=~n;
- delay_ms(500);
- if(KEY==0)
- return;
- }
-
- for(m=0;m<8;m++)//左递减
- {
- n1 +=(0x01<<m);
- LED=n1;
- delay_ms(500);
- if(KEY==0)
- return;
- }
-
- for(m=0;m<8;m++) //右递增
- {
- n2 +=(0x80>>m);
- LED=~n2;
- delay_ms(500);
- if(KEY==0)
- return;
- }
- for(m=0;m<8;m++) //右递减
- {
- n3=n3+(0x80>>m);
- LED=n3;
- delay_ms(500);
- if(KEY==0)
- return;
- }
- //if(KEY==0) //此两语句可以不用
- //return;
- }
- }
- /******************************************************************************
- ***函数名称: uchar key_read(void)
- ***函数功能: 键盘扫描函数(按键状态机方式)
- ***输入: 无
- ***输出: 返回值。单击,长按,双击
- ******************************************************************************/
- /******/
- uchar key_read(void)
- {
- static uchar key_state = 0; //按键初始状态
- static uint key_time =0; //按键时间间隔计时变量
- uchar key_press; //按键是按下还是抬起变量
- uchar key_return; //按键函数返回
- key_return=N_KEY; //清除返回按键值
- key_press=KEY; //读取当前键值
- switch(key_state)
- {
- case KEY_STATE_0: //按键初始状态0:判断有无按键按下
- if(!key_press) //有按键按下
- {
- key_time=0; //一次10ms,时间间隔计数器清0
- key_state=KEY_STATE_1;//然后进入转到按键确认态1
- }
- break;
- case KEY_STATE_1:
- //按键确认状态1:软件消抖(确定按键是否有效,而不是误触)
- //按键有效的定义:按键持续按下超过设定的消抖时间
- if(!key_press) //按键仍然按下
- {
- key_time++; //一次10ms,时间间隔变量加1
- //消抖时间为SINGLE_KEY_TIME*10MS=30MS
- if(key_time>=SINGLE_KEY_TIME) //如果大于消抖时间
- {
- key_state = KEY_STATE_2;//按键仍然处于按下状态
- }
- //如果按键时间超过消抖时间,即判定为按下的按键有效。
- //按键有效包括两种:单击或长按,继续判定为那种有效按键
- }
- else //按键时间没有超过,判定为误触,按键无效,返回状态0
- {
- key_state = KEY_STATE_0; //返回初始状态
- }
- break; //跳出
- case KEY_STATE_2: //按键状态2:判定按键有效的种类:是单击,还是长按
- if(key_press) //如果按键在设定的长按时间内释放,则判定为单击
- {
- key_time++; //一次10ms,时间间隔变量加1
- //按键弹起(高电平)后计时,计时大于双击30*10MS时间小于长按时间则为单击
- if((key_time >=KEY_INTERVAL)&&( key_time < LONG_KEY_TIME))
- {
- key_return = S_KEY; //返回有效按键值: 单击
- key_time=0; //时间间隔变量清0
- key_state = KEY_STATE_0; //返回按键状态0继续等待按键
- }
- else //否则在此时间段为低电平则为双击
- {
- key_return = D_KEY; //返回有效按键值: 双击
- key_time=0; //时间间隔变量清0
- key_state = KEY_STATE_0; //返回按键状态0继续等待按键
- }
- }
- else // 如果一直按下,则计算按下的时间
- {
- key_time++;
- //如果按键继续按下时间超过设定的长按时间
- //(LONG_KEY_TIME*10ms=300*10ms=3000ms)则判定为长按
- if(key_time >= LONG_KEY_TIME)
- {
- key_return = L_KEY; //返回有效键值:长按
- key_time=0; //时间间隔变量清0
- key_state = KEY_STATE_3; //去状态3,等待按键释放
- }
- }
- break;
- case KEY_STATE_3: //状态3为长按按键释放抬起
- if(key_press) //如果按键为1,释放状态
- {
- key_state = KEY_STATE_0; //按键弹起,进行下一次按键的判定
- }
- break;
- //特殊情况:key_state 是其他值的情况,清0key_state ,这种情况一般出现
- //在没有初始key_state,第一次执行这个函数的时候
- default: key_state = KEY_STATE_0;
- break;
- }
- return key_return; //返回按键值
- }
- /******************************************************************************
- ***函数名称: void Timer0Init()
- ***函数功能: 定时器0初始化
- ***输入: 无
- ***输出: 无
- ******************************************************************************/
- /******/
- void Timer0Init() //10毫秒@12.00MHZ
- {
- TMOD=0x01; //设置定时器工作方式1
- TH0=(65536-10000)/256;
- TL0=(65536-10000)%256; //定时器0预设初值10000US=10MS
- TR0=0; //关闭定时器0,当按键按下才开启定时器0,计算按下的时长
- EA=1; //开总中断
- ET0=1; //允许定时器0中断
- TR0=1; //启动定时器0
- }
- /******************************************************************************
- ***函数名称: void Timer0_Isr() interrupt 1
- ***函数功能: 定时器0中断函数
- ***输入: 无
- ***输出: 无
- ******************************************************************************/
- /******/
- void Timer0_Isr() interrupt 1 //定时器0中断服务函数
- {
- TH0=(65536-10000)/256; //预设初值50MS
- TL0=(65536-10000)%256; //预设初值50MS
- time_10ms_ok=1; //10毫秒到标志
- }
- /**********主程序*******/
- void main(void)
- {
- KEY=0x01; //P1.0作为输入口前必须输出高电平
- Timer0Init(); //调用定时器0初始化函数
- while(1)
- {
- if(time_10ms_ok) //10MS定时时间到
- {
- time_10ms_ok=0; //赋值0,10MS查询一次按键状态
- key_val=key_read(); //读取按键的返回值
- if(key_val==S_KEY) //如果是单击
- {
- keySu++; //变量按键数加1
- if(keySu==5) //如果加到5
- keySu=0; //重新赋值0
- }
- if(key_val==L_KEY) //如果是长按
- {
- keySu=1; //按键数为1
- }
- if(key_val==D_KEY) //如果是双击
- {
- keySu=2; //按键数为2
- }
- switch(keySu) //5路分支
- {
- case 0: move_l();break; //当按键数为0,执行左流水功能
- case 1: move_r();break; //当按键数为1,执行右流水功能
- case 2: flash(); break; //当按键数为2,执行闪烁功能
- case 3: Di_zeng1(); break; //当按键数为3执行左递增右递减,右递增左递减功能
- case 4: Di_zeng2(); break;//当按键数为4执行左递增左递减,右递增右递减功能
- default : break; //默认跳出
- }
- }
- }
- }
复制代码
Keil代码与Proteus8.8仿真下载:
单个按键状态机编程控制5种功能.7z
(75.63 KB, 下载次数: 38)
|