本篇章主要对于启动文件进行讲解,并且纯理论内容,第一次听可能会很多地方看不懂, 但是没关系,当我们学完之后整个内容之后,再回头来学习这一篇章,你会有不同的体验以及感受。

第一章 基础概念

1.1 启动文件的作用

启动文件由汇编编写,是系统上电复位后第一个执行的程序,负责初始化硬件环境并引导进入用户代码,负责为系统运行搭建基础环境。

主要做了以下工作:

初始化堆栈和堆

  • 栈的作用及初始化过程
  • 堆的作用及配置

设置中断向量表

  • 向量表的结构和位置
  • 中断服务函数的关联

执行系统初始化和引导主程序

  • 复位处理流程
  • 系统时钟配置
  • 跳转到主程序

1.2 汇编指令解释

在我们进行启动文件的学习中,不可避免的会会涉及到 ARM 的汇编指令和 Cortex 内核的指令,所以我们需要对一些汇编指令先进行了解。

首先是需要了解关于如何去查询汇编指令。

1.2.1 汇编指令的查询

1、查询 Cortex 汇编指令集

如果我们想了解关于 ST 公司的 Cortex-M4系列的汇编指令集,可以通过 《ARM Cortex-M3与Cortex-M4权威指南》 以及 《Cortex M3与M4权威指南》 进行了解。

2、查询 ARM 汇编指令集

关于 ARM 的汇编指令集可以通过 Keil 软件自带的帮助文档, 通过Help -> uVision Help 可以打开帮助文档,如下所示:

当打开文档后 就可以对需要了解的汇编指令进行检索,虽然检索出来的结果会有很多,但是只需要选择Assembler User Guide 如下以 EQU 指令所示:

1.2.1 汇编指令常用集合

