3. 中断系统基础:NVIC中断控制器、中断优先级、中断向量表、中断使能与屏蔽

中断系统是MCU响应外部或内部异步事件的硬件机制。在串口UART驱动中,中断服务函数(ISR)的正确实现依赖于对中断控制器(NVIC)、优先级分组、向量表映射以及使能/屏蔽操作的深入理解。本章将系统梳理中断系统的四大核心模块,为后续编写UART中断收发驱动奠定基础。

3.1 NVIC中断控制器

嵌套向量中断控制器(Nested Vectored Interrupt Controller, NVIC)是ARM Cortex-M系列MCU中管理中断的核心外设。它负责中断的使能、挂起、优先级裁决以及向量表查找。

功能模块 说明
中断使能/除能 通过NVIC->ISER和NVIC->ICER寄存器控制每个中断源的开关
中断挂起/清除 通过NVIC->ISPR和NVIC->ICPR寄存器管理中断的挂起状态
优先级配置 通过NVIC->IPR寄存器设置每个中断的抢占优先级和子优先级
中断号映射 将外设中断线(如UARTx_IRQn)映射到NVIC内部的中断通道
关键概念:NVIC支持最多240个中断通道(具体数量取决于MCU型号),每个通道独立配置优先级。中断响应延迟通常为12个CPU周期(Cortex-M3/M4),远低于传统MCU的查询方式。

3.2 中断优先级

中断优先级决定了当多个中断同时触发时,CPU优先响应哪一个。ARM Cortex-M使用抢占优先级和子优先级两级结构,通过SCB->AIRCR寄存器中的PRIGROUP位域进行分组。

优先级分组规则

  • 抢占优先级:决定是否打断当前正在执行的中断服务函数。数值越小,优先级越高。
  • 子优先级:当两个中断的抢占优先级相同时,子优先级决定响应顺序。数值越小,优先级越高。
  • 分组方式:4位优先级寄存器被拆分为抢占位和子位,常见分组为3位抢占+1位子优先级(即16级抢占,2级子优先级)。
实践建议:在UART驱动中,通常将UART中断的抢占优先级设为中等(如2),子优先级设为0。这样既不会被SysTick等高优先级中断长时间阻塞,又能优先于GPIO等低优先级外设。

3.3 中断向量表

中断向量表(Vector Table)是存储在Flash或RAM中的一张地址映射表,每个中断源对应一个入口地址,即中断服务函数(ISR)的起始地址。当NVIC裁决某个中断被响应时,CPU直接从向量表中取出对应的ISR地址并跳转执行。

中断向量表结构(Cortex-M) 向量表基址(VTOR) 系统异常向量(前16个:Reset、NMI、HardFault等) 外设中断向量(从第16号开始:UART、TIM、DMA等) UARTx_IRQHandler 地址(0x0800_0040 + 偏移) UART中断服务函数(ISR) void USART1_IRQHandler(void) { ... } 偏移 0x0000 偏移 0x0040 每个向量占4字节(32位地址),UART中断号通常为37~40,对应偏移 = (16 + 中断号) × 4
注意:在程序运行时,可以通过修改VTOR(Vector Table Offset Register)将向量表重定位到RAM中,实现动态中断服务函数切换。这在Bootloader跳转和OTA升级场景中非常关键。

3.4 中断使能与屏蔽

中断使能控制中断源是否能够触发CPU响应;中断屏蔽则用于在临界区临时关闭所有中断或特定优先级的中断,防止数据竞争。

中断使能操作

// 使能UART1中断(以STM32为例)
NVIC_EnableIRQ(USART1_IRQn);   // 设置NVIC->ISER[0]对应位

// 除能UART1中断
NVIC_DisableIRQ(USART1_IRQn);  // 设置NVIC->ICER[0]对应位

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

中断屏蔽机制

屏蔽方式 寄存器/指令 作用范围 典型应用
全局屏蔽 __disable_irq() / __enable_irq() 所有可屏蔽中断 临界区保护,如操作共享变量
优先级屏蔽 BASEPRI寄存器 优先级低于设定值的中断 只屏蔽低优先级中断,保留高优先级响应
特定中断屏蔽 NVIC->ICER 单个中断通道 临时关闭UART中断,防止误触发
UART驱动中的屏蔽策略:在发送数据时,建议使用BASEPRI屏蔽优先级低于UART的中断,而不是全局屏蔽。这样既能保证UART发送不被低优先级中断干扰,又能让SysTick等高优先级中断正常计时。

3.5 中断响应流程

当中断事件发生时,CPU按照以下硬件自动流程执行:

  1. 中断检测:NVIC检测到中断请求(IRQ)有效且未被屏蔽。
  2. 优先级裁决:比较当前中断与正在执行中断的优先级,若更高则抢占。
  3. 入栈保护:自动将R0-R3、R12、LR、PC、xPSR压入当前栈(MSP或PSP)。
  4. 向量表查找:从向量表中读取对应中断号的ISR地址。
  5. 跳转执行:PC跳转到ISR地址,开始执行中断服务函数。
  6. 出栈返回:ISR执行完毕后,执行BX LR指令,硬件自动出栈恢复现场。
中断响应硬件自动流程 ① 中断检测 NVIC检测IRQ ② 优先级裁决 比较抢占优先级 ③ 入栈保护 自动压栈8个寄存器 ④ 向量表查找 读取ISR地址 ⑤ 跳转执行 PC指向ISR ⑥ 出栈返回 BX LR恢复现场 硬件自动完成 无需软件干预 12个CPU周期完成 支持中断嵌套

3.6 中断嵌套与抢占

Cortex-M支持中断嵌套(Nesting),即高优先级中断可以抢占正在执行的低优先级中断服务函数。NVIC通过以下机制保证嵌套安全:

  • 自动入栈:每次中断响应时,硬件自动保存当前上下文,无需软件保存。
  • 优先级屏蔽:在ISR执行期间,NVIC自动屏蔽同级和低优先级中断,防止递归抢占。
  • 尾链技术:当低优先级ISR执行完毕,若此时有高优先级中断等待,CPU直接进入高优先级ISR,跳过出栈/入栈过程,减少延迟。
UART驱动中的嵌套场景:假设UART接收中断优先级为2,定时器中断优先级为1。当UART ISR正在处理接收数据时,定时器中断触发,CPU会暂停UART ISR,先执行定时器ISR,完成后返回UART ISR继续执行。这种机制保证了高实时性任务(如定时器)的及时响应。

3.7 常见问题与避坑指南

问题 原因 解决方案
中断不响应 NVIC未使能中断,或外设中断标志未清除 检查NVIC_EnableIRQ()调用,并在ISR中清除中断标志
中断响应延迟过大 优先级设置不当,被其他中断长时间阻塞 合理分配抢占优先级,避免UART中断被低优先级任务阻塞
中断嵌套导致栈溢出 ISR中使用了大量局部变量或递归调用 ISR中尽量使用全局变量,减少栈使用;增大主栈(MSP)大小
中断标志未清除导致重复触发 ISR中未读取数据寄存器或未写清除位 在ISR开头读取UART数据寄存器(自动清除RXNE标志),或写清除位
重要提醒:在ISR中不要执行耗时操作(如printf、延时循环、复杂计算)。如果必须处理大量数据,建议使用“中断+轮询”或“中断+DMA”模式,ISR只负责设置标志或启动DMA传输,主循环中处理数据。