STM32F4 按键FIFO设计

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

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

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

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

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

声明代码如下:

/**  ******************************************************************************  * @file: bsp_key.h   * @author: xiaofeng   * @version: V1.0  * @date  : 2015.05.21  * @brief  : STM32F4 KEY FIFO  ******************************************************************************  * @attention:  *  ******************************************************************************  *//* Define to prevent recursive inclusion -------------------------------------*/#ifndef __BSP_KEY_H__#define __BSP_KEY_H__#ifdef __cplusplus extern "C" {#endif/* Includes ------------------------------------------------------------------*/#include "stm32f4xx.h"/* Exported types ------------------------------------------------------------*/// 按键IDtypedef enum{KID_K1 = 0,KID_K2,KID_K3,    KID_K4}KEY_ID_E;/*定义键值代码, 必须按如下次序定时每个键的按下、弹起和长按事件推荐使用enum, 不用#define,原因:(1) 便于新增键值,方便调整顺序,使代码看起来舒服点(2) 编译器可帮我们避免键值重复。*/typedef enum{KEY_NONE = 0,/* 0 表示按键事件 */KEY1_DOWN,/* 1键按下 */KEY1_UP,/* 1键弹起 */KEY1_LONG,/* 1键长按 */KEY2_DOWN,/* 2键按下 */KEY2_UP,/* 2键弹起 */KEY2_LONG,/* 2键长按 */}KEY_ENUM;/* Exported constants --------------------------------------------------------*//* Exported macro ------------------------------------------------------------*/#define KEY_COUNT           2   // 按键个数#define KEY_FIFO_SIZE    10      // 按键FIFO大小#define KEY_FILTER_TIME     5       // 按键滤波时间50ms, 只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件#define KEY_LONG_TIME       0    // 长按时间. 0,表示不检测长按键; 其他,检测长按键的时间#define KEY_REPEAT_SPEED    0       // 长按键连发速度. 0,表示不支持连发,上报长按事件;其他,连发按下// 按键口对应的RCC时钟及引脚#define RCC_ALL_KEY (RCC_AHB1Periph_GPIOA )#define GPIO_PORT_K1    GPIOA#define GPIO_PIN_K1    GPIO_Pin_0#define GPIO_PORT_K2    GPIOD#define GPIO_PIN_K2    GPIO_Pin_1/* Exported functions --------------------------------------------------------*/ void KEY_Init(void);void KEY_Scan(void);void KEY_FIFO_Clear(void);uint8_t KEY_FIFO_Get(void);uint8_t KEY_GetState(KEY_ID_E ucKeyID);void KEY_SetParam(uint8_t ucKeyID, uint16_t LongTime, uint8_t  RepeatSpeed);#ifdef __cplusplus}#endif#endif /*****END OF FILE****/

实现代码如下:

