×
单片机 > 单片机程序设计 > 详情

STM32F4 按键FIFO设计

发布时间:2020-08-24 发布时间:
|

设计按键 FIFO 主要有三个方面的好处: 
1.可以有效的记录按键事件的发生,特别是需要实现按键的按下,长按,弹起等事件,使用 FIFO的方式来实现是一种非常好的思路。 
2.系统是非阻塞的,这样系统在检测到按键按下的情况下,由于机械按键抖动的原因不需要在这里等待一段时间,然后再确定按键是否按下。 
3.按键 FIFO 程序在嘀嗒定时器中定期的执行检测,不需要在主程序中一直做检测,这样可以有效的降低系统资源消耗。 

关于按键是否该使用中断方式去实现的问题,很多初学者都比较模糊,我这里从两方面简单说一下,纯属个人见解,如果那位有更好的意见,欢迎提出来。 

从裸机的角度分析 
中断方式:中断方式可以有效的检测到按键按下的消息,并执行相应的程序,但是用中断方式实现按键 FIFO 相对就有点麻烦,如果每个按键都是独立的接一个 IO 引脚,需要我们给每个 IO都设置一个中断,程序中过多的中断会影响系统的稳定性。 
查询方式:查询方式有一个最大的缺点就是需要程序定期的去执行查询,耗费一定的系统资源,实际上耗费不了多大的系统资源,因为这种查询方式也只是查询按键是否按下,按键事件的执行还是在主程序里面实现。 
从 OS 的角度分析 
中断方式:在 OS 中要尽可能少用中断方式,因为在 RTOS 中过多的使用中断会影响系统的稳定性和可预见性(抢占式调度的 OS 基本没有可预见性,基于时间触发调度的可预见性要好很多)。比较重要的事件处理需要用中断的方式。 

查询方式:对于用户按键推荐使用这种查询方式来实现,现在的 OS 基本都带有 CPU 利用率的功能,这个按键 FIFO 占用的还是很小的,基本都在%1 以下。

这个按键 FIFO 程序主要用于扫描扫描独立按键,具有软件滤波机制,具有按键 FIFO。可以检测如下事件: 
1.   按键按下 
2.   按键弹起 
3.   长按键 
4.   长按时自动连发 

声明代码如下:


  1. /** 

  2.   ****************************************************************************** 

  3.   * @file       : bsp_key.h  

  4.   * @author     : xiaofeng  

  5.   * @version    : V1.0 

  6.   * @date       : 2015.05.21 

  7.   * @brief      : STM32F4 KEY FIFO 

  8.   ****************************************************************************** 

  9.   * @attention: 

  10.   * 

  11.   ****************************************************************************** 

  12.   */  

  13.       

  14. /* Define to prevent recursive inclusion -------------------------------------*/  

  15. #ifndef __BSP_KEY_H__  

  16. #define __BSP_KEY_H__  

  17.   

  18. #ifdef __cplusplus  

  19.  extern "C" {  

  20. #endif  

  21.   

  22. /* Includes ------------------------------------------------------------------*/  

  23. #include "stm32f4xx.h"  

  24.   

  25. /* Exported types ------------------------------------------------------------*/  

  26.   

  27. // 按键ID  

  28. typedef enum  

  29. {  

  30.     KID_K1 = 0,  

  31.     KID_K2,  

  32.     KID_K3,  

  33.     KID_K4  

  34. }KEY_ID_E;  

  35.   

  36. /* 

  37.     定义键值代码, 必须按如下次序定时每个键的按下、弹起和长按事件 

  38.     推荐使用enum, 不用#define,原因: 

  39.     (1) 便于新增键值,方便调整顺序,使代码看起来舒服点 

  40.     (2) 编译器可帮我们避免键值重复。 

  41. */  

  42. typedef enum  

  43. {  

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

  45.   

  46.     KEY1_DOWN,              /* 1键按下 */  

  47.     KEY1_UP,                /* 1键弹起 */  

  48.     KEY1_LONG,              /* 1键长按 */  

  49.   

  50.     KEY2_DOWN,              /* 2键按下 */  

  51.     KEY2_UP,                /* 2键弹起 */  

  52.     KEY2_LONG,              /* 2键长按 */  

  53. }KEY_ENUM;  

  54.   

  55. /* Exported constants --------------------------------------------------------*/  

  56. /* Exported macro ------------------------------------------------------------*/  

  57. #define KEY_COUNT           2       // 按键个数  

  58. #define KEY_FIFO_SIZE       10      // 按键FIFO大小  

  59. #define KEY_FILTER_TIME     5       // 按键滤波时间50ms, 只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件  

  60. #define KEY_LONG_TIME       0       // 长按时间. 0,表示不检测长按键; 其他,检测长按键的时间  

  61. #define KEY_REPEAT_SPEED    0       // 长按键连发速度. 0,表示不支持连发,上报长按事件;其他,连发按下  

  62.   

  63. // 按键口对应的RCC时钟及引脚  

  64. #define RCC_ALL_KEY     (RCC_AHB1Periph_GPIOA )  

  65.   

  66. #define GPIO_PORT_K1    GPIOA  

  67. #define GPIO_PIN_K1     GPIO_Pin_0  

  68.   

  69. #define GPIO_PORT_K2    GPIOD  

  70. #define GPIO_PIN_K2     GPIO_Pin_1  

  71.   

  72. /* Exported functions --------------------------------------------------------*/   

  73. void KEY_Init(void);  

  74. void KEY_Scan(void);  

  75.   

  76. void KEY_FIFO_Clear(void);  

  77. uint8_t KEY_FIFO_Get(void);  

  78.   

  79. uint8_t KEY_GetState(KEY_ID_E ucKeyID);  

  80. void KEY_SetParam(uint8_t ucKeyID, uint16_t LongTime, uint8_t  RepeatSpeed);  

  81.   

  82. #ifdef __cplusplus  

  83. }  

  84. #endif  

  85.   

  86. #endif   

  87.   

  88.   

  89. /*****END OF FILE****/  


