【第13期】中断机制详解 :从向量表到ISR

核心概念:中断不仅仅是"跳转",更是一次"现场保护与恢复"的精密操作。

很多人觉得中断就是"硬件调用了一个函数",虽然现象如此,但如果不懂底层的**硬件自动压栈(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 个寄存器(顺序很重要):

  1. xPSR (程序状态寄存器)

  2. PC (程序计数器 - 返回地址)

  3. LR (链接寄存器)

  4. R12

  5. R3

  6. R2

  7. R1

  8. 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 是这个特殊值时,它就会触发硬件机制:

  1. 自动出栈(Unstacking):把之前压入的 8 个寄存器值恢复到 CPU 寄存器中。

  2. 恢复执行:PC 指针指回被打断的地方,程序继续运行,就像什么都没发生过一样。

关键点:

  1. 向量表是硬件中断号与软件函数地址的映射表。

  2. 自动压栈 保存了 R0-R3, R12, LR, PC, xPSR,这是 C 语言写 ISR 的基础。

  3. 中断返回靠的是 LR 中的特殊魔术值(EXC_RETURN)。

相关推荐
chenbingjie_c3 小时前
C++ 进阶核心知识点总结:模板、内存分区、new/delete、内存池
开发语言·c++
Escalating_xu4 小时前
【C 语言】深入理解指针(1·下):指针运算、野指针、assert 与传址实战
java·c语言·开发语言
实心儿儿4 小时前
Qt — Qt 事件
开发语言·qt
周杰偷奶茶5 小时前
【Java】数据类型与变量
java·开发语言
code斗5 小时前
Java数据结构:堆详解
java·开发语言·数据结构
Brilliantwxx5 小时前
【STM32】 NVIC 与 EXTI 中断 · 超详细源码级解析
stm32·单片机·嵌入式硬件
重生之小比特5 小时前
【C++进阶】map和set
java·开发语言·c++
lie..5 小时前
30天从零开始学AI应用开发(Day 12):像产品一样稳:流式输出、报错重试、花钱控制
开发语言·人工智能·php
傻啦嘿哟5 小时前
Python的默认参数把我坑惨了,原来写[]和写None的区别这么大
开发语言·python·机器学习
阿钱真强道6 小时前
20 嵌入式操作系统 | ubus:把自己的程序状态暴露出去
c语言·进程通信·ubus·ubusd·blobmsg