3. 中断系统与EXTI外部中断
在轮询方式下,CPU 必须不断读取按键、串口或传感器状态,效率低且响应延迟不可控。中断机制让外设或外部事件在满足条件时主动打断 CPU 当前执行流,跳转到预先注册的中断服务函数(ISR)处理事件,处理完再返回原位置继续执行。STM32 的中断链路可以概括为:事件源 → EXTI 边沿检测 → NVIC 优先级仲裁 → 中断向量 → ISR 执行 → 清除标志返回。理解这条链路,是编写可靠按键中断的基础。
NVIC 优先级管理
NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器)负责管理所有可屏蔽中断的使能、挂起与优先级。Cortex-M 内核为每个中断分配一个 8 位优先级寄存器,STM32 实际实现通常只使用高 4 位,因此可配置 16 级优先级(0~15,数值越小优先级越高)。这 4 位又被分组为抢占优先级和子优先级两部分,由 NVIC_PriorityGroupConfig() 决定如何划分。
抢占优先级决定“能否嵌套”:高抢占优先级的中断可以打断正在执行的低抢占优先级 ISR,形成中断嵌套。子优先级只在多个中断同时挂起、抢占优先级相同时决定谁先执行,它不能造成嵌套。常见分组为 NVIC_PriorityGroup_2,即 2 位抢占 + 2 位子优先级,各 4 级。例如按键中断设为抢占 1、子 0,串口接收设为抢占 2、子 0,则按键可以打断串口 ISR。
EXTI 中断触发方式
EXTI(External Interrupt/Event Controller)负责检测 GPIO 引脚上的电平跳变。每条 EXTI 线可独立配置为上升沿触发、下降沿触发或双边沿触发。触发方式的选择取决于电路:按键一端接 GPIO、另一端接 GND,并启用内部上拉时,按下瞬间引脚由高变低,应选下降沿;若按键接 VCC 并下拉,则按下为上升沿。机械按键在按下和松开瞬间会产生 5~20 ms 的抖动,若使用双边沿触发,一次按键可能进入多次中断,因此按键场景通常只选单边沿,再配合软件消抖。
EXTI 线与 GPIO 的映射关系需要特别注意:STM32 中 EXTI0~EXTI15 分别对应每个端口的 Pin0~Pin15,即 PA0、PB0、PC0 共用 EXTI0 线,同一时刻只能有一个端口映射到该线。配置时通过 AFIO(或 SYSCFG)的 EXTI_InitStructure.EXTI_Line 与 GPIO_EXTILineConfig() 完成绑定。
中断服务函数编写
每个 EXTI 线都有固定的中断向量名,例如 EXTI0 对应 EXTI0_IRQHandler,EXTI15_10 对应 EXTI15_10_IRQHandler。函数名必须与启动文件中的向量表一致,否则中断触发后会跳转到默认死循环。ISR 内部必须完成两件事:判断具体是哪条线触发(多条线共用向量时),以及清除 EXTI 挂起标志。若忘记清除标志,中断会反复进入。
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) != RESET)
{
/* 软件消抖:延时后再次确认电平 */
delay_ms(10);
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET)
{
key_flag = 1; /* 仅置标志,主循环处理业务 */
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
ISR 应尽量短小:只做标志置位、数据搬运等必要动作,把耗时业务交给主循环。在 ISR 中调用长延时、打印或复杂运算,会阻塞其他中断并影响系统实时性。
按键中断响应与消抖处理
机械按键抖动是按键中断最典型的问题。若在 ISR 中直接执行动作,一次按下可能触发多次。常用消抖方案有三种:
- 延时确认法:进入 ISR 后延时约 10 ms 再读引脚,电平仍为有效状态才确认。实现简单,但会占用中断时间。
- 定时器扫描法:EXTI 只置标志,由 10 ms 周期定时器中断读取引脚并计数,连续多次稳定才确认。实时性好,适合多按键。
- 状态机法:为每个按键维护“空闲—抖动—按下—释放”状态,按采样节拍迁移,可同时处理短按与长按。
下面给出一个可直接参考的按键中断配置与消抖参数表,参数为常见教学示例值,实际应根据按键手册和电路调整。
| 配置项 | 示例值 | 说明 |
|---|---|---|
| GPIO 模式 | PA0 上拉输入 | 按键接 GND,按下为低 |
| EXTI 线 | EXTI_Line0 | 与 PA0 绑定 |
| 触发方式 | 下降沿 | 按下瞬间触发 |
| NVIC 分组 | Group_2 | 2 位抢占 + 2 位子优先级 |
| 抢占/子优先级 | 1 / 0 | 高于串口中断 |
| 消抖延时 | 10 ms | 覆盖典型抖动时间 |
| 确认条件 | 延时后仍为低 | 否则视为抖动丢弃 |
delay_ms() 若基于 SysTick 且 SysTick 优先级低于 EXTI,会导致延时无法推进而死等。教学示例中应确保延时函数不依赖被当前中断屏蔽的时基,或改用定时器扫描法。验证按键中断是否正常,可在 ISR 中翻转一个 LED 或置位标志后由主循环打印计数。若发现一次按下计数增加多次,说明消抖不足;若完全无响应,依次检查 GPIO 时钟、AFIO 时钟、EXTI 线映射、NVIC 使能以及向量函数名是否与启动文件一致。掌握这条从引脚到 ISR 的完整链路,后续的串口、定时器和触摸中断都可以沿用相同的分析框架。