3. 中断系统与实时响应优化

在 C2000 的实时控制回路里,中断是“控制节拍”的来源。ADC 采样结束、PWM 周期匹配、CPU 定时器溢出、eCAN 收发、SCI 接收等事件都会触发中断。中断响应快慢、抖动大小,直接决定电流环带宽、控制精度和系统稳定性。本节围绕 PIE 中断向量表、中断嵌套、中断延迟测量、ISR 入口出口开销分析与最小化策略展开,目标是让读者能定位延迟来源,并把 ISR 的固定开销压到最低。

PIE 中断向量表:从外设到 CPU 的路径

C2000 的中断系统是两级结构:外设级(PIE)和 CPU 级。PIE 把 96 个外设中断源分成 12 组,每组 8 个通道,映射到 12 条 CPU 中断线 INT1~INT12。每个 PIE 通道在 PIE 向量表中占 2 个 16 位字:低字是 ISR 入口地址的低 16 位,高字是高 16 位。CPU 响应中断后,从 PIE 向量表取出地址并跳转。因此,PIE 向量表的正确配置是中断能进入用户 ISR 的前提。

典型配置流程如下:先关闭全局中断(DINT),禁止 CPU 中断(IER = 0x0000),再禁止 PIE 组和通道(PIEIERx = 0,PIEIFRx = 0),然后写 PIE 向量表,最后按需使能 PIE 通道、PIE 组和 CPU 中断线。以 ADCINT1 触发 EPWM1 控制中断为例,ADCINT1 属于 PIE 第 1 组第 1 通道,对应 CPU 的 INT1。配置代码可写成:

EALLOW;
PieVectTable.ADCINT1 = &adc_isr;   // 写入 ISR 入口地址
EDIS;

PieCtrlRegs.PIEIER1.bit.INTx1 = 1; // 使能 PIE 第1组第1通道
PieCtrlRegs.PIEACK.all = 0xFFFF;   // 清所有 PIEACK,允许后续中断
IER |= 0x0001;                     // 使能 CPU INT1
EINT;                              // 开全局中断

这里有一个容易忽略的点:PIEACK。CPU 响应某组中断后,该组的 PIEACK 对应位会被置 1,阻止同组其他中断再次进入,直到 ISR 中写 PIEACK 清除。如果 ISR 忘记写 PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;,同组后续中断会被一直挂起。这是现场最常见的“中断只进一次”故障。

C2000 两级中断路径与 PIE 向量表结构 外设中断源 ADCINT1(采样完成) EPWM1_INT(周期匹配) CPUTIMER0(定时) SCIARX(接收) eCAN0INT(报文) 外设标志置位 → PIEIFR PIE 中断控制器 PIEIFRx:中断标志 12 组 × 8 通道 = 96 源 PIEIERx:使能位 组使能 + 通道使能 PIEACK:应答位 ISR 中必须写 1 清除 PIE 向量表 每通道 2×16bit 低字 + 高字 = ISR 地址 CPU 中断逻辑 INT1~INT12 中断线 PIE 组输出 → CPU 中断请求 IFR:中断标志寄存器 记录挂起的中断 IER:中断使能寄存器 按位使能 INT1~INT12 INTM / 全局中断使能 DINT 关 / EINT 开 取向量 → 跳转 ISR ISR 写 PIEACK 清除应答,允许同组后续中断

中断嵌套:优先级与栈的权衡

默认情况下,CPU 响应中断后 INTM 被置 1,屏蔽所有可屏蔽中断,形成“单级中断”。如果希望高优先级中断打断低优先级 ISR,就需要在低优先级 ISR 中提前执行 EINT,实现嵌套。C2000 的优先级由硬件固定:INT1 最高,INT12 最低;同组内通道号越小优先级越高。嵌套能降低高优先级任务的响应延迟,但代价是栈深度增加、现场保护更复杂。

