单片机:实现用到中断的算法来实现按键消抖(附带源码)

单片机:实现用到中断的算法来实现按键消抖

1. 项目背景与目标

在嵌入式系统中,按键输入是非常常见的操作方式。由于机械按键的物理特性,按键在按下和松开时可能会产生多个快速的信号波动(即抖动),这会导致单片机误识别按键输入。按键的这种抖动现象通常称为“按键抖动”。

为了解决这个问题,可以使用按键消抖算法。按键消抖的核心思想是,在按键输入发生变化时,延时一段时间以确保按键稳定,避免因为抖动造成误触发。我们可以使用中断机制来提高按键检测的响应速度和效率,通过外部中断(或定时器中断)来捕获按键状态变化,并结合消抖算法过滤掉噪声信号。

本项目的目标是实现一个使用外部中断的按键消抖算法,保证按键输入稳定有效。

2. 硬件设计
2.1 硬件组件
  1. 单片机:例如STM32、8051、AVR等,本项目假设使用STM32单片机。
  2. 按键:一个普通的机械按键,用于模拟按键输入。
  3. LED:用作按键触发后的显示效果,表示按键按下的状态。
2.2 硬件连接
  1. 按键连接

  2. 按键的一个端口连接到单片机的GPIO引脚(例如PA0),另一个端口连接到地(GND)。
  3. 按键的输入信号将触发外部中断。
  4. LED连接

  5. LED用于指示按键是否被成功识别并消抖,LED的一个端口连接到单片机的GPIO引脚(例如PA1)。
  6. 外部中断

  7. 配置PA0(或其他引脚)为外部中断输入引脚,当按键按下时,会触发外部中断。
3. 软件设计
3.1 按键消抖的原理

当按键被按下或松开时,由于机械触点的物理特性,会发生短暂的抖动,导致多个触发信号。我们通过以下两种常见方式来进行消抖:

  1. 硬件消抖:通过加电容、滤波电路等硬件方法消除抖动。
  2. 软件消抖:在软件上,使用延时或计时器来确认按键状态是否稳定,避免抖动信号的干扰。

在本项目中,我们将使用软件消抖。通过外部中断触发,在中断服务函数(ISR)中设置一个小的延时或定时器来判断按键状态是否稳定。

3.2 程序设计思路
  1. 外部中断初始化:设置按键对应的GPIO引脚为外部中断模式,选择下降沿或上升沿触发。
  2. 中断服务函数:在中断服务函数中,首先禁用中断,进行短暂延时(消抖时间),然后再次读取按键状态。如果按键状态仍然有效(稳定),则执行相关操作(如点亮LED)。
  3. 消抖时间:根据按键的抖动频率,设定合理的消抖延时。一般来说,10ms至50ms的延时足够处理大多数按键的抖动。
3.3 代码实现

以下是基于STM32单片机的按键消抖实现代码。代码中使用外部中断来检测按键状态,并通过延时实现消抖。

#include "stm32f4xx_hal.h"

// 定义按键引脚和LED引脚
#define KEY_PIN     GPIO_PIN_0
#define LED_PIN     GPIO_PIN_1
#define KEY_PORT    GPIOA
#define LED_PORT    GPIOA

// 按键消抖时间(单位:毫秒)
#define DEBOUNCE_TIME  50

// 标志位,用于控制LED状态
volatile uint8_t key_pressed = 0;

// 外部中断初始化
void EXTI_Init(void) {
    __HAL_RCC_SYSCFG_CLK_ENABLE();   // 使能系统配置时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();    // 使能GPIOA时钟

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = KEY_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // 设置为下降沿触发
    GPIO_InitStruct.Pull = GPIO_NOPULL;           // 不使用上拉或下拉电阻
    HAL_GPIO_Init(KEY_PORT, &GPIO_InitStruct);    // 初始化GPIOA的PA0引脚为外部中断

    // 配置中断优先级并使能外部中断
    HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);  // 设置优先级
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);           // 使能外部中断0(PA0引脚)
}

// 外部中断服务函数
void EXTI0_IRQHandler(void) {
    // 进入中断时禁用中断
    __HAL_GPIO_EXTI_CLEAR_IT(KEY_PIN);  // 清除中断标志位
    HAL_NVIC_DisableIRQ(EXTI0_IRQn);    // 禁用当前中断,避免中断嵌套

    // 延时消抖
    HAL_Delay(DEBOUNCE_TIME);   // 延时50ms进行消抖

    // 检测按键是否仍然按下(稳定状态)
    if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_RESET) {
        // 按键按下,改变LED状态
        key_pressed = !key_pressed;
        if (key_pressed) {
            HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET);  // 点亮LED
        } else {
            HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET); // 熄灭LED
        }
    }

    // 恢复中断
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);  // 重新使能外部中断
}

// GPIO初始化
void GPIO_Init(void) {
    __HAL_RCC_GPIOA_CLK_ENABLE();  // 使能GPIOA时钟

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = LED_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  // 设置为推挽输出
    GPIO_InitStruct.Pull = GPIO_NOPULL;          // 不使用上拉或下拉电阻
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 设置GPIO速度
    HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);   // 初始化LED引脚
}

// 主程序
int main(void) {
    HAL_Init();  // 初始化HAL库
    GPIO_Init(); // 初始化GPIO
    EXTI_Init(); // 初始化外部中断

    while (1) {
        // 主循环可以做其他任务
        // 这里不做任何事,等待中断触发
    }
}
3.4 代码解释
  1. 外部中断初始化EXTI_Init()函数配置了PA0为外部中断输入引脚,触发方式为下降沿(即按键按下时触发)。中断服务函数EXTI0_IRQHandler()会在按键按下时被调用。
  2. 消抖处理:在中断服务函数中,首先禁用中断,避免中断嵌套,然后调用HAL_Delay()进行消抖。HAL_Delay(DEBOUNCE_TIME)将延时50毫秒,以确保按键的物理抖动被滤除。如果按键状态依然为低电平(按下状态),则认为按键被稳定按下,反转LED的状态。
  3. LED控制:当按键按下时,LED灯的状态将被切换。如果LED亮,则按键被识别为按下;如果LED灭,则按键被识别为松开。
4. 仿真与测试
4.1 仿真设计
  1. 在Proteus或其他仿真工具中,创建一个STM32单片机项目。
  2. 配置一个机械按键连接到单片机的PA0引脚,另一个端口连接到地(GND)。
  3. 配置一个LED连接到单片机的PA1引脚。
4.2 仿真步骤
  1. 编译并上传代码到仿真环境。
  2. 在仿真中模拟按键按下和松开,观察LED是否在按键按下时发生变化,并且按键的抖动是否被消除。
5. 总结

本项目成功实现了一个基于中断的按键消抖算法。通过外部中断捕捉按键的变化并结合延时消抖,我们有效地避免了由于按键抖动导致的误触发。中断机制能够及时响应按键事件,而消抖延时确保按键状态稳定。这种方法在需要高精度按键输入的嵌入式系统中非常有用,尤其适用于计算器、遥控器、门禁系统等设备的按键控制。

作者:Katie。

物联沃分享整理
物联沃-IOTWORD物联网 » 单片机:实现用到中断的算法来实现按键消抖(附带源码)

发表回复