3. 中断基础:中断向量表、ISR 编写规则与中断嵌套
在 RTOS 环境下,中断是外设与 CPU 交互的最高优先级通道。理解中断向量表的结构、ISR 的编写规范以及中断嵌套与抢占机制,是调试外设驱动的前提。本节从硬件基础出发,逐步深入到 RTOS 对中断的管理策略。
3.1 中断向量表:系统响应的入口地图
中断向量表(Interrupt Vector Table, IVT)是 CPU 在响应中断时查找服务程序入口地址的索引表。每个中断源对应一个固定的表项,表项中存放的是该中断服务程序(ISR)的起始地址。
关键概念: 向量表通常位于 ROM 或 RAM 起始地址(如 ARM Cortex-M 的 0x00000000),复位后由硬件自动加载。在 RTOS 中,向量表可能被重定位到 RAM 以支持动态修改。
| 区域 | 表项范围 | 用途 | RTOS 关注点 |
|---|---|---|---|
| 异常向量 | 0 – 15 | 系统异常(复位、NMI、HardFault 等) | 通常不修改,由启动文件固化 |
| 外部中断向量 | 16 – N | 外设中断(UART、SPI、TIM、I2C 等) | RTOS 可重定位向量表以支持动态注册 |
| 软件触发中断 | 用户定义 | 任务间通信、系统调用(SVC、PendSV) | RTOS 内核依赖 PendSV 进行上下文切换 |
3.2 中断服务程序(ISR)编写规则
ISR 是中断响应的执行体。在 RTOS 环境下,ISR 的编写比裸机更加严格,必须遵循以下核心规则,否则可能导致系统崩溃或任务调度异常。
3.2.1 ISR 编写黄金法则
- 短小精悍: ISR 应尽快完成关键操作(如读取数据寄存器、清除中断标志),将耗时处理推迟到任务中。
- 避免阻塞: 禁止在 ISR 中调用阻塞型 API(如
vTaskDelay()、vTaskSuspend()),否则会导致系统死锁。 - 使用中断安全 API: 必须调用 RTOS 提供的“FromISR”后缀函数(如
xQueueSendFromISR()),这些函数不会触发上下文切换。 - 保存/恢复上下文: 编译器或 RTOS 入口/出口宏会自动处理,但手动编写汇编 ISR 时必须显式保存所有通用寄存器。
- 清除中断标志: 在 ISR 返回前必须清除外设的中断挂起位,否则会无限触发同一中断。
RTOS 实践提示: 在 FreeRTOS 中,ISR 内调用
xQueueSendFromISR() 后,应检查返回值 pxHigherPriorityTaskWoken。若为 pdTRUE,则在 ISR 退出前调用 portYIELD_FROM_ISR() 触发一次上下文切换,确保高优先级任务及时运行。
3.2.2 ISR 代码模板(FreeRTOS + ARM Cortex-M)
// UART 接收中断服务程序示例
void UART0_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
uint8_t rx_byte;
// 1. 读取数据并清除中断标志
rx_byte = UART0->DR; // 读取数据寄存器自动清除 RX 中断
UART0->ICR |= (1 << 4); // 手动清除其他中断标志(如溢出错误)
// 2. 将数据发送到队列(中断安全版本)
xQueueSendFromISR(xRxQueue, &rx_byte, &xHigherPriorityTaskWoken);
// 3. 如果唤醒高优先级任务,请求上下文切换
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
3.3 中断嵌套与抢占:优先级管理的核心
中断嵌套是指一个 ISR 正在执行时,另一个更高优先级的中断到达,CPU 暂停当前 ISR 转而服务更高优先级中断的机制。抢占则是指高优先级中断可以“打断”低优先级中断的执行。
3.3.1 中断优先级分组
ARM Cortex-M 内核支持中断优先级分组,将优先级寄存器的高位划分为抢占优先级和子优先级。抢占优先级决定是否可以嵌套,子优先级用于同抢占优先级下的仲裁。
| 分组编号 | 抢占优先级位数 | 子优先级位数 | 典型应用场景 |
|---|---|---|---|
| NVIC_PriorityGroup_0 | 0 | 8 | 无嵌套,仅子优先级仲裁 |
| NVIC_PriorityGroup_1 | 1 | 7 | 2 级嵌套深度 |
| NVIC_PriorityGroup_2 | 2 | 6 | 4 级嵌套深度(常用) |
| NVIC_PriorityGroup_3 | 3 | 5 | 8 级嵌套深度 |
| NVIC_PriorityGroup_4 | 4 | 4 | 16 级嵌套深度(RTOS 推荐) |
RTOS 配置警告: FreeRTOS 要求将所有中断优先级配置为“抢占优先级”模式(即分组 4),并将 RTOS 管理的最高中断优先级设置为
configMAX_SYSCALL_INTERRUPT_PRIORITY。高于此优先级的中断不会被 RTOS 临界区屏蔽,用于时间关键型外设(如电机控制 PWM)。
3.3.2 中断嵌套流程
下图展示了三级中断嵌套的执行流程,直观体现抢占与返回顺序。
3.3.3 中断嵌套的 RTOS 注意事项
- 临界区保护: RTOS 的临界区(如
taskENTER_CRITICAL())会屏蔽优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断。高优先级中断(高于该阈值)仍可嵌套,但不得调用任何 RTOS API。 - 栈深度: 每层嵌套都会消耗额外的栈空间。在嵌套深度较大的系统中,必须为 ISR 栈分配足够的空间,避免栈溢出。
- 优先级反转: 中断嵌套不会导致优先级反转,但中断与任务之间的资源竞争可能引发类似问题。建议在 ISR 与任务之间使用无锁队列或中断安全信号量。
调试经验: 当系统出现“中断丢失”或“ISR 执行时间异常”时,首先检查中断优先级分组是否与 RTOS 配置一致。其次,使用逻辑分析仪或 GPIO 翻转法测量 ISR 的实际执行时间,确认是否因嵌套导致延迟超出预期。