MCU(STM32)启动流程详解笔记
深入理解MCU启动原理:https://blog.csdn.net/Setul/article/details/130019922
STM32的启动过程 --- startup_xxxx.s文件解析(MDK和GCC双环境)https://blog.csdn.net/weixin_42328389/article/details/120656722?fromshare=blogdetail\&sharetype=blogdetail\&sharerId=120656722\&sharerefer=PC\&sharesource=rechol\&sharefrom=from_link
STM32的内存管理相关(内存架构,内存管理,map文件分析):https://blog.csdn.net/weixin_42328389/article/details/120622384
一、概述
STM32 启动流程的核心是地址映射机制与启动介质选择,通过硬件配置(BOOT 引脚)和软件初始化(堆栈、数据段搬运),最终引导 CPU 执行用户程序。整体流程可概括为:复位→地址重映射→堆栈初始化→数据段搬运→执行用户代码。
二、内存映射基础
2.1 核心地址空间划分(STM32 Cortex-M4)
STM32 作为 32 位 MCU,寻址能力为 4GB(0x0000 0000~0xFFFF FFFF),地址空间按功能划分为多个 512MB Block,核心区块及用途如下:
Block 0(0x0000 0000~0x1FFF FFFF):含自举区、Flash、系统存储器、选项字节,核心代码存储与启动相关区域。
Block 1(0x2000 0000~0x3FFF FFFF):SRAM 区域,用于存储运行时数据(堆栈、变量),掉电数据丢失。
Block 2(0x4000 0000~0x5FFF FFFF):外设区域,挂载 APB1、APB2、AHB1、AHB2 等总线外设,通过地址访问外设寄存器。
Block 7(0xE000 0000~0xFFFF FFFF):Cortex-M4 内核外设区域,含中断控制器、调试模块等核心硬件。



2.2 核心存储区域详解
|--------------------|--------------------------|-----------------------------------------------------------|
| 存储区域 | 地址范围 | 核心特性与用途 |
| 自举区(重映射区) | 0x0000 0000~0x0007 FFFF | 512KB,复位后默认读取第一条指令的地址,本身不存代码,仅用于地址重映射。 |
| Flash(闪存) | 0x0800 0000~0x0807 FFFF | 512KB(示例型号),非易失性,存放用户代码(.text 段)、只读数据(.rodata 段),掉电数据不丢失。 |
| 系统存储器 | 0x1FFF 0000~0x1FFF 7A0F | 存放出厂自带 Bootloader(ISP 固件),用于固件升级、故障恢复。 |
| SRAM(静态内存) | 0x2000 0000~0x2002 0000 | 128KB(示例型号),易失性,运行时数据存储(堆栈、变量、堆空间)。 |
| 选项字节(Option Bytes) | 0x1FFF C000~0x1FFF C007 | 非易失性配置寄存器,用于硬件级保护(如 Flash 读保护、写保护),防止非法访问。 |
2.3 地址映射原理
CPU 通过 "地址映射" 机制关联地址与物理存储 / 外设:
访问某一地址时,MCU 内部地址译码器将地址映射到对应的存储区域或外设寄存器。
启动阶段的核心是 "自举区重映射",即通过 BOOT 引脚配置,将自举区(0x0000 0000)映射到实际启动介质(Flash/SRAM/ 系统存储器)。
- BOOT 引脚配置与启动介质选择

三、关于boot
3.1 自举区与重映射机制
自举区定义:地址范围 0x0000 0000~0x0007 FFFF(512KB),是 STM32 复位后的 "指令入口地址",CPU 复位后优先读取该地址的第一条指令。
重映射本质:自举区不直接存储代码,仅作为 "地址跳板",通过硬件配置将其指向实际存储介质,实现启动介质的灵活切换。
3.2 BOOT 引脚配置规则
STM32 通过 BOOT0 和 BOOT1 两个引脚的电平组合(高电平 1 / 低电平 0),决定自举区的映射目标,具体配置如下:
BOOT0=0(BOOT1 任意,默认忽略):自举区(0x0000 0000)映射到 Flash(0x0800 0000),正常运行模式,用户程序存储于 Flash,上电后直接执行。
BOOT0=1、BOOT1=0:自举区映射到系统存储器(0x1FFF 0000),ISP 升级模式,通过串口等接口升级 Flash 中的用户程序。
BOOT0=1、BOOT1=1:自举区映射到 SRAM(0x2000 0000),调试 / 临时运行模式,程序仅在 SRAM 中运行,掉电丢失,用于程序调试或临时测试。
3.3 核心作用
通过 BOOT 引脚配置,STM32 可适配三种核心场景:
正常产品运行:选择 Flash 启动,保障程序掉电不丢失。
固件升级:选择系统存储器启动,利用出厂 Bootloader 更新代码。
程序调试:选择 SRAM 启动,避免频繁擦写 Flash,提升调试效率。
四、从 Flash 启动的详细流程(主流启动方式)
当 BOOT0=0 时,系统从 Flash 启动,是最常用的启动模式,流程分为 5 个关键步骤:
四、单片机复位流程
4.1 步骤 1:复位后地址重映射
CPU 复位后,自动访问自举区首地址 0x0000 0000。
因 BOOT0=0,地址重映射机制将 0x0000 0000 指向 Flash 首地址 0x0800 0000,后续指令读取均从 Flash 执行。
4.2 步骤 2:MSP(主堆栈指针)初始化


