CPU 中断系统:中断标志位、中断使能、中断嵌套
好,咱们接着往下聊。上一章我们把中断的“骨架”——中断源和中断节点——搭起来了。这一章,咱们要深入CPU内部,看看中断到底是怎么被“感知”和“响应”的。
说白了,CPU就像一个指挥官。它怎么知道有中断来了?它怎么决定要不要理这个中断?处理一个中断的时候,另一个更紧急的中断来了怎么办?这些,就是本章要解决的问题。
中断标志位:中断来了,CPU怎么知道?
每个中断源,在CPU内部都有一个对应的标志位。这个标志位,就像门口的信箱。中断事件发生了,就往信箱里塞一封信——把标志位置1。
在TC3xx里,这个标志位叫IF(Interrupt Flag)。它位于中断控制寄存器中。我刚开始接触TC3xx时,有个误区:以为标志位是自动清除的。其实不是。
我曾经在一个电机控制项目里,就因为忘了清标志位,导致中断服务函数被无限调用,看门狗都救不回来。后来花了整整一个下午才定位到问题。嗯,从那以后,我写中断服务函数的第一件事,就是清标志位。
清除标志位的操作很简单,往对应的位写1即可:
// 清除SRC寄存器的IF标志位
SRC->U = SRC->U; // 读-修改-写,清除IF位
这里有个细节:写1清零,写0无影响。所以清除时,你不需要关心其他位是什么状态,直接写1就行。
中断使能:指挥官的决定权
标志位有了,但CPU要不要响应这个中断,还得看“使能”位。使能位就像指挥官手里的“允许处理”印章。
TC3xx的中断使能分两级:
- 全局使能:CPU的ICR寄存器里的IE位。这是总开关。
- 局部使能:每个SRC寄存器里的SRE位。这是分开关。
只有两个开关都打开,中断才能被CPU响应。我习惯把全局使能比作“大门”,局部使能比作“房间门”。大门不开,房间门开再多也没用。
使能中断的代码很简单:
// 使能全局中断
IfxCpu_enableInterrupts();
// 使能局部中断(以CCU6为例)
SRC_CCU6_SR0->B.SRE = 1;
你想想看,为什么要有两级使能?其实是为了灵活性。全局使能可以快速关闭所有中断,比如在进入临界区时。局部使能则允许你精细控制每个中断源。
中断嵌套:紧急任务插队机制
中断嵌套,说白了就是“中断里还能再进中断”。这在实时系统中非常常见。
TC3xx支持中断嵌套,但有个前提:中断优先级。每个中断都有一个优先级,数值越小优先级越高(注意,这和有些MCU相反)。
嵌套的规则很简单:
- 高优先级中断可以打断低优先级中断
- 同级中断不能互相打断
- 低优先级中断不能打断高优先级中断
我举个例子:假设系统里有两个中断,一个优先级1(高),一个优先级3(低)。
- CPU正在执行主循环,优先级3的中断来了,CPU响应,进入ISR。
- 在ISR执行到一半时,优先级1的中断来了。
- CPU暂停优先级3的ISR,保存现场,跳去执行优先级1的ISR。
- 优先级1的ISR执行完毕,返回优先级3的ISR继续执行。
- 优先级3的ISR执行完毕,返回主循环。
这就是嵌套。但要注意,嵌套深度不是无限的。TC3xx的CPU有专门的中断堆栈指针(ISP)来管理嵌套。嵌套太深,堆栈会溢出。
中断优先级组:更精细的控制
TC3xx的优先级不止一个数字那么简单。它把优先级分成了服务优先级和仲裁优先级两部分。
| 优先级类型 | 作用 | 位数 |
|---|---|---|
| 服务优先级 | 决定中断能否被CPU响应 | 4位(0-15) |
| 仲裁优先级 | 决定多个中断同时到达时谁先被处理 | 4位(0-15) |
服务优先级就是上面说的嵌套规则用的。仲裁优先级则用于“排队”场景——多个中断同时到达,CPU先处理仲裁优先级高的。
我个人习惯把服务优先级设得比仲裁优先级高一位。比如服务优先级是5,仲裁优先级就设4。这样既能保证嵌套逻辑清晰,又能避免同时到达时的冲突。
实战中的避坑指南
讲到这里,我分享几个实战中踩过的坑:
坑1:中断服务函数里调用printf
我曾经在中断里用printf打印调试信息。结果中断一触发,系统就卡死。为什么?因为printf是阻塞的,而且它本身可能触发其他中断。中断里调用阻塞函数,等于自杀。
正确做法:中断里只做最必要的事——清标志位、读数据、置一个全局标志。真正的处理放到主循环里。
坑2:中断优先级设置冲突
有次我把两个中断设成了相同优先级,结果它们同时触发时,系统行为变得不可预测。后来查手册才知道,同级中断的响应顺序由硬件决定,不同型号可能不一样。
正确做法:每个中断的优先级必须唯一。如果实在需要相同优先级,用仲裁优先级来区分。
坑3:忘记恢复中断状态
在临界区里关闭了全局中断,处理完事情后忘了恢复。结果整个系统变成“聋子”,什么中断都收不到。
正确做法:使用“保存-关闭-恢复”的模式:
bool interruptState = IfxCpu_disableInterrupts();
// 临界区代码
IfxCpu_restoreInterrupts(interruptState);
小结
这一章我们聊了中断标志位、使能机制和嵌套规则。记住三个关键点:
- 标志位必须手动清除,别指望硬件帮你
- 两级使能,全局和局部都要开
- 嵌套有深度限制,别贪多
下一章,我们会讲中断服务函数的编写规范。到时候我会分享一个我自己总结的“ISR模板”,用了好几年,基本没出过问题。咱们下章见。