2. 中断源与中断向量:中断源分类(硬件/软件)、中断向量表(IVT)、中断向量地址映射
好,咱们接着聊。上一章我们把中断的“是什么”和“为什么”讲清楚了。这一章,咱们得深入一点,看看中断到底是从哪来的,以及CPU是怎么找到它的。
说白了,中断系统就像一个大楼里的应急响应中心。你得先知道“谁”能触发警报(中断源),然后还得知道“警报响了之后,应急手册放在哪”(中断向量表)。这两件事搞不明白,后面写中断服务程序就是瞎忙活。
2.1 中断源分类:硬件中断与软件中断
中断源,就是触发中断的源头。我习惯把它们分成两大类:硬件中断和软件中断。你想想看,一个来自外部世界,一个来自程序内部,本质完全不同。
2.1.1 硬件中断
硬件中断,是由芯片外部或内部的硬件模块产生的。它不受CPU控制,是“被动”发生的。
- 外部硬件中断:来自芯片引脚。比如GPIO按键按下、外部传感器信号跳变。我在项目中遇到过,一个电机驱动芯片的故障引脚直接连到了DSP的GPIO上,一旦电机过流,这个引脚就拉低,触发紧急停机中断。嗯,这个设计救过我的板子。
- 内部硬件中断:来自芯片内部外设。比如定时器溢出、ADC转换完成、SCI(串口)接收到数据。这些中断是DSP内部模块自己产生的,不需要外部连线。
核心区别:外部中断需要物理引脚,内部中断不需要。但它们的处理流程在CPU看来是一样的。
2.1.2 软件中断
软件中断,是程序主动“制造”出来的。它不是硬件触发的,而是通过执行一条特定的指令来触发。
- 陷阱(Trap):故意触发的中断,用于系统调用。比如你在RTOS里调用一个API,底层可能就是通过软件中断切换到内核态。
- 异常(Exception):程序出错时触发。比如除零错误、访问非法地址。这其实也算一种软件中断,但它是“被动”的。
我个人习惯把软件中断当作一种“受控的跳转”。你想让CPU立刻去执行一段特殊代码,但又不想用函数调用(因为函数调用会压栈很多寄存器),那就用软件中断。它只压栈关键上下文,效率更高。
我的经验:在调试阶段,我经常在关键位置插入一条软件中断指令。这样一旦程序跑到这里,就会停下来,我可以检查现场。这比设断点灵活多了。
2.2 中断向量表(IVT)
好,现在我们知道中断源有哪些了。但问题是:当某个中断发生时,CPU怎么知道该去执行哪段代码?
答案就是——中断向量表(Interrupt Vector Table,IVT)。
IVT,说白了就是一张“地址映射表”。它存放在内存的固定位置。这张表的每一项(称为“中断向量”)都存放着一个地址,这个地址就是对应中断服务程序(ISR)的入口地址。
我刚开始学DSP时,总觉得IVT很神秘。后来自己画了个图,一下就明白了。
你看,每个中断源都有一个固定的“中断向量号”。CPU拿到这个号后,直接去IVT里查表,找到对应的地址,然后跳过去执行。
注意:IVT的地址是固定的,由芯片厂商定义。你不能随便改。比如C2000系列DSP的复位向量通常位于0x3F FFC0。我曾经见过有人把IVT地址搞错了,结果一上电就跑飞了。
2.3 中断向量地址映射
中断向量地址映射,就是“中断向量号”到“IVT表项地址”的换算关系。这个关系通常很简单:
IVT表项地址 = IVT基地址 + (中断向量号 × 每个表项的大小)
举个例子。假设IVT基地址是0x3F FFC0,每个表项占2个字节(16位地址),那么:
- 中断向量号0(复位):地址 = 0x3F FFC0 + 0×2 = 0x3F FFC0
- 中断向量号1(NMI):地址 = 0x3F FFC0 + 1×2 = 0x3F FFC2
- 中断向量号2(外部中断1):地址 = 0x3F FFC0 + 2×2 = 0x3F FFC4
嗯,这里要注意。不同DSP的IVT表项大小不一样。有些是2字节,有些是4字节(32位地址)。你得查数据手册确认。
核心要点:中断向量地址映射是硬件自动完成的。你只需要在IVT的对应位置填上你的ISR地址就行。CPU会帮你算好该去哪找。
2.4 实际配置示例(以TMS320F28335为例)
光说不练假把式。咱们看个实际例子。在TI的C2000系列DSP上,配置中断向量通常用PIE(外设中断扩展)模块。但底层原理是一样的。
// 定义中断服务函数
interrupt void myTimer0ISR(void)
{
// 处理定时器中断
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // 应答中断
}
// 主函数中配置中断向量
void main(void)
{
// 初始化PIE控制寄存器
InitPieCtrl();
// 禁止所有中断
DINT;
// 初始化PIE向量表
InitPieVectTable();
// 将ISR地址写入PIE向量表
// 定时器0的中断向量在PIE中的位置是1.7(组1,第7个)
EALLOW; // 允许访问受保护的寄存器
PieVectTable.TINT0 = &myTimer0ISR;
EDIS; // 禁止访问受保护的寄存器
// 使能中断
PieCtrlRegs.PIEIER1.bit.INTx7 = 1; // 使能定时器0中断
IER |= M_INT1; // 使能CPU中断1
EINT; // 全局使能中断
while(1)
{
// 主循环
}
}
你看,核心就是那一行 PieVectTable.TINT0 = &myTimer0ISR;。这行代码把ISR的地址写入了PIE向量表的对应位置。当定时器0中断发生时,CPU就会自动跳转到 myTimer0ISR 去执行。
避坑指南:我曾经在配置中断向量时,忘记调用 EALLOW 和 EDIS。结果写向量表的操作被硬件忽略了,中断一直触发不了。查了两天才发现是这个问题。所以,操作受保护的寄存器时,一定要成对使用 EALLOW 和 EDIS。
2.5 总结
这一章我们讲了三个核心概念:
- 中断源:分硬件中断和软件中断。硬件中断来自外部引脚或内部外设,软件中断是程序主动触发的。
- 中断向量表(IVT):一张存放在固定内存地址的表格,每个表项存放一个ISR的入口地址。
- 中断向量地址映射:中断向量号到IVT表项地址的换算关系,由硬件自动完成。
搞懂了这些,你就掌握了中断系统的“骨架”。下一章,我们会往这个骨架上填“血肉”——也就是中断的优先级和嵌套。嗯,那才是真正考验设计能力的地方。
公众号:蓝海资料掘金营,微信deep3321