Flash 首地址(0x0800 0000)存储MSP 初始值(sram栈底地址),CPU 复位后首先读取该值并加载到 MSP 寄存器。
栈的核心参数(以示例配置为例):
栈底地址:0x2002 0000(SRAM 高位地址)。
栈大小:STACK_SIZE = 0x400(2KB)。
生长方向:从高地址向低地址生长(栈顶 SP 初始指向栈底,压栈时 SP 递减,出栈时 SP 递增)。
栈的用途:存储函数局部变量、函数调用返回地址、中断现场保护,是程序运行的基础内存空间。
4.3 步骤 3:PC(程序计数器)初始化
Flash 地址 0x0800 0004 存储复位处理函数(Reset_Handler)的入口地址。
CPU 读取该地址后,PC 指针跳转到 Reset_Handler,开始执行系统初始化逻辑。
4.4 步骤 4:数据段搬运与初始化
Reset_Handler 的核心工作之一是完成 "数据段迁移",确保变量在 SRAM 中正常运行,主要涉及两类数据段:
4.4.1 RW-data 段(读写数据段)
存储:带初始值的全局变量、静态局部变量(如int g_var = 10;)。
存储位置:编译时初始值存于 Flash 的.rodata 段,运行时需搬运到 SRAM 的.data 段。
搬运逻辑:Reset_Handler 通过汇编指令,将 Flash 中 RW-data 的初始值复制到 SRAM 对应地址,确保变量初始值正确。
4.4.2 BSS 段(零初始化数据段)
存储:无初始值或初始值为 0 的全局变量、静态局部变量(如int g_var;或static int s_var = 0;)。
初始化逻辑:系统自动将 BSS 段对应的 SRAM 区域清零,无需从 Flash 搬运数据,节省存储资源。
4.5 步骤 5:进入用户程序(main 函数)
完成堆栈初始化、数据段搬运后,Reset_Handler 调用SystemInit()函数(配置系统时钟、外设时钟等)。
最终跳转到用户编写的main()函数,程序进入用户逻辑执行阶段。
五. 启动文件到底是什么?
启动文件(通常是汇编文件,比如 STM32 的startup_stm32f103xe.s)是由芯片厂商提供、直接与硬件内核(如 Cortex-M3/M4)对接的底层代码,它的作用是:
-
定义中断向量表(包括复位向量);
-
完成 CPU 上电后最基础的初始化(栈、堆、全局变量等);
-
最终 "接力" 跳转到用户代码的
main函数。
具体作用: 
5.1 硬件自动执行(上电复位后):
-
CPU从向量表的第一个条目(0x00000000)读取初始栈指针(MSP)
-
从向量表的第二个条目(0x00000004)读取复位向量(Reset_Handler的地址)
-
CPU跳转到
Reset_Handler函数
5.2 启动文件的初始化工作
启动文件(如startup_stm32xxxx.s)是一段汇编代码,主要完成以下任务:

