3. 中断控制器(PIE):外设中断扩展模块

好,咱们接着聊中断。上一章我们把 CPU 级的中断讲清楚了,但有个问题你发现没有?C2000 系列 DSP 的外设那么多——ADC、ePWM、CAP、QEP、SCI、SPI、I2C……每个外设都可能产生中断请求。如果每个外设都直接连到 CPU 的中断引脚上,那 CPU 得有多少个引脚才够用?

所以 TI 的设计师们想了个办法:加一个中间层。这个中间层就是 PIE——外设中断扩展模块。说白了,PIE 就是一个“中断路由器”。它把 96 个外设中断请求,分成了 12 组,每组 8 个,然后映射到 CPU 的 12 条中断线上(INT1~INT12)。

我个人习惯把 PIE 理解成一个“多路复用器”。嗯,这样想就清晰多了。

核心一句话:PIE 的作用就是“多对一”映射——多个外设中断共享一条 CPU 中断线。

3.1 PIE 模块的作用

PIE 模块到底干了哪些事?我总结为三点:

  1. 中断扩展:把有限的中断输入线(INT1~INT12)扩展成 96 个中断通道。每个通道对应一个外设中断源。
  2. 中断分组:96 个中断分成 12 组,每组 8 个。每组对应一个 CPU 中断线。
  3. 中断优先级管理:每组内部有固定的优先级(通道 1 最高,通道 8 最低)。组与组之间也有优先级(INT1 最高,INT12 最低)。

我在项目中遇到过一个问题:两个外设同时触发中断,结果优先级低的那个一直得不到响应。后来查了手册才发现,PIE 的优先级是硬件固定的,不能软件调整。所以设计中断时,一定要把实时性要求高的外设放在低编号的组里。

3.2 PIE 中断分组

咱们来看看 PIE 是怎么分组的。下面这张表是 C2000 系列典型的 PIE 中断映射表(以 TMS320F28335 为例):

CPU 中断线 PIE 组 通道 1 通道 2 通道 3 通道 4 通道 5 通道 6 通道 7 通道 8
INT1 PIE 组 1 PWM1 PWM2 PWM3 PWM4 PWM5 PWM6 PWM7 PWM8
INT2 PIE 组 2 PWM9 PWM10 PWM11 PWM12 CAP1 CAP2 CAP3 CAP4
INT3 PIE 组 3 CAP5 CAP6 QEP1 QEP2 QEP3 QEP4 QEP5 QEP6
INT4 PIE 组 4 ADCINT1 ADCINT2 ADCINT3 ADCINT4 ADCINT5 ADCINT6 ADCINT7 ADCINT8
INT5 PIE 组 5 SCIRX SCITX SPIRX SPITX I2CRX I2CTX CANRX CANTX
…… …… …… …… …… …… …… …… …… ……

你想想看,如果没有 PIE,这 96 个中断源怎么接?CPU 根本忙不过来。有了 PIE,CPU 只需要处理 12 条中断线,每条线上最多挂 8 个外设。这就是分组的妙处。

下面这张 SVG 图展示了 PIE 中断分组的整体架构:

PIE 中断分组架构图 外设中断源(96 个) ADC · ePWM · eCAP · eQEP · SCI · SPI · I2C · CAN · 定时器 · 外部中断 PIE 中断扩展模块 组 1 (INT1) 组 2 (INT2) 组 3 (INT3) …… 组 12 (INT12) 每组 8 个通道,共 96 个中断向量 CPU 中断线(INT1 ~ INT12) CPU 只处理 12 条中断线,每条线对应一个 PIE 组 CPU 内核(C28x)

3.3 PIE 中断使能

PIE 的中断使能,说白了就是“开哪个通道的闸门”。每个 PIE 组都有一个使能寄存器 PIEIER(PIE Interrupt Enable Register),共 8 位,每一位对应一个通道。

举个例子,你想使能 PIE 组 1 的通道 3(PWM3 中断),代码这样写:

// 使能 PIE 组 1 的通道 3
PieCtrlRegs.PIEIER1.bit.INTx3 = 1;

// 或者用位操作
PieCtrlRegs.PIEIER1.all |= 0x04;  // 位 2 对应通道 3

