介绍
一个常见的问题是 C28x 中断是否可以嵌套。本文解释了如何通过对中断服务例程 (ISR) 代码进行简单更改来实现中断嵌套。
本文假设读者已经熟悉以下内容:
-
C28x PIE 模块:控制寄存器、向量表、PIE 组
-
C28x 中断控制寄存器:特别是 IER、IFR、INTM。
有关这些主题的更多信息,请参阅以下内容:
-
TMS320C28x CPU 和指令集参考指南 (spru430) 中记录了 CPU 级别的中断以及 CPU 如何响应中断
-
该研讨会材料包含的C28x中断的信息。
-
特定器件系列的技术参考手册 (TRM) 的 PIE 部分描述了外设中断扩展块 (PIE)。
中断优先级
硬件优先级
中断由 C28x 硬件自动确定优先级。所有中断的优先级可以在特定于特定设备系列的系统控制指南中找到。下表显示了复用中断的优先级。与 CPU INT1 对应的组 1 具有最高优先级。每组中有 8 个中断,其中 INTx.1 的优先级最高,INTx.8 的优先级最低。
优先级 | 组 | 中断 |
---|---|---|
最高 | ||
第一组 | INT1.1 | |
第一组 | INT1.2 | |
第一组 | INT1.8 | |
第 2 组 | INT2.1 | |
第 2 组 | INT2.2 | |
第 12 组 | INT12.1 | |
第 12 组 | INT12.2 | |
最低 | 第 12 组 | INT12.8 |
PIE 中断组织
PIE 块的组织方式使中断按逻辑顺序排列。通常需要更高优先级的中断在表中组织得更高,因此默认情况下会以更高的优先级提供服务。
28x 系统中的中断可以分类如下(从高到低排序):
-
非周期性、快速响应
这些是随时可能发生的中断,当它们发生时,必须尽快为它们提供服务。通常,这些中断监视外部事件。
此类中断分配给 PIE 组 1 和 PIE 组 2 中的前几个中断。该位置为它们提供了 PIE 组内的最高优先级。此外,Group 1 复用到 CPU 中断 INT1。CPU INT1 具有最高的硬件优先级。PIE 组 2 多路复用到 CPU INT2,这是第二高的硬件优先级。
-
周期性、快速响应
这些中断发生在一个已知的时期,当它们发生时,必须尽快处理它们以最小化延迟。A/D 转换器就是一个很好的例子。必须以最小延迟处理 A/D 样本。此类中断分配给 PIE 表中的组 1。组 1 多路复用到 CPU INT1。CPU INT1 具有最高的硬件优先级。
-
定期
这些中断发生在一个已知的周期,并且必须在下一个中断之前得到服务。一些 PWM 中断就是一个例子。许多寄存器被隐藏,因此用户有完整的时间来更新寄存器值。此类中断映射到组 2 - 组 5。这些组被多路复用到 CPU INT3 到 INT5(ePWM、eCAP 和 eQEP),它们是次低的硬件优先级。
-
定期,缓冲
这些中断发生在周期性事件中,但被缓冲,因此处理器仅在缓冲区准备好填充/清空时才需要服务此类中断。所有串行端口(SCI / SPI / I2C / CAN)都具有 FIFO 或多个邮箱,以便 CPU 有足够的时间来响应事件而不必担心丢失数据。此类中断映射到 INT6、INT8 和 INT9,它们是下一个最低的硬件优先级。
C28x 中断响应 - 无嵌套(默认行为)
本文的其余部分描述了 C28x CPU 如何响应中断请求。假设中断在 (a) 外设级别、(b) PIE 级别和 (c) CPU 级别(IER 和 INTM)启用,现在 CPU 已准备好开始中断服务程序。下表显示了服务中断时所采取的步骤。
-
硬件:项目由硅本身执行。软件方面不需要任何操作。
-
软件:项目在软件中执行。使用编译器时,这些步骤由编译器针对任何中断处理。如果中断在汇编中,那么这些项目必须由用户处理。
请注意以下几点:
-
当中断服务程序开始时,中断被自动禁止。
-
步骤 1-3 受到硬件中断的保护。
-
第 4 步通过设置 INTM 和 DBGM(全局中断屏蔽)位禁用中断。这可以防止在 ISR 期间处理新的中断。
-
INTM 和 DBGM 将保持设置,除非软件重新启用中断或 CPU 从 ISR 返回。
-
自动上下文恢复包括 INTM 和 DBGM 位。
步 | 硬件 | 软件 | 笔记 |
---|---|---|---|
1 | 清空流水线 自动上下文保存 | C28x CPU 参考指南 (SPRU430) 中记录了中断保护上下文保存 | |
2 | 清除相应的 IFRx 位 | 中断保护 | |
3 | 清除相应的 IERx 位 | 中断保护 | |
4 | 设置 INTM/DBGM 和 Clear LOOP、EALLOW、IDLESTAT | INTM=1 意味着现在禁用可屏蔽中断 这些位在 ST1 中,它在步骤 1 中保存在堆栈中。 | |
5 | 从 PIE 请求中断向量 | ||
6 | PC = 中断向量 | ||
7 | 执行ASP指令 | 在 C/C++ 中,编译器负责这个。确保堆栈偶对齐。 INTM=1,因此仍禁用可屏蔽中断。 | |
8 | 手动上下文保存(如果需要) | 在 C/C++ 中,编译器负责这个。 取决于 ISR 中使用的寄存器。 | |
9 | 执行 ISR 例程 清除任何需要的标志(例如:外设级别) 确认 PIE 组 (PIEACK) | ||
10 | 手动上下文还原(如果需要) | 在 C/C++ 中,编译器负责这个 | |
11 | 执行NASP指令 | 在 C/C++ 中,编译器负责这个。 恢复 ASP 所做的任何对齐 | |
12 | 执行 IRET 指令 | 中断返回 | |
13 | 自动上下文还原 | 这会将 INTM 恢复到其先前的状态(启用中断)。DBGM 和 IER | |
14 | PC = 返回地址 | ||
15 | 继续执行 |
全局和组优先级
应用软件控制何时在中断服务例程中重新启用中断。这可能在例程开始时或在某些时间关键代码完成之后。
软件可以控制两个:
-
全局优先级:可以通过操作 CPU IER 寄存器来管理此优先级。该寄存器控制 16 个可屏蔽的 CPU 中断(INT1 - INT16)。
-
组优先级:这可以通过操作 PIE 模块的中断使能寄存器 (PIEIERx) 来管理。仅更改同一组的 PIEIERx 寄存器非常重要。
警告
不要在该组的 ISR 之外修改 PIEIER 寄存器。例如,PIEIER1 只能在组 1 的 ISR 中修改。同样,PIEIER2 只能在组 2 的ISR 中修改。
该修改应该在组的 PIEACK 位仍然设置时完成,因此在完成修改时不会向 CPU 发送中断。
如果违反此规则,则会触发虚假的 INTx.1 中断。如果应用程序需要这样做,则必须遵循设备 TRM 中的外设中断 -> 系统控制章节下的禁用中断部分中概述的过程,以避免这些虚假中断。
添加简单的软件优先级(嵌套)
因此,嵌套中断所需的步骤是:
第 1 步:设置全局优先级:
-
修改 IER 寄存器以允许处理具有更高用户优先级的 CPU 中断。
-
注意:此时 IER 已经保存在栈上。
第 2 步:设置组优先级:(可选)
-
修改相应的 PIEIERx 寄存器以允许处理具有更高用户设置优先级的组中断。
-
不要从除此 ISR 服务的组之外的另一组清除 PIEIER 寄存器位。这样做可能会导致发生错误的中断。
第 3 步:启用中断:
-
有三个步骤可以做到这一点:
-
清除 PIEACK 位
-
等待至少一个周期
-
清除 INTM 位。使用汇编语句 asm(" CLRC INTM"); 或 TI 示例使用 #define EINT asm(" CLRC INTM")
-
第 4 步:
运行 ISR 的主要部分
第 5 步:
设置 INTM 以禁用中断。使用 asm(" SETC INTM"); 或 TI 示例使用 #define DINT asm(" SETC INTM")
第 6 步:
恢复 PIEIERx(可选,取决于步骤 2)
第 7 步:从 ISR 返回
-
这将自动恢复 INTM 和 IER。
示例代码
// // C28x ISR Code // // Enable nested interrupts // // interrupt
void EPWM1_TZINT_ISR(void)
{
uint16_t TempPIEIER;
TempPIEIER = PieCtrlRegs.PIEIER2.all; // Save PIEIER register for later
IER |= 0x002; // Set global priority by adjusting IER
IER &= 0x002;
PieCtrlRegs.PIEIER2.all &= 0x0002; // Set group priority by adjusting PIEIER2 to allow INT2.2 to interrupt current ISR
PieCtrlRegs.PIEACK.all = 0xFFFF; // Enable PIE interrupts
asm(" NOP"); // Wait one cycle
EINT; // Clear INTM to enable interrupts
//
// Insert ISR Code here.......
// for now just insert a delay
//
for(i = 1; i <= 10; i++) {}
//
// Restore registers saved:
//
DINT;
PieCtrlRegs.PIEIER2.all = TempPIEIER;
}
示例:使用掩码值管理优先级
在大多数情况下,系统只需要嵌套一两个中断。这可以通过前面显示的示例轻松处理。但是,提供了一个示例,该示例涵盖了组中每个中断的可能性。此示例提供了一种使用在编译时配置的简单掩码值来管理全局和组优先级的方法。这允许轻松管理优先级。该方案包含在 C2000Ware 中。请参阅特定设备的设备支持中的软件优先级示例。
示例配置
该示例使用的配置在 DSP280x_SWPrioritizedIsrLevels.h 文件中完成。配置优先级: * 分配全局优先级
INT1PL - INT16PL
这些值用于为 CPU IER 寄存器控制的 16 个中断中的每一个分配优先级。值 1 是最高优先级,而值 16 是最低优先级。可以为多个中断分配相同的优先级。在这种情况下,默认硬件优先级将决定首先服务哪个。优先级 0 用于指示未使用中断。
-
分配 PIE 组优先级 GxyPL(其中 x = PIE 组编号 1 - 12,y = 中断编号 1 - 8) 这些值用于为 PIE 组内的 8 个中断中的每一个分配优先级。值 1 是最高优先级,而值 8 是最低优先级。可以为多个中断分配相同的优先级。在这种情况下,默认硬件优先级将决定首先服务哪个。优先级 0 用于指示未使用中断。
编译器将使用分配的全局和组优先级来生成可用于更改 IER 和 PIEIERx 寄存器的掩码值。应用程序在每个 ISR 中使用这些掩码值来指定允许在 ISR 内服务哪些中断。
生成的 MASK 值
编译时生成的掩码是: * IER 掩码值:
MINT1 - MINT16
用户分配的 INT1PL - INT16PL 值在编译时用于计算每个 CPU 中断的 IER 掩码。该掩码值将在 ISR 内使用,以允许具有更高优先级的 CPU 中断中断当前 ISR,从而以更高的优先级级别提供服务。
-
PIEIERxy 掩码值:MGxy(其中 x = PIE 组编号 1 - 12,y = 中断编号 1 - 8) 分配的组优先级 (GxyPL) 在编译时用于计算每个 PIE 组的 PIEIERx 掩码。该掩码值将在 ISR 中使用,以允许同一组内具有更高分配优先级的中断中断当前 ISR,从而以更高的优先级级别提供服务。
使用 MASK 值
在中断服务程序中,全局优先级和组优先级可以通过软件更改,以允许服务其他中断。步骤与之前描述的相同。唯一的区别是 IER 和 PIEIERx 的掩码值已在 DSP28_SWPrioritizedIsrLevels.h 文件中进行管理。
第 1 步:设置全局优先级
-
修改 IER 以允许来自与当前 ISR 相同的 PIE 组的 CPU 中断。
-
修改 IER 以允许处理具有更高用户定义优先级的 CPU 中断。
第 2 步:设置组优先级
-
将当前 PIEIERx 值保存到临时寄存器。
-
然后设置 PIEIER 寄存器以允许处理 PIE 组内具有更高优先级的中断。
第 3 步:启用中断
-
这需要3个步骤:
-
通过向 PIEACK 寄存器写入全 1 来启用所有 PIE 中断组
-
等待至少一个周期
-
通过清除 INTM 启用全局中断
-
第 4 步:执行 ISR。
在步骤 1-3 中启用的中断(具有更高软件优先级的中断)将被允许中断当前的 ISR,因此首先得到服务。
第 5 步:
禁用中断
第 6 步:
恢复 PIEIERx 寄存器
第 7 步:
退出
// // C28x ISR Code // // Enable nested interrupts using masks defined // in the software prioritization example code // // Connected to PIEIER2_1 (use MINT2 and MG21 masks) //
if (G21PL != 0)
interrupt void EPWM1_TZINT_ISR(void) // EPWM1 Trip Zone
{
uint16_t TempPIEIER;
TempPIEIER = PieCtrlRegs.PIEIER2.all;
IER |= M_INT2;
IER &= MINT2; // Set "global" priority
PieCtrlRegs.PIEIER2.all &= MG21; // Set "group" priority
PieCtrlRegs.PIEACK.all = 0xFFFF; // Enable PIE interrupts
asm(" NOP"); // Wait one cycle
EINT; // Clear INTM to enable interrupts
//
// Insert ISR Code here.......
// for now just insert a delay
//
for(i = 1; i <= 10; i++) {}
//
// Restore registers saved:
//
DINT;
PieCtrlRegs.PIEIER2.all = TempPIEIER;
}