3. 中断优先级概念:抢占优先级与子优先级的定义、优先级分组的概念

在 ARM Cortex-M 系列 MCU 中,NVIC(嵌套向量中断控制器)支持多级中断优先级管理。理解“抢占优先级”与“子优先级”的区别,以及“优先级分组”的配置机制,是正确设计中断嵌套逻辑的基础。本节将系统阐述这两个核心概念及其在 NVIC 中的实现方式。

3.1 抢占优先级(Preempt Priority)

抢占优先级决定了中断是否能够打断当前正在执行的中断服务程序(ISR)。当两个中断同时发生时,抢占优先级更高的中断会优先执行;如果高抢占优先级的中断在低优先级中断执行期间触发,则会发生中断嵌套——当前 ISR 被暂停,高优先级 ISR 开始执行,待其完成后恢复被暂停的 ISR。

  • 数值越小,优先级越高:在 NVIC 中,优先级数值与优先级等级成反比。例如,抢占优先级数值为 0 的中断优先级最高,数值为 3 的中断优先级最低。
  • 嵌套触发条件:只有抢占优先级更高的中断才能打断当前 ISR。相同抢占优先级的中断之间不会发生嵌套。
  • 应用场景:实时性要求极高的任务(如定时器溢出、外部紧急事件)应分配较高的抢占优先级。
常见误区:初学者常误以为“数值越大优先级越高”。请牢记:在 Cortex-M NVIC 中,优先级数值越小,逻辑优先级越高。这是硬件设计约定,与某些 RTOS 的优先级定义相反。

3.2 子优先级(Subpriority)

子优先级用于在同一抢占优先级组内,进一步区分中断的响应顺序。当两个中断具有相同的抢占优先级时,子优先级数值较小的中断会优先执行。但子优先级不会触发中断嵌套——即相同抢占优先级的中断之间不会相互打断。

  • 仅用于仲裁:子优先级只在多个同抢占优先级中断同时挂起时,决定哪个先被响应。
  • 无嵌套能力:即使子优先级不同,只要抢占优先级相同,当前 ISR 不会被另一个同组中断打断。
  • 典型用途:在多个外设中断共享同一抢占优先级时,通过子优先级区分处理的紧急程度。

3.3 优先级分组(Priority Grouping)

优先级分组是 NVIC 提供的一种灵活机制,允许开发者将中断优先级寄存器中的位域划分为抢占优先级位和子优先级位。Cortex-M3/M4/M7 等内核使用 8 位优先级寄存器(实际有效位数由芯片厂商决定),通过 SCB->AIRCR 寄存器的 PRIGROUP 字段配置分组方式。

下表列出了常见的优先级分组配置(以 8 位寄存器为例,实际有效位数为 4 位时同理):

PRIGROUP 值 抢占优先级位数 子优先级位数 抢占优先级等级数 子优先级等级数 总优先级等级数
0 8 0 256 1 256
1 7 1 128 2 256
2 6 2 64 4 256
3 5 3 32 8 256
4 4 4 16 16 256
5 3 5 8 32 256
6 2 6 4 64 256
7 1 7 2 128 256
实际有效位数说明:大多数 MCU(如 STM32F1/F4)只使用优先级寄存器的高 4 位,低 4 位固定为 0。此时上表中的“8 位”应替换为“4 位”,总优先级等级数为 16。例如,PRIGROUP=3 时,抢占优先级占 3 位(8 级),子优先级占 1 位(2 级)。

3.4 优先级分组与中断嵌套的关系

优先级分组直接决定了系统中中断嵌套的深度和灵活性。以下 SVG 图展示了不同分组配置下,中断嵌套的层级结构:

