3.1 复位和向量表
2026/4/26大约 7 分钟启动流程复位向量表STM32
3.1 复位和向量表
📚 本节导读
学习时长: 约 45 分钟
难度级别: ⭐⭐☆☆☆(基础级)
前置知识: 0.3 STM32基础架构
🎯 学习目标
- 理解什么是复位
- 掌握向量表的结构
- 理解复位后的执行流程
- 了解异常和中断的区别
- 理解OneOS与向量表的关系
一、复位机制
1.1 什么是复位
复位(Reset)是让CPU回到初始状态的操作。复位后:
- CPU寄存器恢复到默认值
- 外设恢复到复位状态
- 从向量表加载SP和PC,开始执行
1.2 复位类型
| 复位类型 | 触发方式 | 说明 |
|---|---|---|
| 上电复位 | 电源上电 | 首次加电,完整的硬件初始化 |
| 引脚复位 | NRST引脚 | 外部复位信号,软件可用 |
| 看门狗复位 | 看门狗超时 | 软件错误保护,防止死机 |
| 软件复位 | 软件指令 | 主动复位,例如NVIC_SystemReset() |
1.3 复位后的状态
复位后CPU的关键状态:
- SP(栈指针): 从向量表偏移0x00处加载
- PC(程序计数器): 从向量表偏移0x04处加载
- 其他寄存器: 恢复到复位值(详见Cortex-M3手册)
二、向量表详解
2.1 向量表的概念
向量表是一个存储异常和中断处理函数地址的数组。
类比:
- 向量表就像电话簿
- 索引是"异常/中断号"
- 内容是"处理函数的地址"
2.2 真实向量表代码解析
让我们看一下项目中STM32F103zet6-atk-elite板子使用的真实向量表:
文件位置:
projects/stm32f103zet6-atk-elite/board/startup/startup_stm32f103xe_arm.s向量表定义(第56-142行):
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY, ALIGN=10
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
EXPORT ___initial_sp
__Vectors DCD __initial_sp ; 0x00: 栈顶地址
DCD Reset_Handler ; 0x04: 复位处理函数
DCD NMI_Handler ; 0x08: NMI处理
DCD HardFault_Handler ; 0x0C: 硬件错误
DCD MemManage_Handler ; 0x10: 内存管理
DCD BusFault_Handler ; 0x14: 总线错误
DCD UsageFault_Handler ; 0x18: 使用错误
DCD 0 ; 0x1C: 保留
DCD 0 ; 0x20: 保留
DCD 0 ; 0x24: 保留
DCD 0 ; 0x28: 保留
DCD SVC_Handler ; 0x2C: SVC
DCD DebugMon_Handler ; 0x30: 调试监控
DCD 0 ; 0x34: 保留
DCD PendSV_Handler ; 0x38: PendSV
DCD SysTick_Handler ; 0x3C: SysTick
; 外部中断(第79-141行)
DCD WWDG_IRQHandler ; 窗口看门狗
DCD PVD_IRQHandler ; PVD
DCD TAMPER_IRQHandler ; 篡改检测
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI0
DCD EXTI1_IRQHandler ; EXTI1
DCD EXTI2_IRQHandler ; EXTI2
DCD EXTI3_IRQHandler ; EXTI3
DCD EXTI4_IRQHandler ; EXTI4
DCD DMA1_Channel1_IRQHandler ; DMA1通道1
DCD DMA1_Channel2_IRQHandler ; DMA1通道2
DCD DMA1_Channel3_IRQHandler ; DMA1通道3
DCD DMA1_Channel4_IRQHandler ; DMA1通道4
DCD DMA1_Channel5_IRQHandler ; DMA1通道5
DCD DMA1_Channel6_IRQHandler ; DMA1通道6
DCD DMA1_Channel7_IRQHandler ; DMA1通道7
DCD ADC1_2_IRQHandler ; ADC1&2
DCD USB_HP_CAN1_TX_IRQHandler ; USB高优先级或CAN1 TX
DCD USB_LP_CAN1_RX0_IRQHandler ; USB低优先级或CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; EXTI 9-5
DCD TIM1_BRK_IRQHandler ; TIM1 Break
DCD TIM1_UP_IRQHandler ; TIM1 Update
DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger
DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare
DCD TIM2_IRQHandler ; TIM2
DCD TIM3_IRQHandler ; TIM3
DCD TIM4_IRQHandler ; TIM4
DCD I2C1_EV_IRQHandler ; I2C1 Event
DCD I2C1_ER_IRQHandler ; I2C1 Error
DCD I2C2_EV_IRQHandler ; I2C2 Event
DCD I2C2_ER_IRQHandler ; I2C2 Error
DCD SPI1_IRQHandler ; SPI1
DCD SPI2_IRQHandler ; SPI2
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD USART3_IRQHandler ; USART3
DCD EXTI15_10_IRQHandler ; EXTI 15-10
DCD RTC_Alarm_IRQHandler ; RTC Alarm
DCD USBWakeUp_IRQHandler ; USB Wakeup
DCD TIM8_BRK_IRQHandler ; TIM8 Break
DCD TIM8_UP_IRQHandler ; TIM8 Update
DCD TIM8_TRG_COM_IRQHandler ; TIM8 Trigger
DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare
DCD ADC3_IRQHandler ; ADC3
DCD FSMC_IRQHandler ; FSMC
DCD SDIO_IRQHandler ; SDIO
DCD TIM5_IRQHandler ; TIM5
DCD SPI3_IRQHandler ; SPI3
DCD UART4_IRQHandler ; UART4
DCD UART5_IRQHandler ; UART5
DCD TIM6_IRQHandler ; TIM6
DCD TIM7_IRQHandler ; TIM7
DCD DMA2_Channel1_IRQHandler ; DMA2通道1
DCD DMA2_Channel2_IRQHandler ; DMA2通道2
DCD DMA2_Channel3_IRQHandler ; DMA2通道3
DCD DMA2_Channel4_5_IRQHandler ; DMA2通道4&5
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors注: 栈和堆的详细配置将在3.2节"启动文件解析"中讲解。
2.3 向量表结构详解
| 地址偏移 | 用途 | 对应函数/值 |
|---|---|---|
| 0x00 | 初始栈指针 | __initial_sp |
| 0x04 | 复位向量 | Reset_Handler ← 最重要! |
| 0x08 | NMI向量 | NMI_Handler |
| 0x0C | 硬件错误向量 | HardFault_Handler |
| 0x10 | 内存管理向量 | MemManage_Handler |
| 0x14 | 总线错误向量 | BusFault_Handler |
| 0x18 | 使用错误向量 | UsageFault_Handler |
| 0x1C-0x28 | 保留 | - |
| 0x2C | SVC向量 | SVC_Handler |
| 0x30 | 调试监控向量 | DebugMon_Handler |
| 0x34 | 保留 | - |
| 0x38 | PendSV向量 | PendSV_Handler |
| 0x3C | SysTick向量 | SysTick_Handler |
| 0x40+ | 外部中断 | 各外设中断处理函数 |
关键点:
__initial_sp(0x00): 栈指针的初始值,指向SRAM的末尾Reset_Handler(0x04): 复位后第一个执行的函数!
2.4 关键技术点说明
为什么对齐到1024字节(ALIGN=10)?
ALIGN=10 表示2^10 = 1024字节对齐,原因:
1. Cortex-M3的向量表基地址寄存器(VTOR)要求最低10位为0
2. 这是硬件设计的要求,确保向量表可以被正确映射
3. 对齐可以提高访问效率向量表的地址映射
地址0x00000000与0x08000000的关系:
取决于boot引脚配置:
- BOOT0=0,BOOT1=X:从Flash启动,0x00000000映射到0x08000000
- BOOT0=1,BOOT1=0:从系统存储器启动
- BOOT0=1,BOOT1=1:从SRAM启动
复位时,CPU总是从地址0读取向量表,但实际访问的物理地址取决于boot配置。向量表重映射
向量表重映射机制:
1. 默认向量表在Flash:0x08000000
2. 可以通过VTOR寄存器将向量表重映射到RAM
3. OneOS在某些场景下会使用RAM向量表(例如运行时动态注册中断)三、复位执行流程
3.1 Reset_Handler解析
让我们看一下真实的Reset_Handler代码(第144-155行):
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
; 第一步:调用 SystemInit()
LDR R0, =SystemInit
BLX R0
; 第二步:调用 __main(C库初始化)
LDR R0, =__main
BX R0
ENDPReset_Handler做了两件事:
- 调用
SystemInit()→ 系统初始化(时钟、外设等) - 调用
__main→ C库初始化(最终调用main())
3.2 完整执行流程
3.3 关键步骤详解
步骤1: 加载栈指针(SP)
复位后,CPU首先从地址 0x00000000 读取数据
这个数据就是 __initial_sp(初始栈指针)
然后CPU把这个值加载到 SP 寄存器
为什么先加载SP?
- 确保执行代码时栈可用
- 函数调用、局部变量等都需要栈
- 如果SP未设置就执行代码,会产生不可预期的行为
在我们的项目中:
- Stack_Size = 0x400 = 1024 字节
- __initial_sp = Stack_Mem + Stack_Size = 0x20001000步骤2: 加载程序计数器(PC)
接着,CPU从地址 0x00000004 读取数据
这个数据就是 Reset_Handler 的地址
然后CPU把这个值加载到 PC 寄存器
PC指向Reset_Handler,开始执行步骤3: 执行Reset_Handler
PC指向 Reset_Handler
CPU开始执行复位处理函数
Reset_Handler 会:
1. 调用 SystemInit() → 初始化时钟、向量表偏移等
2. 调用 __main → C库初始化,最终调用main()四、异常与中断
4.1 异常概述
异常是CPU内部产生的事件,由指令执行引起。
| 异常类型 | 异常号 | 优先级 | 说明 |
|---|---|---|---|
| Reset | 1 | -3(最高) | 复位 |
| NMI | 2 | -2 | 不可屏蔽中断 |
| HardFault | 3 | -1 | 硬件错误 |
| MemManage | 4 | 0 | 内存访问错误 |
| BusFault | 5 | 1 | 总线错误 |
| UsageFault | 6 | 2 | 使用错误(例如未定义指令) |
| SVC | 11 | 3 | SVC指令(系统调用) |
| DebugMon | 12 | 4 | 调试事件 |
| PendSV | 14 | 5 | PendSV请求(用于任务切换) |
| SysTick | 15 | 6 | 系统时钟节拍 |
4.2 中断概述
中断是外部外设产生的事件,由硬件信号触发。
STM32F103的外部中断示例:
- WWDG_IRQHandler → 窗口看门狗
- EXTI0_IRQHandler → 外部中断0
- TIM2_IRQHandler → 定时器2
- USART1_IRQHandler → 串口1
- 等等...(共60个外部中断)
4.3 异常与中断的区别
| 特性 | 异常 | 中断 |
|---|---|---|
| 来源 | CPU内部 | 外部外设 |
| 触发 | 指令执行 | 硬件信号 |
| 优先级 | 固定(部分可编程) | 全部可编程 |
| 数量 | 固定16个 | 可变(取决于芯片) |
| 处理方式 | 异常处理函数 | 中断服务程序(ISR) |
💡 本节总结
重点回顾
- 复位: 让CPU回到初始状态
- 向量表: 存储异常/中断处理函数地址
- 复位流程: 复位后先加载SP,再加载PC
- 异常与中断: 异常来自CPU内部,中断来自外部外设
- 向量表位置: 默认在0x08000000(Flash起始地址)
- 对齐要求: 向量表需要1024字节对齐
下一步
接下来请学习:3.2 启动文件解析