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 模块到底干了哪些事?我总结为三点:
- 中断扩展:把有限的中断输入线(INT1~INT12)扩展成 96 个中断通道。每个通道对应一个外设中断源。
- 中断分组:96 个中断分成 12 组,每组 8 个。每组对应一个 CPU 中断线。
- 中断优先级管理:每组内部有固定的优先级(通道 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 中断分组的整体架构:
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 中断响应流程
一个完整的中断响应流程,我画了个流程图:
- 外设产生中断请求 → 对应的 PIE 通道标志位
PIEIFR置 1 - 如果该通道的
PIEIER使能了 → 向 CPU 对应的中断线发出请求 - CPU 检查
IER寄存器,如果对应的位使能了 → 进入中断服务程序 - 硬件自动清除
PIEIFR标志位(注意:是硬件清除,不是软件) - 执行 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