单片机:实现用到中断的算法来实现按键消抖(附带源码)
单片机:实现用到中断的算法来实现按键消抖
1. 项目背景与目标
在嵌入式系统中,按键输入是非常常见的操作方式。由于机械按键的物理特性,按键在按下和松开时可能会产生多个快速的信号波动(即抖动),这会导致单片机误识别按键输入。按键的这种抖动现象通常称为“按键抖动”。
为了解决这个问题,可以使用按键消抖算法。按键消抖的核心思想是,在按键输入发生变化时,延时一段时间以确保按键稳定,避免因为抖动造成误触发。我们可以使用中断机制来提高按键检测的响应速度和效率,通过外部中断(或定时器中断)来捕获按键状态变化,并结合消抖算法过滤掉噪声信号。
本项目的目标是实现一个使用外部中断的按键消抖算法,保证按键输入稳定有效。
2. 硬件设计
2.1 硬件组件
- 单片机:例如STM32、8051、AVR等,本项目假设使用STM32单片机。
- 按键:一个普通的机械按键,用于模拟按键输入。
- LED:用作按键触发后的显示效果,表示按键按下的状态。
2.2 硬件连接
-
按键连接:
- 按键的一个端口连接到单片机的GPIO引脚(例如PA0),另一个端口连接到地(GND)。
- 按键的输入信号将触发外部中断。
-
LED连接:
- LED用于指示按键是否被成功识别并消抖,LED的一个端口连接到单片机的GPIO引脚(例如PA1)。
-
外部中断:
- 配置PA0(或其他引脚)为外部中断输入引脚,当按键按下时,会触发外部中断。
3. 软件设计
3.1 按键消抖的原理
当按键被按下或松开时,由于机械触点的物理特性,会发生短暂的抖动,导致多个触发信号。我们通过以下两种常见方式来进行消抖:
- 硬件消抖:通过加电容、滤波电路等硬件方法消除抖动。
- 软件消抖:在软件上,使用延时或计时器来确认按键状态是否稳定,避免抖动信号的干扰。
在本项目中,我们将使用软件消抖。通过外部中断触发,在中断服务函数(ISR)中设置一个小的延时或定时器来判断按键状态是否稳定。
3.2 程序设计思路
- 外部中断初始化:设置按键对应的GPIO引脚为外部中断模式,选择下降沿或上升沿触发。
- 中断服务函数:在中断服务函数中,首先禁用中断,进行短暂延时(消抖时间),然后再次读取按键状态。如果按键状态仍然有效(稳定),则执行相关操作(如点亮LED)。
- 消抖时间:根据按键的抖动频率,设定合理的消抖延时。一般来说,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 代码解释
- 外部中断初始化:
EXTI_Init()
函数配置了PA0为外部中断输入引脚,触发方式为下降沿(即按键按下时触发)。中断服务函数EXTI0_IRQHandler()
会在按键按下时被调用。 - 消抖处理:在中断服务函数中,首先禁用中断,避免中断嵌套,然后调用
HAL_Delay()
进行消抖。HAL_Delay(DEBOUNCE_TIME)
将延时50毫秒,以确保按键的物理抖动被滤除。如果按键状态依然为低电平(按下状态),则认为按键被稳定按下,反转LED的状态。 - LED控制:当按键按下时,LED灯的状态将被切换。如果LED亮,则按键被识别为按下;如果LED灭,则按键被识别为松开。
4. 仿真与测试
4.1 仿真设计
- 在Proteus或其他仿真工具中,创建一个STM32单片机项目。
- 配置一个机械按键连接到单片机的PA0引脚,另一个端口连接到地(GND)。
- 配置一个LED连接到单片机的PA1引脚。
4.2 仿真步骤
- 编译并上传代码到仿真环境。
- 在仿真中模拟按键按下和松开,观察LED是否在按键按下时发生变化,并且按键的抖动是否被消除。
5. 总结
本项目成功实现了一个基于中断的按键消抖算法。通过外部中断捕捉按键的变化并结合延时消抖,我们有效地避免了由于按键抖动导致的误触发。中断机制能够及时响应按键事件,而消抖延时确保按键状态稳定。这种方法在需要高精度按键输入的嵌入式系统中非常有用,尤其适用于计算器、遥控器、门禁系统等设备的按键控制。
作者:Katie。