1. CMS32 中断系统概述:中断向量表、中断优先级、中断嵌套机制
各位工程师朋友,咱们今天正式进入 CMS32 的中断世界。说实话,中断系统是单片机实时响应的灵魂。我做了这么多年嵌入式,见过太多因为中断配置不当导致的“灵异现象”——程序跑着跑着突然死机,或者响应慢得像蜗牛。其实,问题多半出在中断向量表、优先级和嵌套这三个核心点上。
咱们先搭个框架,看看中断系统到底长什么样。
1.1 中断向量表——中断的“电话簿”
中断向量表,说白了就是一张地址映射表。每个中断源对应一个唯一的向量号,向量号指向中断服务函数(ISR)的入口地址。CPU 一旦检测到中断请求,就根据向量号查表,跳转到对应的 ISR 去执行。
我个人习惯在工程启动文件(比如 startup_cms32.s)里直接定义向量表。CMS32 的向量表默认放在 0x00000000 起始地址,每个向量占 4 字节,共 64 个中断向量 + 16 个系统异常向量。
| 向量号 | 偏移地址 | 中断源 | 默认优先级 |
|---|---|---|---|
| 0 | 0x0000 | 复位 | 最高 |
| 1 | 0x0004 | NMI | 次高 |
| 2~15 | 0x0008~0x003C | 系统异常(HardFault 等) | 固定 |
| 16~79 | 0x0040~0x013C | 外设中断(TIM、ADC、UART 等) | 可配置 |
嗯,这里要注意:CMS32 支持向量表重定位。你可以通过设置 VTOR 寄存器,把向量表搬到 SRAM 或 Flash 的其他位置。我在做 Bootloader 项目时就用过这个功能——把向量表搬到 RAM 里,运行时动态修改中断入口,实现灵活的固件升级。
1.2 中断优先级——谁更重要?
中断优先级决定了多个中断同时触发时,CPU 先响应谁。CMS32 支持 4 位可编程优先级,也就是 0~15 共 16 级。数值越小,优先级越高。0 级最高,15 级最低。
我记得有一次调试一个电机控制项目,电流采样中断和通信中断同时触发,结果电机转速波动很大。查了半天才发现,我把电流采样中断的优先级设成了 7,通信中断设成了 3。通信中断频繁打断采样,导致电流环失控。后来我把采样中断提到优先级 1,问题立刻解决。
优先级配置通过 NVIC 的 IPR 寄存器实现。每个中断源占用一个字节的 IPR 寄存器,高 4 位有效,低 4 位保留。
// 设置 TIM2 中断优先级为 3(数值越小优先级越高)
NVIC_SetPriority(TIM2_IRQn, 3);
// 设置 ADC 中断优先级为 1
NVIC_SetPriority(ADC_IRQn, 1);
// 使能中断
NVIC_EnableIRQ(TIM2_IRQn);
NVIC_EnableIRQ(ADC_IRQn);
1.3 中断嵌套机制——中断里还能有中断?
中断嵌套,就是高优先级中断可以打断低优先级中断的服务函数。CMS32 的 NVIC 支持硬件自动压栈和出栈,嵌套深度最多可达 16 层(受限于堆栈空间)。
你想想看,如果低优先级中断正在执行,高优先级中断来了,CPU 会怎么做?
- 保存当前中断的上下文(R0~R3、R12、LR、PC、xPSR)到堆栈
- 加载高优先级中断的向量,跳转到对应的 ISR
- 高优先级 ISR 执行完毕,执行异常返回指令
- CPU 自动恢复低优先级中断的上下文,继续执行
这个过程完全由硬件完成,不需要软件干预。但要注意:如果低优先级中断里调用了不可重入的函数(比如 printf),高优先级中断也调用同一个函数,就会出问题。我踩过这个坑——调试时用串口打印,结果中断嵌套导致打印缓冲区被破坏,数据全乱套了。
下面这张图展示了 CMS32 中断系统的整体架构,帮你理清中断源、NVIC、CPU 之间的协作关系。
1.4 中断响应流程——从触发到执行
一个完整的中断响应流程,我习惯分成 6 步:
- 中断请求: 外设产生中断事件,硬件置位中断挂起位
- 优先级仲裁: NVIC 比较当前中断与正在执行中断的优先级
- 向量表查找: CPU 根据向量号从向量表中取出 ISR 入口地址
- 自动压栈: 硬件自动将 R0~R3、R12、LR、PC、xPSR 压入堆栈
- 执行 ISR: CPU 跳转到 ISR 执行,软件处理中断事件
- 异常返回: ISR 执行完毕,硬件自动出栈,恢复现场
1.5 常见问题与避坑
- 中断丢失: 如果同一个中断源在 ISR 执行期间再次触发,CMS32 会挂起该中断,但只挂起一次。如果连续触发多次,只会响应一次。解决办法:在 ISR 里尽快清除中断标志位。
- 优先级反转: 低优先级中断持有某个资源,高优先级中断等待该资源,导致高优先级被阻塞。我曾在多中断共享一个 SPI 总线时遇到过,后来用互斥量 + 关中断解决了。
- 堆栈溢出: 嵌套深度太大,堆栈空间不够。CMS32 的默认堆栈大小是 512 字节,如果嵌套 5 层以上,建议加大到 1024 或 2048 字节。