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

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个回答
#define 回答时间:2015-11-29 16:04:34
不错                 
天外飞仙 回答时间:2016-7-28 16:06:12
确实不错        
上官梦舞 回答时间:2018-1-17 15:19:22
wakup引脚做普通按键两种用途应该都能吧,不过wakup之后需要重新配置各种功能,不知道有没有好的方法。
robter 回答时间:2018-2-20 08:56:00
很好的按键解决方法,学习了
zts329547875 回答时间: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
学习,感谢楼主分享
12下一页

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

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