1.1 中断向量表基础概念

1.1.1 什么是中断向量表

中断向量表(Interrupt Vector Table,IVT)是嵌入式系统中最核心的数据结构之一。它本质上是一个地址表,表中每个条目(向量)存储着对应中断或异常处理程序的入口地址。当CPU检测到中断或异常事件时,硬件自动从向量表中取出对应的地址,并跳转到该地址执行处理程序。

核心定义:中断向量表 = 一张由硬件定义起始位置、按中断号索引的地址跳转表。每个表项宽度通常为4字节(32位系统)或8字节(64位系统)。
术语 说明
向量表基地址 向量表在内存中的起始物理地址,由芯片复位后硬件固定或通过寄存器配置
向量表项 每个中断/异常对应的入口地址存储单元
中断号 硬件为每个中断源分配的编号,作为向量表的索引偏移
异常向量 CPU内部异常(如复位、硬故障、缺页)对应的固定向量表项
中断向量表结构示意图 内存地址空间 向量0:复位 向量1:NMI 向量2:硬故障 向量3:SVCall 向量4:... 向量16:IRQ0 向量17:IRQ1 向量18:IRQ2 向量19:IRQ3 向量20:... 向量N-2 向量N-1 向量N 基地址 + (中断号 × 4) → 取出处理函数入口地址 CPU硬件自动索引

1.1.2 向量表在内存中的位置

向量表的存放位置由芯片架构和启动配置共同决定。不同架构有不同约定:

  • Cortex-M 系列(ARMv7-M/v8-M): 向量表默认位于地址 0x00000000,可通过 SCB->VTOR 寄存器重定位到任意地址(需对齐到 512 字节边界)。
  • Cortex-A 系列: 异常向量表支持高向量模式(0xFFFF0000)和低向量模式(0x00000000),通过系统控制寄存器 SCTLR.V 位选择。
  • RISC-V: 通过 mtvec(机器模式)和 stvec(监管模式)寄存器设置向量表基地址,支持直接模式和向量模式。
  • x86: 中断描述符表(IDT)通过 LIDT 指令加载,基地址可位于内存任意位置。
架构 默认基地址 重定位方式 对齐要求
Cortex-M3/M4/M7 0x00000000 VTOR 寄存器 512 字节
Cortex-A 0x00000000 / 0xFFFF0000 SCTLR.V 位 无特殊要求
RISC-V 由 mtvec/stvec 指定 直接写 CSR 4 字节(直接模式)/ 128 字节(向量模式)
x86 由 LIDT 加载 LIDT 指令 8 字节对齐(IDTR 限长)
常见陷阱: 向量表重定位后,如果新地址未按架构要求对齐,CPU 在取向量时可能发生对齐错误,导致系统直接进入硬件故障(HardFault)或死锁。Cortex-M 的 VTOR 寄存器要求低 9 位为 0,即基地址必须 512 字节对齐。
Cortex-M 向量表重定位机制 Flash 默认向量表 基地址 0x00000000 存放复位后初始向量 RAM 运行时向量表 基地址 0x20000000 运行时动态更新中断入口 VTOR 重定位 重定位流程 1. 启动时从 Flash 0x00000000 取复位向量 → 2. 执行启动代码 → 3. 将向量表复制到 RAM 4. 设置 VTOR = RAM 基地址 → 5. 后续中断从 RAM 取向量

1.1.3 向量表与 CPU 启动流程的关系

向量表在 CPU 启动流程中扮演第一入口的角色。以典型的 Cortex-M 启动为例,硬件复位后的执行序列完全依赖向量表:

  1. 复位后硬件行为: CPU 从地址 0x00000000 读取初始栈指针(MSP),从 0x00000004 读取复位向量(Reset_Handler 地址)。
  2. 执行启动代码: 跳转到 Reset_Handler,完成时钟配置、全局变量初始化、BSS 段清零等操作。
  3. 调用 main(): 启动代码最后调用 C 运行时库的 __main 或直接跳转到 main()。
  4. 中断使能后: 任何中断触发时,CPU 自动从向量表基地址 + (中断号 × 4) 取出处理函数地址并跳转。
工程启示: 如果向量表配置错误(如复位向量指向无效地址),CPU 在启动瞬间就会跑飞。调试时若发现仿真器无法连接或复位后 PC 指针异常,应首先检查向量表前两个表项(栈指针和复位向量)是否正确。
CPU 启动流程与向量表关系 上电 / 复位 硬件复位信号释放 读取向量表前两项 0x00000000 → MSP | 0x00000004 → PC 执行 Reset_Handler 时钟、变量、BSS 初始化 跳转到 main() 用户应用程序运行 中断触发 → 查向量表 → 跳转 ISR

1.1.4 向量表初始化典型代码

以下是一个基于 Cortex-M 的向量表定义示例,展示了如何用 C 语言构造向量表并确保其位于正确位置:

/* 定义中断处理函数原型 */
void Reset_Handler(void);
void NMI_Handler(void);
void HardFault_Handler(void);
void SVC_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);

/* 外设中断处理函数 */
void UART0_IRQHandler(void);
void TIMER0_IRQHandler(void);

/* 使用 __attribute__((section(".isr_vector"))) 将向量表放到指定段 */
__attribute__((section(".isr_vector")))
void (* const g_pfnVectors[])(void) = {
    (void (*)(void))&__StackTop,    /* 0x00000000: 初始栈指针 */
    Reset_Handler,                  /* 0x00000004: 复位向量 */
    NMI_Handler,                    /* 0x00000008: NMI */
    HardFault_Handler,              /* 0x0000000C: 硬故障 */
    0, 0, 0, 0,                    /* 保留 */
    SVC_Handler,                    /* 0x00000010: SVCall */
    0,                              /* 保留 */
    0,                              /* 保留 */
    PendSV_Handler,                 /* 0x00000014: PendSV */
    SysTick_Handler,                /* 0x00000018: SysTick */
    /* 外设中断从第16号开始 */
    UART0_IRQHandler,               /* 中断号16 */
    TIMER0_IRQHandler,              /* 中断号17 */
    /* ... 后续中断向量 */
};
关键检查点:
  • 向量表第一个表项必须是有效的栈指针地址(不能为 NULL 或无效 RAM 地址)。
  • 第二个表项(复位向量)必须指向可执行的代码区域(Flash 或 RAM)。
  • 所有未使用的中断向量应指向一个默认的 Default_Handler,避免意外触发时系统跑飞。
  • 链接脚本中必须将 .isr_vector 段放置在目标架构要求的起始地址。