ZZZ-04-STM32启动过程

实际上,在 main() 出现之前,单片机已经默默做了很多“脏活累活”。我们可以把这个过程想象成工厂开工前的准备流程

1. 启动过程全景图

当单片机掉电后再上电,或者你按下 Reset(复位) 键时,芯片内部会发生以下连锁反应:

sequenceDiagram participant HW as 硬件 (CPU) participant Flash as Flash (0x08000000) participant Startup as 启动文件 (.s) participant System as SystemInit 函数 participant Main as main() 函数 HW->>Flash: 1. 读取栈顶指针 (MSP) HW->>Flash: 2. 读取复位向量 (Reset Vector) Flash->>Startup: 3. 跳转到复位程序入口 Startup->>System: 4. 执行时钟初始化 System->>Startup: 初始化完成 Startup->>Main: 5. 跳转到用户代码

2. 详细步骤拆解

第一步:找“地图”和“办公桌”

当复位信号产生,CPU 会雷打不动地去 Flash 的起始地址(0x0800 0000) 寻找两块最重要的数据:

  1. 栈顶指针 (MSP):告诉 CPU 办公桌(SRAM)的顶端在哪里,方便以后存放临时变量。
  2. 复位向量 (Reset Vector):这其实是一个地址,指向“第一行程序”在 Flash 里的位置。

第二步:进入汇编世界(启动文件)

你可能在工程里见过一个叫 startup_stm32xxxx.s 的文件,这就是启动文件。它是用汇编语言写的。

  • 为什么用汇编? 因为此时 C 语言的环境(比如变量、堆栈)还没搭好,只能用最原始的汇编指令。
  • 这个文件的任务是:定义中断向量表。它告诉 CPU:如果发生了复位去哪、发生了中断去哪。

第三步:初始化“心跳”(SystemInit)

在进入 main 之前,启动文件会先调用一个 C 函数叫 SystemInit()

  • 任务:配置外部晶振,把芯片的频率从低速(刚启动时通常很慢)提升到正常工作频率(比如 72MHz)。
  • 这就是为什么你的程序一跑起来就是全速的。

第四步:搬运数据(数据初始化)

前面 [[ZZZ-02-Flash和SRAM]] 中说到:

  • 代码在 Flash。
  • 变量在 SRAM。 如果你写了 int a = 10;,这个“10”在断电时是存在 Flash 里的。 在这一步,启动代码会把 Flash 里的初始值“搬”到 SRAM 的变量地址里。这样等你进入 main 的时候,变量 a 已经变成 10 了。

第五步:跳转到 main

所有环境都搭好了,“办公桌”清理干净了,“时钟”也准了。启动文件最后执行一条跳转指令: BL main 至此,你熟悉的 C 语言世界大幕开启!

阶段 发生位置 主要任务
硬件阶段 CPU 内核 获取栈顶地址和首条指令地址
软件阶段 (汇编) startup_xxx.s 配置中断向量表,初始化堆栈
系统初始化 system_stm32xxx.c 设置系统时钟 (RCC)
数据搬运 库代码 将 Flash 中的变量初始值拷贝到 SRAM
应用阶段 main.c 执行你写的代码

为什么要了解这个

  • 调试死机:如果你的程序还没进 main 就挂了,你得知道要去启动文件或者 SystemInit 里找原因。
  • 理解中断:中断向量表就在启动文件里。如果你想深入底层,这是必经之路。
  • 节省空间:理解了数据搬运,你就会明白为什么全局变量如果不初始化会占空间,以及如何优化内存。
Licensed under CC BY-NC-SA 4.0