新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > MCU实战经验:多种的按键处理

MCU实战经验:多种的按键处理

作者:时间:2016-06-30来源:网络收藏

按键通常有:IO口按键(BUTTON),AD按键(通过AD采样电压),IR(遥控器)
按按键功能分:有短按键,长按键,连续按键。打个比方,遥控电视机,按一下音量键,音量增加1,这个就是短按键。按住音量键不放,音量连续加,这个就是连续按键。按住一个按键5s,系统会复位,这个是长按键。

本文引用地址:http://www.eepw.com.cn/article/201606/293391.htm

1、IO口按键,就是我们比较常见的一个IO接一个按键,或者是一个矩阵键盘。很多新人的处理方法可能是采样延时的方法,当年我也是这样的,如下

  1. if(GETIO==low)  

  2.  {   

  3.    delay_10ms()  

  4. if(GETIO==low)  

  5.    {  

  6. //得到按键值

  7.    }  

  8.  }  

 

这种方法虽然简单,但是有很大弊端。首先 Delay浪费很多时间,影响系统。第二,无法判断长短按键,连续按键。第三,如果这个按键是开关机按键系统在低功耗状态下,需要中断唤醒,这种方法比较容易出问题,如STM8S系列的 halt 模式。

所以我们一般在产品开发的过程中,采用扫描的方法,就是每隔10ms 去检测IO的状态,看是否有按键,然后去抖动,判断按键功能。参考代码如下,这段代码是之前在一个论坛看到的比我自己写的更加优秀,所以拿出来和大家分享一下,也顺便感谢一下作者。这段代码,容易修改,可以根据自己的时间需要,进行长短按键,连续按键,还有组合按键的判断。

  1. /* 按键滤波时间50ms, 单位10ms

  2.  *只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件

  3.  */

  4. #define BUTTON_FILTER_TIME         5

  5. #define BUTTON_LONG_TIME         300                /* 持续1秒,认为长按事件 */

  6. /*

  7.         每个按键对应1个全局的结构体变量。

  8.         其成员变量是实现滤波和多种按键状态所必须的

  9. */

  10. typedefstruct

  11. {  

  12. /* 下面是一个函数指针,指向判断按键手否按下的函数 */

  13.         unsigned char  (*IsKeyDownFunc)(void); /* 按键按下的判断函数,1表示按下 */

  14.         unsigned char  Count;                        /* 滤波器计数器 */

  15.         unsigned char  FilterTime;                /* 滤波时间(最大255,表示2550ms) */

  16.         unsigned short LongCount;                /* 长按计数器 */

  17.         unsigned short LongTime;                /* 按键按下持续时间, 0表示不检测长按 */

  18.         unsigned char   State;                        /* 按键当前状态(按下还是弹起) */

  19.         unsigned char  KeyCodeUp;                /* 按键弹起的键值代码, 0表示不检测按键弹起 */

  20.         unsigned char  KeyCodeDown;        /* 按键按下的键值代码, 0表示不检测按键按下 */

  21.         unsigned char  KeyCodeLong;        /* 按键长按的键值代码, 0表示不检测长按 */

  22.         unsigned char  RepeatSpeed;        /* 连续按键周期 */

  23.         unsigned char  RepeatCount;        /* 连续按键计数器 */

  24. }BUTTON_T;  

  25. typedefenum

  26. {  

  27.         KEY_NONE = 0,                        /* 0 表示按键事件 */

  28.         KEY_DOWN_Power,                        /* 按键键按下 */

  29.         KEY_UP_Power,                        /* 按键键弹起 */

  30.         KEY_LONG_Power,                        /* 按键键长按 */

  31.         KEY_DOWN_Power_TAMPER        /* 组合键,Power键和WAKEUP键同时按下 */

  32. }KEY_ENUM;  

  33. BUTTON_T s_Powerkey;                  

  34. //是否有按键按下接口函数

  35. unsigned char  IsKeyDownUser(void)                   

  36. {if (0==GPIO_ReadInputPin(POWER_KEY_PORT, POWER_KEY_PIN) ) return 1;return 0;}  

  37. void  PanakeyHard_Init(void)  

  38. {  

  39.    GPIO_Init (POWER_KEY_PORT, POWER_KEY_PIN, GPIO_MODE_IN_FL_NO_IT);//power key

  40. }  

  41. void  PanakeyVar_Init(void)  

  42. {  

  43. /* 初始化USER按键变量,支持按下、弹起、长按 */

  44.         s_Powerkey.IsKeyDownFunc = IsKeyDownUser;                /* 判断按键按下的函数 */

  45.         s_Powerkey.FilterTime = BUTTON_FILTER_TIME;                /* 按键滤波时间 */

  46.         s_Powerkey.LongTime = BUTTON_LONG_TIME;                        /* 长按时间 */

  47.         s_Powerkey.Count = s_Powerkey.FilterTime / 2;                /* 计数器设置为滤波时间的一半 */

  48.         s_Powerkey.State = 0;                                                        /* 按键缺省状态,0为未按下 */

  49.         s_Powerkey.KeyCodeDown = KEY_DOWN_Power;                        /* 按键按下的键值代码 */

  50.         s_Powerkey.KeyCodeUp =KEY_UP_Power;                                /* 按键弹起的键值代码 */

  51.         s_Powerkey.KeyCodeLong = KEY_LONG_Power;                        /* 按键被持续按下的键值代码 */

  52.         s_Powerkey.RepeatSpeed = 0;                                                /* 按键连发的速度,0表示不支持连发 */

  53.         s_Powerkey.RepeatCount = 0;                                                /* 连发计数器 */

  54. }  

  55. void Panakey_Init(void)  

  56. {  

  57.         PanakeyHard_Init();                /* 初始化按键变量 */

  58.         PanakeyVar_Init();                /* 初始化按键硬件 */

  59. }  

  60. /*

  61. *********************************************************************************************************

  62. *        函 数 名: bsp_DetectButton

  63. *        功能说明: 检测一个按键。非阻塞状态,必须被周期性的调用。

  64. *        形    参:按键结构变量指针

  65. *        返 回 值: 无

  66. *********************************************************************************************************

  67. */

  68. void Button_Detect(BUTTON_T *_pBtn)  

  69. {  

  70. if (_pBtn->IsKeyDownFunc())  

  71.         {  

  72. if (_pBtn->Count < _pBtn->FilterTime)  

  73.                 {  

  74.                         _pBtn->Count = _pBtn->FilterTime;  

  75.                 }  

  76. elseif(_pBtn->Count < 2 * _pBtn->FilterTime)  

  77.                 {  

  78.                         _pBtn->Count++;  

  79.                 }  

  80. else

  81.                 {  

  82. if (_pBtn->State == 0)  

  83.                         {  

  84.                                 _pBtn->State = 1;  

  85. /* 发送按钮按下的消息 */

  86. if (_pBtn->KeyCodeDown > 0)  

  87.                                 {  

  88. /* 键值放入按键FIFO */

  89.                                         Pannelkey_Put(_pBtn->KeyCodeDown);// 记录按键按下标志,等待释放

  90.                                 }  

  91.                         }  

  92. if (_pBtn->LongTime > 0)  

  93.                         {  

  94. if (_pBtn->LongCount < _pBtn->LongTime)  

  95.                                 {  

  96. /* 发送按钮持续按下的消息 */

  97. if (++_pBtn->LongCount == _pBtn->LongTime)  

  98.                                         {  

  99. /* 键值放入按键FIFO */

  100.                                                 Pannelkey_Put(_pBtn->KeyCodeLong);          

  101.                                         }  

  102.                                 }  

  103. else

  104.                                 {  

  105. if (_pBtn->RepeatSpeed > 0)  

  106.                                         {  

  107. if (++_pBtn->RepeatCount >= _pBtn->RepeatSpeed)  

  108.                                                 {  

  109.                                                         _pBtn->RepeatCount = 0;  

  110. /* 常按键后,每隔10ms发送1个按键 */

  111.                                                         Pannelkey_Put(_pBtn->KeyCodeDown);          

  112.                                                 }  

  113.                                         }  

  114.                                 }  

  115.                         }  

  116.                 }  

  117.         }  

  118. else

  119.         {  

  120. if(_pBtn->Count > _pBtn->FilterTime)  

  121.                 {  

  122.                         _pBtn->Count = _pBtn->FilterTime;  

  123.                 }  

  124. elseif(_pBtn->Count != 0)  

  125.                 {  

  126.                         _pBtn->Count--;  

  127.                 }  

  128. else

  129.                 {  

  130. if (_pBtn->State == 1)  

  131.                         {  

  132.                                 _pBtn->State = 0;  

  133. /* 发送按钮弹起的消息 */

  134. if (_pBtn->KeyCodeUp > 0) /*按键释放*/

  135.                                 {  

  136. /* 键值放入按键FIFO */

  137.                                 Pannelkey_Put(_pBtn->KeyCodeUp);          

  138.                                 }  

  139.                         }  

  140.                 }  

  141.                 _pBtn->LongCount = 0;  

  142.                 _pBtn->RepeatCount = 0;  

  143.         }  

  144. }  

  145. //功能说明: 检测所有按键。10MS 调用一次

  146. void Pannelkey_Polling(void)  

  147. {  

  148.         Button_Detect(&s_Powerkey);                /* USER 键 */

  149. }  

  150. void Pannelkey_Put(void)  

  151. {  

  152. // 定义一个队列 放入按键值        

  153. }  