这里有个细节要注意:PIE 使能是“两级使能”。你不仅要使能 PIE 通道,还要使能对应的 CPU 中断线。我刚开始学的时候经常漏掉这一步,结果中断死活不触发。

我的习惯:初始化中断时,先配外设,再配 PIE,最后开 CPU 中断。顺序反了容易出问题。

完整的使能流程是这样的:

// 第一步:使能 PIE 组
PieCtrlRegs.PIEIER1.bit.INTx3 = 1;

// 第二步:使能 CPU 中断线
IER |= M_INT1;  // INT1 对应 PIE 组 1

// 第三步:全局中断使能
EINT;  // 等价于 asm(" clrc INTM")

3.4 PIE 中断标志

中断标志位是 PIE 模块的“状态指示灯”。每个 PIE 通道都有一个中断标志位 PIEIFR(PIE Interrupt Flag Register)。当外设产生中断请求时,对应的标志位自动置 1。

标志位的作用有两个:

  • 查询状态:你可以轮询标志位,看哪个外设产生了中断。
  • 软件触发:手动写 1 到标志位,可以软件触发中断。这在调试时特别有用。

我曾经在调试一个电机控制程序时,死活找不到中断不响应的原因。后来用软件触发的方式,手动置位 PIE 标志位,发现中断服务程序能正常执行。这才确定是外设配置的问题,而不是 PIE 的问题。

清除标志位也有讲究:

// 清除 PIE 组 1 的通道 3 标志位
PieCtrlRegs.PIEIFR1.bit.INTx3 = 0;

// 或者清除整个组
PieCtrlRegs.PIEIFR1.all = 0;

注意:不要在中断服务程序里直接清除 PIE 组的所有标志位!我曾经这么干过,结果把同时到达的其他中断给丢了。正确的做法是:在 ISR 中只清除当前处理的通道标志位,然后由硬件自动处理其他通道。

3.5 PIE 中断响应流程

一个完整的中断响应流程,我画了个流程图:

  1. 外设产生中断请求 → 对应的 PIE 通道标志位 PIEIFR 置 1
  2. 如果该通道的 PIEIER 使能了 → 向 CPU 对应的中断线发出请求
  3. CPU 检查 IER 寄存器,如果对应的位使能了 → 进入中断服务程序
  4. 硬件自动清除 PIEIFR 标志位(注意:是硬件清除,不是软件)
  5. 执行 ISR → 返回主程序

这里有个容易踩的坑:PIE 的 PIEIFR 标志位是硬件自动清除的,但前提是 CPU 响应了中断。如果 CPU 因为全局中断关闭(INTM=1)而没有响应,那么 PIEIFR 会一直保持为 1。等你打开全局中断时,这个中断会立即触发。

嗯,这个特性有时候是好事,有时候是坏事。看你怎么用。

3.6 实战经验总结

最后,分享几个我在项目中积累的经验:

  • 中断优先级规划:把实时性要求高的中断(如 PWM 故障、过流保护)放在 INT1~INT3 组。把通信类中断(SCI、SPI)放在 INT5~INT8 组。
  • 避免中断嵌套过深:PIE 本身不支持硬件中断嵌套。如果你需要嵌套,得在 ISR 中手动开中断。我建议非必要不嵌套,容易出 bug。
  • 调试技巧:用 PIEIFR 寄存器判断中断是否到达。如果标志位置 1 了但 ISR 没执行,说明 CPU 中断没开或者 IER 没配好。
  • 初始化顺序:先配外设,再配 PIE,最后开 CPU 中断。这个顺序我踩过坑,记住了。

一句话总结:PIE 是 DSP 中断系统的“交通枢纽”。搞懂了 PIE 的分组、使能和标志位,你就掌握了 C2000 中断系统的 70%。剩下的 30% 是具体外设的配置,那个相对简单。

好,这一章就到这里。PIE 的内容其实不难,关键是理解它的“分组映射”思想。下一章我们讲中断向量表,那是中断系统的“目录”,告诉你每个中断号对应哪个 ISR。


公众号:蓝海资料掘金营,微信deep3321