1、设计按键FIFO的优点 要介绍实现按键FIFO的优点,首先要了解FIFO的一些基本概念。FIFO即First In First Out,是一种先进先出的数据缓存方式,例如在超市购物之后我们会提着满满的购物车来到收银台排在结账队伍的最后等待付款,先排队的客户先付款离开,后面排队的只有等待前面付款离开才能进行付款。说白了FIFO就是这样一种先进先出机制,先存入的数据在读取时最先被读取到。 设计按键FIFO注意有三个方面的优点(来自于安富莱电子Eric2013大佬总结): 1、可以有效记录按键事件的发生,特别是系统要实现记录按键按下、松开、长按时,使用FIFO来实现是一种不错的选择方式。 2、系统是阻塞的,这样系统在检测到按键按下的情况,由于机械按键抖动的原因不需要在这里等待一段时间,然后在确定按键是否正常按下。 3、按键FIFO程序在系统定时器中定时检测按键状态,确认按键按下后将状态写入FIFO中,不一定在主程序中一直做检测,这样可以有效降低系统资源的消耗。
2、按键的硬件设计
按键的原理图如上图所示,对于KEY0~KEY2这三个按键,一端接地,另一端连接stm32的GPIO端口。当按键按下时相应的IO口被拉低,如果把GPIO口配置为输入模式,此时读取相应的IO口电平,就可以检测到按键是否被按下。对于KEY_UP按键则是与前面三个按键相反,IO口配置为输入模式时,读取到高电平时表示按键按下。因为机械固有的物理特性,按键按下内部弹簧片在瞬间接触的时候会有力学的回弹,造成2-8毫秒内信号不稳定,所以在设计检测机械按键是否按下的程序时,应考虑到按键消抖问题。
3、按键FIFO代码的设计 3.1 按键FIFO代码主要框图
bsp_KeyScan()检测到按键状态时以3*x+1关系计算出(x代表按键编号)写入FIFO值,例如: FIFO中读取值1---------------->按键1按下 FIFO中读取值2---------------->按键1弹开 FIFO中读取值3---------------->按键1长按 注意:在配置按键GPIO相应模式时,如果按键硬件设计没有电阻上拉,那么在配置GPIO口时必须将GPIO口内部配置成上拉状态,否则对读取按键结构有影响!
3.2 按键FIFO代码实现 bsp_key.c实现
bsp_key.c文件中核心函数是bsp_DetectKey(),这个函数先通过函数指针调用检测按键GPIO口状态的函数,判断是否有按键按下。 检测到按键按下时先进行相应的消抖操作,消抖持续时间通过bsp_key.h中定义的宏 KEY_FILTER_TIME设置。确定按键按下后,如果在此之前按键是松开的就把相应标志位置位,把按键按下的状态值写入到FIFO 中。如果使能了检测按键长按功能(LongCount>0),程序会继续检测按键是否达到长按时间,如果到达长按时间,将长按状态值写入到FIFO中,在继续检测是否支持长按时状态连续发送功能,如果使能了长按时连 续发送功能(RepeatSpeed>0),达到周期发送时间时,会继续写入FIFO按键按下状态值。 检测到按键释放时,首先也会进行相应滤波处理。确认按键松开后,若之前按键处于按下状态,会将检查到的按键松开状况写入到FIFO中,然后清除长按、重复发生的计数值,为下次查询做准备。 bsp_key.h实现 - #ifndef __BSP_KEY_H
- #define __BSP_KEY_H
- #define KEY_COUNT 4 /* 按键个数, 4个独立按键 */
- /* 根据应用程序的功能重命名按键宏 */
- #define KEY_DOWN_K0 KEY_0_DOWN
- #define KEY_UP_K0 KEY_0_UP
- #define KEY_LONG_K0 KEY_0_LONG
- #define KEY_DOWN_K1 KEY_1_DOWN
- #define KEY_UP_K1 KEY_1_UP
- #define KEY_LONG_K1 KEY_1_LONG
- #define KEY_DOWN_K2 KEY_2_DOWN
- #define KEY_UP_K2 KEY_2_UP
- #define KEY_LONG_K2 KEY_2_LONG
- #define KEY_DOWN_WP KEY_4_DOWN /* 上 */
- #define KEY_UP_WP KEY_4_UP
- #define KEY_LONG_WP KEY_4_LONG
- /* 按键ID, 主要用于bsp_KeyState()函数的入口参数 */
- typedef enum
- {
- KID_K1 = 0,
- KID_K2,
- KID_K3,
- KID_WAKE_UP,
- }KEY_ID_E;
- /*
- 按键滤波时间50ms, 单位10ms。
- 只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件
- 即使按键电路不做硬件滤波,该滤波机制也可以保证可靠地检测到按键事件
- */
- #define KEY_FILTER_TIME 5
- #define KEY_LONG_TIME 100 /* 单位10ms, 持续1秒,认为长按事件 */
- /*
- 每个按键对应1个全局的结构体变量。
- */
- typedef struct
- {
- /* 下面是一个函数指针,指向判断按键手否按下的函数 */
- uint8_t (*IsKeyDownFunc)(void); /* 按键按下的判断函数,1表示按下 */
- uint8_t Count; /* 滤波器计数器 */
- uint16_t LongCount; /* 长按计数器 */
- uint16_t LongTime; /* 按键按下持续时间, 0表示不检测长按 */
- uint8_t State; /* 按键当前状态(按下还是弹起) */
- uint8_t RepeatSpeed; /* 连续按键周期 */
- uint8_t RepeatCount; /* 连续按键计数器 */
- }KEY_T;
- /*
- 定义键值代码, 必须按如下次序定时每个键的按下、弹起和长按事件,检测按键时使用
- */
- typedef enum
- {
- KEY_NONE = 0, /* 0 表示按键事件 */
- KEY_0_DOWN, /* 1键按下 */
- KEY_0_UP, /* 1键弹起 */
- KEY_0_LONG, /* 1键长按 */
- KEY_1_DOWN, /* 2键按下 */
- KEY_1_UP, /* 2键弹起 */
- KEY_1_LONG, /* 2键长按 */
- KEY_2_DOWN, /* 3键按下 */
- KEY_2_UP, /* 3键弹起 */
- KEY_2_LONG, /* 3键长按 */
- KEY_3_DOWN, /* 4键按下 */
- KEY_3_UP, /* 4键弹起 */
- KEY_3_LONG, /* 4键长按 */
- }KEY_ENUM;
- /* 按键FIFO用到变量 */
- #define KEY_FIFO_SIZE 10
- typedef struct
- {
- uint8_t Buf[KEY_FIFO_SIZE]; /* 键值缓冲区 */
- uint8_t Read; /* 缓冲区读指针1 */
- uint8_t Write; /* 缓冲区写指针 */
- }KEY_FIFO_T;
- /* 供外部调用的函数声明 */
- void bsp_InitKey(void);
- void bsp_KeyScan(void);
- void bsp_PutKey(uint8_t _KeyCode);
- uint8_t bsp_GetKey(void);
- uint8_t bsp_GetKeyState(KEY_ID_E _ucKeyID);
- void bsp_SetKeyParam(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t _RepeatSpeed);
- void bsp_ClearKey(void);
- #endif
复制代码
|