3. 中断系统基础
中断系统是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地址并跳转执行。
注意:在程序运行时,可以通过修改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按照以下硬件自动流程执行:
- 中断检测:NVIC检测到中断请求(IRQ)有效且未被屏蔽。
- 优先级裁决:比较当前中断与正在执行中断的优先级,若更高则抢占。
- 入栈保护:自动将R0-R3、R12、LR、PC、xPSR压入当前栈(MSP或PSP)。
- 向量表查找:从向量表中读取对应中断号的ISR地址。
- 跳转执行:PC跳转到ISR地址,开始执行中断服务函数。
- 出栈返回:ISR执行完毕后,执行BX LR指令,硬件自动出栈恢复现场。
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传输,主循环中处理数据。