2、遥控器按键,遥控器解码的一般就有两种:脉宽调制和脉冲调制,这里就不细讲解码的过程了。这里详细解码之后,如何处理遥控器按键实现遥控器按键的长短按功能和连续按键功能。代码裁剪过,大家根据实际需要改动。其实AD按键,通过AD采样获得按键值之后,可以采取如下面的一样方法处理,提一个函数接口即可

 

  1. typedefstruct

  2. {  

  3.   unsigned char count;//

  4.   unsigned char LongkeyFlag;/*是否长按键,1代表是*/

  5.   unsigned char  PreKeyValue;/*按键值的一个备份,用于释放按键值*/

  6. }ScanKeyDef;  

  7. #define SHORT_PRESS_TIME_IR                16 // 10ms 

  8. #define SERIES_PRESS_TIME_IR            10  

  9. #define LONG_PRESS_TIME_IR                    22

  10. #define KEY_RELEASE_TIME_OUT_IR     12  // 10ms 

  11. //提供5个接口函数,如下。 

  12. unsigned char get_irkey(void);  

  13. unsigned char ISSeriesKey(unsigned char temp);//按键是否需要做连续按键

  14. unsigned char changeSeriesKey(unsigned char temp);//转换连续按键值

  15. unsigned char ISLongKey(unsigned char temp);//按键是否需要做连续按键

  16. unsigned char changeToLongKey(unsigned char temp);//转换连续按键值

  17. unsigned char KeyScan(void)   

  18. {  

  19.     unsigned char  KeyValue = KEY_NONE,  

  20.                                 KeyValueTemp = KEY_NONE;  

  21. static   unsigned char KeyReleaseTimeCount =0;  

  22.     KeyValueTemp = get_irkey();  

  23. if(KeyValueTemp != KEY_NONE)  

  24.     {  

  25.         ScanKeyDef.count++;  

  26.         KeyReleaseTimeCount =0;  

  27. if(ScanKeyDef.count < LONG_PRESS_TIME_IR )  

  28.         {  

  29.             ScanKeyDef.LongkeyFlag = 0;  

  30. if((ScanKeyDef.count == SERIES_PRESS_TIME_IR) && ISSeriesKey(KeyValueTemp)) //处理连续按键

  31.                 {  

  32.                     KeyValue = changeSeriesKey ( KeyValueTemp );  

  33.                     ScanKeyDef.PreKeyValue = KEY_NONE;  

  34.                 }  

  35. elseif ( ScanKeyDef.count  < SHORT_PRESS_TIME_IR )  

  36.             {  

  37.                 ScanKeyDef.PreKeyValue = KeyValueTemp;  

  38.             }  

  39. else

  40.             {  

  41.                 ScanKeyDef.PreKeyValue  = KEY_NONE; // 无效按键

  42.             }  

  43.         }  

  44. elseif ( ScanKeyDef.count  == LONG_PRESS_TIME_IR )  

  45.         {  

  46. if (ISLongKey(KeyValueTemp))  

  47.             {  

  48.                 {  

  49.                    ScanKeyDef.LongkeyFlag = 1;  

  50.                    KeyValue = changeToLongKey ( KeyValueTemp );  

  51.                }  

  52.           }  

  53.             ScanKeyDef.PreKeyValue = KEY_NONE;  

  54.         }  

  55. elseif (ScanKeyDef.count > LONG_PRESS_TIME_IR )  

  56.         {  

  57.             ScanKeyDef.PreKeyValue  = KEY_NONE; //无效按键

  58.         }  

  59.     }  

  60. else//release & no press

  61.     {  

  62.         KeyReleaseTimeCount ++;  

  63. if(KeyReleaseTimeCount >= KEY_RELEASE_TIME_OUT_IR)  

  64.         {  

  65. if ( ScanKeyDef.PreKeyValue != KEY_NONE ) //释放按键值

  66.             {  

  67. if ( ScanKeyDef.LongkeyFlag == 0 )  

  68.                 {  

  69.                     KeyValue =ScanKeyDef.PreKeyValue ;  

  70.                 }  

  71.             }            

  72.             ScanKeyDef.count  = 0;  

  73.             ScanKeyDef.LongkeyFlag = 0;  

  74.            ScanKeyDef.PreKeyValue = KEY_NONE;  

  75.         }  

  76.     }  

  77. return(KeyValue);  

  78. }  



关键词: MCU 按键处理

评论


相关推荐

技术专区

关闭