是否开启嵌套,取决于控制架构。对于电流环,通常要求 ADC 中断绝对优先且不可被打断,此时应保持 INTM 屏蔽,把 ISR 做短。对于“慢任务 + 快保护”结构,例如通信接收 ISR 中需要立即响应 PWM 故障保护,则可在通信 ISR 中开 EINT,让 TZ 故障中断抢占。需要注意:嵌套时每个 ISR 都要保存和恢复被破坏的寄存器,且不能依赖全局变量在嵌套中的一致性。

中断延迟测量:从事件到 ISR 第一条指令

中断延迟定义为“中断事件发生”到“ISR 第一条有效指令执行”之间的时间。它由四部分组成:外设标志同步、PIE 仲裁、CPU 响应与取向量、ISR 入口保护。测量方法通常用 GPIO 翻转配合示波器:在中断触发源处翻转一个 GPIO,在 ISR 第一条指令处翻转另一个 GPIO,两路信号的时间差即为总延迟。也可以用 CPU 定时器在 ISR 入口读计数,但要注意读计数器本身有开销。

以 200 MHz 主频为例,一个时钟周期为 5 ns。典型 PIE 中断响应中,外设同步约 2~3 周期,PIE 仲裁约 3~4 周期,CPU 响应与取向量约 8~14 周期,合计约 13~21 周期,即 65~105 ns。若 ISR 入口有寄存器压栈、浮点上下文保存,还会再增加几十到上百纳秒。测量时应把 GPIO 翻转放在 ISR 最前面,避免把用户代码算进延迟。

ISR 入口出口开销分析与最小化

ISR 的固定开销主要来自:自动上下文保存(ACC、P、ST0、ST1、DP 等)、手动保存的寄存器、函数调用与返回、以及编译器生成的栈操作。C28x 在中断响应时会自动保存部分寄存器,但若 ISR 使用了其他寄存器,编译器会在入口插入保存指令,在出口插入恢复指令。这些开销在每次中断都发生,频率越高影响越大。

最小化策略可以从以下几个方向入手:

  • 用 __interrupt 关键字并保持 ISR 短小:让编译器只保存必要寄存器,避免在 ISR 中调用复杂函数。
  • 把非关键处理移出 ISR:ISR 只做数据搬运、标志清除和触发后台任务,计算放到主循环或低优先级任务。
  • 减少 ISR 内分支和函数调用:分支预测失败和调用开销都会增加抖动。
  • 使用寄存器变量和局部变量:减少对全局内存的访问,降低总线冲突。
  • 合理设置 PIEACK 清除位置:在 ISR 早期清除 PIEACK,可让同组后续中断尽早排队,但必须确保共享数据已处理完。
  • 避免在 ISR 中做浮点运算:若必须使用 FPU,注意 FPU 上下文保存开销,必要时用定点或查表替代。
在调试中断延迟时,建议先用示波器测“事件→ISR 入口”的裸延迟,再逐步加入用户代码,观察每一步增加的时间。这样能把“系统固定开销”和“用户代码开销”分开,避免误判。
不要在 ISR 中长时间关中断或执行阻塞操作。若 ISR 执行时间超过中断周期,会导致中断丢失或控制周期抖动。一般建议 ISR 执行时间不超过中断周期的 50%,高实时性场合控制在 30% 以内。

一个可复用的检查清单

检查项判断依据常见问题
PIE 向量表地址正确ISR 能进入且不跑飞向量表未对齐或写错通道
PIEACK 已清除同组中断能连续进入只进一次中断
IER / PIEIER 使能匹配目标中断能触发组使能或通道使能漏配
INTM 状态符合设计嵌套行为符合预期误开嵌套导致栈溢出
ISR 执行时间可控示波器测 GPIO 翻转宽度ISR 过长导致丢中断
共享变量保护主循环与 ISR 数据一致读写竞争导致数据错乱

把以上内容落到工程实践,核心思路是:先保证中断路径正确(PIE 向量表、PIEACK、使能位),再测量延迟并定位瓶颈,最后通过缩短 ISR、减少保存恢复、合理嵌套来优化。对于实时控制,稳定比极限更重要——一个抖动小、可预测的中断响应,往往比一个平均很快但偶尔超时的系统更有价值。