当前位置: 首页 > news >正文

【备赛】按键消抖+长短按键区分

一、按键消抖

这里记录 状态机 + 系统滴答定时器 的方法来实现按键消抖。

这是一种非阻塞式的方式实现,比较不错。

周期性检测 → 记录时间戳 → 超时后确认 → 处理释放。

// 按键状态定义
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 被完全占用

  • 后果

    • 系统无法响应其他任务(如屏幕刷新、通信)

    • 若按键被意外卡住,程序将永久死锁

相关文章:

  • AI工具箱最新使用教程
  • 【有啥问啥】All-to-All 通信:原理、实现与应用
  • YOLOv8架构中的SAConv空洞卷积机制:性能优化与未来发展方向
  • C++ 线程库使用详解
  • 局部适应的分子标记筛选
  • 京准电钟:NTP精密时钟服务器在自动化系统中的作用
  • 2025年2月最新一区SCI-海市蜃楼搜索优化算法Mirage search optimization-附Matlab免费代码
  • 5、进程间有哪些通信方式【高频】
  • 蓝桥杯之日期问题2
  • 前端如何进行性能优化
  • Hbase分布式——储存机制
  • 人工智能中的特征是什么?
  • MySQL常用命令大全(可复制使用)
  • 深入理解 JavaScript 中的 this 指向
  • android计算屏幕尺寸dpi
  • Java设计模式-基于MVC的WEB设计模式
  • 【Springboot知识】Logback从1.2.x升级到1.3.x需要注意哪些点?
  • 谈谈 ES 6.8 到 7.10 的功能变迁(5)- 任务和集群管理
  • MotionLayout(二):MotionLayout是什么?MotionLayout调试技巧、KeyFrame关键帧等等
  • Desktop Builder 操作概述
  • 王毅同巴基斯坦副总理兼外长达尔通电话
  • 中美经贸高层会谈在瑞士日内瓦开始举行
  • 河北邯郸一酒店婚宴发生火灾:众人惊险逃生,酒店未买保险
  • 道指跌逾100点,特斯拉涨近5%
  • 巴基斯坦称对印度发起军事行动
  • 冯德莱恩:欧美贸易谈判前不会前往美国会见特朗普