优先级分组与中断嵌套层级示意图 分组 A:PRIGROUP=3 抢占 3 位 / 子优先 1 位 抢占优先级 0(最高) 子优先级 0 抢占优先级 1 子优先级 0 / 1 抢占优先级 2 子优先级 0 / 1 抢占优先级 3(最低) 子优先级 0 / 1 分组 B:PRIGROUP=6 抢占 2 位 / 子优先 2 位 抢占优先级 0(最高) 子优先级 0~3 抢占优先级 1 子优先级 0~3 抢占优先级 2 子优先级 0~3 抢占优先级 3(最低) 子优先级 0~3 关键说明 • 分组 A:8 级抢占 × 2 级子优先 嵌套深度可达 8 层 • 分组 B:4 级抢占 × 4 级子优先 嵌套深度最多 4 层 • 子优先级仅用于同组仲裁 不触发嵌套 • 抢占优先级位数越多 嵌套层级越深 • 子优先级位数越多 同组内仲裁越精细

3.5 优先级分组配置示例(STM32)

在 STM32 标准库或 HAL 库中,优先级分组通过 NVIC_PriorityGroupConfig() 函数配置。以下代码展示了如何设置分组并初始化中断优先级:

/* 配置优先级分组:抢占优先级 4 位,子优先级 0 位(即 16 级抢占,无子优先级) */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

/* 配置 USART1 中断:抢占优先级 2,子优先级 0 */
NVIC_InitTypeDef NVIC_InitStructure;
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);

/* 配置 TIM2 中断:抢占优先级 1,子优先级 0(优先级高于 USART1) */
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
重要提醒:优先级分组应在系统初始化早期(通常在 main 函数开头)配置一次,且在整个程序运行期间不应再次修改。重新配置分组会导致所有已设置的中断优先级重新解释,可能引发不可预料的嵌套行为。

3.6 优先级分组选择策略

在实际项目中,选择优先级分组需要权衡嵌套深度与同组仲裁粒度。以下表格总结了不同场景下的推荐分组:

应用场景 推荐分组 抢占优先级等级数 子优先级等级数 理由
强实时控制(如电机驱动) PRIGROUP=3(4 位有效) 8 2 需要多层嵌套,同时保留子优先级区分同组外设
大量外设中断(如传感器阵列) PRIGROUP=5(4 位有效) 2 8 抢占层级少,但同组内可精细仲裁多个外设
简单系统(如 LED 控制) PRIGROUP=7(4 位有效) 1 16 无嵌套需求,所有中断同优先级,仅靠子优先级排序
混合实时系统(如工业通信) PRIGROUP=4(4 位有效) 4 4 平衡嵌套深度与仲裁粒度,适用性最广

3.7 中断优先级配置流程图

以下 SVG 流程图总结了中断优先级配置的完整步骤:

中断优先级配置流程图 1. 确定系统所需抢占优先级等级数 分析外设实时性需求 2. 选择优先级分组(PRIGROUP) 参考分组表,匹配等级数需求 3. 调用分组配置函数 NVIC_PriorityGroupConfig() 4. 为每个中断分配优先级值 设置抢占优先级 + 子优先级 5. 初始化 NVIC 并使能中断 NVIC_Init() / HAL_NVIC_SetPriority() 6. 验证嵌套行为是否符合预期

3.8 常见问题与避坑指南

  • 分组配置后不可更改:一旦调用分组配置函数,所有中断优先级的位域解释方式即固定。中途更改会导致已配置的优先级数值含义错乱。
  • 有效位数因芯片而异:STM32F0 使用 2 位有效优先级(4 级),STM32F1/F4 使用 4 位(16 级),STM32H7 使用 8 位(256 级)。务必查阅芯片参考手册。
  • 子优先级不产生嵌套:即使子优先级不同,相同抢占优先级的中断之间不会打断。如果需要嵌套,必须分配不同的抢占优先级。
  • 优先级数值不要超过有效范围:例如在 4 位有效系统中,抢占优先级最大值为 15(0~15),子优先级最大值为 15。超出部分会被硬件截断。
核心记忆口诀:
“抢占定嵌套,子优先定顺序;数值越小越优先,分组一次定终身。”