实现代码如下:


  1. /** 

  2.   ****************************************************************************** 

  3.   * @file       : bsp_key.h  

  4.   * @author     : xiaofeng  

  5.   * @version    : V1.0 

  6.   * @date       : 2015.05.21 

  7.   * @brief      : STM32F4 KEY FIFO 

  8.   ****************************************************************************** 

  9.   * @attention: 

  10.   * 

  11.   ****************************************************************************** 

  12.   */  

  13. /* Includes ------------------------------------------------------------------*/  

  14. #include "bsp_key.h"  

  15. /* Private typedef -----------------------------------------------------------*/  

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

  17. typedef struct  

  18. {  

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

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

  21.   

  22.     uint8_t  Count;         /* 滤波器计数器 */  

  23.     uint8_t  State;         /* 按键当前状态(按下还是弹起) */  

  24.     uint16_t LongCount;     /* 长按计数器 */  

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

  26.     uint8_t  RepeatSpeed;   /* 连续按键周期 */  

  27.     uint8_t  RepeatCount;   /* 连续按键计数器 */  

  28. }KEY_T;  

  29.   

  30. // 按键FIFO用到变量   

  31. typedef struct  

  32. {  

  33.     uint8_t Buf[KEY_FIFO_SIZE];     /* 键值缓冲区 */  

  34.     uint8_t Read;                   /* 缓冲区读指针 */  

  35.     uint8_t Write;                  /* 缓冲区写指针 */  

  36. }KEY_FIFO_T;  

  37.   

  38. /* Private define ------------------------------------------------------------*/  

  39. /* Private macro -------------------------------------------------------------*/  

  40. /* Private variables ---------------------------------------------------------*/  

  41. static KEY_T s_tBtn[KEY_COUNT];  

  42. static KEY_FIFO_T s_tKey;       /* 按键FIFO变量,结构体 */  

  43.   

  44. /* Private function prototypes -----------------------------------------------*/  

  45. static void KEY_FIFO_Init(void);  

  46. static void KEY_GPIO_Config(void);  

  47. static void KEY_FIFO_Put(uint8_t _KeyCode);  

  48. static void KEY_Detect(uint8_t i);  

  49.   

  50. /* Private functions ---------------------------------------------------------*/  

  51. /** 

  52.   * @brief  :   KEY初始化 

  53.   * @note       :   

  54.   * @param  : 

  55.   * @retval : 

  56.   */  

  57. void KEY_Init(void)  

  58. {  

  59.     KEY_GPIO_Config();  

  60.     KEY_FIFO_Init();  

  61. }  

  62.   

  63. /** 

  64.   * @brief  : 清空按键FIFO缓冲区 

  65.   * @note   : 无   

  66.   * @param  : 无 

  67.   * @retval : 无 

  68.   */  

  69. void KEY_FIFO_Clear(void)  

  70. {  

  71.     s_tKey.Read = s_tKey.Write;  

  72. }  

  73.   

  74. /** 

  75.   * @brief  : 从按键FIFO缓冲区读取一个键值 

  76.   * @note   : 无   

  77.   * @param  : 

  78.   * @retval : 按键代码 

  79.   */  

  80. uint8_t KEY_FIFO_Get(void)  

  81. {  

  82.     uint8_t ret;  

  83.   

  84.     if (s_tKey.Read == s_tKey.Write)  

  85.     {  

  86.         return KEY_NONE;  

  87.     }  

  88.     else  

  89.     {  

  90.         ret = s_tKey.Buf[s_tKey.Read];  

  91.   

  92.         if (++s_tKey.Read >= KEY_FIFO_SIZE)  

  93.         {  

  94.             s_tKey.Read = 0;  

  95.         }  

  96.         return ret;  

  97.     }  

  98. }  

  99. /** 

  100.   * @brief  : 读取按键的状态 

  101.   * @note   : 无   

  102.   * @param  : ucKeyID : 按键ID,从0开始 

  103.   * @retval : 1 表示按下, 0 表示未按下 

  104.   */  

  105. uint8_t KEY_GetState(KEY_ID_E ucKeyID)  

  106. {  

  107.     return s_tBtn[ucKeyID].State;  

  108. }  

  109. /** 

  110.   * @brief  : 设置按键参数 

  111.   * @note   : 无   

  112.   * @param  : ucKeyID : 按键ID,从0开始 

  113.   *           LongTime : 长按事件时间 

  114.   *           RepeatSpeed : 连发速度 

  115.   * @retval : 无 

  116.   */  

  117. void KEY_SetParam(uint8_t ucKeyID, uint16_t LongTime, uint8_t  RepeatSpeed)  

  118. {  

  119.     s_tBtn[ucKeyID].LongTime = LongTime;        /* 长按时间 0 表示不检测长按键事件 */  

  120.     s_tBtn[ucKeyID].RepeatSpeed = RepeatSpeed;  /* 长按键连发的速度,0表示不支持连发 */  

  121.     s_tBtn[ucKeyID].RepeatCount = 0;            /* 连发计数器 */  

  122. }  

  123.   

  124. /** 

  125.   * @brief  : 扫描所有按键。非阻塞,被周期性的调用(如systick中断) 

  126.   * @note   : 无   

  127.   * @param  : 无 

  128.   * @retval : 无 

  129.   */  

  130. void KEY_Scan(void)  

  131. {  

  132.     uint8_t i;  

  133.   

  134.     for (i = 0; i 

  135.     {  

  136.         KEY_Detect(i);  

  137.     }  

  138. }  

  139.   

  140. /***************************************************************************/  

  141. /** 

  142.   * @brief  : 配置按键对应的GPIO 

  143.   * @note   : 无   

  144.   * @param  : 无 

  145.   * @retval : 无 

  146.   */  

  147. static void KEY_GPIO_Config(void)  

  148. {  

  149.     GPIO_InitTypeDef GPIO_InitStructure;  

  150.   

  151.     /* 第1步:打开GPIO时钟 */  

  152.     RCC_AHB1PeriphClockCmd(RCC_ALL_KEY, ENABLE);  

  153.   

  154.     /* 第2步:配置所有的按键GPIO为浮动输入模式(实际上CPU复位后就是输入状态) */  

  155.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;        /* 设为输入口 */  

  156.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      /* 设为推挽模式 */  

  157.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;    /* 无需上下拉电阻 */  

  158.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   /* IO口最大速度 */  

  159.   

  160.     GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K1;  

  161.     GPIO_Init(GPIO_PORT_K1, &GPIO_InitStructure);  

  162.   

  163.     GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K2;  

  164.     GPIO_Init(GPIO_PORT_K2, &GPIO_InitStructure);  

  165. }  

  166. /** 

  167.   * @brief  : 判断按键是否按下 

  168.   * @note   : 无   

  169.   * @param  : 无 

  170.   * @retval : 1 表示按下,0表示未按下 

  171.   */  

  172. static uint8_t IsKey1Down(void) {if (GPIO_ReadInputDataBit(GPIO_PORT_K1, GPIO_PIN_K1) == 0) return 1;else return 0;}  

  173. static uint8_t IsKey2Down(void) {if (GPIO_ReadInputDataBit(GPIO_PORT_K2, GPIO_PIN_K2) == 0) return 1;else return 0;}  

  174. /** 

  175.   * @brief  : 初始化按键变量 

  176.   * @note   : 无   

  177.   * @param  : 无 

  178.   * @retval : 无 

  179.   */  

  180. static void KEY_FIFO_Init(void)  

  181. {  

  182.     uint8_t i;  

  183.   

  184.     /* 对按键FIFO读写指针清零 */  

  185.     s_tKey.Read = 0;  

  186.     s_tKey.Write = 0;  

  187.   

  188.     /* 给每个按键结构体成员变量赋一组缺省值 */  

  189.     for (i = 0; i 

  190.     {  

  191.         s_tBtn[i].LongTime = KEY_LONG_TIME;         /* 长按时间 0 表示不检测长按键事件 */  

  192.         s_tBtn[i].Count = KEY_FILTER_TIME / 2;      /* 计数器设置为滤波时间的一半 */  

  193.         s_tBtn[i].State = 0;                        /* 按键缺省状态,0为未按下 */  

  194.         s_tBtn[i].RepeatSpeed = KEY_REPEAT_SPEED;   /* 按键连发的速度,0表示不支持连发 */  

  195.         s_tBtn[i].RepeatCount = 0;                  /* 连发计数器 */  

  196.     }  

  197.   

  198.     /* 判断按键按下的函数 */  

  199.     s_tBtn[0].IsKeyDownFunc = IsKey1Down;  

  200.     s_tBtn[1].IsKeyDownFunc = IsKey2Down;  

  201. }  

  202. /** 

  203.   * @brief  : 将1个键值压入按键FIFO缓冲区 

  204.   * @note   : 无   

  205.   * @param  : KeyCode : 按键代码 

  206.   * @retval : 无 

  207.   */  

  208. static void KEY_FIFO_Put(uint8_t _KeyCode)  

  209. {  

  210.     s_tKey.Buf[s_tKey.Write] = _KeyCode;  

  211.   

  212.     if (++s_tKey.Write  >= KEY_FIFO_SIZE)  

  213.     {  

  214.         s_tKey.Write = 0;  

  215.     }  

  216. }  

  217. /** 

  218.   * @brief  : 检测一个按键。非阻塞状态,必须被周期性的调用 

  219.   * @note   : 无   

  220.   * @param  : 按键数 

  221.   * @retval : 无 

  222.   */  

  223. static void KEY_Detect(uint8_t i)  

  224. {  

  225.     KEY_T *pBtn;  

  226.   

  227.     pBtn = &s_tBtn[i];  

  228.     if (pBtn->IsKeyDownFunc())  

  229.     {// 按键按下  

  230.         if (pBtn->Count 

  231.         {  

  232.             pBtn->Count = KEY_FILTER_TIME;  

  233.         }  

  234.         else if(pBtn->Count 

  235.         {  

  236.             pBtn->Count++;  

  237.         }  

  238.         else  

  239.         {  

  240.             if (pBtn->State == 0)  

  241.             {  

  242.                 pBtn->State = 1;  

  243.   

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

  245.                 KEY_FIFO_Put((uint8_t)(3 * i + 1));  

  246.             }  

  247.   

  248.             if (pBtn->LongTime > 0)  

  249.             {  

  250.                 if (pBtn->LongCount LongTime)  

  251.                 {  

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

  253.                     if (++pBtn->LongCount == pBtn->LongTime)  

  254.                     {  

  255.                         if (pBtn->RepeatSpeed > 0)  

  256.                         {  

  257.                             pBtn->LongCount = 0;  

  258.                               

  259.                             if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)  

  260.                             {  

  261.                                 pBtn->RepeatCount = 0;  

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

  263.                                 KEY_FIFO_Put((uint8_t)(3 * i + 1));  

  264.                             }            

  265.                         }  

  266.                         else  

  267.                         {  

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

  269.                             KEY_FIFO_Put((uint8_t)(3 * i + 3));  

  270.                         }  

  271.                     }  

  272.                 }  

  273.             }  

  274.         }  

  275.     }  

  276.     else  

  277.     {// 按键抬起  

  278.         if(pBtn->Count > KEY_FILTER_TIME)  

  279.         {  

  280.             pBtn->Count = KEY_FILTER_TIME;  

  281.         }  

  282.         else if(pBtn->Count != 0)  

  283.         {  

  284.             pBtn->Count--;  

  285.         }  

  286.         else  

  287.         {  

  288.             if (pBtn->State == 1)  

  289.             {  

  290.                 pBtn->State = 0;  

  291.   

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

  293.                 KEY_FIFO_Put((uint8_t)(3 * i + 2));  

  294.             }  

  295.         }  

  296.   

  297.         pBtn->LongCount = 0;  

  298.         pBtn->RepeatCount = 0;  

  299.     }  

  300. }  

  301.   

  302. /*****END OF FILE****/  


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
51单片机CO2检测显示程序解析