核心概念:中断不仅仅是"跳转",更是一次"现场保护与恢复"的精密操作。
很多人觉得中断就是"硬件调用了一个函数",虽然现象如此,但如果不懂底层的**硬件自动压栈(Stacking)和向量表(Vector Table)**机制,遇到"中断里死机"或者"进不去中断"时就会束手无策。
我们以最通用的 ARM Cortex-M 架构为例(STM32/GD32等均适用),剖析这一过程。
1. 中断向量表 (The Vector Table):CPU 的通讯录
CPU 收到中断信号(比如一个 GPIO 电平变化),它怎么知道去执行哪段代码?答案就在中断向量表。
-
物理本质 :它本质上是一个函数指针数组 ,通常存放在 Flash 的起始地址(
0x0800 0000或0x0000 0000)。 -
结构:
-
第 0 项:栈顶地址初始值(MSP)。
-
第 1 项:Reset Handler(复位程序入口)。
-
第 2 项:NMI Handler。
-
第 3 项:HardFault Handler...
-
第 15+ 项:外设中断(SysTick, UART, Timer...)。
-
代码视角的向量表 (通常在 .s 启动文件中):
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; HardFault Handler
...
DCD USART1_IRQHandler ; USART1
思考一下? 为什么我们在 C 代码里写的函数名(如 void USART1_IRQHandler(void))能被 CPU 找到?
答案 :因为链接脚本和启动文件配合,将这个 C 函数的地址填入了向量表的对应位置。如果你的函数名拼错了,链接器找不到对应的符号,就会填入默认的 Default_Handler(通常是个死循环),导致中断触发后系统卡死。
2. 惊险一跳:硬件自动压栈 (Stacking)
这是 ARM Cortex-M 最人性化的设计。在老式的 8 位机或某些架构中,进入中断后,程序员必须手动写汇编代码保存寄存器。
而在 Cortex-M 中,当 CPU 响应中断时,硬件会自动 将当前执行现场的关键寄存器压入堆栈(Stack)。
自动压入栈的 8 个寄存器(顺序很重要):
-
xPSR (程序状态寄存器)
-
PC (程序计数器 - 返回地址)
-
LR (链接寄存器)
-
R12
-
R3
-
R2
-
R1
-
R0
为什么是这几个?
-
R0-R3, R12 是 C 语言调用约定(AAPCS)中的"调用者保存寄存器"(Caller-saved)。编译器默认函数调用可以随意覆盖这几个寄存器,所以中断来了必须先存起来,否则原来的逻辑就乱了。
-
PC 被保存,是为了知道中断结束后回哪里继续执行。
调试技巧 :当你遇到 HardFault 或者莫名其妙的死机时,查看栈顶的内存数据,就能看到死机前一瞬间这 8 个寄存器的值。其中栈里的 PC 值 就是死机前正在执行的那行代码!
3. ISR (中断服务程序) 的执行
因为硬件已经帮我们保存了易失性寄存器,所以我们可以直接用标准 C 语言 写中断服务函数,完全不需要加 __interrupt 之类的非标准关键字(这是 ARM 相比 8051/PIC 的一大进步)。
void TIM2_IRQHandler(void) {
// 1. 检查标志位 (防止误触发)
if (TIM2->SR & TIM_SR_UIF) {
// 2. 清除标志位 (必须!否则退出中断后立马又进,死循环)
TIM2->SR = ~TIM_SR_UIF;
// 3. 执行业务逻辑 (越快越好!)
g_tick_count++;
}
}
4. 中断返回:神奇的 EXC_RETURN
普通函数返回是用 RET 或 POP PC。但中断结束时,CPU 怎么知道要从堆栈里把刚才那 8 个寄存器弹出来恢复现场呢?
秘密在于 LR (Link Register)。
当进入中断时,硬件会将 LR 设置为一个特殊值(例如 0xFFFFFFF9),而不是普通的返回地址。 当 CPU 执行到 BX LR 指令,发现 LR 是这个特殊值时,它就会触发硬件机制:
-
自动出栈(Unstacking):把之前压入的 8 个寄存器值恢复到 CPU 寄存器中。
-
恢复执行:PC 指针指回被打断的地方,程序继续运行,就像什么都没发生过一样。
关键点:
-
向量表是硬件中断号与软件函数地址的映射表。
-
自动压栈 保存了
R0-R3, R12, LR, PC, xPSR,这是 C 语言写 ISR 的基础。 -
中断返回靠的是 LR 中的特殊魔术值(EXC_RETURN)。