/**  ******************************************************************************  * @file: bsp_key.h   * @author: xiaofeng   * @version: V1.0  * @date  : 2015.05.21  * @brief  : STM32F4 KEY FIFO  ******************************************************************************  * @attention:  *  ******************************************************************************  *//* Includes ------------------------------------------------------------------*/#include "bsp_key.h"/* Private typedef -----------------------------------------------------------*/// 每个按键对应1个全局的结构体变量。typedef struct{/* 下面是一个函数指针,指向判断按键手否按下的函数 */uint8_t (*IsKeyDownFunc)(void); /* 按键按下的判断函数,1表示按下 */uint8_t  Count;/* 滤波器计数器 */    uint8_t  State;/* 按键当前状态(按下还是弹起) */uint16_t LongCount;/* 长按计数器 */uint16_t LongTime;/* 按键按下持续时间, 0表示不检测长按 */uint8_t  RepeatSpeed;/* 连续按键周期 */uint8_t  RepeatCount;/* 连续按键计数器 */}KEY_T;// 按键FIFO用到变量 typedef struct{uint8_t Buf[KEY_FIFO_SIZE];/* 键值缓冲区 */uint8_t Read;/* 缓冲区读指针 */uint8_t Write;/* 缓冲区写指针 */}KEY_FIFO_T;/* Private define ------------------------------------------------------------*//* Private macro -------------------------------------------------------------*//* Private variables ---------------------------------------------------------*/static KEY_T s_tBtn[KEY_COUNT];static KEY_FIFO_T s_tKey;/* 按键FIFO变量,结构体 *//* Private function prototypes -----------------------------------------------*/static void KEY_FIFO_Init(void);static void KEY_GPIO_Config(void);static void KEY_FIFO_Put(uint8_t _KeyCode);static void KEY_Detect(uint8_t i);/* Private functions ---------------------------------------------------------*//**  * @brief:KEY初始化  * @note:    * @param :  * @retval:  */void KEY_Init(void){    KEY_GPIO_Config();    KEY_FIFO_Init();}/**  * @brief: 清空按键FIFO缓冲区  * @note: 无    * @param : 无  * @retval: 无  */void KEY_FIFO_Clear(void){s_tKey.Read = s_tKey.Write;}/**  * @brief: 从按键FIFO缓冲区读取一个键值  * @note: 无    * @param :  * @retval: 按键代码  */uint8_t KEY_FIFO_Get(void){uint8_t ret;if (s_tKey.Read == s_tKey.Write){return KEY_NONE;}else{ret = s_tKey.Buf[s_tKey.Read];if (++s_tKey.Read >= KEY_FIFO_SIZE){s_tKey.Read = 0;}return ret;}}/**  * @brief: 读取按键的状态  * @note: 无    * @param : ucKeyID : 按键ID,从0开始  * @retval: 1 表示按下, 0 表示未按下  */uint8_t KEY_GetState(KEY_ID_E ucKeyID){return s_tBtn[ucKeyID].State;}/**  * @brief: 设置按键参数  * @note: 无    * @param : ucKeyID : 按键ID,从0开始  *  LongTime : 长按事件时间  *  RepeatSpeed : 连发速度  * @retval: 无  */void KEY_SetParam(uint8_t ucKeyID, uint16_t LongTime, uint8_t  RepeatSpeed){s_tBtn[ucKeyID].LongTime = LongTime;/* 长按时间 0 表示不检测长按键事件 */s_tBtn[ucKeyID].RepeatSpeed = RepeatSpeed;/* 长按键连发的速度,0表示不支持连发 */s_tBtn[ucKeyID].RepeatCount = 0;/* 连发计数器 */}/**  * @brief: 扫描所有按键。非阻塞,被周期性的调用(如systick中断)  * @note: 无    * @param : 无  * @retval: 无  */void KEY_Scan(void){uint8_t i;for (i = 0; i < KEY_COUNT; i++){KEY_Detect(i);}}/***************************************************************************//**  * @brief: 配置按键对应的GPIO  * @note: 无    * @param : 无  * @retval: 无  */static void KEY_GPIO_Config(void){GPIO_InitTypeDef GPIO_InitStructure;/* 第1步:打开GPIO时钟 */RCC_AHB1PeriphClockCmd(RCC_ALL_KEY, ENABLE);/* 第2步:配置所有的按键GPIO为浮动输入模式(实际上CPU复位后就是输入状态) */GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;/* 设为输入口 */GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;/* 设为推挽模式 */GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;/* 无需上下拉电阻 */GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;/* IO口最大速度 */GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K1;GPIO_Init(GPIO_PORT_K1, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K2;GPIO_Init(GPIO_PORT_K2, &GPIO_InitStructure);}/**  * @brief: 判断按键是否按下  * @note: 无    * @param : 无  * @retval: 1 表示按下,0表示未按下  */static uint8_t IsKey1Down(void) {if (GPIO_ReadInputDataBit(GPIO_PORT_K1, GPIO_PIN_K1) == 0) return 1;else return 0;}static uint8_t IsKey2Down(void) {if (GPIO_ReadInputDataBit(GPIO_PORT_K2, GPIO_PIN_K2) == 0) return 1;else return 0;}/**  * @brief: 初始化按键变量  * @note: 无    * @param : 无  * @retval: 无  */static void KEY_FIFO_Init(void){uint8_t i;/* 对按键FIFO读写指针清零 */s_tKey.Read = 0;s_tKey.Write = 0;/* 给每个按键结构体成员变量赋一组缺省值 */for (i = 0; i < KEY_COUNT; i++){s_tBtn[i].LongTime = KEY_LONG_TIME;/* 长按时间 0 表示不检测长按键事件 */s_tBtn[i].Count = KEY_FILTER_TIME / 2;/* 计数器设置为滤波时间的一半 */s_tBtn[i].State = 0;/* 按键缺省状态,0为未按下 */s_tBtn[i].RepeatSpeed = KEY_REPEAT_SPEED;   /* 按键连发的速度,0表示不支持连发 */s_tBtn[i].RepeatCount = 0;/* 连发计数器 */}/* 判断按键按下的函数 */s_tBtn[0].IsKeyDownFunc = IsKey1Down;s_tBtn[1].IsKeyDownFunc = IsKey2Down;}/**  * @brief: 将1个键值压入按键FIFO缓冲区  * @note: 无    * @param : KeyCode : 按键代码  * @retval: 无  */static void KEY_FIFO_Put(uint8_t _KeyCode){s_tKey.Buf[s_tKey.Write] = _KeyCode;if (++s_tKey.Write  >= KEY_FIFO_SIZE){s_tKey.Write = 0;}}/**  * @brief: 检测一个按键。非阻塞状态,必须被周期性的调用  * @note: 无    * @param : 按键数  * @retval: 无  */static void KEY_Detect(uint8_t i){KEY_T *pBtn;pBtn = &s_tBtn[i];if (pBtn->IsKeyDownFunc()){// 按键按下if (pBtn->Count < KEY_FILTER_TIME){pBtn->Count = KEY_FILTER_TIME;}else if(pBtn->Count < 2 * KEY_FILTER_TIME){pBtn->Count++;}else{if (pBtn->State == 0){pBtn->State = 1;/* 发送按钮按下的消息 */KEY_FIFO_Put((uint8_t)(3 * i + 1));}if (pBtn->LongTime > 0){                if (pBtn->LongCount < pBtn->LongTime)                {                    /* 发送按钮持续按下的消息 */                    if (++pBtn->LongCount == pBtn->LongTime)                    {                        if (pBtn->RepeatSpeed > 0)                        {                            pBtn->LongCount = 0;                                                        if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)                            {                                pBtn->RepeatCount = 0;                                /* 常按键后,每隔10ms发送1个按键 */                                KEY_FIFO_Put((uint8_t)(3 * i + 1));                            }                                  }                        else                        {                            /* 键值放入按键FIFO */                            KEY_FIFO_Put((uint8_t)(3 * i + 3));                        }                    }                }}}}else{// 按键抬起if(pBtn->Count > KEY_FILTER_TIME){pBtn->Count = KEY_FILTER_TIME;}else if(pBtn->Count != 0){pBtn->Count--;}else{if (pBtn->State == 1){pBtn->State = 0;/* 发送按钮弹起的消息 */KEY_FIFO_Put((uint8_t)(3 * i + 2));}}pBtn->LongCount = 0;pBtn->RepeatCount = 0;}}/*****END OF FILE****/




0 0
原创粉丝点击