STM32F103的启动过程

指令的介绍

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
  1. LDR R0, =SystemInit → 拿到时钟初始化函数的地址;
  2. BLX R0 → 调用SystemInit初始化主频,执行完后LR的值会让程序回到第 3 步;
  3. LDR R0, =__main → 拿到 C 库初始化函数的地址;
  4. BX R0 → 跳转到__main,完成数据段 / BSS 段初始化后进入main函数,不再返回。

四、 特殊标记解释

代码中还有两个容易混淆的标记,不是指令但很关键:

  1. [WEAK]

    • 含义:弱定义 。如果用户在 C 文件中定义了同名函数(如Reset_Handler),链接器会优先使用用户的版本;如果用户没定义,就用汇编文件中的版本。
    • 用途:允许用户重写启动文件中的默认函数(比如自定义复位逻辑)。
  2. |.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

代码整体功能总结

这段汇编代码的核心作用是:

  1. 定义 STM32 运行所需的栈(Stack)堆(Heap) 空间;
  2. 构建中断向量表(内核复位 / 异常 / 外设中断的入口地址表);
  3. 实现核心的复位处理函数 (Reset_Handler),完成系统时钟初始化并跳转到 C 库的__main(最终进入用户main函数);
  4. 定义所有异常 / 中断的默认处理函数;
  5. 初始化栈和堆的内存布局,适配不同的 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函数会告诉编译器栈和堆的内存范围,是链接器分配内存的依据。

总结

  1. 栈和堆:代码开头定义了 1KB 栈、512 字节堆,是程序运行的内存基础,栈存临时数据、堆存动态分配数据;
  2. 中断向量表:复位后内核先读栈顶地址,再读复位函数地址,是程序启动的 "导航表";
  3. 复位处理:先初始化系统时钟(SystemInit),再通过 C 库__main 完成内存初始化,最终跳转到用户 main 函数;
  4. 默认中断处理 :未重写的中断会进入死循环,[WEAK]特性允许用户在 C 文件中重写中断函数。

这段代码是 STM32 的 "启动基石",理解它能解决很多底层问题(比如栈溢出导致 HardFault、中断没响应、动态内存分配失败等)。

相关推荐
无畏jh18 小时前
TLE5012B磁阻芯片解读
嵌入式硬件·汽车嵌入式·磁阻芯片
培林将军18 小时前
Altium Designer 22的安装与汉化
嵌入式硬件·ad工具安装
idcardwang18 小时前
xl9555-IO拓展芯片
stm32·单片机·嵌入式硬件
Y1rong18 小时前
STM32之EXTI
stm32·单片机·嵌入式硬件
兆龙电子单片机设计18 小时前
【STM32项目开源】STM32单片机智能语音家居控制系统
stm32·单片机·嵌入式硬件·物联网·开源·自动化
TaidL18 小时前
茂捷M1020电感式编码器芯片赋能工业智能升级,适用于工业及机器人等领域的各种应用场景
单片机·嵌入式硬件
意法半导体STM3218 小时前
【官方原创】SAU对NSC分区的影响 LAT1578
stm32·单片机·嵌入式硬件·mcu·信息安全·trustzone·stm32开发
SmartRadio18 小时前
MK8000(UWB射频芯片)与DW1000的协议适配
c语言·开发语言·stm32·单片机·嵌入式硬件·物联网·dw1000
LDR00618 小时前
芯片电路的引脚标识代表什么?
stm32·单片机·嵌入式硬件