软件按键消抖的几种方式

按键消抖的几种方式说明

此工程是:1.按键中断消抖 2.按键定时器中断

软件按键消抖实现方式1

  • 循环阻塞判断(浪费CPU资源)

    复制代码
    int main(void)
    {
        while (1)
        {
            if (HAL_GPIO_ReadPin(Button1_GPIO_Port, Button1_Pin) == GPIO_PIN_RESET)
            {
                HAL_Delay(20);
                if(HAL_GPIO_ReadPin(Button1_GPIO_Port, Button1_Pin) == GPIO_PIN_RESET)
                {
                    printf("Key 1 pressed.\n");
                    HAL_GPIO_TogglePin(Led1_GPIO_Port, Led1_Pin);
                    while(HAL_GPIO_ReadPin(Button1_GPIO_Port, Button1_Pin) == GPIO_PIN_RESET);      // 等待按键松开
                }
            }
        }
    }

软件按键消抖实现方式2

  • EXTI外部中断
    1.将按键GPIO设置为外部中断输入方式,中断捕获类型可根据实际电路设置为上升沿或下降沿,这里我们配置为内部上拉、下降沿中断方式。
    2.设置中断优先级,打开中断
    3.写中断回调函数(小心有坑)

    复制代码
    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
        if(GPIO_Pin == KEY1_Pin){
            Delay_ms(20);   //注意此外部中断优先级比SysTick高的时候,千万不要使用HAL_Delay函数,否则会由于低优先级的嘀嗒中断发生在高优先级的外部中断而死机。
            if(GPIO_Pin == KEY1_Pin){
                // 认为按键已经按下,执行逻辑
            }
        }
    }
  • 另一种方案,将延时放在中断服务函数中
    那问题来了,通过上面一步一步分析HAL库的I/O中断处理过程,就知道在用户处理函数之前的"HAL_GPIO_EXTI_IRQHandler()"确认中断端口中就已经将中断标志位消除了(在用户处理函数之前),意味着抖动仍然能触发中断。然后再通过阅读相关文档,发现STM32中断是依靠向量表机制,也就是说只要触发了中断,一般情况下总是要去响应和清除相应的中断标志位。所以我认为在用户处理函数这么做可能可以解决问题,但以我个人经验,效果并不是很好,原因就是解决问题的方法不太对。
    个人认为更正确的做法是在清除标志位之前延迟等待抖动消失,防止因抖动在此将中断标志位置为有效。即需要修改HAL库(Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio.c)中的"HAL_GPIO_EXTI_IRQHandler"函数。如图,在"__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);"之前添加延迟"Delay_ms((uint32_t)20);"

软件按键消抖实现方式3

  • 定时器中断
    使用定时器每隔10ms读取一次按键的状态,然后进行判断。

    // 按键状态枚举
    typedef enum {
    KEY_STATE_RELEASE = 0x00, // 按键未按下
    KEY_STATE_SHAKE = 0x01, // 按键抖动检测中
    KEY_STATE_PRESS = 0x02 // 按键确认按下
    } KeyState_TypeDef;

    // 按键数据结构
    typedef struct {
    GPIO_TypeDef *Port; // 按键GPIO端口
    uint16_t Pin; // 按键GPIO引脚
    KeyState_TypeDef State; // 当前状态
    void (*PressCallback)(void); // 按下后的回调函数
    } Key_TypeDef;

    // 假设你有一个按键Key1
    Key_TypeDef Key1 = {KEY1_GPIO_Port, KEY1_Pin, KEY_STATE_RELEASE, NULL};

    // 按键扫描状态机函数(需在定时中断中周期调用,如10ms一次)
    void Key_Scan_Handler(Key_TypeDef *Key)
    {
    uint8_t current_level = HAL_GPIO_ReadPin(Key->Port, Key->Pin);

    复制代码
    switch (Key->State) {
      // 状态1:初始释放状态
      case KEY_STATE_RELEASE:
        if (current_level == GPIO_PIN_RESET) { // 检测到低电平(按下)
          Key->State = KEY_STATE_SHAKE;        // 转入消抖状态
        }
        break;
    
      // 状态2:消抖状态
      case KEY_STATE_SHAKE:
        if (current_level == GPIO_PIN_RESET) { // 再次确认仍是低电平
          Key->State = KEY_STATE_PRESS;        // 确认按键有效按下
          if (Key->PressCallback != NULL) {
            Key->PressCallback();  // 执行按下回调函数
          }
        } else {
          Key->State = KEY_STATE_RELEASE; // 是抖动,返回释放状态
        }
        break;
    
      // 状态3:按下状态
      case KEY_STATE_PRESS:
        if (current_level == GPIO_PIN_SET) { // 检测到按键已释放(高电平)
          Key->State = KEY_STATE_RELEASE;    // 返回释放状态
        }
        break;
    
      default:
        Key->State = KEY_STATE_RELEASE;
        break;
    }

    }

    // 定时器更新中断回调函数
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
    if (htim->Instance == TIM6_Instance) { // 假设使用TIM6
    Key_Scan_Handler(&Key1); // 周期执行按键扫描
    }
    }

总结

1.中断处理时间越短越好,不然严重浪费CPU资源

2.stm32 hal库 HAL_Delay用的 滴答定时器 延时的,并默认是 优先级最低的,如果外部中断 优先级高于或等于 滴答定时器优先级,会造成中断里面的HAL_Delay无限延时,造成锁死退不出去!

3.保险起见,还是别用HAL_Delay了

相关推荐
无人装备硬件开发爱好者6 小时前
STM32G474 + 1.32 寸 OLED(128×96)俄罗斯方块游戏实现指南
stm32·嵌入式硬件·游戏
三佛科技-134163842126 小时前
SM2850P无电感离线稳压器 5V输出 典型应用电路分析(管脚、关键设计要点)
单片机·嵌入式硬件·物联网·智能家居·pcb工艺
潜创微科技7 小时前
IT6636+USB 协同芯片 3 进 1 出 HDMI2.1 KVM 切换器一体化方案
嵌入式硬件·音视频
dqsh068 小时前
关于STM32G474芯片有规律的自动重启的问题
stm32·单片机·嵌入式硬件·系统重启·原因解析
时空自由民.8 小时前
BLDC无刷直流电机作为发电机的波形图
单片机
JSMSEMI118 小时前
JSM63006 5A 28V三相无刷电机驱动电路
单片机·嵌入式硬件
国产芯片设计8 小时前
【LCD驱动实战】单颗YL1621脚位不足?双芯片联动驱动方案详解
stm32·单片机·mcu·51单片机·硬件工程
不怕犯错,就怕不做9 小时前
RK3562的CPU如何降频及关闭硬件编解码
linux·驱动开发·嵌入式硬件
Hical_W9 小时前
Hical 踩坑实录五部曲(二):MSVC / GCC / Clang 三平台 C++20 编译差异
linux·windows·经验分享·嵌入式硬件·macos·开源·c++20
bubiyoushang88811 小时前
基于 Freescale S12 单片机的 Bootloader 开发
单片机·嵌入式硬件·mongodb