你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

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

[复制链接]
tan-404462 发布时间:2014-4-29 12:47

【MCU实战经验】+基于STM32和PID算法的小车车速控制

【MCU实战经验】+ STM32F103RBT6三个USART发送与接收设置

【MCU实战经验】+用stm32单片机做J-Link和ST-Link(PCB工程+固件库+资料工具)


之前的一个项目按键比较多,面板上面有按键,遥控器,处理的稍微复杂一点,MCU使用的是STM8S005K6.
关于按键部分的处理,现在拿处理来和大家分享一下,说的不对的地方还请各位大侠请教,大家共同进步。

按键通常分有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.         每个按键对应1个全局的结构体变量。
  8.         其成员变量是实现滤波和多种按键状态所必须的
  9. */
  10. typedef struct
  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. typedef enum
  26. {
  27.         KEY_NONE = 0,                        /* 0 表示按键事件 */

  28.         KEY_DOWN_Power,                        /* 按键键按下 */
  29.         KEY_UP_Power,                        /* 按键键弹起 */
  30.         KEY_LONG_Power,                        /* 按键键长按 */
  31.         
  32.         KEY_DOWN_Power_TAMPER        /* 组合键,Power键和WAKEUP键同时按下 */
  33. }KEY_ENUM;

  34. BUTTON_T s_Powerkey;               
  35. //是否有按键按下接口函数
  36. unsigned char  IsKeyDownUser(void)                 
  37. {if (0==GPIO_ReadInputPin(POWER_KEY_PORT, POWER_KEY_PIN) ) return 1;return 0;}


  38. void  PanakeyHard_Init(void)
  39. {
  40.    GPIO_Init (POWER_KEY_PORT, POWER_KEY_PIN, GPIO_MODE_IN_FL_NO_IT);//power key
  41. }
  42. void  PanakeyVar_Init(void)
  43. {
  44.         /* 初始化USER按键变量,支持按下、弹起、长按 */
  45.         s_Powerkey.IsKeyDownFunc = IsKeyDownUser;                /* 判断按键按下的函数 */
  46.         s_Powerkey.FilterTime = BUTTON_FILTER_TIME;                /* 按键滤波时间 */
  47.         s_Powerkey.LongTime = BUTTON_LONG_TIME;                        /* 长按时间 */
  48.         s_Powerkey.Count = s_Powerkey.FilterTime / 2;                /* 计数器设置为滤波时间的一半 */
  49.         s_Powerkey.State = 0;                                                        /* 按键缺省状态,0为未按下 */
  50.         s_Powerkey.KeyCodeDown = KEY_DOWN_Power;                        /* 按键按下的键值代码 */
  51.         s_Powerkey.KeyCodeUp =KEY_UP_Power;                                /* 按键弹起的键值代码 */
  52.         s_Powerkey.KeyCodeLong = KEY_LONG_Power;                        /* 按键被持续按下的键值代码 */
  53.         s_Powerkey.RepeatSpeed = 0;                                                /* 按键连发的速度,0表示不支持连发 */
  54.         s_Powerkey.RepeatCount = 0;                                                /* 连发计数器 */               
  55. }
  56. void Panakey_Init(void)
  57. {
  58.         PanakeyHard_Init();                /* 初始化按键变量 */
  59.         PanakeyVar_Init();                /* 初始化按键硬件 */
  60. }
复制代码