指令名称作用备注
EQU给数字常量取一个符号名,相当于 C 语言中的 #define用于定义常量符号
AREA定义一个新的代码段或数据段AREA STACK, NOINIT, READWRITE, ALIGN=3 定义堆栈区域
SPACE分配内存空间例如 Stack_Mem SPACE 0x800 分配 2KB 的栈空间
PRESERVES当前文件堆栈需按照 8 字节对齐可能与实际指令名存在差异(标准汇编中通常为 PRESERVE8
EXPORT声明一个标号具有全局属性,可被外部文件使用类似 C 语言的 extern
DCD以字(4 字节)为单位分配内存,要求 4 字节对齐,并初始化内存例如 DCD Reset_Handler 定义中断向量表
PROC定义子程序,与 ENDP 成对使用,表示子程序结束Reset_Handler PROC 定义复位处理函数
WEAK弱定义:如果外部文件声明了标号,则优先使用外部定义;若未定义也不报错编译器特有指令,非 ARM 原生指令
IMPORT声明标号来自外部文件,类似 C 语言的 externIMPORT SystemInit 引入外部函数
B跳转到一个标号B Reset_Handler 跳转到复位处理函数
ALIGN对指令或数据的存放地址进行对齐(默认 4 字节对齐)编译器特有指令,需配合立即数使用(如 ALIGN 3 表示 8 字节对齐)
END表示文件结束汇编文件末尾必须包含此指令
IF, ELSE, ENDIF汇编条件分支语句,类似 C 语言的 if-else用于条件编译,例如根据芯片型号选择代码段

1.3 内核寄存器组

摘抄自 《ARM Cortex-M3与Cortex-M4权威指南》

Cortex-M3 和 Cortex-M4 处理器包含多个寄存器,用于数据处理和控制。这些寄存器大部分按寄存器组的方式进行管理。数据处理指令通常会指定源寄存器、目标寄存器和操作类型。

Cortex-M 处理器采用“加载-存储架构”(Load-Store Architecture),即数据操作只能在寄存器之间进行,访问存储器时必须使用专门的加载(Load)或存储(Store)指令。寄存器的使用可以提升指令执行效率,减少对存储器的访问。

Cortex-M3 和 Cortex-M4 共有 16 个 32 位寄存器,其中 13 个是通用寄存器(R0~R12),另外 3 个具有特殊用途,如图所示。

1.3.1 通用寄存器(R0~R12)

寄存器 R0~R12 属于通用寄存器,其中:

  • R0~R7:称为低寄存器,大部分 16 位 Thumb 指令只能访问这些寄存器。

  • R8~R12:称为高寄存器,它们可用于 32 位指令,也能被部分 16 位指令访问(如 MOV 指令)。

通用寄存器的初始值未定义,程序启动时需要手动初始化。

1.3.2 堆栈指针(SP, R13)

R13 作为堆栈指针(Stack Pointer, SP),用于管理栈操作。Cortex-M 处理器提供两个物理堆栈指针:

  • 主堆栈指针(MSP,Main Stack Pointer):默认使用,在异常处理和特权模式下运行。

  • 进程堆栈指针(PSP,Process Stack Pointer):用于线程模式,可以为不同任务提供独立的栈空间(适用于 RTOS)。

堆栈指针的选择由 CONTROL 寄存器的 SPSEL 位控制,MSP 和 PSP 都要求 32 位对齐。

1.3.3 链接寄存器(LR, R14)

R14 也称链接寄存器(Link Register, LR),用于存储子程序返回地址:

  • 函数调用时,LR 保存返回地址,BL 指令会自动更新 LR。

  • 异常处理时,LR 存储特殊的 EXC_RETURN 值,指示异常返回时如何恢复上下文。

如果函数嵌套调用,必须将 LR 的值保存到栈,否则调用新函数时 LR 会被覆盖,导致返回地址丢失。

1.3.4 程序计数器(PC, R15)

R15 也称程序计数器(Program Counter, PC),用于存储当前执行的指令地址。

  • 读取 PC 时,得到的是当前指令的下一条指令地址(由于流水线机制)。

  • 修改 PC 可以实现跳转,例如 BX 指令用于返回 LR 存储的地址。

由于 Thumb 指令集的对齐要求:

  • PC 的最低位(LSB)始终为 0。

  • 在某些跳转操作中,需要将目标地址的 LSB 置 1,指示 Thumb 状态,否则会触发异常。

5. 寄存器名称对照表

不同的汇编工具允许使用不同的寄存器名称,如下表所示:

标准寄存器名其他可用名称说明
R0~R12-通用寄存器
R13SP, sp堆栈指针(可切换 MSP/PSP)
R14LR, lr链接寄存器(存储返回地址)
R15PC, pc程序计数器(存储指令地址)

第二章 启动模式

2.1 Flash启动

原理

  • 地址映射:STM32F407的Flash起始地址为 0x08000000,但芯片启动时会将该地址映射到 0x00000000,使程序从Flash直接运行。

  • 执行流程:上电后,内核从 0x00000000(实际是 0x08000000)读取栈顶地址和复位向量,跳转到 Reset_Handler 初始化系统。

配置方法

  • 硬件配置:BOOT0引脚接地(BOOT0=0),BOOT1无关(默认启动Flash)。

  • 代码位置:用户程序需烧录到 0x08000000 开始的Flash区域。

应用场景

  • 常规开发:程序固化在Flash中,断电后不丢失。

  • 比喻:类似电脑从硬盘启动操作系统。

2.2 SRAM启动

原理

  • 地址映射:SRAM起始地址为 0x20000000,启动时将该地址映射到 0x00000000,程序直接在SRAM中运行。
  • 临时性:SRAM内容断电丢失,需通过调试器(如J-Link)将代码加载到SRAM。

配置方法

  • 硬件配置:BOOT0=1,BOOT1=1。
  • 调试配置:在IDE(如Keil)中设置下载目标为SRAM,并指定起始地址。

应用场景

  • 快速调试:无需擦写Flash,适合反复修改代码的场景。
  • 比喻:类似电脑从U盘启动临时系统,用于故障排查。

2.3 系统存储器启动

原理

  • 内置BootLoader:芯片出厂时固化在系统存储区(地址 0x1FFF0000),支持通过串口、USB等接口烧录程序。
  • 映射机制:启动时系统存储区映射到 0x00000000,运行BootLoader。

配置方法

  • 硬件配置:BOOT0=0,BOOT1=1。
  • 烧录工具:使用STM32CubeProgrammer或串口工具(如FlyMcu)。

应用场景

  • 恢复模式:当Flash中的程序损坏时,通过BootLoader重新烧录。
  • 无调试器环境:仅需串口线即可更新程序。
  • 比喻:类似手机进入Recovery模式刷机。

第三章 启动文件详解

3.1 启动流程

在启动文件中的第一段注释中其实已经对STM32F407启动文件的启动流程进行了说明,具体内容如下:

;******************** (C) COPYRIGHT 2016 STMicroelectronics ********************
;* File Name          : startup_stm32f40_41xxx.s
;* Author             : MCD Application Team
;* @version           : V1.8.0
;* @date              : 09-November-2016
;* Description        : STM32F40xxx/41xxx devices vector table for MDK-ARM toolchain. 
;*                      This module performs:
;*                      - Set the initial SP
;*                      - Set the initial PC == Reset_Handler
;*                      - Set the vector table entries with the exceptions ISR address
;*                      - Configure the system clock and the external SRAM mounted on 
;*                        STM324xG-EVAL board to be used as data memory (optional, 
;*                        to be enabled by user)
;*                      - Branches to __main in the C library (which eventually
;*                        calls main()).
;*                      After Reset the CortexM4 processor is in Thread mode,
;*                      priority is Privileged, and the Stack is set to Main.
;* <<< Use Configuration Wizard in Context Menu >>>   
;*******************************************************************************
; 
; Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
; You may not use this file except in compliance with the License.
; You may obtain a copy of the License at:
; 
;        http://www.st.com/software_license_agreement_liberty_v2
; 
; Unless required by applicable law or agreed to in writing, software 
; distributed under the License is distributed on an "AS IS" BASIS, 
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
; 
;*******************************************************************************

经过翻译后,内容如下:

;******************** (C) 版权所有 2016 STMicroelectronics ​********************
;* 文件名           : startup_stm32f40_41xxx.s
;* 作者             : MCD 应用团队
;* @version         : V1.8.0
;* @date            : 2016年11月09日
;* 描述             : STM32F40xxx/41xxx 设备的中断向量表,用于 MDK-ARM 工具链。
;*                    本模块执行以下操作:
;*                    - 设置初始堆栈指针(SP)
;*                    - 设置初始程序计数器(PC)为 Reset_Handler
;*                    - 用异常中断服务例程(ISR)地址填充向量表条目
;*                    - 配置系统时钟和外部SRAM(挂载在STM324xG-EVAL开发板上,
;*                      可选功能,需用户手动启用)作为数据存储器
;*                    - 跳转到C库中的 __main(最终调用 main() 函数)
;*                    复位后,Cortex-M4 处理器处于线程模式,
;*                    优先级为特权级,堆栈设置为Main类型。
;* <<< 使用右键菜单中的配置向导(Configuration Wizard)>>>   
;*******************************************************************************
;
; 遵循 MCD-ST Liberty SW 许可证协议 V2 授权("许可证")
; 除非遵守许可证,否则不得使用此文件。
; 您可以在以下网址获取许可证副本:
;
;        http://www.st.com/software_license_agreement_liberty_v2
;
; 除非适用法律要求或书面同意,本软件按"原样"分发,
; 无任何明示或暗示的担保或条件。详见许可证中的特定语言条款和限制。
;
;*******************************************************************************

对于启动流程的详细解释如下:

初始化堆栈指针 SP=_initial_sp

初始化 PC 指针 =Reset_Handler

初始化中断向量表

配置系统时钟

调用 C 库函数 _main 初始化用户堆栈,从而最终调用 main 函数

3.2 Stack 栈

代码段

; Amount of memory (in bytes) allocated for Stack
; Tailor this value to your application needs
; <h> Stack Configuration
;   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

; 翻译如下
; 为堆栈分配的内存大小(单位:字节)
; 根据应用需求调整此值
; <h> 堆栈配置
;   <o> 栈大小(单位:字节) <0x0-0xFFFFFFFF:8>
; </h>

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

本段代码的配置主要是为单片机开辟栈空间。在 C 语言中,我们已经学习了栈的作用,主要用于局部变量、函数调用、函数形参等内容。

如果忘记了内存分区的内容,也可以参考下表:

区域生命周期分配方式典型用途
堆栈函数调用期自动分配局部变量、函数调用链
动态分配手动分配动态数据结构
全局区整个程序静态分配全局变量、静态变量
代码区只读固定分配程序指令

需要注意的是,如果程序中定义的变量较多时,就需要调整栈空间的大小,即在启动文件中进行修改。常见的错误是程序运行过程中突然进入硬件错误 (Hard Fault),这可能是由于栈空间不足导致的内存管理错误。

此外,栈空间的大小必须小于单片机的实际 SRAM 容量,否则会导致程序崩溃。

3.2.1 代码解释

1、栈空间定义

; EQU 指令用于定义常量,这里将 Stack_Size 设为 0x400(1KB)。

; 该值可根据应用需求进行调整。

Stack_Size      EQU     0x00000400  ; 1KB堆栈空间

2、内存段声明

; AREA 用于定义一个新的内存区域。
    
; STACK 是该区域的名称。
    
; NOINIT 表示不进行初始化。
    
; READWRITE 允许读写。
    
; ALIGN=3 设定 8 字节对齐(`2^3 = 8`)。


               AREA    STACK, NOINIT, READWRITE, ALIGN=3

3、栈内存分配

; SPACE 用于分配指定大小的内存。
    
; 这里分配 Stack_Size(1KB)。

Stack_Mem       SPACE   Stack_Size

4、栈顶指针定义

; 这是栈顶指针的标签。
    
; 处理器启动时,栈指针 `SP` 将初始化为 `__initial_sp` 的地址。

__initial_sp

标号 __initial_sp 紧挨着 SPACE 语句放置,表示栈的栈顶地址。

栈的生长方向通常是向下(地址递减),__initial_sp 代表栈顶,SP(栈指针)初始化后会从此处向下增长。

访问栈时,PUSH 指令会减少 SPPOP 指令会增加 SP

如果栈空间不足,可能会触发 Hard Fault

3.2.2 指令解释

1、EQU

汇编伪指令

  • 功能:定义符号常量,类似C语言的#define

  • 语法symbol EQU value

  • 特点:不占用内存空间,仅在编译阶段替换

2、AREA

段定义指令

  • 功能:用于定义一个新的数据段或代码段。

  • 语法AREA 名称, [属性]

  • 示例:以前面的代码为示例 AREA STACK, NOINIT, READWRITE, ALIGN=3

    • STACK 只是命名,可以自定义。

    • NOINIT 表示不初始化数据。

    • READWRITE 允许读写。

    • ALIGN=n:对齐方式。

3、NOINIT

段属性修饰符

  • 功能:表示该区域不会被初始化,适用于不需要在启动时清零的数据,如栈空间。

  • 特点

    • 内存区域不会被初始化(不生成初始化数据)
    • 上电时内容随机,适合栈、堆等不需要初始值的区域

补充指令INIT, 需要进行初始化的区域。

4、READWRITE

  • 内存访问权限定义

  • 权限组合:

    • READONLY:代码段默认属性

    • READWRITE:数据段默认属性

    • 部分架构支持EXECUTE属性

5、 ALIGN

内存对齐控制

  • 语法ALIGN=n 设定数据对齐方式。

    • ALIGN=3 表示 8 字节对齐(2^3 = 8)。
  • 对齐原则

    • Cortex-M系列要求栈指针必须8字节对齐(ALIGN=3)

    • 对齐错误可能导致Hard Fault

    • 对齐能提高内存访问效率

6、SPACE

  • 功能:在内存中分配指定大小的未初始化空间(相当于C语言中的未初始化数组)

  • 语法label SPACE expression

    • label:可选标签,标记分配空间的起始地址

    • expression:要分配的字节数(必须是常数表达式)

  • 关键特性

    • 分配的空间内容在启动时是未定义的(随机值)

    • 通常用于定义堆、栈等动态内存区域

    • 不生成实际代码,仅影响内存布局

3.3 Heap 堆

代码段

; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

; <h> 堆配置
; <o> 堆大小(以字节为单位)<0x0-0xFFFFFFFF:8>
; </h>

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

                PRESERVE8
                THUMB

本段代码的配置主要是为单片机开辟堆空间。在 C 语言中,堆用于动态分配内存(如 malloccalloc 等函数申请的内存),需手动管理其分配与释放。若使用不当可能导致内存泄漏或碎片。

需特别注意:

堆大小调整:动态分配需求超过堆容量时,malloc 返回 NULL 导致分配失败

内存容量限制:堆总大小需小于单片机 SRAM 容量,且不得与其他内存区域重叠

生长方向:堆通常向高地址增长(__heap_base 起始,__heap_limit 结束)

对齐要求ALIGN=3 和 PRESERVE8 确保8字节对齐,避免硬件异常

3.3.1 代码解释

1、堆空间定义

; EQU 指令用于定义常量,这里将 Heap_Size 设为 0x200(512B)。
Heap_Size       EQU     0x00000200  ; 512B 堆空间
  • Heap_Size 设定了堆的大小,可以根据实际需求调整。

  • 在嵌入式系统中,堆的大小一般较小,应根据内存容量合理分配。

2、内存段声明

; AREA 指令定义了一个新的内存区域。
; HEAP 是该区域的名称。
; NOINIT 表示不进行初始化。
; READWRITE 允许读写。
; ALIGN=3 设定 8 字节对齐(`2^3 = 8`)。
                
                AREA    HEAP, NOINIT, READWRITE, ALIGN=3

  • HEAP 只是一个名称,可自定义,但通常使用 HEAP 表示堆区。

  • NOINIT 使该区域不会在启动时初始化,适用于动态分配的内存空间。

  • READWRITE 允许程序在运行时对堆区进行读写操作。

  • ALIGN=3 设定 8 字节对齐,以满足某些架构对动态内存管理的要求。

3、堆起始地址

__heap_base
  • __heap_base 作为堆的起始地址,用于指示堆空间的起始位置。

  • 堆的增长方向通常是向上(地址递增),与栈相反。

4、堆内存分配

Heap_Mem        SPACE   Heap_Size
  • SPACE 指令用于分配指定大小的内存,这里分配 Heap_Size(512B)。

  • 该内存区域可用于动态分配,例如 malloc()free()

5、堆结束地址

__heap_limit
  • __heap_limit 作为堆的结束地址,用于指示堆的边界。

  • 在动态内存管理中,__heap_limit 可能用于检测内存溢出或堆区溢出。

4、指令修饰

PRESERVE8
THUMB
  • PRESERVE8:确保栈指针 (SP) 始终是 8 字节对齐的,以满足 Cortex-M 处理器的要求。

  • THUMB:指定使用 Thumb 指令集,以确保 ARM 处理器正确解析指令。

3.3.1 指令解释

1、PRESERVE8

  • 功能:确保栈指针 (SP) 始终是 8 字节对齐的,强制当前数据段/代码段保持8字节对齐。

  • 必要性

    • Cortex-M 处理器要求栈指针必须 8 字节对齐,否则会触发 Hard Fault
    • Cortex-M 架构要求栈指针必须8字节对齐,否则触发硬件异常
  • 典型应用:堆、栈等需要严格对齐的内存区域

2、THUMB

  • 功能:声明后续代码使用 Thumb 指令集,以确保 ARM 处理器正确解析指令。

  • 架构特性:Thumb 以16位指令为主,代码密度高,适合资源受限的嵌入式系统

  • 强制要求:Cortex-M 全系列仅支持 Thumb 模式

3、堆边界符号

  • __heap_base:堆起始地址,动态分配从此处开始

  • __heap_limit:堆结束地址,内存分配不可越过此地址

  • 管理机制:通过 sbrk() 系统调用维护堆指针,详见标准库实现

3.4 中断向量表

中断向量表是单片机启动时的核心数据结构,用于存储系统异常和外部中断的入口地址。关键特性:

  1. 物理定位:复位时被映射到地址0x00000000(可通过重映射修改)

  2. 严格顺序:每个条目对应特定中断源,位置不可更改

  3. 地址存储:每个条目存储4字节地址(DCD指令)

  4. 自动跳转:发生中断时,CPU自动从向量表获取处理函数地址

3.4.1 初始化部分-代码解释

; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

  • AREA RESET, DATA, READONLY:定义一个数据区域为RESET,该区域可读可使用。

  • EXPORT __Vectors:将 __Vectors 该转向表输出,使其可以被外部调用。

  • EXPORT __Vectors_EndEXPORT __Vectors_Size 为该表的结束点以及大小。

3.4.2 向量表部分-代码解释

__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 detection                        
                DCD     TAMP_STAMP_IRQHandler             ; Tamper and TimeStamps through the EXTI line            
                DCD     RTC_WKUP_IRQHandler               ; RTC Wakeup through the EXTI line                       
				; 中间部分省略   
				
                DCD     OTG_HS_IRQHandler                 ; USB OTG HS                                      
                DCD     DCMI_IRQHandler                   ; DCMI                                            
                DCD     CRYP_IRQHandler                   ; CRYP crypto                                     
                DCD     HASH_RNG_IRQHandler               ; Hash and Rng
                DCD     FPU_IRQHandler                    ; FPU
                                         
__Vectors_End

当内核响应了一个发生的异常后,对应的异常服务例程 (ESR) 就会执行。为了决定 ESR 的入口地址,内核使用了“向量表查表机制”。这里使用一张向量表。

向量表其实是一个 WORD(32 位整数)数组,每个下标对应一种异常,该下标元素的值则是该 ESR 的入口地址。向量表在地址空间中的位置是可以设置的,通过 NVIC 中的一个重定位寄存器来指出向量表的地址。

在复位后,该寄存器的值为 0。

因此,在地址 0 (即 FLASH 地址 0)处必须包含一张向量表,用于初始时的异常分配。

要注意的是这里有个另类:0 号类型并不是什么入口地址,而是给出了复位后 MSP 的初值。

可以通过查询 《STM32F4xx中文参考手册》 的 234 页查询下表。

3.4.3 结束部分-代码解释

__Vectors_Size  EQU  __Vectors_End - __Vectors

                AREA    |.text|, CODE, READONLY

  • __Vectors_Size EQU __Vectors_End - __Vectors 计算向量表大小。

  • AREA |.text|, CODE, READONLY 定义一个可执行的可读代码区域。

3.4.4 指令解释

1、 EXPORT

  • 功能:用于向链接器导出符号,使其可以在其他模块中访问。

  • 示例

    EXPORT  __Vectors
    
    • __Vectors 是中断向量表的起始地址,导出后可被外部引用。

2、 DCD

  • 功能:定义 32 位数据并占用32位存储。

  • 示例

    __Vectors       DCD     __initial_sp
    
    • __initial_sp 是栈顶指针的初始值。

3.5 复位程序

复位程序(Reset Handler)是系统上电或复位时执行的第一段代码,负责初始化系统环境,并最终跳转到主程序入口。

代码段

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

3.5.1 代码解释

1. 复位处理程序声明

Reset_Handler    PROC
  • 功能:声明一个名为 Reset_Handler 的过程(函数),作为复位中断服务程序。

  • 复位入口:根据向量表定义,复位后 CPU 自动跳转到此地址执行。

2. 弱定义导出符号

                 EXPORT  Reset_Handler [WEAK]
  • EXPORT:将该符号导出,允许其他文件引用。

  • [WEAK]:弱定义属性,若用户自定义同名函数,则优先使用用户版本。

3. 引入外部符号

        IMPORT  SystemInit
        IMPORT  __main
  • SystemInit:系统初始化函数(通常由芯片厂商提供),用于配置时钟、内存控制器等。

  • __main:C 标准库入口函数,负责初始化全局变量、堆栈等 C 语言运行环境。

4. 系统初始化调用

                 LDR     R0, =SystemInit ; 加载 SystemInit 地址到 R0
                 BLX     R0              ; 跳转执行 SystemInit
  • LDR:将 SystemInit 的函数地址加载到寄存器 R0。

  • BLX:带链接(返回地址保存到 LR)的跳转指令,执行 SystemInit 后返回。

5. 跳转至 C 环境

                 LDR     R0, =__main     ; 加载 __main 地址到 R0
                 BX      R0              ; 跳转到 __main
                 ENDP
  • __main:并非用户编写的 main 函数,而是 C 库的初始化入口。

  • BX:直接跳转指令,不保存返回地址(程序不再返回复位处理程序)。

3.5.2 指令解释

1. PROC / ENDP

  • 功能:定义函数过程的开始(PROC)与结束(ENDP)。

  • 语法

    函数名 PROC
    ; 代码
    函数名 ENDP
    
  • 作用:明确函数范围,便于调试器和链接器处理。

2. EXPORT

  • 功能:导出符号,使其他文件可通过该符号访问当前代码段。

  • 弱属性([WEAK]:允许用户覆盖默认实现。若用户未定义 Reset_Handler,则使用此默认版本。

3. IMPORT

  • 功能:声明外部符号(如函数或变量),告知汇编器该符号在其他文件中定义。

  • 示例

    • SystemInit:通常位于 system_<芯片型号>.c 文件中。

    • __main:由 C 标准库提供,负责初始化运行时环境。

4. LDR

  • 功能:将内存地址或常量加载到寄存器。

  • 语法LDR Rd, =Label

    • Rd:目标寄存器(如 R0)。

    • Label:要加载的符号地址(如 SystemInit)。

  • 作用:此处用于获取函数入口地址。

5. BLX

  • 功能:带链接(Link)的跳转指令,支持切换指令集(ARM/Thumb)。

  • 行为

    • 将返回地址(下一条指令地址)存入 LR 寄存器。

    • 跳转到目标地址执行。

    • 用于函数调用(如 SystemInit)。

6. BX

  • 功能:跳转指令,支持切换指令集。

  • 语法BX Rn

    • Rn:寄存器,存储目标地址。
  • 特点:不保存返回地址,用于单向跳转(如进入 __main 后不再返回)。

3.5.3 执行流程

  1. 复位触发:上电或复位后,CPU 从向量表第二个条目获取 Reset_Handler 地址。

  2. 硬件初始化:执行 SystemInit,配置时钟、电源、内存控制器等。

  3. C 环境初始化:跳转至 __main,完成以下操作:

    • .data 段从 FLASH 复制到 RAM(初始化全局变量)。

    • 清零 .bss 段(未初始化的全局变量)。

    • 调用用户 main() 函数。

  4. 程序运行:用户 main() 开始执行。

3.5.4 注意事项

1、__mainmain 的区别:

符号来源功能
__mainC 标准库初始化运行时环境,调用用户 main
main用户代码用户程序入口

2、 自定义复位处理程序

  • 若需修改初始化流程,可重新实现 Reset_Handler 函数(覆盖弱符号)。

  • 示例:

void Reset_Handler(void) {
	Custom_Init();    // 自定义初始化
	SystemInit();      // 保留原有初始化
	__main();          // 进入 C 环境
}

3、调试问题

- 若未实现 `SystemInit`,程序可能运行在默认低速时钟下。
    
- 若 `__main` 未正确初始化堆栈,可能导致 `HardFault`。

3.6 中断复位函数

中断复位函数用于处理异常和中断,确保系统能够在出现错误时执行适当的处理,防止程序崩溃。以下是相关的中断处理函数定义。

; 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  TAMP_STAMP_IRQHandler             [WEAK]         
                EXPORT  RTC_WKUP_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_Stream0_IRQHandler           [WEAK]                                
                EXPORT  DMA1_Stream1_IRQHandler           [WEAK]                                   
                EXPORT  DMA1_Stream2_IRQHandler           [WEAK]                                   
                EXPORT  DMA1_Stream3_IRQHandler           [WEAK]                                   
                EXPORT  DMA1_Stream4_IRQHandler           [WEAK]                                   
                EXPORT  DMA1_Stream5_IRQHandler           [WEAK]                                   
                EXPORT  DMA1_Stream6_IRQHandler           [WEAK]                                   
                EXPORT  ADC_IRQHandler                    [WEAK]                         
                EXPORT  CAN1_TX_IRQHandler                [WEAK]                                                
                EXPORT  CAN1_RX0_IRQHandler               [WEAK]                                               
                EXPORT  CAN1_RX1_IRQHandler               [WEAK]                                                
                EXPORT  CAN1_SCE_IRQHandler               [WEAK]                                                
                EXPORT  EXTI9_5_IRQHandler                [WEAK]                                    
                EXPORT  TIM1_BRK_TIM9_IRQHandler          [WEAK]                  
                EXPORT  TIM1_UP_TIM10_IRQHandler          [WEAK]                
                EXPORT  TIM1_TRG_COM_TIM11_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  OTG_FS_WKUP_IRQHandler            [WEAK]                        
                EXPORT  TIM8_BRK_TIM12_IRQHandler         [WEAK]                 
                EXPORT  TIM8_UP_TIM13_IRQHandler          [WEAK]                 
                EXPORT  TIM8_TRG_COM_TIM14_IRQHandler     [WEAK] 
                EXPORT  TIM8_CC_IRQHandler                [WEAK]                                   
                EXPORT  DMA1_Stream7_IRQHandler           [WEAK]                                          
                EXPORT  FSMC_IRQHandler                   [WEAK]                                             
                EXPORT  SDIO_IRQHandler                   [WEAK]                                             
                EXPORT  TIM5_IRQHandler                   [WEAK]                                             
                EXPORT  SPI3_IRQHandler                   [WEAK]                                             
                EXPORT  UART4_IRQHandler                  [WEAK]                                            
                EXPORT  UART5_IRQHandler                  [WEAK]                                            
                EXPORT  TIM6_DAC_IRQHandler               [WEAK]                   
                EXPORT  TIM7_IRQHandler                   [WEAK]                    
                EXPORT  DMA2_Stream0_IRQHandler           [WEAK]                                  
                EXPORT  DMA2_Stream1_IRQHandler           [WEAK]                                   
                EXPORT  DMA2_Stream2_IRQHandler           [WEAK]                                    
                EXPORT  DMA2_Stream3_IRQHandler           [WEAK]                                    
                EXPORT  DMA2_Stream4_IRQHandler           [WEAK]                                 
                EXPORT  ETH_IRQHandler                    [WEAK]                                         
                EXPORT  ETH_WKUP_IRQHandler               [WEAK]                     
                EXPORT  CAN2_TX_IRQHandler                [WEAK]                                               
                EXPORT  CAN2_RX0_IRQHandler               [WEAK]                                               
                EXPORT  CAN2_RX1_IRQHandler               [WEAK]                                               
                EXPORT  CAN2_SCE_IRQHandler               [WEAK]                                               
                EXPORT  OTG_FS_IRQHandler                 [WEAK]                                       
                EXPORT  DMA2_Stream5_IRQHandler           [WEAK]                                   
                EXPORT  DMA2_Stream6_IRQHandler           [WEAK]                                   
                EXPORT  DMA2_Stream7_IRQHandler           [WEAK]                                   
                EXPORT  USART6_IRQHandler                 [WEAK]                                           
                EXPORT  I2C3_EV_IRQHandler                [WEAK]                                              
                EXPORT  I2C3_ER_IRQHandler                [WEAK]                                              
                EXPORT  OTG_HS_EP1_OUT_IRQHandler         [WEAK]                      
                EXPORT  OTG_HS_EP1_IN_IRQHandler          [WEAK]                      
                EXPORT  OTG_HS_WKUP_IRQHandler            [WEAK]                        
                EXPORT  OTG_HS_IRQHandler                 [WEAK]                                      
                EXPORT  DCMI_IRQHandler                   [WEAK]                                             
                EXPORT  CRYP_IRQHandler                   [WEAK]                                     
                EXPORT  HASH_RNG_IRQHandler               [WEAK]
                EXPORT  FPU_IRQHandler                    [WEAK]

WWDG_IRQHandler                                                       
PVD_IRQHandler                                      
TAMP_STAMP_IRQHandler                  
RTC_WKUP_IRQHandler                                
FLASH_IRQHandler                                                       
RCC_IRQHandler                                                            
EXTI0_IRQHandler                                                          
EXTI1_IRQHandler                                                           
EXTI2_IRQHandler                                                          
EXTI3_IRQHandler                                                         
EXTI4_IRQHandler                                                          
DMA1_Stream0_IRQHandler                                       
DMA1_Stream1_IRQHandler                                          
DMA1_Stream2_IRQHandler                                          
DMA1_Stream3_IRQHandler                                          
DMA1_Stream4_IRQHandler                                          
DMA1_Stream5_IRQHandler                                          
DMA1_Stream6_IRQHandler                                          
ADC_IRQHandler                                         
CAN1_TX_IRQHandler                                                            
CAN1_RX0_IRQHandler                                                          
CAN1_RX1_IRQHandler                                                           
CAN1_SCE_IRQHandler                                                           
EXTI9_5_IRQHandler                                                
TIM1_BRK_TIM9_IRQHandler                        
TIM1_UP_TIM10_IRQHandler                      
TIM1_TRG_COM_TIM11_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                            
OTG_FS_WKUP_IRQHandler                                
TIM8_BRK_TIM12_IRQHandler                      
TIM8_UP_TIM13_IRQHandler                       
TIM8_TRG_COM_TIM14_IRQHandler  
TIM8_CC_IRQHandler                                               
DMA1_Stream7_IRQHandler                                                 
FSMC_IRQHandler                                                            
SDIO_IRQHandler                                                            
TIM5_IRQHandler                                                            
SPI3_IRQHandler                                                            
UART4_IRQHandler                                                          
UART5_IRQHandler                                                          
TIM6_DAC_IRQHandler                            
TIM7_IRQHandler                              
DMA2_Stream0_IRQHandler                                         
DMA2_Stream1_IRQHandler                                          
DMA2_Stream2_IRQHandler                                           
DMA2_Stream3_IRQHandler                                           
DMA2_Stream4_IRQHandler                                        
ETH_IRQHandler                                                         
ETH_WKUP_IRQHandler                                
CAN2_TX_IRQHandler                                                           
CAN2_RX0_IRQHandler                                                          
CAN2_RX1_IRQHandler                                                          
CAN2_SCE_IRQHandler                                                          
OTG_FS_IRQHandler                                                    
DMA2_Stream5_IRQHandler                                          
DMA2_Stream6_IRQHandler                                          
DMA2_Stream7_IRQHandler                                          
USART6_IRQHandler                                                        
I2C3_EV_IRQHandler                                                          
I2C3_ER_IRQHandler                                                          
OTG_HS_EP1_OUT_IRQHandler                           
OTG_HS_EP1_IN_IRQHandler                            
OTG_HS_WKUP_IRQHandler                                
OTG_HS_IRQHandler                                                   
DCMI_IRQHandler                                                            
CRYP_IRQHandler                                                    
HASH_RNG_IRQHandler
FPU_IRQHandler
   
                B       .

                ENDP

                ALIGN
; Dummy Exception Handlers (infinite loops which can be modified)
; 默认异常处理程序(可修改的无限循环)

; 系统异常处理程序
NMI_Handler     PROC
                EXPORT  NMI_Handler                [WEAK]
                B       .
                ENDP
; ...其他系统异常处理程序(同上格式)

; 外部中断处理程序
Default_Handler PROC
                EXPORT  WWDG_IRQHandler            [WEAK]                                        
                EXPORT  PVD_IRQHandler             [WEAK]                      
                ; ...其他外部中断导出声明
                B       .
                ENDP

; 外部中断处理程序别名定义
WWDG_IRQHandler                                                       
PVD_IRQHandler                                      
; ...其他外部中断别名(共108个)
                B       .
                ENDP
                ALIGN

3.6.1 代码解释

1、系统异常默认处理

NMI_Handler     PROC
                EXPORT  NMI_Handler [WEAK]
                B       .
                ENDP
  • 功能:定义不可屏蔽中断(NMI)的默认处理程序

  • 结构特征

    • 使用PROC/ENDP定义函数体

    • [WEAK]允许用户自定义同名函数覆盖

    • B .表示无限循环(原地跳转)

2、外部中断统一处理

Default_Handler PROC
                EXPORT  WWDG_IRQHandler [WEAK]
                ; ...其他中断导出
                B       .
                ENDP
  • 设计原理

    • 所有外部中断共用同一个默认处理程序

    • 通过别名机制(EQU)将各中断名指向同一地址

    • 减少重复代码,节省ROM空间

3、 中断处理程序别名

WWDG_IRQHandler
PVD_IRQHandler
; ...其他中断名
  • 等效关系

    • 每个中断名都是Default_Handler的别名

    • 类似C语言的#define WWDG_IRQHandler Default_Handler

4、 对齐控制

                ALIGN
  • 作用:确保后续代码按指定对齐方式

  • 必要性:满足ARM架构的指令对齐要求

3.6.2 指令解释

1、 PROC/ENDP

  • 功能:定义函数作用域 开始和结束。

  • 语法

    函数名 PROC
    ; 代码
    函数名 ENDP
    
  • 特点:在汇编层面明确函数边界

2、 EXPORT [WEAK]

  • 弱定义特性
使用场景行为
用户未定义处理程序使用默认无限循环
用户定义同名函数自动替换为自定义实现

3、 B .

  • 机器码:0xE7FE(Thumb指令集)

  • 执行效果

    • B:无条件跳转指令
    • .:表示当前地址(形成死循环)
  • 调试意义:触发该中断时程序挂起,便于定位未处理的中断

3.7用户堆栈初始

;*******************************************************************************
; 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

;************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE*****

3.7 用户堆栈初始化

代码段

;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
                 IF      :DEF:__MICROLIB           ; 条件编译判断是否使用MICROLIB
    
                 EXPORT  __initial_sp              ; 导出栈顶指针
                 EXPORT  __heap_base               ; 导出堆起始地址
                 EXPORT  __heap_limit              ; 导出堆结束地址
    
                 ELSE                               ; 不使用MICROLIB的情况
    
                 IMPORT  __use_two_region_memory   ; 引入双区内存管理标识
                 EXPORT  __user_initial_stackheap  ; 导出堆栈初始化函数
                 
__user_initial_stackheap
                 LDR     R0, =Heap_Mem            ; 加载堆起始地址到R0
                 LDR     R1, =(Stack_Mem + Stack_Size) ; 计算栈结束地址到R1
                 LDR     R2, =(Heap_Mem + Heap_Size)   ; 计算堆结束地址到R2
                 LDR     R3, =Stack_Mem           ; 加载栈起始地址到R3
                 BX      LR                       ; 返回调用位置

                 ALIGN                            ; 地址对齐

                 ENDIF                            ; 结束条件编译

                 END                              ; 文件结束

3.7 用户堆栈初始化

代码段

;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
                 IF      :DEF:__MICROLIB           ; 条件编译判断是否使用MICROLIB
    
                 EXPORT  __initial_sp              ; 导出栈顶指针
                 EXPORT  __heap_base               ; 导出堆起始地址
                 EXPORT  __heap_limit              ; 导出堆结束地址
    
                 ELSE                               ; 不使用MICROLIB的情况
    
                 IMPORT  __use_two_region_memory   ; 引入双区内存管理标识
                 EXPORT  __user_initial_stackheap  ; 导出堆栈初始化函数
                 
__user_initial_stackheap
                 LDR     R0, =Heap_Mem            ; 加载堆起始地址到R0
                 LDR     R1, =(Stack_Mem + Stack_Size) ; 计算栈结束地址到R1
                 LDR     R2, =(Heap_Mem + Heap_Size)   ; 计算堆结束地址到R2
                 LDR     R3, =Stack_Mem           ; 加载栈起始地址到R3
                 BX      LR                       ; 返回调用位置

                 ALIGN                            ; 地址对齐

                 ENDIF                            ; 结束条件编译

                 END                              ; 文件结束

3.7.1 代码解释

1、 MICROLIB模式(简化C库)

IF :DEF:__MICROLIB
   EXPORT __initial_sp  ; 栈顶地址(由启动文件已定义)
   EXPORT __heap_base   ; 堆起始地址(HEAP段起始)
   EXPORT __heap_limit  ; 堆结束地址(HEAP段结束)

适用场景:使用ARM精简库时(通过MDK勾选Use MicroLIB启用)

特点

  • 不需要用户手动初始化堆栈

  • 堆栈范围由链接脚本自动管理

  • 仅需导出预定义符号供库函数使用

小谭老师的提醒:一定要记得勾选 Use MicroLIB 否则容易出现调试中进入硬件错误的问题,因为 __MICROLIB 这个宏是定义在 Use MicroLIB 中。

2、 标准C库模式

ELSE
   IMPORT __use_two_region_memory  ; 声明使用双区内存模型
   EXPORT __user_initial_stackheap ; 必须实现的初始化函数

__user_initial_stackheap
   LDR R0, =Heap_Mem              ; R0 = 堆起始地址
   LDR R1, =(Stack_Mem + Stack_Size) ; R1 = 栈结束地址(栈向下生长)
   LDR R2, =(Heap_Mem + Heap_Size)   ; R2 = 堆结束地址
   LDR R3, =Stack_Mem             ; R3 = 栈起始地址
   BX LR                          ; 返回

寄存器参数约定

寄存器含义用途
R0堆起始地址(heap_base)供malloc等函数使用
R1栈结束地址(栈底)用于栈溢出检测
R2堆结束地址(heap_limit)限制堆扩展边界
R3栈起始地址(栈顶)初始化栈指针
  • 双区内存模型
    • 栈和堆使用独立的内存区域
    • 栈向下生长(高地址→低地址)
    • 堆向上生长(低地址→高地址)

3、 通用控制指令

ALIGN    ; 确保地址按4字节对齐
END      ; 结束汇编文件

3.7.2 指令解释

1、 条件编译指令

指令功能等效C语法
IF :DEF:XXX判断符号XXX是否已定义#ifdef XXX
ELSE条件分支#else
ENDIF结束条件编译块#endif

2、 IMPORT/EXPORT

  • IMPORT __use_two_region_memory

    • 声明使用标准C库的双区内存管理模式

    • 必须与__user_initial_stackheap配合使用

  • EXPORT __user_initial_stackheap

    • 标准C库要求的初始化函数

    • 在程序启动时由C库自动调用

3、 LDR 地址加载

  • 语法变体

    LDR Rn, =Label  ; 加载Label的32位地址到寄存器
    LDR Rn, [Rm]    ; 从Rm指向的内存加载数据
    
  • 代码中的用途

    • 将预定义的堆栈符号地址加载到指定寄存器

    • 地址值由链接阶段确定(参见启动文件中的HEAP/STACK定义)

4、 BX LR

  • 功能:跳转到链接寄存器存储的地址(函数返回)

  • 特点

    • 不进行栈操作(与BL不同)

    • 此处用于传递堆栈参数后立即返回

3.7.3 内存布局示意图

graph TD
    RAM起始地址 --> Stack_Mem
    Stack_Mem --> |Stack_Size| Stack_Mem+Stack_Size
    Stack_Mem+Stack_Size --> Heap_Mem
    Heap_Mem --> |Heap_Size| Heap_Mem+Heap_Size
    Heap_Mem+Heap_Size --> RAM结束地址

    classDef stack fill:#f9d,stroke:#333;
    classDef heap fill:#8df,stroke:#333;
    class Stack_Mem,Stack_Mem+Stack_Size stack
    class Heap_Mem,Heap_Mem+Heap_Size heap

3.7.4 注意事项

1、 堆栈溢出检测

  • 标准库会根据R1/R3检测栈溢出(若SP超过R3)

  • 堆分配超过R2会导致malloc返回NULL

2、 内存对齐要求

区域最小对齐实现方式
栈顶8字节启动文件中的ALIGN=3
堆起始4字节链接脚本控制

3、常见错误

  • 未实现__user_initial_stackheap:链接时报undefined symbol错误

  • 堆栈区域重叠:导致数据覆盖,需在链接脚本中检查HEAP/STACK定义

4、性能优化

  • MICROLIB模式节省约2KB代码空间,但缺失部分标准库功能

  • 双区模式适合需要独立堆栈保护的应用

5、 调试方法:

  • __user_initial_stackheap设置断点,观察寄存器值

  • 使用__heap_limit__initial_sp监控堆栈使用情况