STM32 启动流程解析:上电后第一条代码是如何执行的

发布时间:2026/8/1 21:24:33
STM32 启动流程解析:上电后第一条代码是如何执行的 一、前言绝大多数 STM32 开发者日常基于标准库、HAL 库开发业务代码从main()函数开始编写久而久之形成一个错误认知单片机上电直接执行main函数。实际上main只是整套上电初始化链路的最终业务入口芯片从上电上电电压爬升、硬件复位、BOOT 引脚采样到汇编底层初始化、内存段搬运、时钟配置中间一整套严谨的固化流程全部执行完毕后才会跳转到 C 语言主函数。平时遇到的上电不运行、全局变量上电数值随机、IAP 程序跳转死机、偶尔启动异常、中断跳转失效等问题根源几乎都是对底层启动机制不了解。本文按照硬件→内核→汇编启动文件→内存映射→代码落地→故障排查线性逻辑梳理剔除重复内容附带流程图、核心汇编源码、内存分区详解全部为工程可落地干货。二、前置基础STM32 三块启动存储介质STM32 内核上电第一步是根据引脚电平选择程序运行载体芯片内部三块物理存储区域功能完全不同Main Flash 主闪存掉电非易失存储用户编译生成的固件默认烧录于此产品正式运行的标准启动区域地址基地址0x08000000。System Memory 系统存储区ST 出厂固化的 ISP Bootloader用户无法擦写修改仅用于串口 / USB DFU 下载程序也就是常说的 “下载模式”。SRAM 静态内存掉电数据丢失正常运行存放变量、堆栈、堆内存仅用于调试、RAM 内运行代码、IAP 调试基地址0x20000000。三、硬件上电第一动作BOOT 引脚选择启动模式3.1 BOOT 引脚采样规则电源上电、复位电平稳定后芯片硬件自动锁存BOOT0、BOOT1引脚电平决定程序从哪一块存储器启动三种模式唯一对应关系表格BOOT0BOOT1启动存储区域使用场景低电平任意Main Flash产品正常运行常规接法 BOOT0 下拉 GND高电平低电平System Memory串口 ISP 下载固件高电平高电平SRAM代码调试、RAM 运行程序3.2 硬件上电完整时序纯硬件行为无软件参与电源电压缓慢爬升达到芯片最小工作电压阈值内部上电复位电路生效CPU 强制保持复位状态硬件锁存 BOOT0/BOOT1 引脚电平确定启动基地址内部 RC 或外部晶振起振等待时钟稳定复位信号释放Cortex-M 内核正式开始取指执行指令。关键点上电前期所有动作都是硬件自动完成还没有任何一行用户代码被执行。四、Cortex-M 内核强制启动规则真正第一条指令在哪里4.1 内核固定取址机制Cortex-M3/M4/M0 内核复位释放后会强制从选定启动区域的固定偏移地址读取两个关键值以最常用 Flash 启动为例地址0x0800 0000存放主堆栈指针 MSP 初始值地址0x0800 0004存放复位中断服务函数 Reset_Handler 入口地址CPU 先把 0 地址数据载入 MSP 寄存器完成堆栈初始化再把 0x04 地址赋值给程序计数器 PC直接跳转到复位中断函数。4.2 核心结论上电执行的第一条有效代码是启动文件中的汇编指令main()函数是汇编完成所有底层初始化后间接跳转进入中断向量表前两项固定为栈顶地址 复位入口由编译器自动填充。五、编译后程序内存分段原理启动文件搬运的底层依据在讲解启动文件汇编逻辑前必须理解固件编译后的四大内存段整个启动文件一大半工作都是在处理这些段的拷贝与清零Code 代码段存放所有函数指令、常量字符串const修饰数据固化在 Flash 中只读不可改写。Data 数据段存放已初始化全局变量例int g_val 100;。编译时存储在 Flash上电必须拷贝到可读写的 SRAM 中才能修改。BSS 零初始化段存放未初始化全局变量、static 静态局部变量例int g_buf[100];。不占用 Flash 空间上电在 SRAM 中将整片内存清零这就是全局变量默认值为 0 的根本原因。Heap Stack 堆与栈全部位于 SRAM栈用于函数调用、局部变量、中断现场压栈堆用于malloc动态内存申请大小在启动文件中宏定义配置。六、启动文件 startup.s 完整执行流程 核心汇编源码启动文件如startup_stm32f10x_md.s是整个启动流程的核心载体纯汇编编写优先级高于所有 C 代码。复位后进入Reset_Handler按固定 6 步完成初始化下面附带精简可看懂的汇编代码。6.1 Reset_Handler 完整执行步骤步骤 1初始化主堆栈指针 MSP向量表首地址取出栈顶地址写入 MSPC 语言函数调用、中断嵌套、局部变量全部依赖堆栈堆栈未初始化 CPU 无法运行 C 代码。 启动文件头部宏定义堆栈大小示例asmStack_Size EQU 0x00000400 ; 1KB栈空间 Heap_Size EQU 0x00000200 ; 512B堆空间步骤 2Data 段从 Flash 拷贝到 SRAM将已初始化全局变量从只读 Flash 搬运到可读写内存对应汇编拷贝逻辑汇编代码Reset_Handler PROC ; 1. Data段拷贝Flash - SRAM LDR R0, _sidata ; Flash中data段起始 LDR R1, _sdata ; SRAM中data段起始 LDR R2, _edata ; SRAM中data段结束 Copy_Data_Loop CMP R1, R2 LDRCC R3, [R0], #4 STRCC R3, [R1], #4 BCC Copy_Data_Loop步骤 3BSS 段内存清零对未初始化全局变量所在内存区域全部置 0避免上电随机脏数据汇编代码; 2. BSS段清零 LDR R1, _sbss LDR R2, _ebss MOV R3, #0 Zero_BSS_Loop CMP R1, R2 STRCC R3, [R1], #4 BCC Zero_BSS_Loop步骤 4调用 SystemInit () 系统初始化函数汇编跳转进入 C 语言SystemInit函数完成三件关键工作配置系统时钟源HSI/HSE/PLL 倍频设置主频配置 AHB、APB 总线分频系数给外设分配时钟重定向中断向量表基地址SCB-VTOR。工程干货做 IAP 固件升级时二级 APP 必须重新执行向量表偏移否则全部中断失效。步骤 5跳转进入 main 函数底层全部初始化完成后使用汇编无条件跳转指令进入用户 C 语言入口汇编代码; 3. 调用系统时钟初始化 BL SystemInit ; 跳转到主函数正式交付业务代码 B main ENDP6.2 整套启动线性流程图可直接画成框图plaintext硬件上电 → 电源稳定 → 复位锁定 → BOOT引脚电平采样 → 释放复位 → 内核读取MSP栈地址与复位入口地址 → 进入Reset_Handler汇编函数 → Data段Flash拷贝至SRAM → BSS段内存清零 → SystemInit时钟向量表配置 → 汇编跳转main() → HAL库初始化 → 外设初始化 → 主循环运行七、启动流程对应工程高频故障排查把理论对应到实际问题快速定位启动异常原因程序可下载但上电不运行BOOT0 引脚未下拉芯片停留在 ISP 下载模式复位电路 RC 参数不合理长期处于复位状态。全局变量上电数值随机、数组数据错乱启动文件被错误修改Data 段拷贝或 BSS 段清零逻辑被删除编译器分散加载文件.sct 配置异常。IAP 跳转 APP 后死机、中断不触发跳转前未关闭中断、未重新设置 MSP 栈指针、APP 内部未重新偏移中断向量表。上电偶尔启动、低温启动失败外部晶振负载电容不匹配起振时间过长电源上电斜率太慢复位释放过早。外部中断、定时器中断偶尔进不去SystemInit 中向量表偏移未配置或跳转后向量表地址冲突。八、常见认知误区纠正 工程启动优化技巧8.1 五大经典误区误区main 是上电第一行代码正解main 是初始化完成后的入口真正首行为复位汇编指令。误区全局变量自动归零是 C 语言语法特性正解完全由启动文件 BSS 段汇编清零实现删除清零代码变量全是随机值。误区BOOT 引脚只有下载时有用正解启动模式由它唯一决定是启动故障首要排查点。误区变量初始化由编译器完成正解编译器仅分配地址映射上电动态搬运靠启动文件执行。误区SRAM 启动模式产品可以随便用正解掉电丢失仅用于调试量产必须 Flash 启动。8.2 上电启动速度优化实操方案针对需要快速开机的产品基于启动流程做轻量化优化优先使用内部高速 HSI 时钟规避外部晶振起振延时在SystemInit中关闭所有未使用外设时钟SPI、I2C、DAC 等减少超大全局数组、结构体降低 Data 段拷贝耗时精简启动文件中未使用的中断向量减小查表耗时主函数开头减少阻塞式延时外设采用延后初始化。九、全文总结STM32 上电启动分为三层硬件层 BOOT 模式选择→内核层固定地址取指→汇编层内存与系统初始化上电第一条执行代码为启动文件Reset_Handler汇编指令main只是业务层入口Data 段拷贝、BSS 段清零、堆栈初始化、时钟配置、向量表重定向是 C 语言程序运行的五大底层必要条件理解这套启动机制不仅可以解决各类启动死机、变量异常、IAP 升级 BUG更能吃透 Flash 与 SRAM 的程序映射关系是嵌入式底层能力、面试考核、产品稳定量产的核心知识点。