-
初始化栈和堆:在 SRAM 中划分栈空间(用于函数调用、局部变量)和堆空间(用于
malloc等动态内存分配)。 -
初始化中断向量表。
-
初始化全局变量:
-
将 Flash 中
.rwdata段的 "初始化了特定值的变量" 复制到 SRAM 的.data段。 -
将 SRAM 中
.bss段的 "未初始化或初始化为 0 的变量" 清零。
-
-
初始化系统外设:如配置系统时钟(SystemInit 函数,部分启动文件会调用)。
-
启动文件完成所有初始化后,通过汇编指令跳转到用户编写的
main函数,此时才真正开始执行你写的第一行用户代码。
5.3一图流总结
六 中断向量表
通过上文的Boot ROM与Boot Loader我们知道:Boot ROM会读取Boot引脚的电平配置来选择启动方式。
6.1 BOOT 配置:决定 "程序从哪里读"
单片机通过BOOT 引脚(如 STM32 的 BOOT0、BOOT1)配置程序的启动介质,这一步决定了 "中断向量表和代码存在哪里":
-
BOOT0=0:从主 Flash启动(最常用,用户程序存放在这里)。
-
BOOT0=1,BOOT1=0:从系统存储器启动(用于出厂引导程序(厂家的bootloader)或 ISP 在线编程)。
-
BOOT0=1,BOOT1=1:从SRAM启动(多用于调试场景)。
6.2 向量表的 "唯一性" 与 "偏移":
-
系统中只有一份 "逻辑上的 中断向量表 " ,但它的 "物理存储位置" 可以通过
SCB->VTOR偏移到 Flash 或 SRAM。 -
也就是说,向量表的 "内容(各中断的服务函数地址)" 是固定的,但 "存储这张表的物理地址" 可以切换 ------ 这就是 "偏移" 的本质。
6.3 复位向量指向的复位服务函数
(如 Reset_Handler )是唯一的 ,但它的 "存储地址" 会随向量表的偏移而变化:
-
场景 1:向量表在 Flash(
SCB->VTOR = 0x08000000), 复位向量(0x08000004)指向的是Flash 中存储的Reset_Handler函数的地址。 -
场景 2:向量表在 SRAM (
SCB->VTOR = 0x20000000) 此时需要先将Flash 中的向量表复制到 SRAM (包括Reset_Handler的地址),然后SCB->VTOR指向 SRAM 起始地址。复位向量(0x20000004)指向的是Flash 中存储的Reset_Handler函数的地址。(与 Flash 中原始地址一致,因为向量表是复制过来的)。
6.4 中断向量表里存的是什么?
中断向量表的每一个条目(比如第0条是初始栈指针MSP,第1条是复位向量),存储的并不是指令代码本身,而是一个地址值(32位指针)。
- 对于复位向量,这个地址值就是
Reset_Handler函数在内存中的入口地址。
6.5 函数的地址(入口地址)是固定的吗?
在编译和链接阶段,链接器会根据链接脚本(Linker Script)为每一个函数分配一个固定的逻辑地址。假设 Reset_Handler 函数被链接器定位到了 Flash 区域的 0x08000C00 这个地址。
那么,无论中断向量表放在哪里(向量表偏移),Reset_Handler 这个函数的本体,其物理位置就在 Flash 的 0x08000C00 处,不会移动。而向量表中的复位向量就永远指向0x08000C00 这个地址。
6.6 中断向量表:"程序入口" 的索引表
上文说过,中断向量表是一个地址列表,存储了复位向量、所有中断服务函数的入口地址。它位于启动介质的起始地址区域(如 Flash 的0x0800 0000处)。
它的结构(以 Cortex-M 内核为例):
七、变量存储区域详解(SRAM 内部划分)
上图的 STACK_SIZE 栈大小应该为 1Kbytes
SRAM 作为运行时数据存储区,内部按功能划分为多个区域,从高地址到低地址分布如下:
栈(单片机自己的Stack):
地址范围:0x2001 FC00~0x2002 0000(栈顶→栈底)。
用途:函数调用、局部变量存储、中断现场保护。
特性:自动分配与释放,生长方向为高地址→低地址,溢出会导致程序崩溃。
堆(单片机自己的Heap):
地址范围:0x2001 8000~0x2001 FBFF(示例配置:HEAP_SIZE=0x800,FreeRTOS_HEAP_SIZE=15KB)。
用途:动态内存分配(如malloc()、free()),FreeRTOS 的任务栈、队列等也基于堆分配。
特性:手动分配与释放,生长方向为低地址→高地址,需避免内存泄漏。
FreeRTOS 堆(ucHeap):

专门用于 FreeRTOS 操作系统的内存管理,独立于普通堆,避免操作系统与用户程序内存冲突。
BSS 段:
地址范围:0x2000 8000~0x2001 7FFF。
存储:未初始化或初始值为 0 的全局 / 静态变量,系统启动时自动清零。
Data 段:
地址范围:0x2000 0000~0x2000 7FFF。
存储:已初始化的全局 / 静态变量,初始值从 Flash 搬运而来。
八、补充疏漏知识点
8.1 完整启动流程时序(从复位到 main)
上电 / 复位→CPU 读取 BOOT 引脚电平→确定自举区映射目标。
从自举区映射地址读取 MSP 初始值→初始化主堆栈。
读取 PC 初始值(复位处理函数地址)→跳转到 Reset_Handler。
Reset_Handler 执行:关闭中断→配置时钟→搬运 RW-data 段→清零 BSS 段→开启中断。
调用 SystemInit ()→跳转到 main ()→执行用户程序。
8.2 核心存储区域补充说明
RO-data 段(只读数据段):存储于 Flash,包含字符串常量(如"hello")、const 修饰的全局变量,不可修改,掉电不丢失。
选项字节(Option Bytes):除读保护外,还支持写保护(指定 Flash 区域不可擦写)、看门狗配置(硬件看门狗使能 / 禁用)等,修改后需重启生效。
系统存储器:内置的 Bootloader 支持 UART、I2C 等接口的 ISP 升级,无需外接编程器即可更新 Flash 程序。
8.3 关键注意事项
栈大小配置需匹配程序复杂度:函数嵌套层级深、局部变量多时,需增大 STACK_SIZE,避免栈溢出。
数据段搬运的必要性:Flash 为只读存储,带初始值的变量需搬运到可读写的 SRAM 才能修改。
BOOT 引脚配置错误的影响:若 BOOT0=1 但未烧录系统存储器程序,会导致 MCU 无法启动(死机)