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

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

很多人觉得中断就是"硬件调用了一个函数",虽然现象如此,但如果不懂底层的**硬件自动压栈(Stacking)向量表(Vector Table)**机制,遇到"中断里死机"或者"进不去中断"时就会束手无策。

我们以最通用的 ARM Cortex-M 架构为例(STM32/GD32等均适用),剖析这一过程。

1. 中断向量表 (The Vector Table):CPU 的通讯录

CPU 收到中断信号(比如一个 GPIO 电平变化),它怎么知道去执行哪段代码?答案就在中断向量表

  • 物理本质 :它本质上是一个函数指针数组 ,通常存放在 Flash 的起始地址(0x0800 00000x0000 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

普通函数返回是用 RETPOP 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)。

相关推荐
伯明翰java2 小时前
Java数据类型与变量
java·开发语言
渣渣盟2 小时前
Linux邮件服务器快速搭建指南
linux·服务器·开发语言
ArrebolJiuZhou2 小时前
00 arm开发环境的搭建
linux·arm开发·单片机·嵌入式硬件
易水寒陈2 小时前
使用J-Link RTT Viewer
stm32·单片机
BD_Marathon2 小时前
Promise基础语法
开发语言·前端·javascript
Aotman_3 小时前
JavaScript MutationObserver用法( 监听DOM变化 )
开发语言·前端·javascript·vue.js·前端框架·es6
少一倍的优雅3 小时前
hi3863(ws63)智能小车 (三)PWM驱动马达
单片机·嵌入式硬件·hi3863
xingzhemengyou13 小时前
STM32 内存空间中的选项字节
stm32·单片机
Bruce_Liuxiaowei3 小时前
Nmap+Fofa 一体化信息搜集工具打造
运维·开发语言·网络·网络安全