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;,同组后续中断会被一直挂起。这是现场最常见的“中断只进一次”故障。
中断嵌套:优先级与栈的权衡
默认情况下,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 上下文保存开销,必要时用定点或查表替代。
一个可复用的检查清单
| 检查项 | 判断依据 | 常见问题 |
|---|---|---|
| PIE 向量表地址正确 | ISR 能进入且不跑飞 | 向量表未对齐或写错通道 |
| PIEACK 已清除 | 同组中断能连续进入 | 只进一次中断 |
| IER / PIEIER 使能匹配 | 目标中断能触发 | 组使能或通道使能漏配 |
| INTM 状态符合设计 | 嵌套行为符合预期 | 误开嵌套导致栈溢出 |
| ISR 执行时间可控 | 示波器测 GPIO 翻转宽度 | ISR 过长导致丢中断 |
| 共享变量保护 | 主循环与 ISR 数据一致 | 读写竞争导致数据错乱 |
把以上内容落到工程实践,核心思路是:先保证中断路径正确(PIE 向量表、PIEACK、使能位),再测量延迟并定位瓶颈,最后通过缩短 ISR、减少保存恢复、合理嵌套来优化。对于实时控制,稳定比极限更重要——一个抖动小、可预测的中断响应,往往比一个平均很快但偶尔超时的系统更有价值。