这一章在干嘛?
外部中断实验:NVIC 优先级分组、EXTI 配置、中断服务函数、按键中断触发 LED。理解后所有外设中断(串口/TIM/ADC)都有思路。
16.1 NVIC 和 EXTI 简介
16.1.1 NVIC 嵌套向量中断控制器
Cortex-M4 内核自带 NVIC,支持 256 个中断。F407 用了 82 个:
- 10 个系统异常(Reset / NMI / HardFault / …);
- 82 个可屏蔽中断(EXTI / TIM / USART / …)。
NVIC 中断优先级:
- 抢占优先级(pre-emption):高优先级能打断低优先级(嵌套);
- 响应优先级(sub):同时到达时谁先响应;
- STM32 分 5 组(0~4),实际用到的「抢占优先级位数」不同:
| 优先级分组 | 抢占优先级位数 | 子优先级位数 | 常用范围 |
|---|---|---|---|
| 0 | 0 bit | 4 bit | 0 |
| 1 | 1 bit | 3 bit | 0~1 |
| 2 | 2 bit | 2 bit | 0~3 |
| 3 | 3 bit | 1 bit | 0~7 |
| 4 | 4 bit | 0 bit | 0~15 |
原书图 16.1.1.1:NVIC 中断优先级分组
推荐分组 2(2 bit 抢占 + 2 bit 子),既支持嵌套又不复杂。
16.1.2 EXTI 外部中断
EXTI(External Interrupt)负责把 IO 电平变化转换为 NVIC 中断。
| 线路 | 来源 |
|---|---|
| EXTI0~EXTI15 | GPIOx_Pin0~15 |
| EXTI16 | PVD 输出 |
| EXTI17 | RTC 闹钟 |
| EXTI18 | USB OTG_FS 唤醒 |
| EXTI21 | RTC Tamper / TimeStamp |
| EXTI22 | RTC Wakeup |
每条 EXTI 只能连接到一个 GPIO 的一个 pin,例如 EXTI0 可绑 PA0/PB0/…/PI0 中任意一个。
16.2 硬件设计
使用 KEY0/KEY1/KEY_UP 接 PE4/PE3/PA0 作外部中断触发。LED0/LED1 接 PF9/PF10 作中断响应。
16.3 程序设计
16.3.1 编程步骤
- 开启 GPIOE / GPIOA / SYSCFG 时钟;
- 配置 GPIO 为输入模式;
HAL_NVIC_EnableIRQ(EXTI4_IRQn);HAL_NVIC_SetPriority(EXTI4_IRQn, 2, 0);设置优先级;HAL_GPIO_Init的 Mode 设为GPIO_MODE_IT_FALLING(下降沿触发);- 中断服务函数
EXTI4_IRQHandler→ 调HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4)→ 回调HAL_GPIO_EXTI_Callback。
16.3.2 关键代码
void EXTI4_IRQHandler(void) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
delay_ms(20);
if (GPIO_Pin == GPIO_PIN_4) { // KEY0
if (KEY0 == 0) LED0_TOGGLE();
}
}
原书图 16.3.2.1:外部中断触发流程图
中断里能做的事有限:不要调 HAL_Delay(SysTick 中断可能与 EXTI 中断冲突),用定时器延时;不要做耗时操作,只置标志位让 main 处理。
16.4 下载验证
按 KEY0 → LED0 翻转;KEY_UP → LED1 翻转。CPU 平时可做其他事,按下时才被「唤醒」。
中断标志位模式 最佳实践:中断回调里只 g_key0_pressed = 1; 在 main 里循环检查再处理。
抢占优先级与响应优先级的区别?
抢占可嵌套(高打断低),同抢占时响应决定顺序。
EXTI0 可以同时绑定 PA0 和 PB0 吗?
不能,每个 EXTI 编号只能绑一个 IO。
为什么中断里不能调 HAL_Delay?
HAL_Delay 依赖 SysTick 中断,而 EXTI 中断可能抢占 SysTick,导致卡死。