目标:理解优先级分组与嵌套,实现 EXTI 外部中断和 UART 中断接收
NVIC 优先级分组(由 SCB->AIRCR 寄存器的 PRIGROUP 位决定)。STM32 把中断优先级分为抢占优先级和子优先级两部分,但两者的位数比例可以调整:
| 分组 | 抢占优先级位数 | 子优先级位数 | 可配置优先级数量 |
|---|---|---|---|
NVIC_PRIORITYGROUP_0 |
0 | 4 | 抢占: 1种, 子: 16级 |
NVIC_PRIORITYGROUP_1 |
1 | 3 | 抢占: 2种, 子: 8级 |
NVIC_PRIORITYGROUP_2 |
2 | 2 | 抢占: 4种, 子: 4级 |
NVIC_PRIORITYGROUP_3 |
3 | 1 | 抢占: 8种, 子: 2级 |
NVIC_PRIORITYGROUP_4(默认) |
4 | 0 | 抢占: 16种, 子: 1级 |
数值越小,优先级越高!("越小越优先"是 ARM Cortex 的统一规则)
中断排队:多个中断等待 CPU 执行,NVIC 根据优先级决定谁先执行。
中断嵌套:一个中断正在执行时,另一个更高优先级的中断到来,暂停当前中断去执行更高的中断。
判定规则:
抢占优先级不同 → 高抢占优先级可以嵌套低抢占优先级的中断
抢占优先级相同 → 不嵌套,按子优先级排队
子优先级也相同 → 按中断向量号(硬件固定),号小的先执行
优先级分组 Group 2(2位抢占 + 2位子优先级)。五个中断按到达时间排列:
| 中断 | 抢占优先级 | 子优先级 | 到达顺序 |
|---|---|---|---|
| A | 1 | 0 | ① |
| B | 0 | 1 | ② |
| C | 0 | 2 | ③ |
| D | 2 | 0 | ④ |
| E | 3 | 0 | ⑤ |
执行流(数值越小优先级越高):
① A(抢占1) 开始执行
└─ ② B(抢占0) 到来 → 0 > 1 → **嵌套!暂停 A,B 开始**
└─ ③ C(抢占0, 子2) 到来 → 同组不嵌套 → 排队(子2 < B的子1,C在B之后)
└─ B 完成 → C 开始执行
└─ C 完成 → **回到 A**(A 被抢占后恢复)
└─ ④ D(抢占2) 到来 → 2 < 1 → 排队(D在A后面)
└─ A 完成 → D 开始 → ⑤ E(抢占3) 在 D 后面排队
最终执行顺序:A → B → C → A(恢复) → D → E
CubeMX → NVIC Settings 中:
HAL_NVIC_SetPriority(IRQn, PreemptPriority, SubPriority) 或在 CubeMX 中图形化设置| 函数 | 说明 |
|---|---|
HAL_GPIO_EXTI_IRQHandler(PIN) |
ISR 中调用,检测边沿并调回调 |
HAL_GPIO_EXTI_Callback(PIN) |
用户重写此回调(弱函数) |
/* stm32f1xx_it.c — 中断服务函数 */
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}
/* main.c — 回调函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_0)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
}
}
已在 04-UART串口.md 详细说明,核心模式:
HAL_UART_Receive_IT() 启动中断接收
↓
硬件接收 1 字节 → 触发中断
↓
HAL_UART_RxCpltCallback() 被调用
↓
用户在回调中重新调用 HAL_UART_Receive_IT()
stm32f1xx_it.c,不要直接修改__weak 弱函数),不要改 ISRHAL_Delay()(它依赖 SysTick 中断,同优先级可能导致死锁)