CPU 中断系统:中断标志位、中断使能、中断嵌套

好,咱们接着往下聊。上一章我们把中断的“骨架”——中断源和中断节点——搭起来了。这一章,咱们要深入CPU内部,看看中断到底是怎么被“感知”和“响应”的。

说白了,CPU就像一个指挥官。它怎么知道有中断来了?它怎么决定要不要理这个中断?处理一个中断的时候,另一个更紧急的中断来了怎么办?这些,就是本章要解决的问题。

中断标志位:中断来了,CPU怎么知道?

每个中断源,在CPU内部都有一个对应的标志位。这个标志位,就像门口的信箱。中断事件发生了,就往信箱里塞一封信——把标志位置1。

在TC3xx里,这个标志位叫IF(Interrupt Flag)。它位于中断控制寄存器中。我刚开始接触TC3xx时,有个误区:以为标志位是自动清除的。其实不是。

注意:IF标志位必须由软件手动清除!硬件只负责置位,不负责清零。如果你忘了清标志位,同一个中断会反复触发,系统直接卡死。

我曾经在一个电机控制项目里,就因为忘了清标志位,导致中断服务函数被无限调用,看门狗都救不回来。后来花了整整一个下午才定位到问题。嗯,从那以后,我写中断服务函数的第一件事,就是清标志位。

清除标志位的操作很简单,往对应的位写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(低)。

  1. CPU正在执行主循环,优先级3的中断来了,CPU响应,进入ISR。
  2. 在ISR执行到一半时,优先级1的中断来了。
  3. CPU暂停优先级3的ISR,保存现场,跳去执行优先级1的ISR。
  4. 优先级1的ISR执行完毕,返回优先级3的ISR继续执行。
  5. 优先级3的ISR执行完毕,返回主循环。

这就是嵌套。但要注意,嵌套深度不是无限的。TC3xx的CPU有专门的中断堆栈指针(ISP)来管理嵌套。嵌套太深,堆栈会溢出。

我的建议:嵌套深度控制在3层以内。超过3层,出问题的概率会指数级上升。我在一个通信项目中,曾经嵌套到5层,结果堆栈溢出,系统跑飞。后来强制规定嵌套不超过3层,再也没出过问题。

中断优先级组:更精细的控制

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模板”,用了好几年,基本没出过问题。咱们下章见。