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 外部中断信号链与按键消抖时序 按键 + 上拉 PA0 高→低 EXTI0 边沿检测 下降沿触发 NVIC 优先级仲裁 抢占1 / 子0 中断向量跳转 EXTI0_IRQHandler ISR 执行 消抖 + 清标志 按键抖动与消抖时序(示意) 高 低 t 抖动区 5~20ms 稳定按下 松开抖动 10ms 周期采样 确认有效 → 执行按键动作 中断嵌套示意 串口 ISR(抢占2) 按键 ISR(抢占1) 串口续 高抢占优先级可打断低优先级

中断服务函数编写

每个 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_22 位抢占 + 2 位子优先级
抢占/子优先级1 / 0高于串口中断
消抖延时10 ms覆盖典型抖动时间
确认条件延时后仍为低否则视为抖动丢弃
在 ISR 中调用 delay_ms() 若基于 SysTick 且 SysTick 优先级低于 EXTI,会导致延时无法推进而死等。教学示例中应确保延时函数不依赖被当前中断屏蔽的时基,或改用定时器扫描法。

验证按键中断是否正常,可在 ISR 中翻转一个 LED 或置位标志后由主循环打印计数。若发现一次按下计数增加多次,说明消抖不足;若完全无响应,依次检查 GPIO 时钟、AFIO 时钟、EXTI 线映射、NVIC 使能以及向量函数名是否与启动文件一致。掌握这条从引脚到 ISR 的完整链路,后续的串口、定时器和触摸中断都可以沿用相同的分析框架。