单片机按键检测方案(一)
一、介绍
该按键实现方案摘录自安富莱,具备按键短按、按键抬起、按键长按和按键长按连发的功能;使用了一个缓存数组用于装载按键值,缓存数组的大小和按键的个数有关。
注意:按键扫描函数需要每隔10ms调用一次,如果需要修改请细看程序。
二、代码实现
/* * key.c * * Created on: Jul 30, 2020 * Author: Mr.W */ #include "./key/key.h" static KEY_T s_tBtn[KEY_FIFO_SIZE]; /* 定义按键,按键的数量为KEY_FIFO_SIZE */ static KEY_FIFO_T s_tKey; /* 按键FIFO变量,结构体 */ /** * @brief 获取按键的状态 * @retval none * @author Mr.W * @date 2020-7-30 */ static uint8_t IsKeyDown0(void) {if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_RESET) return 1;else return 0;} static uint8_t IsKeyDown1(void) { /* 待添加其它按键 */ return 0;} static uint8_t IsKeyDown2(void) { /* 待添加其它按键 */ return 0;} static uint8_t IsKeyDown3(void) { /* 待添加其它按键 */ return 0;} static uint8_t IsKeyDown4(void) { /* 待添加其它按键 */ return 0;} static uint8_t IsKeyDown5(void) { /* 待添加其它按键 */ return 0;} static uint8_t IsKeyDown6(void) { /* 待添加其它按键 */ return 0;} static uint8_t IsKeyDown7(void) { /* 待添加其它按键 */ return 0;} static uint8_t IsKeyDown8(void) { /* 待添加其它按键 */ return 0;} static uint8_t IsKeyDown9(void) { /* 待添加其它按键 */ return 0;} /** * @brief 按键引脚初始化 * @retval none * @author Mr.W * @date 2020-7-30 */ static void key_gpio_cfg(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); /*Configure GPIO pin : GPIO_PIN_13 */ GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); } /** * @brief 初始化按键参数值 * @retval none * @author Mr.W * @date 2020-7-30 */ static void key_initKeyVar(void) { uint8_t i; /* 对按键FIFO读写指针清零 */ s_tKey.Read = 0; s_tKey.Write = 0; /* 给每个按键结构体成员变量赋一组缺省值 */ for (i = 0; i < KEY_FIFO_SIZE; i++) { s_tBtn[i].LongTime = KEY_LONG_TIME; /* 长按时间 0 表示不检测长按键事件 */ s_tBtn[i].LongCount = KEY_FILTER_TIME/2; /* 计数器设置为滤波时间的一半 */ s_tBtn[i].Count = 0; /* 计数器设置为滤波时间的一半 */ s_tBtn[i].State = 0; /* 按键缺省状态,0为未按下 */ s_tBtn[i].RepeatSpeed = 0; /* 按键连发的速度,0表示不支持连发 */ s_tBtn[i].RepeatCount = 0; /* 连发计数器 */ } /* 如果需要单独更改某个按键的参数,可以在此单独重新赋值 */ /* 比如,我们希望按键1按下超过1秒后,自动重发相同键值 */ s_tBtn[0].LongTime = KEY_LONG_TIME; s_tBtn[0].RepeatSpeed = 5; /* 每隔50ms自动发送键值 */ /* 判断按键按下的函数 */ s_tBtn[0].IsKeyDownFunc = IsKeyDown0; s_tBtn[1].IsKeyDownFunc = IsKeyDown1; s_tBtn[2].IsKeyDownFunc = IsKeyDown2; s_tBtn[3].IsKeyDownFunc = IsKeyDown3; s_tBtn[4].IsKeyDownFunc = IsKeyDown4; s_tBtn[5].IsKeyDownFunc = IsKeyDown5; s_tBtn[6].IsKeyDownFunc = IsKeyDown6; s_tBtn[7].IsKeyDownFunc = IsKeyDown7; s_tBtn[8].IsKeyDownFunc = IsKeyDown8; s_tBtn[9].IsKeyDownFunc = IsKeyDown9; } /** * @brief 将按键值放入缓冲区 * @param 按键编码 * @retval none * @author Mr.W * @date 2020-7-30 */ static void key_putKey(uint8_t _keyCode) { s_tKey.Buf[s_tKey.Write] = _keyCode; if(++s_tKey.Write >= KEY_FIFO_SIZE) { s_tKey.Write = 0; } } /** * @brief 查询被触发的按键 * @retval none * @author Mr.W * @date 2020-7-30 */ static void key_detectKey(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; /* 将短按键值放入缓冲区,按键值从1开始*/ key_putKey((uint8_t)(3 * i + 1)); } if(pBtn->LongTime > 0) { if(pBtn->LongCount < pBtn->LongTime) { /* 发送按钮持续按下的消息 */ if(++pBtn->LongCount >= pBtn->LongTime) { /* 将长按键值放入按键FIFO */ key_putKey((uint8_t)(3 * i + 1 + 2)); } } else { if (pBtn->RepeatSpeed > 0) { if (++pBtn->RepeatCount >= pBtn->RepeatSpeed) { pBtn->RepeatCount = 0; /* 长按键后,每隔50ms发送1个按键 */ key_putKey((uint8_t)(3 * i + 1)); } } } } } } 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_putKey((uint8_t)(3 * i + 1 + 1)); } } pBtn->LongCount = 0; pBtn->RepeatCount = 0; } } /** * @brief 按键初始化 * @retval none * @author Mr.W * @date 2020-7-30 */ void key_init(void) { key_gpio_cfg(); key_initKeyVar(); } /** * @brief 扫描按键(每隔10ms扫描一次按键) * @retval none * @author Mr.W * @date 2020-7-30 */ void key_keyScan(void) { uint8_t i; for (i = 0; i < KEY_FIFO_SIZE; i++) { key_detectKey(i); } } /** * @brief 获取按键值 * @retval 获取到的按键值 * @author Mr.W * @date 2020-7-30 */ uint8_t key_getKey(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; } }
/* * key.h * * Created on: Jul 30, 2020 * Author: Mr.W */ #ifndef KEY_H_ #define KEY_H_ /* Includes ------------------------------------------------------------------*/ #include "stm32l0xx_hal.h" /* 按键滤波时间50ms, 单位10ms。 只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件 即使按键电路不做硬件滤波,该滤波机制也可以保证可靠地检测到按键事件 */ #define KEY_FILTER_TIME 5 #define KEY_LONG_TIME 100 /* 单位10ms, 持续1秒,认为长按事件 */ typedef enum { KEY_NONE = 0, /* 0 表示按键事件 */ KEY_1_DOWN, /* 1键按下 */ KEY_1_UP, /* 1键弹起 */ KEY_1_LONG, /* 1键长按 */ KEY_2_DOWN, /* 2键按下 */ KEY_2_UP, /* 2键弹起 */ KEY_2_LONG, /* 2键长按 */ KEY_3_DOWN, /* 3键按下 */ KEY_3_UP, /* 3键弹起 */ KEY_3_LONG, /* 3键长按 */ KEY_4_DOWN, /* 4键按下 */ KEY_4_UP, /* 4键弹起 */ KEY_4_LONG, /* 4键长按 */ KEY_5_DOWN, /* 5键按下 */ KEY_5_UP, /* 5键弹起 */ KEY_5_LONG, /* 5键长按 */ KEY_6_DOWN, /* 6键按下 */ KEY_6_UP, /* 6键弹起 */ KEY_6_LONG, /* 6键长按 */ KEY_7_DOWN, /* 7键按下 */ KEY_7_UP, /* 7键弹起 */ KEY_7_LONG, /* 7键长按 */ KEY_8_DOWN, /* 8键按下 */ KEY_8_UP, /* 8键弹起 */ KEY_8_LONG, /* 8键长按 */ KEY_9_DOWN, /* 9键按下 */ KEY_9_UP, /* 9键弹起 */ KEY_9_LONG, /* 9键长按 */ KEY_10_DOWN, /* 10键按下 */ KEY_10_UP, /* 10键弹起 */ KEY_10_LONG, /* 10键长按 */ }KEY_ENUM; 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; #define KEY_FIFO_SIZE 10 typedef struct{ uint8_t Buf[KEY_FIFO_SIZE]; /* 键值缓冲区 */ uint8_t Read; /* 缓冲区读指针1 */ uint8_t Write; /* 缓冲区写指针 */ }KEY_FIFO_T; void key_init(void); void key_keyScan(void); uint8_t key_getKey(void); #endif /* KEY_H_ */
void StartTask02(void const * argument) { uint8_t key_value; /* Infinite loop */ for(;;) { key_keyScan(); key_value = key_getKey(); switch(key_value) { case 0: break; case 1: HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); break; case 4: HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); break; default: break; } osDelay(10); } }
#endif