找回密码
 立即注册

QQ登录

只需一步,快速开始

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

51单片机学习:基于状态机的按键对时程序(短按,长按,连发)

[复制链接]
跳转到指定楼层
楼主
之前的电子钟程序中,用的按键消抖处理方法是10ms的延时,这种方法效率比较低
所以现在利用状态机原理重写一下,效率很高啊

4个独立按键中用到3个,
keys5用于切换对时分秒等状态,keys2是减小数值,keys3是增加数值

同时可以判断按键的"短按,长按,连发"等功能
小于2秒视为短按,
大于2秒视为长按,
在长按状态下每0.2秒自动连发一次, 这样对时的时候就不用按N次了


程序分很多个文件 ,Keil uVision4 打包





  1. #include "keyScan.h"
  2. #include <reg52.h>

  3. /*按键初始化,若io没有复用的话可以省略此步骤
  4. void KeyInit(void)
  5. {
  6.     keyS2 = 1 ;
  7.     keyS3 = 1 ;
  8.     keyS4 = 1 ;
  9.     keyS5 = 1 ;
  10.         //即P3|=0xf0;            
  11. }*/

  12. static u8 getKey(void)                    //获取P3口值
  13. {
  14.     if(key_down == keyS2)
  15.         {
  16.                 return KEYS2_VALUE ;
  17.         }

  18.     if(key_down == keyS3 )
  19.         {
  20.           return KEYS3_VALUE ;
  21.         }

  22.     if(key_down == keyS4 )
  23.         {
  24.                 return KEYS4_VALUE ;
  25.         }
  26.          
  27.     if(key_down == keyS5 )
  28.         {
  29.                 return KEYS5_VALUE ;
  30.         }

  31.     return key_up ;    //0xf0  没有任何按键
  32. }

  33. //函数每10ms被调用一次,而我们弹性按键过程时一般都20ms以上
  34. //所以每次按键至少调用本函数2次
  35. u8 read_key(u8* pKeyValue)                          
  36. {
  37.     static u8  s_u8keyState=0;        //未按,普通短按,长按,连发等状态
  38.     static u16 s_u16keyTimeCounts=0;  //在计时状态的计数器
  39.         static u8  s_u8LastKey = key_up ; //保存按键释放时的P3口数据

  40.     u8 keyTemp=0;                          //键对应io口的电平
  41.     s8 key_return=0;                //函数返回值
  42.     keyTemp=key_up & getKey();  //提取所有的key对应的io口

  43.     switch(s_u8keyState)           //这里检测到的是先前的状态
  44.     {
  45.         case state_keyUp:   //如果先前是初始态,即无动作
  46.         {
  47.             if(key_up!=keyTemp) //如果键被按下
  48.             {
  49.                 s_u8keyState=state_keyDown; //更新键的状态,普通被按下
  50.             }
  51.         }
  52.         break;
  53.         
  54.         case state_keyDown: //如果先前是被按着的
  55.         {
  56.             if(key_up!=keyTemp) //如果现在还被按着
  57.             {
  58.                 s_u8keyState=state_keyTime; //转换到计时态
  59.                 s_u16keyTimeCounts=0;
  60.                                 s_u8LastKey = keyTemp;     //保存键值
  61.             }
  62.             else
  63.             {
  64.                 s_u8keyState=state_keyUp; //键没被按着,回初始态,说明是干扰
  65.             }
  66.         }
  67.         break;
  68.         
  69.         case state_keyTime:  //如果先前已经转换到计时态(值为3)
  70.         {  //如果真的是手动按键,必然进入本代码块,并且会多次进入
  71.             if(key_up==keyTemp) //如果未按键
  72.             {
  73.                 s_u8keyState=state_keyUp;
  74.                 key_return=return_keyPressed;    //返回1,一次完整的普通按键
  75.                 //程序进入这个语句块,说明已经有2次以上10ms的中断,等于已经消抖
  76.                 //那么此时检测到按键被释放,说明是一次普通短按
  77.             }
  78.             else  //在计时态,检测到键还被按着
  79.             {
  80.                 if(++s_u16keyTimeCounts>key_longTimes) //时间达到2秒
  81.                 {
  82.                     s_u8keyState=state_keyLong;  //进入长按状态
  83.                     s_u16keyTimeCounts=0;                  //计数器清空,便于进入连发重新计数
  84.                     key_return=return_keyLong;   //返回state_keyLong
  85.                 }
  86.                 //代码中,在2秒内如果我们一直按着key的话,返回值只会是0,不会识别为短按或长按的
  87.             }
  88.         }
  89.         break;
  90.         
  91.         case state_keyLong:  //在长按状态检测连发  ,每0.2秒发一次
  92.         {
  93.             if(key_up==keyTemp)
  94.             {
  95.                s_u8keyState=state_keyUp;
  96.             }
  97.             else //按键时间超过2秒时
  98.             {
  99.                 if(++s_u16keyTimeCounts>key_autoTimes)//10*20=200ms
  100.                 {
  101.                     s_u16keyTimeCounts=0;
  102.                     key_return=return_keyAuto;  //每0.2秒返回值的第2位置位(1<<2)
  103.                 }//连发的时候,肯定也伴随着长按
  104.             }
  105.             key_return |= return_keyLong;  //0x02是肯定的,0x04|0x02是可能的
  106.         }
  107.         break;
  108.         
  109.         default:
  110.         break;
  111.     }
  112.         *pKeyValue = s_u8LastKey ; //返回键值
  113.     return key_return;
  114. }
复制代码

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

使用道具 举报

沙发
ID:161747 发表于 2017-1-16 08:15 | 只看该作者
没看到打包文件啊。
回复

使用道具 举报

板凳
ID:976756 发表于 2021-11-1 18:02 | 只看该作者
没有看到打包文件
回复

使用道具 举报

地板
ID:954686 发表于 2021-11-2 08:04 | 只看该作者
方法不错,状态机,没有死等延时
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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