中断(NVIC)
1. 基本概念
-
NVIC = Nested Vectored Interrupt Controller,嵌套向量中断控制器,Cortex-M4 内核外设。
-
F407 有 10 个系统异常 (复位、NMI、HardFault、SysTick 等)+ 82 个外部中断(USART、SPI、EXTI...)。
-
中断号在
IRQn_Type枚举里:负数 = 系统异常,0 以上 = 外部中断。
2. 优先级
F407 只用 4 位优先级(0~15),数字越小越优先。
| 作用 | 能否打断 | |
|---|---|---|
| 抢占优先级 | 高的打断低的(中断嵌套) | 能 |
| 子优先级 | 抢占相同时决定谁先响应 | 不能 |
优先级分组(4 位怎么切)
由 HAL_NVIC_SetPriorityGrouping() 设置,CubeMX 默认 Group_4(4 位全给抢占)。
| 分组 | 抢占位 | 子优先级位 | 抢占范围 | 子优先级范围 |
|---|---|---|---|---|
| Group_0 | 0 | 4 | 无 | 0~15 |
| Group_1 | 1 | 3 | 0~1 | 0~7 |
| Group_2 | 2 | 2 | 0~3 | 0~3 |
| Group_3 | 3 | 1 | 0~7 | 0~1 |
| Group_4(默认) | 4 | 0 | 0~15 | 0 |
3. 配中断就 3 步
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); // ① 分组(默认,一般不用调)
HAL_NVIC_SetPriority(USART1_IRQn, 2, 0); // ② 设优先级:抢占2,子0
HAL_NVIC_EnableIRQ(USART1_IRQn); // ③ 开 NVIC 门
两层门都要开:
| 层 | 谁管 | 作用 |
|---|---|---|
| NVIC 层 | HAL_NVIC_EnableIRQ() |
CPU 允许响应这个中断 |
| 外设层 | HAL_UART_Receive_IT() / 外设自己的使能位 |
外设事件发生时触发中断 |
4. 中断服务函数(ISR)
在 stm32f4xx_it.c 里,函数名必须和启动文件向量表里的完全一致。
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1); // CubeMX 已生成,不用自己读寄存器
}
-
名字写错 → 跳到启动文件里的空死循环,中断失效。
-
HAL_UART_IRQHandler()帮你判断中断类型、清标志、调回调。
5. 回调机制(以串口接收为例)
串口收到一个字节
→ CPU 跳 USART1_IRQHandler() CubeMX 已写
→ HAL_UART_IRQHandler() HAL 已写
→ 判断是接收完成
→ HAL_UART_RxCpltCallback() 你重写,HAL 自动调
你要做的就两件事
// ① main 里开接收中断
uint8_t rx_byte;
HAL_UART_Receive_IT(&huart1, &rx_byte, 1);
// ② 重写回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1) {
// rx_byte 就是收到的字节
HAL_UART_Receive_IT(&huart1, &rx_byte, 1); // 重新开,等下一个
}
}
单次模式:收完一个字节外设中断自动关闭,不重新调
Receive_IT()就收不到下一个。
6. 函数分类总结
| 类型 | 例子 | 谁调 |
|---|---|---|
| CubeMX 生成 | USART1_IRQHandler() |
中断来了 CPU 自动跳 |
| HAL 库自带 | HAL_UART_IRQHandler() |
ISR 里调 |
| HAL 库自带,你调 | HAL_NVIC_SetPriority() / EnableIRQ() / HAL_UART_Receive_IT() |
main 里调 |
| HAL 空壳,你重写 | HAL_UART_RxCpltCallback() |
HAL 自动调 |
7. CubeMX 里配中断
-
优先级分组 :左侧
System Core→NVIC→Priority Group选4 bits(默认Group_4,4位全给抢占,一般不用改) -
开中断:在每个外设自己的NVIC Settings里勾对应中断(如USART1页面勾USART1 global interrupt),不是在System Core→NVIC里统一开
-
优先级数值:勾完中断后在同一页面填Preemption Priority和Sub Priority,数字越小越优先
-
HAL_Init()里已设好分组 :
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4),一般不用手动调