按键通常有: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. 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. else if(_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. else if(_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. typedef struct
  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. else if ( ScanKeyDef.count  < SHORT_PRESS_TIME_IR )
  36. {
  37. ScanKeyDef.PreKeyValue = KeyValueTemp;
  38. }
  39. else
  40. {
  41. ScanKeyDef.PreKeyValue  = KEY_NONE; // 无效按键
  42. }
  43. }
  44. else if ( 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. else if (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. }

最新文章

  1. 开发者最爱的Firebug停止更新和维护
  2. 【转载】Java反射: 数组
  3. 反汇编工具capstone安装后import error
  4. 在Swift中使用JavaScript的方法和技巧
  5. C#4.0 特性
  6. [转帖]海森矩阵(Hessian matrix)
  7. .NET微信通过授权获取用户的基本信息
  8. OracleGateway11gR2访问异构数据库(MSSQL)配置文档(转)
  9. C++的构造函数和析构函数
  10. UVA 714 Copying Books 最大值最小化问题 (贪心 + 二分)
  11. ASP.NET -- repeater控件的使用
  12. 基于FFMPEG和SDL实现视频播放器
  13. jdk源码剖析五:JDK8-废弃永久代(PermGen)迎来元空间(Metaspace)
  14. 【Android 应用开发】AndroidUI设计之 布局管理器 - 详细解析布局实现
  15. spark安装
  16. [原]Webpack 3 + AngularJS1.* + Bootstrap 4 + Mapbox-gl
  17. Confluence 6 home 目录
  18. Zabbix4.0报警配置-企业微信报警
  19. 实验三:xen环境下的第一个虚拟机的安装
  20. XHR的对象及用法

热门文章

  1. iOS9中如何在日历App中创建一个任意时间之前开始的提醒(三)
  2. 7.0、Android Studio命令行工具
  3. (NO.00005)iOS实现炸弹人游戏(三):从主场景类谈起
  4. 【一天一道LeetCode】#202. Happy Number
  5. Arquillian Exception:java.lang.NoClassDefFoundError
  6. Chipmunk僵尸物理对象的出现和解决(八)
  7. Java实现栈之计算器
  8. HTML入门笔记案例展示(2)
  9. 发布一个参考tornado的高性能c++网络库:libtnet
  10. ISLR系列:(4.1)模型选择 Subset Selection