找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1395|回复: 0
打印 上一主题 下一主题
收起左侧

单片机单个按键状态机编程控制5种不同LED闪法程序与仿真

[复制链接]
跳转到指定楼层
楼主
LED流水灯堆叠设计,且按键不占用CPU时间,上电显示第1功能,单个按键的单击显示第2种功能,单按第5次返回第1功能,任意功能长按2S以上返回第2功能.程序注释全。附件包含程序源码和PROTUSE仿真图。

仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)


单片机源程序如下:
  1. /*****************************************************************************
  2. *名称:头文件,预定义。
  3. *功能:包含头文件,预定义,端口定义
  4. ******************************************************************************/
  5. #include<reg51.h>             //包含 51 单片机寄存器定义的头文件
  6. #define uchar unsigned char    //定义uchar可在函数中当unsign char使用,取值不超过255。
  7. #define uint unsigned int      //定义uint可在函数中当unsign int使用,取值不超过65535。
  8. sbit KEY=P1^0;             //定义按键KEY为P1.0
  9. #define LED P2            //定义LED为P2口
  10. #define KEY_STATE_0   0    //按键初始状态
  11. #define KEY_STATE_1   1    //按键消抖
  12. #define KEY_STATE_2   2    //按键按下功能种类是单击,双击还是长按
  13. #define KEY_STATE_3   3    //按键弹起

  14. #define SINGLE_KEY_TIME 3 //SINGLE_KEY_TIME*10MS = 30MS?判定单击的时间长度,软件消抖
  15. #define KEY_INTERVAL 30  //KEY_INTERVAL*10MS?= 300MS 判定双击的时间间隔
  16. #define LONG_KEY_TIME 200 //LONG_KEY_TIME*10MS? ?= 2S? ?判定长按的时间长度

  17. #define N_KEY   0   //按键没动作
  18. #define S_KEY   1   //单击
  19. #define L_KEY   2   //长按
  20. #define D_KEY   3  //双击
  21. uchar code Zuoyi[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};   //左流水数组,低电平导通
  22. uchar code Youyi[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};   //右流水数组,低电平导通
  23. uchar keySu=0;            // 定义按键功能状态变量
  24. uchar key_val;           // 按键值
  25. uchar time_10ms_ok=0;  //10MS定时标志
  26. //define LongCount 100; //延时长按变量100X10MS=1S
  27. /******************************************************************************
  28. ***函数名称: void delay_ms(uint x)
  29. ***函数功能: MS延时(按下按键时,可以跳出循环体)
  30. ***输入:     无
  31. ***输出:     无
  32. ******************************************************************************/
  33. /*******/
  34. void delay_ms(uint x)           // 延时函数,延时x MS,
  35. {
  36.       uint i;                //定义无符号字符型变量j值域为0-65536.
  37. uchar j;                          //定义无符号字符型变量i值域为0-255.
  38. if(KEY)              //如果按键为1时,则执行延时循环
  39. {
  40.             for(i=0;i<x;i++)  //  循环x遍   
  41. {
  42.         for(j=0;j<112;j++)       
  43.                  ;                //空语句等待一个机器周期
  44.              }
  45.       }
  46.       else               //如果按键按下则跳出循环  
  47.       return;
  48. }


  49. /******************************************************************************
  50. ***函数名称: void move_l(void)
  51. ***函数功能: 左流水(P2口低位向高位流动跑马)
  52. ***输入:     无
  53. ***输出:     无
  54. ******************************************************************************/
  55. /******/

  56. void move_l(void)                   //1.左流水函数
  57. {
  58. uchar b;                    //循环变量b
  59. while(1)                    //死循环
  60. {
  61.                   for(b=0;b<8;b++)     // 循环8遍           
  62. {
  63.                    LED=Zuoyi[b]; //调用左流水数组

  64.                    delay_ms(500);    //延时500MS
  65.                    if(KEY==0)        //如果有按键按下
  66.                          return;          //退出循环
  67.             }
  68.                   //if(KEY==0)            //此两语句可以不用
  69.                       //return;
  70.      }
  71. }
  72. /******************************************************************************
  73. ***函数名称: void move_r(void)
  74. ***函数功能: 右流水(P2口高位向低位流动跑马)
  75. ***输入:     无
  76. ***输出:     无
  77. ******************************************************************************/
  78. /******/
  79. void move_r(void)                     //2.右流水函数
  80. {
  81. uchar b;                      //循环变量b
  82. while(1)                     //无限循环
  83.   {
  84.               for(b=0;b<8;b++)      // 循环8遍  
  85. {
  86.              LED=Youyi[b];    //调用右流水数组
  87. delay_ms(500);      //延时500MS
  88.        if(KEY==0)          //如果有按键按下则退出循环
  89.                    return;            //退出循环
  90.         }
  91.              //if(KEY==0)              //此两语句可以不用
  92.                 //return;
  93. }
  94. }

  95. /******************************************************************************
  96. ***函数名称: void flash(void)
  97. ***函数功能: 闪烁
  98. ***输入:     无
  99. ***输出:     无
  100. ******************************************************************************/
  101. /******/

  102. void flash(void)           //3.闪烁
  103. {
  104.    while(1)            //无限循环
  105.    {
  106.      LED=0x00;      //亮
  107. delay_ms(500);   //延时500MS
  108.        LED=0xff;      //灭
  109. delay_ms(500);  //延时500MS(毫秒)

  110. if(KEY==0)     //如果有按键按下则退出循环
  111.               return;        //退出循环
  112.    }
  113. }

  114. /******************************************************************************
  115. ***函数名称: void Di_zeng1 (void)
  116. ***函数功能:.左递增右递减,,右递增左递减
  117. ***输入:     无
  118. ***输出:     无
  119. ******************************************************************************/
  120. /******/
  121. void Di_zeng1 (void)    //4.左递增右递减,,右递增左递减
  122. {
  123. while(1)
  124.         {
  125.       uchar m;     //循环变量
  126. uchar n=0;   //中间变量
  127. uchar n1=0;  //中间变量
  128. uchar n2=0;  //中间变量
  129. uchar n3=0;  //中间变量

  130.       for(m=0;m<8;m++)           //左递增,循环8位
  131.         {
  132.            n +=(0x01<<m);       //  1左移m位再赋值给n
  133.                          LED=~n;            //n取反后点亮LED灯
  134.            delay_ms(500);     //延时500毫秒
  135.            if(KEY==0)        //如果按键按下
  136.                  return;          //退出循环
  137.                   }
  138.   
  139.         for(m=0;m<8;m++)      //右递减,循环8位
  140.         {
  141.             n1 +=(0x80>>m);    // 1右移m位再赋值给n1
  142. LED=n1;           //n1取反后点亮LED灯
  143.             delay_ms(500);     //延时500毫秒

  144.             if(KEY==0)    //如果按键按下
  145.                   return;      //退出循环
  146. }
  147.                        

  148. for(m=0;m<8;m++)  //右递增
  149. {
  150.       n2 +=(0x80>>m);
  151.             LED=~n2;
  152.       delay_ms(500);
  153.       if(KEY==0)
  154.             return;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
  155.   }
  156. for(m=0;m<8;m++)     //左递减
  157. {
  158.      n3=n3+(0x01<<m);
  159.            LED=n3;
  160.      delay_ms(500);
  161.      if(KEY==0)
  162.            return;
  163.   }
  164.          //if(KEY==0)       //此两语句可以不用
  165.                   //return;
  166. }
  167. }

  168. /******************************************************************************
  169. ***函数名称: void Di_zeng2 (void)
  170. ***函数功能:左递增左递减,,右递增右递减
  171. ***输入:     无
  172. ***输出:     无
  173. ******************************************************************************/
  174. /******/
  175. void Di_zeng2 (void)    //5.左递增左递减,,右递增右递减
  176. {
  177. while(1)
  178.         {
  179.       uchar m,n=0;
  180.       uchar n1=0;
  181.                 uchar n2=0;
  182.       uchar n3=0;
  183.       for(m=0;m<8;m++)   //左递增
  184.         {
  185.            n=n+(0x01<<m);
  186.                          LED=~n;
  187.            delay_ms(500);
  188.            if(KEY==0)
  189.                  return;
  190.                   }
  191.   
  192.         for(m=0;m<8;m++)//左递减
  193.         {
  194.             n1 +=(0x01<<m);
  195. LED=n1;
  196.             delay_ms(500);
  197.             if(KEY==0)
  198.                   return;
  199.         }
  200.                        

  201. for(m=0;m<8;m++)  //右递增
  202. {
  203.       n2 +=(0x80>>m);
  204.             LED=~n2;
  205.        delay_ms(500);
  206.       if(KEY==0)
  207.             return;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
  208.   }
  209. for(m=0;m<8;m++)     //右递减
  210. {
  211.      n3=n3+(0x80>>m);
  212.            LED=n3;
  213.      delay_ms(500);
  214.      if(KEY==0)
  215.            return;
  216.   }
  217.        //if(KEY==0)       //此两语句可以不用
  218.                 //return;
  219. }
  220. }


  221. /******************************************************************************
  222. ***函数名称: uchar key_read(void)
  223. ***函数功能: 键盘扫描函数(按键状态机方式)
  224. ***输入:     无
  225. ***输出:     返回值。单击,长按,双击
  226. ******************************************************************************/
  227. /******/
  228. uchar key_read(void)
  229. {
  230. static uchar key_state = 0;    //按键初始状态
  231. static uint key_time =0;      //按键时间间隔计时变量
  232. uchar key_press;            //按键是按下还是抬起变量
  233. uchar key_return;           //按键函数返回
  234. key_return=N_KEY;         //清除返回按键值
  235. key_press=KEY;           //读取当前键值
  236. switch(key_state)
  237. {
  238. case KEY_STATE_0:  //按键初始状态0:判断有无按键按下
  239. if(!key_press)       //有按键按下
  240. {
  241. key_time=0;     //一次10ms,时间间隔计数器清0
  242. key_state=KEY_STATE_1;//然后进入转到按键确认态1
  243. }
  244. break;
  245. case KEY_STATE_1:
  246. //按键确认状态1:软件消抖(确定按键是否有效,而不是误触)
  247. //按键有效的定义:按键持续按下超过设定的消抖时间
  248. if(!key_press)      //按键仍然按下
  249. {
  250. key_time++;     //一次10ms,时间间隔变量加1
  251.              //消抖时间为SINGLE_KEY_TIME*10MS=30MS
  252. if(key_time>=SINGLE_KEY_TIME) //如果大于消抖时间              
  253. {
  254. key_state = KEY_STATE_2;//按键仍然处于按下状态
  255. }
  256. //如果按键时间超过消抖时间,即判定为按下的按键有效。
  257. //按键有效包括两种:单击或长按,继续判定为那种有效按键
  258. }
  259. else //按键时间没有超过,判定为误触,按键无效,返回状态0
  260. {
  261. key_state = KEY_STATE_0;   //返回初始状态
  262.             }
  263. break;          //跳出
  264.    case KEY_STATE_2:  //按键状态2:判定按键有效的种类:是单击,还是长按
  265. if(key_press)    //如果按键在设定的长按时间内释放,则判定为单击
  266. {
  267. key_time++; //一次10ms,时间间隔变量加1
  268. //按键弹起(高电平)后计时,计时大于双击30*10MS时间小于长按时间则为单击
  269. if((key_time >=KEY_INTERVAL)&&( key_time < LONG_KEY_TIME))
  270. {
  271. key_return = S_KEY;         //返回有效按键值: 单击
  272. key_time=0;                 //时间间隔变量清0
  273. key_state = KEY_STATE_0;   //返回按键状态0继续等待按键
  274.              }
  275.                  else //否则在此时间段为低电平则为双击
  276.                  {
  277. key_return = D_KEY;         //返回有效按键值: 双击
  278. key_time=0;               //时间间隔变量清0
  279. key_state = KEY_STATE_0;   //返回按键状态0继续等待按键
  280. }
  281.               }
  282. else    // 如果一直按下,则计算按下的时间
  283. {
  284. key_time++;
  285. //如果按键继续按下时间超过设定的长按时间
  286. //(LONG_KEY_TIME*10ms=300*10ms=3000ms)则判定为长按

  287. if(key_time >= LONG_KEY_TIME)  
  288. {
  289. key_return = L_KEY;         //返回有效键值:长按
  290. key_time=0;                //时间间隔变量清0
  291. key_state = KEY_STATE_3;   //去状态3,等待按键释放
  292. }                              
  293. }
  294. break;
  295. case KEY_STATE_3:    //状态3为长按按键释放抬起
  296. if(key_press)  //如果按键为1,释放状态         
  297. {
  298. key_state = KEY_STATE_0; //按键弹起,进行下一次按键的判定
  299. }
  300. break;
  301. //特殊情况:key_state 是其他值的情况,清0key_state ,这种情况一般出现
  302. //在没有初始key_state,第一次执行这个函数的时候
  303. default: key_state = KEY_STATE_0;
  304. break;

  305. }
  306. return key_return; //返回按键值
  307. }


  308. /******************************************************************************
  309. ***函数名称: void Timer0Init()   
  310. ***函数功能: 定时器0初始化
  311. ***输入:     无
  312. ***输出:     无
  313. ******************************************************************************/
  314. /******/
  315. void Timer0Init()                  //10毫秒@12.00MHZ
  316. {
  317.       TMOD=0x01;            //设置定时器工作方式1
  318.       TH0=(65536-10000)/256;
  319.       TL0=(65536-10000)%256; //定时器0预设初值10000US=10MS
  320.       TR0=0;             //关闭定时器0,当按键按下才开启定时器0,计算按下的时长
  321.       EA=1;             //开总中断
  322.       ET0=1;            //允许定时器0中断
  323.       TR0=1;           //启动定时器0
  324. }


  325. /******************************************************************************
  326. ***函数名称: void Timer0_Isr() interrupt 1
  327. ***函数功能: 定时器0中断函数
  328. ***输入:     无
  329. ***输出:     无
  330. ******************************************************************************/
  331. /******/
  332. void Timer0_Isr() interrupt 1    //定时器0中断服务函数
  333. {
  334.      TH0=(65536-10000)/256;    //预设初值50MS
  335.      TL0=(65536-10000)%256;   //预设初值50MS
  336.      time_10ms_ok=1;         //10毫秒到标志
  337. }


  338. /**********主程序*******/
  339. void main(void)
  340. {
  341. KEY=0x01;              //P1.0作为输入口前必须输出高电平
  342. Timer0Init();           //调用定时器0初始化函数
  343. while(1)
  344. {
  345.          if(time_10ms_ok)            //10MS定时时间到
  346.          {
  347.               time_10ms_ok=0;      //赋值0,10MS查询一次按键状态
  348. key_val=key_read();    //读取按键的返回值
  349. if(key_val==S_KEY)     //如果是单击
  350. {
  351.           keySu++;       //变量按键数加1
  352.           if(keySu==5)    //如果加到5
  353.           keySu=0;       //重新赋值0
  354. }
  355.       if(key_val==L_KEY)     //如果是长按
  356. {
  357.           keySu=1;       //按键数为1
  358. }
  359.         if(key_val==D_KEY)   //如果是双击
  360. {
  361.              keySu=2;      //按键数为2
  362. }

  363. switch(keySu)   //5路分支
  364. {
  365.         case 0: move_l();break;     //当按键数为0,执行左流水功能
  366. case 1: move_r();break;    //当按键数为1,执行右流水功能
  367. case 2: flash(); break;     //当按键数为2,执行闪烁功能
  368. case 3: Di_zeng1(); break; //当按键数为3执行左递增右递减,右递增左递减功能
  369. case 4: Di_zeng2(); break;//当按键数为4执行左递增左递减,右递增右递减功能
  370.         default : break;        //默认跳出
  371. }
  372. }
  373. }
  374. }
复制代码

Keil代码与Proteus8.8仿真下载:
单个按键状态机编程控制5种功能.7z (75.63 KB, 下载次数: 38)


评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏6 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表