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


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

    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. /* 
    8.         每个按键对应1个全局的结构体变量。 
    9.         其成员变量是实现滤波和多种按键状态所必须的 
    10. */  
    11. typedef struct  
    12. {  
    13.         /* 下面是一个函数指针,指向判断按键手否按下的函数 */  
    14.         unsigned char  (*IsKeyDownFunc)(void); /* 按键按下的判断函数,1表示按下 */  
    15.    
    16.         unsigned char  Count;                        /* 滤波器计数器 */  
    17.         unsigned char  FilterTime;                /* 滤波时间(最大255,表示2550ms) */  
    18.         unsigned short LongCount;                /* 长按计数器 */  
    19.         unsigned short LongTime;                /* 按键按下持续时间, 0表示不检测长按 */  
    20.         unsigned char   State;                        /* 按键当前状态(按下还是弹起) */  
    21.         unsigned char  KeyCodeUp;                /* 按键弹起的键值代码, 0表示不检测按键弹起 */  
    22.         unsigned char  KeyCodeDown;        /* 按键按下的键值代码, 0表示不检测按键按下 */  
    23.         unsigned char  KeyCodeLong;        /* 按键长按的键值代码, 0表示不检测长按 */  
    24.         unsigned char  RepeatSpeed;        /* 连续按键周期 */  
    25.         unsigned char  RepeatCount;        /* 连续按键计数器 */  
    26. }BUTTON_T;  
    27.    
    28. typedef enum  
    29. {  
    30.         KEY_NONE = 0,                        /* 0 表示按键事件 */  
    31.    
    32.         KEY_DOWN_Power,                        /* 按键键按下 */  
    33.         KEY_UP_Power,                        /* 按键键弹起 */  
    34.         KEY_LONG_Power,                        /* 按键键长按 */  
    35.           
    36.         KEY_DOWN_Power_TAMPER        /* 组合键,Power键和WAKEUP键同时按下 */  
    37. }KEY_ENUM;  
    38.    
    39. BUTTON_T s_Powerkey;                  
    40. //是否有按键按下接口函数  
    41. unsigned char  IsKeyDownUser(void)                   
    42. {if (0==GPIO_ReadInputPin(POWER_KEY_PORT, POWER_KEY_PIN) ) return 1;return 0;}  
    43.    
    44.    
    45.  void  PanakeyHard_Init(void)  
    46. {  
    47.    GPIO_Init (POWER_KEY_PORT, POWER_KEY_PIN, GPIO_MODE_IN_FL_NO_IT);//power key  
    48. }  
    49.  void  PanakeyVar_Init(void)  
    50. {  
    51.         /* 初始化USER按键变量,支持按下、弹起、长按 */  
    52.         s_Powerkey.IsKeyDownFunc = IsKeyDownUser;                /* 判断按键按下的函数 */  
    53.         s_Powerkey.FilterTime = BUTTON_FILTER_TIME;                /* 按键滤波时间 */  
    54.         s_Powerkey.LongTime = BUTTON_LONG_TIME;                        /* 长按时间 */  
    55.         s_Powerkey.Count = s_Powerkey.FilterTime / 2;                /* 计数器设置为滤波时间的一半 */  
    56.         s_Powerkey.State = 0;                                                        /* 按键缺省状态,0为未按下 */  
    57.         s_Powerkey.KeyCodeDown = KEY_DOWN_Power;                        /* 按键按下的键值代码 */  
    58.         s_Powerkey.KeyCodeUp =KEY_UP_Power;                                /* 按键弹起的键值代码 */  
    59.         s_Powerkey.KeyCodeLong = KEY_LONG_Power;                        /* 按键被持续按下的键值代码 */  
    60.         s_Powerkey.RepeatSpeed = 0;                                                /* 按键连发的速度,0表示不支持连发 */  
    61.         s_Powerkey.RepeatCount = 0;                                                /* 连发计数器 */                  
    62. }  
    63. void Panakey_Init(void)  
    64. {  
    65.         PanakeyHard_Init();                /* 初始化按键变量 */  
    66.         PanakeyVar_Init();                /* 初始化按键硬件 */  
    67. }  
    68. /* 
    69. ********************************************************************************************************* 
    70. *        函 数 名: bsp_DetectButton 
    71. *        功能说明: 检测一个按键。非阻塞状态,必须被周期性的调用。 
    72. *        形    参:按键结构变量指针 
    73. *        返 回 值: 无 
    74. ********************************************************************************************************* 
    75. */  
    76.  void Button_Detect(BUTTON_T *_pBtn)  
    77. {  
    78.         if (_pBtn->IsKeyDownFunc())  
    79.         {  
    80.                 if (_pBtn->Count < _pBtn->FilterTime)  
    81.                 {  
    82.                         _pBtn->Count = _pBtn->FilterTime;  
    83.                 }  
    84.                 else if(_pBtn->Count < 2 * _pBtn->FilterTime)  
    85.                 {  
    86.                         _pBtn->Count++;  
    87.                 }  
    88.                 else  
    89.                 {  
    90.                         if (_pBtn->State == 0)  
    91.                         {  
    92.                                 _pBtn->State = 1;  
    93.    
    94.                                 /* 发送按钮按下的消息 */  
    95.                                 if (_pBtn->KeyCodeDown > 0)  
    96.                                 {  
    97.                                         /* 键值放入按键FIFO */  
    98.                                         Pannelkey_Put(_pBtn->KeyCodeDown);// 记录按键按下标志,等待释放  
    99.    
    100.                                 }  
    101.                         }  
    102.    
    103.                         if (_pBtn->LongTime > 0)  
    104.                         {  
    105.                                 if (_pBtn->LongCount < _pBtn->LongTime)  
    106.                                 {  
    107.                                         /* 发送按钮持续按下的消息 */  
    108.                                         if (++_pBtn->LongCount == _pBtn->LongTime)  
    109.                                         {  
    110.                                                 /* 键值放入按键FIFO */  
    111.                                                 Pannelkey_Put(_pBtn->KeyCodeLong);          
    112.                                   
    113.                                         }  
    114.                                 }  
    115.                                 else  
    116.                                 {  
    117.                                         if (_pBtn->RepeatSpeed > 0)  
    118.                                         {  
    119.                                                 if (++_pBtn->RepeatCount >= _pBtn->RepeatSpeed)  
    120.                                                 {  
    121.                                                         _pBtn->RepeatCount = 0;  
    122.                                                         /* 常按键后,每隔10ms发送1个按键 */  
    123.                                                         Pannelkey_Put(_pBtn->KeyCodeDown);          
    124.                                   
    125.                                                 }  
    126.                                         }  
    127.                                 }  
    128.                         }  
    129.                 }  
    130.         }  
    131.         else  
    132.         {  
    133.                 if(_pBtn->Count > _pBtn->FilterTime)  
    134.                 {  
    135.                         _pBtn->Count = _pBtn->FilterTime;  
    136.                 }  
    137.                 else if(_pBtn->Count != 0)  
    138.                 {  
    139.                         _pBtn->Count--;  
    140.                 }  
    141.                 else  
    142.                 {  
    143.                         if (_pBtn->State == 1)  
    144.                         {  
    145.                                 _pBtn->State = 0;  
    146.    
    147.                                 /* 发送按钮弹起的消息 */  
    148.                                 if (_pBtn->KeyCodeUp > 0) /*按键释放*/  
    149.                                 {  
    150.                                         /* 键值放入按键FIFO */  
    151.                                 Pannelkey_Put(_pBtn->KeyCodeUp);          
    152.                           
    153.                                 }  
    154.                         }  
    155.                 }  
    156.    
    157.                 _pBtn->LongCount = 0;  
    158.                 _pBtn->RepeatCount = 0;  
    159.         }  
    160. }  
    161. //功能说明: 检测所有按键。10MS 调用一次  
    162. void Pannelkey_Polling(void)  
    163. {  
    164.         Button_Detect(&s_Powerkey);                /* USER 键 */  
    165. }  
    166. void Pannelkey_Put(void)  
    167. {  
    168.           
    169.   // 定义一个队列 放入按键值          
    170. }  
       
    2、遥控器按键,遥控器解码的一般就有两种:脉宽调制和脉冲调制,这里就不细讲解码的过程了。这里详细解码之后,如何处理遥控器按键实现遥控器按键的长短按功能和连续按键功能。代码裁剪过,大家根据实际需要改动。其实AD按键,通过AD采样获得按键值之后,可以采取如下面的一样方法处理,提一个函数接口即可

    1.    typedef struct  
    2. {  
    3.   unsigned char count;//  
    4.   unsigned char LongkeyFlag;/*是否长按键,1代表是*/  
    5.   unsigned char  PreKeyValue;/*按键值的一个备份,用于释放按键值*/  
    6.     
    7. }ScanKeyDef;  
    8.    
    9. #define SHORT_PRESS_TIME_IR                16 // 10ms   
    10. #define SERIES_PRESS_TIME_IR            10    
    11. #define LONG_PRESS_TIME_IR                    22  
    12. #define KEY_RELEASE_TIME_OUT_IR     12  // 10ms   
    13. //提供5个接口函数,如下。   
    14. unsigned char get_irkey(void);  
    15. unsigned char ISSeriesKey(unsigned char temp);//按键是否需要做连续按键  
    16. unsigned char changeSeriesKey(unsigned char temp);//转换连续按键值  
    17. unsigned char ISLongKey(unsigned char temp);//按键是否需要做连续按键  
    18. unsigned char changeToLongKey(unsigned char temp);//转换连续按键值  
    19.    
    20.    
    21. unsigned char KeyScan(void)   
    22. {  
    23.     unsigned char  KeyValue = KEY_NONE,  
    24.                                 KeyValueTemp = KEY_NONE;  
    25.     static   unsigned char KeyReleaseTimeCount =0;  
    26.    
    27.     KeyValueTemp = get_irkey();  
    28.    
    29.     if(KeyValueTemp != KEY_NONE)  
    30.     {  
    31.         ScanKeyDef.count++;  
    32.         KeyReleaseTimeCount =0;  
    33.    
    34.         if(ScanKeyDef.count < LONG_PRESS_TIME_IR )  
    35.         {  
    36.             ScanKeyDef.LongkeyFlag = 0;  
    37.             if((ScanKeyDef.count == SERIES_PRESS_TIME_IR) && ISSeriesKey(KeyValueTemp)) //处理连续按键  
    38.                 {  
    39.                     KeyValue = changeSeriesKey ( KeyValueTemp );  
    40.                     ScanKeyDef.PreKeyValue = KEY_NONE;  
    41.                 }  
    42.             else if ( ScanKeyDef.count  < SHORT_PRESS_TIME_IR )  
    43.             {  
    44.                 ScanKeyDef.PreKeyValue = KeyValueTemp;  
    45.             }  
    46.             else  
    47.             {  
    48.               
    49.                 ScanKeyDef.PreKeyValue  = KEY_NONE; // 无效按键  
    50.             }  
    51.         }  
    52.         else if ( ScanKeyDef.count  == LONG_PRESS_TIME_IR )  
    53.         {  
    54.          
    55.            if (ISLongKey(KeyValueTemp))  
    56.             {  
    57.                 {  
    58.                    ScanKeyDef.LongkeyFlag = 1;  
    59.                    KeyValue = changeToLongKey ( KeyValueTemp );  
    60.                }  
    61.           }  
    62.             ScanKeyDef.PreKeyValue = KEY_NONE;  
    63.            
    64.         }  
    65.         else if (ScanKeyDef.count > LONG_PRESS_TIME_IR )  
    66.         {  
    67.         
    68.             ScanKeyDef.PreKeyValue  = KEY_NONE; //无效按键  
    69.         }  
    70.     }  
    71.     else//release & no press  
    72.     {  
    73.         KeyReleaseTimeCount ++;  
    74.         if(KeyReleaseTimeCount >= KEY_RELEASE_TIME_OUT_IR)  
    75.         {  
    76.     
    77.             if ( ScanKeyDef.PreKeyValue != KEY_NONE ) //释放按键值  
    78.             {  
    79.                 if ( ScanKeyDef.LongkeyFlag == 0 )  
    80.                 {  
    81.              
    82.                     KeyValue =ScanKeyDef.PreKeyValue ;  
    83.                 }  
    84.             }            
    85.             ScanKeyDef.count  = 0;  
    86.             ScanKeyDef.LongkeyFlag = 0;  
    87.            ScanKeyDef.PreKeyValue = KEY_NONE;  
    88.       
    89.         }  
    90.     }  
    91.     return(KeyValue);  
    92. }  
  • 相关阅读:
    Oracle目录结构及创建新数据库
    Oracle登陆及修改用户密码
    前端开发笔记
    2014年11月6日17:57:13
    游戏体验篇 二
    游戏前端开发随笔【2】
    游戏体验篇 一
    游戏 之 前端系统开发
    换个手机号也是醉了
    winsock2.h的SB东西
  • 原文地址:https://www.cnblogs.com/alan666/p/8312001.html
Copyright © 2020-2023  润新知