单片机的启动流程
完整流程
上电或复位: 芯片通电或RESET引脚被拉低。
读取BOOT引脚: 硬件逻辑在复位信号的上升沿锁存 BOOT0 和 BOOT1 引脚的电平状态。
执行内存重映射(M3/M4才重映射,M7没有重映射):
- 如果 BOOT0=0,内部总线矩阵将主闪存(0x08000000)的地址重映射到 0x00000000。
- 如果 BOOT0=1, BOOT1=0,将系统存储器(0x1FFFF000)重映射到 0x00000000。
- 如果 BOOT0=1, BOOT1=1,将SRAM(0x20000000)重映射到 0x00000000。
CPU开始执行:
CPU的PC指针指向 0x00000000,开始它的生命周期。它首先从 0x00000000 读取栈顶指针(MSP)的初始值,并加载到SP寄存器。然后从 0x00000004 读取复位处理函数(Reset_Handler)的地址,并加载到PC寄存器。这段描述适用全部模式:Flash、System Memory、SRAM
运行软件程序:
- 主闪存模式:CPU开始执行Reset_Handler,在回调函数中分别执行SystemInit和__main,最后执行main()函数。
- 系统存储器模式:CPU开始执行ST官方的ISP Bootloader中的Reset Handler,在回调函数中做初始化处理后,官方的ISP程序运行,等待通过串口等接收新固件(执行bootloader)。
- SRAM模式:CPU开始执行0x00000004指向的函数地址下的程序(若用户有将程序拷贝或者是通过写SRAM的方式编写程序,则可正常运行;若SRAM为空,则CPU会触发硬件错误(HardFault)。
流程解析
MCU触发了复位事件后,芯片内部的硬件逻辑会检测BOOT0和BOOT1引脚是高电平还是低电平,根据两个BOOT脚的高低电平组合,将不同的物理存储器的起始地址重映射到0x00000000这个地址上。
紧接着CPU会从0x00000000执行程序,CPU会先将PC指针指向0x00000000,将该地址存放的栈顶指针MSP的初始值取出并加载到CPU寄存器SP中,然后偏移4个字节,将0x00000004地址下存放的复位处理函数Reset_Handler地址取出并加载到CPU寄存器PC中。
然后CPU就开始执行Reset_Handler函数下的程序,在几种模式下,主要是主闪存模式(即BOOT0为0的模式),CPU开始执行程序Reset_Handler(虚函数),默认为执行system_stm32f10x.c文件下的SystemInit()函数,这个函数主要负责最基本的硬件初始化,如配置系统时钟。
紧接着执行C库入口函数__main,在该函数下会建立一个符合C语言标准的程序执行环境,包括:分散加载初始化、复制.data段、清零.bss段、C/C++库初始化、堆初始化、IO初始化、FPU初始化、调用C++静态构造函数。处理完这些就会调用用户编写的main()函数了。
__main()的具体工作
| 步骤 | 任务 | 目标 | 涉及内存区域 |
|---|---|---|---|
| 1 | 分散加载初始化 | 建立正确的内存数据映像 | .data, .bss (SRAM) |
| 1.1 | 复制.data段 | 将Flash中的初始值赋给SRAM中的全局/静态变量 | .data |
| 1.2 | 清零.bss段 | 确保未初始化的全局/静态变量为0 | .bss |
| 2 | C/C++库初始化 | 使标准库函数可用 | Heap, Stack (SRAM) |
| 2.1 | 初始化堆 | 为malloc()等动态内存分配做准备 | Heap |
| 2.2 | 初始化I/O | 为printf()等函数准备底层接口 | - |
| 2.3 | 初始化FPU | 使能浮点运算单元(如果需要) | - |
| 2.4 | 调用C++静态构造函数 | 正确构造全局C++对象 | - |
| 3 | 调用main() | 将控制权移交给用户代码 | Stack |
| 4 | 处理main()返回 | 提供一个确定的程序终止行为 | - |
