2. NVIC 简介:定义、位置与核心功能

在 Cortex-M 系列 MCU 的中断体系中,NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器) 是管理所有中断请求的核心硬件模块。它直接决定了中断的响应顺序、嵌套深度以及优先级仲裁方式。理解 NVIC 的结构与功能,是进行中断配置的前提。

2.1 NVIC 的定义

NVIC 是 ARM Cortex-M 处理器内核中集成的中断控制器,负责处理来自片内外设和外部引脚的所有异常(Exception)与中断(Interrupt)请求。与传统的 8 位或 16 位 MCU 的中断控制器不同,NVIC 具备以下关键特性:

  • 可嵌套性:高优先级中断可以抢占低优先级中断的服务例程,实现多级中断嵌套。
  • 向量化:每个中断源对应一个固定的中断向量地址,CPU 响应中断时直接从向量表中加载服务函数入口,无需软件查询中断源。
  • 可编程优先级:支持多达 8~256 级(取决于具体 Cortex-M 内核版本)的可编程中断优先级。
  • 尾链(Tail-Chaining):当多个中断同时等待处理时,NVIC 自动跳过冗余的入栈/出栈操作,直接执行下一个中断服务函数,降低延迟。
关键概念: 在 Cortex-M 中,“异常”包括系统异常(如 HardFault、SysTick)和外部中断。NVIC 统一管理所有异常,但通常我们所说的“中断配置”特指外部中断(IRQ)。

2.2 NVIC 在 Cortex-M 内核中的位置

NVIC 位于 Cortex-M 处理器核心内部,紧邻 CPU 和系统总线。下图展示了 NVIC 在典型 Cortex-M3/M4 内核架构中的位置与数据流关系:

NVIC 在 Cortex-M 内核中的位置 Cortex-M 内核架构 — NVIC 位置与数据流 片内外设 GPIO / TIM / USART ADC / SPI / I2C 外部中断输入 EXTI 线 0~15 PVD / RTC 闹钟 NVIC 嵌套向量中断控制器 优先级分组 中断使能 / 挂起 向量表索引 尾链 / 迟到处理 CPU 核心 Cortex-M3/M4 自动入栈 R0-R3 加载向量地址 执行 ISR 中断请求 系统总线矩阵(AHB / APB) 说明:NVIC 位于 CPU 与外设之间,接收中断请求后,根据优先级仲裁结果向 CPU 发出中断信号。 CPU 响应后,NVIC 提供向量地址,CPU 自动完成现场保护并跳转至 ISR。
架构要点: NVIC 不通过总线访问外设寄存器,而是直接通过专用中断线接收请求。这种紧耦合设计使得中断响应延迟极低(通常为 12 个 CPU 周期)。

2.3 NVIC 的主要功能

NVIC 的功能可以归纳为以下五个核心模块,每个模块对应一组可编程的寄存器:

功能模块 描述 关键寄存器
中断使能/除能 独立控制每个中断源的使能状态,除能的中断不会被 NVIC 响应。 ISER / ICER
中断挂起/清除 软件可以手动挂起一个中断(用于测试或软件触发),或清除挂起状态。 ISPR / ICPR
中断优先级配置 为每个中断分配抢占优先级和子优先级,支持优先级分组。 IPR0~IPR59
中断状态查询 读取当前正在服务的中断号、挂起状态、激活状态。 IABR / 挂起状态寄存器
系统异常控制 管理 SysTick、PendSV、SVC 等系统异常的优先级和触发方式。 SHPR1~SHPR3

2.4 NVIC 的优先级管理机制

NVIC 使用 优先级分组(Priority Grouping) 机制将优先级寄存器中的位划分为抢占优先级和子优先级两部分:

  • 抢占优先级(Preempt Priority):决定中断是否可以打断正在执行的低优先级中断。数值越小,优先级越高。
  • 子优先级(Subpriority):当两个中断的抢占优先级相同时,子优先级决定谁先被响应。子优先级不参与嵌套抢占。

优先级分组由 SCB->AIRCR 寄存器的 PRIGROUP 位域控制。以 Cortex-M3/M4 为例,支持 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
常见误区: 优先级数值越小,优先级越高。例如,抢占优先级为 0 的中断可以抢占抢占优先级为 1 的中断。许多初学者容易将数值大小与优先级高低混淆。

2.5 NVIC 的嵌套与尾链行为

NVIC 在中断处理过程中自动管理以下三种关键场景:

  1. 嵌套(Nesting):当高优先级中断请求到达时,NVIC 暂停当前 ISR,自动将 CPU 寄存器入栈,然后执行高优先级 ISR。高优先级 ISR 返回后,自动恢复低优先级 ISR 的执行。
  2. 尾链(Tail-Chaining):当低优先级 ISR 即将返回时,如果有一个高优先级中断正在等待,NVIC 不会执行出栈/入栈操作,而是直接执行高优先级 ISR,节省 12~16 个 CPU 周期。
  3. 迟到(Late-Arriving):在低优先级 ISR 的入栈阶段,如果高优先级中断到达,NVIC 会放弃低优先级 ISR 的入栈,直接处理高优先级中断,进一步降低延迟。
NVIC 嵌套与尾链时序对比 NVIC 中断处理时序:嵌套 vs 尾链 时间→ 嵌套场景: 低优先级 ISR 入栈 高优先级 ISR 出栈 恢复低优先级 尾链场景: 低优先级 ISR 尾链(无入栈) 高优先级 ISR 出栈 延迟对比 嵌套:入栈+出栈 延迟约 24 周期 尾链:跳过入栈 延迟约 12 周期 节省约 50% 开销
实战提示: 尾链机制是 NVIC 提升实时性能的关键。在中断频繁的系统中(如通信协议栈),合理利用尾链可以显著降低 CPU 开销。但尾链是硬件自动行为,开发者无需手动配置,只需确保中断优先级设置正确即可。

2.6 NVIC 与系统控制块(SCB)的关系

NVIC 与 系统控制块(SCB,System Control Block) 紧密协作。SCB 负责管理系统异常(如 HardFault、MemManage)的优先级和配置,而 NVIC 管理外部中断。两者共享部分寄存器空间,但功能分工明确:

  • SCB:位于地址 0xE000_ED00,控制系统异常优先级(SHPR1~SHPR3)、系统异常使能、以及系统复位。
  • NVIC:位于地址 0xE000_E100,控制外部中断的使能、挂起、优先级和激活状态。

在编程时,通常使用 CMSIS 核心层提供的 API 来操作 NVIC,无需直接访问寄存器地址:

// 使能中断 IRQn
NVIC_EnableIRQ(USART1_IRQn);

// 设置中断优先级(抢占优先级 1,子优先级 0)
NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(0, 1, 0));

// 清除挂起位
NVIC_ClearPendingIRQ(USART1_IRQn);

// 读取当前激活的中断号
uint32_t active_irq = NVIC_GetActive(USART1_IRQn);