*********************************************************************************************************
*        函 数 名: bsp_DetectButton
*        功能说明: 检测一个按键。非阻塞状态,必须被周期性的调用。
*        形    参:按键结构变量指针
*        返 回 值: 无
*********************************************************************************************************
  1. */
  2. void Button_Detect(BUTTON_T *_pBtn)
  3. {
  4.         if (_pBtn->IsKeyDownFunc())
  5.         {
  6.                 if (_pBtn->Count < _pBtn->FilterTime)
  7.                 {
  8.                         _pBtn->Count = _pBtn->FilterTime;
  9.                 }
  10.                 else if(_pBtn->Count < 2 * _pBtn->FilterTime)
  11.                 {
  12.                         _pBtn->Count++;
  13.                 }
  14.                 else
  15.                 {
  16.                         if (_pBtn->State == 0)
  17.                         {
  18.                                 _pBtn->State = 1;

  19.                                 /* 发送按钮按下的消息 */
  20.                                 if (_pBtn->KeyCodeDown > 0)
  21.                                 {
  22.                                         /* 键值放入按键FIFO */
  23.                                         Pannelkey_Put(_pBtn->KeyCodeDown);// 记录按键按下标志,等待释放

  24.                                 }
  25.                         }

  26.                         if (_pBtn->LongTime > 0)
  27.                         {
  28.                                 if (_pBtn->LongCount < _pBtn->LongTime)
  29.                                 {
  30.                                         /* 发送按钮持续按下的消息 */
  31.                                         if (++_pBtn->LongCount == _pBtn->LongTime)
  32.                                         {
  33.                                                 /* 键值放入按键FIFO */
  34.                                                 Pannelkey_Put(_pBtn->KeyCodeLong);        
  35.                                 
  36.                                         }
  37.                                 }
  38.                                 else
  39.                                 {
  40.                                         if (_pBtn->RepeatSpeed > 0)
  41.                                         {
  42.                                                 if (++_pBtn->RepeatCount >= _pBtn->RepeatSpeed)
  43.                                                 {
  44.                                                         _pBtn->RepeatCount = 0;
  45.                                                         /* 常按键后,每隔10ms发送1个按键 */
  46.                                                         Pannelkey_Put(_pBtn->KeyCodeDown);        
  47.                                 
  48.                                                 }
  49.                                         }
  50.                                 }
  51.                         }
  52.                 }
  53.         }
  54.         else
  55.         {
  56.                 if(_pBtn->Count > _pBtn->FilterTime)
  57.                 {
  58.                         _pBtn->Count = _pBtn->FilterTime;
  59.                 }
  60.                 else if(_pBtn->Count != 0)
  61.                 {
  62.                         _pBtn->Count--;
  63.                 }
  64.                 else
  65.                 {
  66.                         if (_pBtn->State == 1)
  67.                         {
  68.                                 _pBtn->State = 0;

  69.                                 /* 发送按钮弹起的消息 */
  70.                                 if (_pBtn->KeyCodeUp > 0) /*按键释放*/
  71.                                 {
  72.                                         /* 键值放入按键FIFO */
  73.                                 Pannelkey_Put(_pBtn->KeyCodeUp);        
  74.                         
  75.                                 }
  76.                         }
  77.                 }

  78.                 _pBtn->LongCount = 0;
  79.                 _pBtn->RepeatCount = 0;
  80.         }
  81. }
  82. //功能说明: 检测所有按键。10MS 调用一次
  83. void Pannelkey_Polling(void)
  84. {
  85.         Button_Detect(&s_Powerkey);                /* USER 键 */
  86. }
  87. void Pannelkey_Put(void)
  88. {
  89.         
  90.   // 定义一个队列 放入按键值        
  91. }
