一、什么是中断优先级?
想象一下你在看书,同时手机响了,水也烧开了——你需要决定先处理哪件事。中断优先级就是帮单片机做这个决定的规则。
在STM32F407中,有:
抢占优先级:就像插队权限,高抢占可以打断低抢占
子优先级:当抢占相同时,谁先执行
二、优先级分组(最重要的一步!)
STM32F407把4位优先级分成两部分,有5种分法:
| 分组 | 抢占位数 | 子优先级位数 | 抢占级数 | 子优先数 |
|---|---|---|---|---|
| 0 | 0位 | 4位 | 1个级别 | 16个级别 |
| 1 | 1位 | 3位 | 2个级别 | 8个级别 |
| 2 | 2位 | 2位 | 4个级别 | 4个级别 |
| 3 | 3位 | 1位 | 8个级别 | 2个级别 |
| 4 | 4位 | 0位 | 16个级别 | 1个级别 |
记住:先选分组,再设优先级!
三、设置三步曲
第1步:选择分组(整个程序只设一次)
// 常用分组2:4个抢占级别,每个抢占内有4个子优先级 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);第2步:配置具体外设的中断
// 以串口1中断为例 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // 中断源 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能 NVIC_Init(&NVIC_InitStructure);第3步:编写中断服务函数
void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { // 处理接收数据 USART_ClearITPendingBit(USART1, USART_IT_RXNE); } }四、实用规则(记住这些就够用了)
规则1:先选固定分组
推荐新手用分组2:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 这样有:抢占优先级0-3,子优先级0-3 // 数字越小,优先级越高规则2:重要中断设高抢占
// 紧急的(如看门狗):抢占优先级设小数字 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 普通的(如串口):抢占优先级设大数字 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;规则3:同类型中断用子优先级区分
// 两个串口,抢占相同,用子优先级区分 串口1:抢占=2,子优先级=0 // 优先处理 串口2:抢占=2,子优先级=1 // 稍后处理规则4:系统中断的固定优先级
复位:优先级-3(最高)
硬件错误:优先级-2
不可屏蔽中断:优先级-1
五、完整示例:按键和串口中断
#include "stm32f4xx.h" int main(void) { // 第1步:设置分组(整个程序只调用一次!) NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 第2步:配置按键中断(外部中断0) NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 高抢占 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // 第3步:配置串口1中断 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 低抢占 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // 其他初始化... while(1) { // 主循环 } } // 按键中断服务函数 void EXTI0_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0) != RESET) { // 处理按键 EXTI_ClearITPendingBit(EXTI_Line0); } } // 串口中断服务函数 void USART1_IRQHandler(void) { // 处理串口 }六、常见问题
Q1:为什么我的中断不执行?
忘记使能总中断:
__enable_irq();忘记使能具体外设的中断
优先级设置冲突
Q2:怎么选择分组?
简单应用:分组2(4×4组合够用了)
需要很多打断:分组4(16个抢占级),比如FreeRTOS
需要精细排序:分组0(16个子优先级)
Q3:优先级数字能随便写吗?
不能!如果分组2,抢占只能0-3,子优先只能0-3
七、简单记忆口诀
先设分组定规矩,整个程序只一次。
抢占决定谁插队,子优决定同队序。
数字越小越优先,紧急中断抢占高。
分组推荐用2号,四种抢占足够用。
总结
先调用
NVIC_PriorityGroupConfig()设分组(推荐Group2)再配置每个中断的抢占和子优先级
数字越小,优先级越高
高抢占可以打断低抢占
同抢占时,高子优先先执行