本文概述:STM32的中断优先级由NVIC(嵌套向量中断控制器)管理,分为抢占优先级和响应优先级两级。抢占优先级决定能否嵌套中断,响应优先级决定同级别中断的响应顺序。优先级数值越小,优先级越高。优先级分组有5种(0到4组),决定了4位优先级中几位用于抢占、几位用于响应。项目中一般选用分组2或分组4,根据中断嵌套需求确定。配置时要注意:整个系统只能使用一种优先级分组,初始化时统一设置。

核心要点速览

  • 两级优先级:抢占优先级(可嵌套)+ 响应优先级(同级别排序)

  • 数值越小优先级越高:这是最容易记反的地方

  • 5种分组:分组0到分组4,分配抢占位和响应位的比例不同

  • 全局唯一:整个系统只能设一种分组,不能不同外设用不同分组

  • 优先级范围:STM32F1系列使用4位优先级,数值0到15

面试题

STM32的中断优先级你了解吗?讲一讲抢占优先级和响应优先级的区别,以及优先级分组怎么配置。

面试官考察点

这道题考察的是你对STM32中断系统的理解深度。很多人用了很久STM32也没搞清楚抢占优先级和响应优先级到底有什么区别,只是照着例程配置。面试官想知道你是否真的理解NVIC的工作机制,以及你在实际项目中是怎么规划中断优先级的。

回答思路

第一步
先讲清楚两个优先级的概念

STM32的每个外部中断都有4位优先级寄存器,这4位被分成两部分:

  • 抢占优先级(Preempt Priority):决定能不能打断正在执行的中断。高抢占优先级的中断可以打断低抢占优先级的中断,形成中断嵌套。这是最核心的一级。

  • 响应优先级(Sub Priority / 子优先级):决定同抢占优先级下的响应顺序。如果两个中断的抢占优先级相同,它们之间不能互相嵌套,但同时触发时响应优先级高的先执行。

关键原则:优先级的数值越小,优先级越高。比如抢占优先级0比抢占优先级1更高。这一点很多人容易说反,一定要记清楚。

第二步
再讲优先级分组

4位优先级怎么分配给抢占和响应,是由优先级分组决定的。分组通过SCB->AIRCR寄存器的PRIGROUP位来设置。STM32一共有5种分组方式:

优先级分组抢占优先级位数响应优先级位数抢占优先级范围响应优先级范围
分组00位4位0级(无嵌套)0到15
分组11位3位0到10到7
分组22位2位0到30到3
分组33位1位0到70到1
分组44位0位0到150级(无响应级)

最常用的是分组2和分组4。分组2有4级抢占+4级响应,兼顾嵌套和排序;分组4有16级抢占但没有响应优先级,适合需要精细控制中断嵌套的场景。

第三步
配置方法与注意事项

配置中断优先级的步骤:

  1. 系统初始化时调用NVIC_PriorityGroupConfig()设置优先级分组,整个系统只能设置一次

  2. 对每个外设中断,调用NVIC_Init()或直接写寄存器设置抢占优先级和响应优先级

  3. 使能对应的中断通道

// 设置优先级分组为分组2(2位抢占+2位响应)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

// 配置EXTI0中断:抢占优先级1,响应优先级2
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

重要注意:优先级分组是全局的,所有中断共用同一个分组设置。不能说串口用分组2,定时器用分组4,这是不行的。所以在项目设计阶段就要规划好用哪个分组。

第四步
实际项目中的优先级规划

在实际项目中规划中断优先级,可以参考这些原则:

  • 系统滴答定时器(SysTick)通常设为较低的抢占优先级,不影响实时性更高的中断

  • 通信接口(USB、CAN)根据实时性要求设为中高优先级

  • 定时器中断用于精确采样或控制时,设为较高优先级

  • 外部按键中断可以设为较低优先级,避免频繁打断关键任务

  • 特别重要的紧急中断(如过流保护、故障检测)设为最高抢占优先级

举个例子:我之前做的一个电机控制项目,用的是分组2。电流采样中断设为抢占优先级0(最高),保证采样时序准确;PWM中断和编码器中断设为抢占优先级1;串口通信设为抢占优先级2;按键和一些状态检测设为抢占优先级3。这样既保证了控制环路的实时性,又不会让通信和按键打断核心控制逻辑。

面试官追问

追问1:中断优先级分组为什么只能设一次?设错了会怎么样?

因为优先级分组是通过SCB->AIRCR寄存器的PRIGROUP位来配置的,这个寄存器是全局的,所有中断通道共用同一个分组设置。如果你在不同地方设置了不同的分组,后设置的会覆盖先设置的,导致前面配置的中断优先级不符合预期。比如你先设了分组4配置了一堆中断,后来某个驱动又设成了分组2,那么之前中断的抢占优先级和响应优先级的位分配就全变了,优先级关系会乱掉。所以规范做法是在系统初始化最开始的时候设置一次,后面就不要再动了。

追问2:同优先级的两个中断同时触发,会怎么处理?

分两种情况。第一种是抢占优先级相同但响应优先级不同,同时触发时响应优先级高的先执行,执行完了再执行响应优先级低的,它们之间不会互相嵌套。第二种是抢占优先级和响应优先级都相同,同时触发时,按照中断向量表中的排序来决定先后,编号小的中断先执行。比如EXTI0_IRQn和EXTI1_IRQn如果优先级完全相同,EXTI0的编号小,会先响应。

追问3:中断服务函数里能做哪些事,不能做哪些事?

中断服务函数的原则是越短越好,快进快出。可以做的事情包括:读取标志位、清除中断标志、读写寄存器、给全局变量赋值、触发信号量或消息队列等轻量操作。不适合做的事情包括:长时间的计算、延时函数(尤其是操作系统的延时)、调用不可重入的函数、使用printf等耗时的串口输出、动态内存分配。中断服务函数里如果处理事情太多,会影响其他中断的响应,严重的还会导致系统卡死。

追问4:你遇到过中断优先级相关的bug吗?怎么解决的?

可以说一个真实或典型的案例:有一次我做的项目里,串口接收中断和定时器中断都是同一个抢占优先级,串口接收数据量大的时候,串口中断处理时间变长,导致定时器中断被推迟,PWM输出的周期抖动变大。后来我把定时器中断的抢占优先级调高一级,让它可以打断串口中断,问题就解决了。这个经历让我认识到中断优先级规划不是随便设的,要根据实时性需求认真排优先级。

失分表达

常见失分回答

  • "优先级数值越大越高" —— 说反了,这是最基础的概念错误

  • "抢占优先级和响应优先级差不多吧" —— 差不多就是没搞懂,直接减分

  • "分组随便选一个就行" —— 说明你没有根据项目需求做规划

  • "我都是从库函数里直接复制的,没太关注这个" —— 等于告诉面试官你只会调库不会思考

准备动作

  • 找一份STM32参考手册的NVIC章节,系统看一遍优先级相关的寄存器描述

  • 亲手写代码验证:两个不同优先级的中断,高优先级能不能打断低优先级

  • 整理你做过的项目里中断优先级的分配表,讲清楚为什么这么分配

  • 了解一下Cortex-M内核异常优先级的概念(Reset、NMI、HardFault等不可屏蔽的异常)

  • 了解中断优先级和FreeRTOS任务优先级的区别,这两个是不同的东西,不要混淆

核心关键词

STM32、NVIC、中断优先级、抢占优先级、响应优先级、优先级分组、中断嵌套、AIRCR寄存器、嵌入式面试