【备赛】按键消抖+长短按键区分
一、按键消抖
这里记录 状态机 + 系统滴答定时器 的方法来实现按键消抖。
这是一种非阻塞式的方式实现,比较不错。
周期性检测 → 记录时间戳 → 超时后确认 → 处理释放。
// 按键状态定义
typedef enum {
KEY_STATE_IDLE,//空
KEY_STATE_DEBOUNCE,//防抖
KEY_STATE_PRESSED//压
} KeyState;
KeyState key_state = KEY_STATE_IDLE;
uint32_t key_timestamp = 0;
void key_scan()
{
//按键一
switch(key_state)
{
case KEY_STATE_IDLE://状态一
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
{
key_state = KEY_STATE_DEBOUNCE;
key_timestamp = HAL_GetTick();//记录当前时间戳
}
break;
case KEY_STATE_DEBOUNCE://状态二
if(HAL_GetTick() - key_timestamp >= 20)
{
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
{
key_state = KEY_STATE_PRESSED;
//执行任务
//led_show(1);
count ++ ;
}
else {key_state = KEY_STATE_IDLE;}//抖动误判
}
break;
case KEY_STATE_PRESSED://状态三
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) != GPIO_PIN_RESET)
{
key_state = KEY_STATE_IDLE;
}
break;
}
//按键二
//按键三
//按键四
}
二、区分长按键和短按键
// 状态定义(修正注释)
typedef enum {
KEY_STATE_IDLE, // 空闲
KEY_STATE_PRESS_DEBOUNCE,// 按下消抖
KEY_STATE_PRESSED, // 已确认按下(等待长按触发)
KEY_STATE_LONG_PRESS, // 长按已触发
KEY_STATE_RELEASE_DEBOUNCE // 释放消抖
} KeyState;
KeyState key_state = KEY_STATE_IDLE;
uint32_t key_press_start_time = 0;
uint32_t key_release_start_time = 0;
void key_scan() {
switch(key_state) {
// 初始状态:检测按下
case KEY_STATE_IDLE:
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
key_state = KEY_STATE_PRESS_DEBOUNCE;
key_press_start_time = HAL_GetTick();
}
break;
// 按下消抖(等待20ms)
case KEY_STATE_PRESS_DEBOUNCE:
if (HAL_GetTick() - key_press_start_time >= 20) {
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
key_state = KEY_STATE_PRESSED; // 进入持续检测状态
key_press_start_time = HAL_GetTick(); // 重置计时
} else {
key_state = KEY_STATE_IDLE; // 抖动误判
}
}
break;
// 已确认按下:持续检测时长
case KEY_STATE_PRESSED:
// 持续按下超过1秒 → 触发长按
if (HAL_GetTick() - key_press_start_time >= 1000) {
key_state = KEY_STATE_LONG_PRESS;
count--; // 长按操作
printf("Long Press Detected!\r\n");
}
// 检测是否提前释放
else if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) != GPIO_PIN_RESET) {
key_state = KEY_STATE_RELEASE_DEBOUNCE;
key_release_start_time = HAL_GetTick();
count++; // 单击操作
printf("Short Press Detected!\r\n");
}
break;
// 长按已触发状态
case KEY_STATE_LONG_PRESS:
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) != GPIO_PIN_RESET) {
key_state = KEY_STATE_RELEASE_DEBOUNCE;
key_release_start_time = HAL_GetTick();
}
break;
// 释放消抖(20ms)
case KEY_STATE_RELEASE_DEBOUNCE:
if (HAL_GetTick() - key_release_start_time >= 20) {
key_state = KEY_STATE_IDLE;
}
break;
}
}
三、按键消抖老方案(有问题)
一般我会用HAL_Delay();函数。
//B1按键
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET) //检测按键是否按下
{
HAL_Delay(5000); // 消抖
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET) // 说明确实按下去了
{
执行任务
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET); // 等待按键释放
}
}
原代码问题分析
阻塞式设计
while(HAL_GPIO_ReadPin(...)); // 卡死在这里等待释放
-
问题:在等待按键释放时,CPU 被完全占用
-
后果:
-
系统无法响应其他任务(如屏幕刷新、通信)
-
若按键被意外卡住,程序将永久死锁
-