EXTI+定时器消抖:按键中断的正确姿势
短文标题:EXTI+定时器消抖:按键中断的正确姿势
你有没有想过一个问题:按键一按,中断触发了。但按下瞬间触点弹跳,中断进了好几次——怎么办?EXTI检测边沿 + 定时器延时消抖。这才是按键中断的标准姿势。EXTI配置三步骤
1. GPIO配置:输入模式,上拉/下拉(确保不浮空)
GPIOA->CRL &= ~(0x0F << 0);
GPIOA->CRL |= (0x08 << 0); // 上拉/下拉输入模式
GPIOA->ODR |= (1 << 0); // 使能内部上拉
2. AFIO配置:选择GPIO端口连接到EXTI线
AFIO->EXTICR[0] &= ~(0x0F << 0);
AFIO->EXTICR[0] |= (0x00 << 0); // PA0→EXTI0
3. EXTI配置:边沿触发 + 中断使
EXTI->FTSR |= (1 << 0); // 下降沿触发(按键按下)
EXTI->IMR |= (1 << 0); // 中断使能
NVIC_EnableIRQ(EXTI0_IRQn); // NVIC使能
按键中断 + 定时器消抖(标准流程)中断服务函数里只做一件事:启动定时器延时20ms,不清除挂起位会反复触发。
void EXTI0_IRQHandler(void)
{
if (EXTI->PR & (1 << 0)) {
EXTI->PR = (1 << 0); // 清挂起位
TIM6->ARR = 20000; // 20ms @ 1MHz
TIM6->CR1 |= TIM_CR1_CEN; // 启动定时器
}
}
定时器中断里确认按键状态:
void TIM6_IRQHandler(void)
{
if (TIM6->SR & TIM_SR_UIF) {
TIM6->SR &= ~TIM_SR_UIF;
if (GPIO_ReadInputPin(GPIOA, GPIO_PIN_0) == 0) {
// 按键真的按下了,执行动作
}
}
}
原理:中断触发后等20ms(躲过抖动期),再读引脚电平确认。真按下了才处理,抖动被滤除。为什么不能直接在EXTI中断里延时?
void EXTI0_IRQHandler(void)
{
HAL_Delay(20); // ❌ 大忌!
}
- 阻塞其他中断:高优先级中断进不来
- 浪费CPU:20ms空转
- 按键响应变慢:中断处理时间太长
中断线共享(教程5.12节),EXTI0连接PA0、PB0、PC0……多个引脚共享同一中断线。需要多个中断引脚时,分散到不同编号(PA0、PA1、PA2)。如果只能同编号,在中断里读各引脚电平判断。优先级配置建议(教程5.6节)按键中断的抢占优先级设中低(如3~5),不要设太高(0留给紧急任务如过流保护)。优先级太高会阻塞其他关键中断。
这个故事的启示,EXTI中断不是“检测到边沿就处理按键”,而是“检测到边沿启动消抖流程”。中断只做标记,定时器负责确认。别在中断里做耗时的事。
写在最后,按键中断的正确姿势:EXTI触发→清挂起→启动定时器→定时器确认→处理按键。三步走,抖动去无踪。
(本文灵感源于于振南《新概念ARM32单片机》教程第5.6节、第5.12节、第6.1节。)
觉得有用?点赞、转发,让更多人学会EXTI+定时器消抖的标准流程。