复制代码


   
2,遥控器按键,遥控器解码的一般就有两种 脉宽调制和脉冲调制,这里就不细讲解码的过程了。这里详细解码之后,如何处理遥控器按键
   实现遥控器按键的长短按功能和连续按键功能。
   代码裁剪过,大家根据实际需要改动
  其实AD按键,通过AD采样获得按键值之后,可以采取如下面的一样方法处理,提一个函数接口即可
  1.    typedef struct
  2. {
  3.   unsigned char count;//
  4.   unsigned char LongkeyFlag;/*是否长按键,1代表是*/
  5.   unsigned char  PreKeyValue;/*按键值的一个备份,用于释放按键值*/
  6.   
  7. }ScanKeyDef;

  8. #define SHORT_PRESS_TIME_IR                16 // 10ms
  9. #define SERIES_PRESS_TIME_IR            10  
  10. #define LONG_PRESS_TIME_IR                    22
  11. #define KEY_RELEASE_TIME_OUT_IR     12  // 10ms
  12. //提供5个接口函数,如下。
  13. unsigned char get_irkey(void);
  14. unsigned char ISSeriesKey(unsigned char temp);//按键是否需要做连续按键
  15. unsigned char changeSeriesKey(unsigned char temp);//转换连续按键值
  16. unsigned char ISLongKey(unsigned char temp);//按键是否需要做连续按键
  17. unsigned char changeToLongKey(unsigned char temp);//转换连续按键值


  18. unsigned char KeyScan(void)
  19. {
  20.     unsigned char  KeyValue = KEY_NONE,
  21.                                 KeyValueTemp = KEY_NONE;
  22.     static   unsigned char KeyReleaseTimeCount =0;

  23.     KeyValueTemp = get_irkey();

  24.     if(KeyValueTemp != KEY_NONE)
  25.     {
  26.         ScanKeyDef.count++;
  27.         KeyReleaseTimeCount =0;

  28.         if(ScanKeyDef.count < LONG_PRESS_TIME_IR )
  29.         {
  30.             ScanKeyDef.LongkeyFlag = 0;
  31.             if((ScanKeyDef.count == SERIES_PRESS_TIME_IR) && ISSeriesKey(KeyValueTemp)) //处理连续按键
  32.                 {
  33.                     KeyValue = changeSeriesKey ( KeyValueTemp );
  34.                     ScanKeyDef.PreKeyValue = KEY_NONE;
  35.                 }
  36.             else if ( ScanKeyDef.count  < SHORT_PRESS_TIME_IR )
  37.             {
  38.                 ScanKeyDef.PreKeyValue = KeyValueTemp;
  39.             }
  40.             else
  41.             {
  42.             
  43.                 ScanKeyDef.PreKeyValue  = KEY_NONE; // 无效按键
  44.             }
  45.         }
  46.         else if ( ScanKeyDef.count  == LONG_PRESS_TIME_IR )
  47.         {
  48.       
  49.            if (ISLongKey(KeyValueTemp))
  50.             {
  51.                 {
  52.                    ScanKeyDef.LongkeyFlag = 1;
  53.                    KeyValue = changeToLongKey ( KeyValueTemp );
  54.                }
  55.           }
  56.             ScanKeyDef.PreKeyValue = KEY_NONE;
  57.          
  58.         }
  59.         else if (ScanKeyDef.count > LONG_PRESS_TIME_IR )
  60.         {
  61.       
  62.             ScanKeyDef.PreKeyValue  = KEY_NONE; //无效按键
  63.         }
  64.     }
  65.     else//release & no press
  66.     {
  67.         KeyReleaseTimeCount ++;
  68.         if(KeyReleaseTimeCount >= KEY_RELEASE_TIME_OUT_IR)
  69.         {
  70.   
  71.             if ( ScanKeyDef.PreKeyValue != KEY_NONE ) //释放按键值
  72.             {
  73.                 if ( ScanKeyDef.LongkeyFlag == 0 )
  74.                 {
  75.            
  76.                     KeyValue =ScanKeyDef.PreKeyValue ;
  77.                 }
  78.             }         
  79.             ScanKeyDef.count  = 0;
  80.             ScanKeyDef.LongkeyFlag = 0;
  81.            ScanKeyDef.PreKeyValue = KEY_NONE;
  82.    
  83.         }
  84.     }
  85.     return(KeyValue);
  86. }     
复制代码



收藏 12 评论19 发布时间:2014-4-29 12:47

举报

19个回答
上官梦舞 回答时间:2018-1-17 15:19:22
wakup引脚做普通按键两种用途应该都能吧,不过wakup之后需要重新配置各种功能,不知道有没有好的方法。
#define 回答时间:2015-11-29 16:04:34
不错                 
啊我俄方 回答时间:2016-7-28 16:06:12
确实不错        
robter 回答时间:2018-2-20 08:56:00
很好的按键解决方法,学习了
西点钟灵毓秀 回答时间:2018-3-11 23:08:45
很好的按键检测方法,学习了
ljz1992 回答时间:2018-5-31 15:10:22
不错,学习了!
mrliangg 回答时间:2018-6-5 14:03:00
不错,学习了!
Hotspicy 回答时间:2018-6-21 09:37:01
学习了
dark_psycho 回答时间:2018-6-21 10:40:50
学习,感谢楼主分享
西奥伟 回答时间:2018-12-27 17:18:51
可以的,学习一下
robter1 回答时间:2019-1-4 10:58:53
很好的学习资料,感谢楼主的好资料
jackyang0507 回答时间:2019-1-11 17:24:24
学习中,谢谢分享。
xujiantj 回答时间:2019-1-15 10:23:55
不错,学习了
xujiantj 回答时间:2019-1-29 13:02:28
学习了
12下一页

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版