指令的介绍
ARM 汇编语言的基本语法
css
[LABEL] OPERATION [OPERAND] [:COMMENT]
其中,[ ]]内的是可选项。
LABEL :标号。是指令、变量或数据的地址或者常量。此项为可选项,如果有区必须顶格书写,后面不能加冒号。
OPERATION :指令、宏指令、伪指令或伪操作。此项为必选项,但不能在一行开头顶格书写,而且前后必须有空格。特别注意,
在 ARM 汇编程序中,一条指令伪指令、寄存器名可以全部为大写字母,也可以全部为小写字母,但不要大小写混合使用。
OPERAND :操作的对象(即操作数)。可以是常量、变量,标号、寄存器或表达式。此项为可选项,若有多个操作数,操作数之间用
逗号隔开。
COMMENT:程序注释,增强代码的可读性。此项为可选项,由分号开始,可以顶格写。
一、 伪指令(汇编器指令,用于定义 / 声明,不运行)
伪指令是给汇编器(如 ARMASM)看的,作用是定义内存区域、符号、常量等,不会被 CPU 执行。
| 指令 | 代码中的用法 | 作用解释 |
|---|---|---|
EQU |
Stack_Size EQU 0x400 |
等值定义 ,类似 C 语言的#define。把Stack_Size赋值为0x400(1024),后续代码中Stack_Size等价于0x400 |
AREA |
AREA STACK, NOINIT, READWRITE, ALIGN=3 |
定义内存区域 ,用于划分不同功能的内存段:- STACK:区域名称- NOINIT:不初始化(复位后数据随机)- READWRITE:可读写(栈 / 堆需要)- ALIGN=3:按2^3=8字节对齐(Cortex-M3 栈要求 8 字节对齐) |
SPACE |
Stack_Mem SPACE Stack_Size |
分配内存空间 。在STACK区域中,分配Stack_Size字节的连续内存,命名为Stack_Mem |
PRESERVE8 |
PRESERVE8 |
8 字节对齐声明。告诉汇编器,当前代码和数据要保证 8 字节对齐,满足 Cortex-M3 内核的要求 |
THUMB |
THUMB |
指令集声明。指定后续代码使用 Thumb-2 指令集(Cortex-M3 只支持 Thumb/Thumb-2,不支持 ARM 指令集) |
EXPORT |
EXPORT __Vectors |
导出符号 。把__Vectors这个符号暴露给链接器,让其他文件(如 C 文件、链接脚本)能找到它 |
IMPORT |
IMPORT __main |
导入符号 。声明__main这个符号不在当前汇编文件中,需要从其他文件(如 C 库)导入 |
DCD |
DCD __initial_sp |
定义字数据 。在当前内存地址,存储__initial_sp的地址值 (32 位)。向量表就是用DCD定义的,每个表项占 4 字节 |
PROC/ENDP |
Reset_Handler PROC ... ENDP |
函数开始 / 结束标记 。PROC表示一个函数的开始,ENDP表示结束,类似 C 语言的{} |
IF/ELSE/ENDIF |
IF :DEF:__MICROLIB ... ELSE ... ENDIF |
条件汇编 。判断__MICROLIB宏是否定义:- 若定义(使用精简 C 库),执行IF块- 否则执行ELSE块 |
END |
END |
文件结束标记。告诉汇编器,当前文件的汇编代码到此结束 |
二、 机器指令(CPU 执行的指令,真正干活的)
这类指令会被编译成机器码,由 Cortex-M3 内核直接执行,代码中主要用到跳转和加载指令。
| 指令 | 代码中的用法 | 作用解释 |
|---|---|---|
LDR |
LDR R0, =SystemInit |
加载寄存器指令 (伪指令形式,汇编器会自动处理)。把SystemInit函数的地址 加载到R0寄存器中。注意:LDR R0, =xxx 是 ARM 汇编的 "伪操作",实际会生成LDR R0, [PC, #offset],从常量池读取地址 |
BLX |
BLX R0 |
带链接的跳转并切换指令集 。- BL:跳转前,把当前PC+4的值保存到LR(返回地址寄存器),实现函数调用后的返回- X:自动切换指令集(这里 Thumb→Thumb,无变化)作用:跳转到R0指向的函数(如SystemInit),执行完后返回当前位置 |
BX |
BX R0 |
跳转并切换指令集 。跳转到R0指向的地址执行,但不保存返回地址 (即不会回来)。代码中跳转到__main后,就不会再回到Reset_Handler了 |
B |
B . |
无条件跳转指令 。. 表示当前指令的地址 ,B . 就是跳转到自己,形成死循环 。所有默认中断处理函数(如HardFault_Handler)都用这个指令,防止程序跑飞 |
三、 关键指令组合的执行逻辑
结合代码中的核心片段,看指令如何协同工作:
ruby
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
; 1. 把SystemInit的地址加载到R0
LDR R0, =SystemInit
; 2. 跳转到SystemInit执行,执行完返回
BLX R0
; 3. 把__main的地址加载到R0
LDR R0, =__main
; 4. 跳转到__main,永不返回
BX R0
ENDP
LDR R0, =SystemInit→ 拿到时钟初始化函数的地址;BLX R0→ 调用SystemInit初始化主频,执行完后LR的值会让程序回到第 3 步;LDR R0, =__main→ 拿到 C 库初始化函数的地址;BX R0→ 跳转到__main,完成数据段 / BSS 段初始化后进入main函数,不再返回。
四、 特殊标记解释
代码中还有两个容易混淆的标记,不是指令但很关键:
-
[WEAK]- 含义:弱定义 。如果用户在 C 文件中定义了同名函数(如
Reset_Handler),链接器会优先使用用户的版本;如果用户没定义,就用汇编文件中的版本。 - 用途:允许用户重写启动文件中的默认函数(比如自定义复位逻辑)。
- 含义:弱定义 。如果用户在 C 文件中定义了同名函数(如
-
|.text|- 含义:代码段的标准名称,竖线是为了避免和关键字冲突。
- 用途:
AREA |.text|, CODE, READONLY表示把后续代码放到只读的代码段中,Flash 中存储的程序都在这个段。
startup_stm32f103xb.s
ruby
Stack_Size EQU 0x400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
PRESERVE8
THUMB
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD WWDG_IRQHandler ; Window Watchdog
DCD PVD_IRQHandler ; PVD through EXTI Line detect
DCD TAMPER_IRQHandler ; Tamper
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2
DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3
DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4
DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5
DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6
DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7
DCD ADC1_2_IRQHandler ; ADC1_2
DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX
DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; EXTI Line 9..5
DCD TIM1_BRK_IRQHandler ; TIM1 Break
DCD TIM1_UP_IRQHandler ; TIM1 Update
DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation
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 Line 15..10
DCD RTC_Alarm_IRQHandler ; RTC Alarm through EXTI Line
DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
AREA |.text|, CODE, READONLY
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
; Dummy Exception Handlers (infinite loops which can be modified)
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
PROC
EXPORT UsageFault_Handler [WEAK]
B .
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B .
ENDP
DebugMon_Handler\
PROC
EXPORT DebugMon_Handler [WEAK]
B .
ENDP
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
Default_Handler PROC
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMPER_IRQHandler [WEAK]
EXPORT RTC_IRQHandler [WEAK]
EXPORT FLASH_IRQHandler [WEAK]
EXPORT RCC_IRQHandler [WEAK]
EXPORT EXTI0_IRQHandler [WEAK]
EXPORT EXTI1_IRQHandler [WEAK]
EXPORT EXTI2_IRQHandler [WEAK]
EXPORT EXTI3_IRQHandler [WEAK]
EXPORT EXTI4_IRQHandler [WEAK]
EXPORT DMA1_Channel1_IRQHandler [WEAK]
EXPORT DMA1_Channel2_IRQHandler [WEAK]
EXPORT DMA1_Channel3_IRQHandler [WEAK]
EXPORT DMA1_Channel4_IRQHandler [WEAK]
EXPORT DMA1_Channel5_IRQHandler [WEAK]
EXPORT DMA1_Channel6_IRQHandler [WEAK]
EXPORT DMA1_Channel7_IRQHandler [WEAK]
EXPORT ADC1_2_IRQHandler [WEAK]
EXPORT USB_HP_CAN1_TX_IRQHandler [WEAK]
EXPORT USB_LP_CAN1_RX0_IRQHandler [WEAK]
EXPORT CAN1_RX1_IRQHandler [WEAK]
EXPORT CAN1_SCE_IRQHandler [WEAK]
EXPORT EXTI9_5_IRQHandler [WEAK]
EXPORT TIM1_BRK_IRQHandler [WEAK]
EXPORT TIM1_UP_IRQHandler [WEAK]
EXPORT TIM1_TRG_COM_IRQHandler [WEAK]
EXPORT TIM1_CC_IRQHandler [WEAK]
EXPORT TIM2_IRQHandler [WEAK]
EXPORT TIM3_IRQHandler [WEAK]
EXPORT TIM4_IRQHandler [WEAK]
EXPORT I2C1_EV_IRQHandler [WEAK]
EXPORT I2C1_ER_IRQHandler [WEAK]
EXPORT I2C2_EV_IRQHandler [WEAK]
EXPORT I2C2_ER_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT USART1_IRQHandler [WEAK]
EXPORT USART2_IRQHandler [WEAK]
EXPORT USART3_IRQHandler [WEAK]
EXPORT EXTI15_10_IRQHandler [WEAK]
EXPORT RTC_Alarm_IRQHandler [WEAK]
EXPORT USBWakeUp_IRQHandler [WEAK]
WWDG_IRQHandler
PVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_IRQHandler
DMA1_Channel3_IRQHandler
DMA1_Channel4_IRQHandler
DMA1_Channel5_IRQHandler
DMA1_Channel6_IRQHandler
DMA1_Channel7_IRQHandler
ADC1_2_IRQHandler
USB_HP_CAN1_TX_IRQHandler
USB_LP_CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_IRQHandler
TIM1_UP_IRQHandler
TIM1_TRG_COM_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
EXTI15_10_IRQHandler
RTC_Alarm_IRQHandler
USBWakeUp_IRQHandler
B .
ENDP
ALIGN
;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
IF :DEF:__MICROLIB
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
ALIGN
ENDIF
END
代码整体功能总结
这段汇编代码的核心作用是:
- 定义 STM32 运行所需的栈(Stack) 和堆(Heap) 空间;
- 构建中断向量表(内核复位 / 异常 / 外设中断的入口地址表);
- 实现核心的复位处理函数 (Reset_Handler),完成系统时钟初始化并跳转到 C 库的
__main(最终进入用户main函数); - 定义所有异常 / 中断的默认处理函数;
- 初始化栈和堆的内存布局,适配不同的 C 库(Microlib / 标准库)。
逐模块详细解释
模块 1:栈(Stack)的定义
栈是程序运行时临时存储数据(函数参数、局部变量、寄存器值)的区域,向下生长(从高地址往低地址用)。
ruby
; 1. 定义栈大小:0x400 = 1024字节(1KB)
Stack_Size EQU 0x400
; 2. 声明一个名为STACK的内存区域:
; NOINIT:不初始化(复位后值随机)
; READWRITE:可读写
; ALIGN=3:按2^3=8字节对齐(Cortex-M3要求栈8字节对齐)
AREA STACK, NOINIT, READWRITE, ALIGN=3
; 3. 分配Stack_Size大小的连续内存空间,命名为Stack_Mem
Stack_Mem SPACE Stack_Size
; 4. 定义__initial_sp(初始栈顶地址),等于Stack_Mem + Stack_Size
; 栈从这个地址开始向下生长
__initial_sp
关键解释:
EQU是汇编的 "赋值" 指令,Stack_Size EQU 0x400就是给栈大小赋值为 1024 字节;SPACE是 "分配内存空间",Stack_Mem SPACE Stack_Size就是在 SRAM 中划出 1024 字节给栈用;__initial_sp是栈顶地址,复位后内核会先读取这个地址作为 MSP(主堆栈指针)的初始值。
模块 2:堆(Heap)的定义
堆是程序运行时动态分配内存(如 C 语言malloc/free)的区域,向上生长(从低地址往高地址用)。
ruby
; 1. 定义堆大小:0x200 = 512字节
Heap_Size EQU 0x200
; 2. 声明名为HEAP的内存区域,属性同栈
AREA HEAP, NOINIT, READWRITE, ALIGN=3
; 3. 堆的起始地址(基地址)
__heap_base
; 4. 分配Heap_Size大小的内存空间
Heap_Mem SPACE Heap_Size
; 5. 堆的结束地址(上限)
__heap_limit
关键解释:
- 堆的大小可根据需求调整(比如动态内存用得多就调大);
__heap_base是堆的起始位置,__heap_limit是堆的末尾,malloc就是从__heap_base开始分配内存,不能超过__heap_limit。
模块 3:编译模式与指令集声明
ruby
; 1. PRESERVE8:保证代码按8字节对齐(Cortex-M3内核要求)
PRESERVE8
; 2. THUMB:指定使用THUMB指令集(Cortex-M3只支持THUMB/THUMB-2指令)
THUMB
模块 4:中断向量表(核心中的核心)
中断向量表是 STM32 复位后第一个读取的内存区域,地址固定在 0x00000000,每 4 字节对应一个中断 / 异常的入口地址。
ruby
; 声明RESET区域:数据段、只读(向量表不能修改)
AREA RESET, DATA, READONLY
; 导出以下符号,让链接器能找到
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
; 向量表开始
__Vectors
DCD __initial_sp ; 第0项:栈顶地址(复位后内核先读这个)
DCD Reset_Handler ; 第1项:复位中断处理函数(核心)
DCD NMI_Handler ; 第2项:不可屏蔽中断处理函数
DCD HardFault_Handler ; 第3项:硬件错误处理(最常见的崩溃原因)
...(中间省略其他异常/外设中断)...
DCD USBWakeUp_IRQHandler ; 最后一项:USB唤醒中断
__Vectors_End ; 向量表结束地址
; 计算向量表总大小(结束地址 - 起始地址)
__Vectors_Size EQU __Vectors_End - __Vectors
关键解释:
DCD是 "定义字" 指令,把后面的符号地址存入当前内存位置;- 复位后,Cortex-M3 内核第一步读
__Vectors第 0 项(__initial_sp)给 MSP,第二步读第 1 项(Reset_Handler)给 PC,程序从此开始执行; - 后面的项对应不同的中断 / 异常,比如
USART1_IRQHandler是串口 1 的中断处理函数,若你在代码中开启了串口 1 中断,就需要重写这个函数,否则会执行默认的死循环。
模块 5:复位处理函数(Reset_Handler)
这是程序复位后真正执行的第一个函数,是启动的核心逻辑:
ruby
; 声明.text代码段(只读、可执行)
AREA |.text|, CODE, READONLY
; 定义复位处理函数,[WEAK]表示"弱定义"(用户可重写)
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main ; 导入C库的__main函数(不是用户的main)
IMPORT SystemInit ; 导入系统初始化函数(在stm32f10x_lib中)
; 1. 加载SystemInit的地址到R0寄存器
LDR R0, =SystemInit
; 2. 跳转到SystemInit执行(初始化系统时钟、总线分频等)
BLX R0
; 3. 加载C库__main的地址到R0
LDR R0, =__main
; 4. 跳转到__main(不返回)
BX R0
ENDP
关键解释:
PROC/ENDP是汇编的 "函数开始 / 结束" 标记;IMPORT是 "导入外部符号",表示SystemInit和__main不在这个汇编文件中,由其他文件(C 文件)提供;BLX R0是 "跳转到 R0 指向的地址并返回",执行完SystemInit(初始化时钟到 72MHz)后会回到这里;BX R0是 "跳转到 R0 指向的地址不返回",__main是 ARM C 库函数,会完成:- 把 Flash 中的初始化数据拷贝到 SRAM(数据段初始化);
- 清零 BSS 段(未初始化的全局变量);
- 初始化堆和栈;
- 最终跳转到用户写的
main()函数。
模块 6:默认异常 / 中断处理函数
所有未被用户重写的中断 / 异常,都会执行这里的默认函数(死循环):
ruby
; 定义默认处理函数(死循环)
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B . ; B . 表示跳转到当前地址,即死循环
ENDP
; 硬件错误处理(程序崩溃时会进这里)
HardFault_Handler PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
; 所有外设中断的默认处理(比如USART1_IRQHandler)
Default_Handler PROC
; 导出所有外设中断的弱定义函数
EXPORT WWDG_IRQHandler [WEAK]
...(省略所有外设中断导出)...
; 所有中断都跳转到死循环
B .
ENDP
关键解释:
[WEAK]是 "弱符号",如果用户在 C 文件中定义了USART1_IRQHandler,链接器会优先使用用户的版本,否则用这里的死循环;B .是汇编的死循环指令,若程序触发了未重写的中断,会卡在这个循环里(比如串口 1 中断没写处理函数,触发后就会死机)。
模块 7:栈和堆的初始化(适配不同 C 库)
这部分是给编译器的内存布局配置,区分是否使用 Microlib(精简版 C 库):
ruby
; 如果使用Microlib(精简版C库)
IF :DEF:__MICROLIB
EXPORT __initial_sp ; 导出栈顶地址
EXPORT __heap_base ; 导出堆起始地址
EXPORT __heap_limit ; 导出堆结束地址
ELSE
; 不使用Microlib,调用__user_initial_stackheap配置内存
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
__user_initial_stackheap
; R0 = 堆起始地址,R1 = 栈顶地址,R2 = 堆结束地址,R3 = 栈起始地址
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR ; 返回
ENDIF
END ; 汇编文件结束
关键解释:
__MICROLIB是 Keil 编译器的宏,开启后使用精简版 C 库(占用资源少,适合单片机);- 非 Microlib 模式下,
__user_initial_stackheap函数会告诉编译器栈和堆的内存范围,是链接器分配内存的依据。
总结
- 栈和堆:代码开头定义了 1KB 栈、512 字节堆,是程序运行的内存基础,栈存临时数据、堆存动态分配数据;
- 中断向量表:复位后内核先读栈顶地址,再读复位函数地址,是程序启动的 "导航表";
- 复位处理:先初始化系统时钟(SystemInit),再通过 C 库__main 完成内存初始化,最终跳转到用户 main 函数;
- 默认中断处理 :未重写的中断会进入死循环,
[WEAK]特性允许用户在 C 文件中重写中断函数。
这段代码是 STM32 的 "启动基石",理解它能解决很多底层问题(比如栈溢出导致 HardFault、中断没响应、动态内存分配失败等)。