2. 启动模式与 BootROM:Boot 引脚配置、系统存储器启动、内置 BootLoader 行为分析

好,咱们正式开始第二讲。上一章我们把多核架构的硬件基础聊透了,这一章要解决一个更实际的问题:芯片上电后,第一行代码到底从哪里来?

说白了,就是启动模式的选择。STM32 给了你几种不同的“入口”,通过几个引脚的电平状态来决定。我刚开始接触 STM32H7 的时候,就因为在启动模式上栽过跟头——板子焊好,程序死活跑不起来,最后发现是 BOOT 引脚电平没拉对。嗯,这种坑踩一次就够了。

2.1 Boot 引脚配置:硬件的“第一选择”

STM32 的启动模式,是通过 BOOT0 和 BOOT1(部分型号是 nBOOT1 或 BOOT2)这两个引脚的电平组合来决定的。你想想看,芯片刚上电,Flash 里还没程序,或者程序跑飞了,总得有个“安全网”吧?Boot 引脚就是干这个的。

以最常见的 STM32H7 系列为例,它的启动模式选择如下:

BOOT0 BOOT1 启动区域 说明
0 X 主 Flash 正常用户程序启动,最常用
1 0 系统存储器 内置 BootLoader,用于串口/DFU 下载
1 1 SRAM 调试或临时运行代码用
个人习惯: 我一般在设计板子时,会把 BOOT0 通过一个 10kΩ 电阻下拉到地,再串联一个按键到 VCC。这样平时是 Flash 启动,按住按键上电就能进入系统存储器 BootLoader。调试时非常方便,不用改硬件。

这里要注意,不同系列的 STM32,BOOT1 的对应关系可能不一样。比如 STM32F1 系列,BOOT1 是复用在 PB2 引脚上的,而 H7 系列则有独立的 nBOOT1 选项字节。查手册时一定要看对应型号的 “Boot mode selection” 章节。

2.2 系统存储器启动:内置 BootLoader 的秘密

当 BOOT0=1、BOOT1=0 时,芯片会从 系统存储器(System Memory) 启动。这块区域是 ROM,出厂时已经烧录了一段固件——也就是我们常说的 内置 BootLoader。

这段 BootLoader 是 ST 官方写的,用户不能修改。它的主要作用是什么?说白了,就是给你一个“后门”,通过串口、USB、CAN 等接口来下载用户程序。你想想看,如果 Flash 里的程序把芯片搞死了,或者新板子第一次烧录,总得有个办法把代码写进去吧?

内置 BootLoader 支持的外设接口,不同型号差异很大。我整理了一个常用对照表:

接口 STM32F1/F4 STM32H7 STM32L4
USART ✓ ✓ ✓
USB DFU 部分支持 ✓ ✓
CAN ✗ ✓ ✗
I2C ✓ ✓ ✓
SPI ✓ ✓ ✓
我曾经踩过的坑: 有一次用 STM32H743,想通过 USB DFU 下载程序。结果插上 USB 后电脑死活不识别设备。查了半天才发现,H7 的 USB DFU 需要外部晶振提供精确的 48MHz 时钟,而我板子上用的内部 RC 振荡器,频率偏差太大,导致 USB 枚举失败。后来换成外部晶振,一次成功。

2.3 内置 BootLoader 行为分析:从复位到等待命令

好,现在我们来深入看看,当芯片从系统存储器启动后,BootLoader 到底干了些什么。

它的执行流程大致如下:

  1. 初始化硬件: 配置系统时钟、复位所有外设、初始化堆栈。
  2. 检测启动引脚: 再次确认 BOOT 引脚状态,确保是从系统存储器启动。
  3. 配置选定的通信外设: 根据芯片型号,初始化 USART1、USART3、USB OTG、CAN 等接口。
  4. 发送启动信息: 通过选定的接口发送 0x7F(0x7F 是 BootLoader 的握手信号)。
  5. 等待主机命令: 进入循环,等待上位机(如 STM32CubeProgrammer)发送命令。

这个流程里,有一个关键点:BootLoader 会先检测各通信接口,看哪个接口有数据过来。它并不是同时监听所有接口,而是按优先级顺序轮询。比如 H7 系列,会先检测 USART1,再检测 USART3,然后是 USB DFU,最后是 CAN。

核心知识点: 内置 BootLoader 的通信协议是固定的。它使用 YMODEM 协议(串口)或 DFU 协议(USB)。如果你要自己写上位机,必须严格按照协议来。ST 官方提供了 STM32CubeProgrammer,我建议你直接用这个,省心。

下面我用一个简单的流程图,把整个启动模式选择到 BootLoader 执行的过程串起来:

STM32 启动模式与 BootLoader 执行流程 上电复位 读取 BOOT0 / BOOT1 引脚电平 BOOT0=1? 且 BOOT1=0? 否 主 Flash 启动 是 系统存储器启动 BootLoader 初始化 时钟/外设/堆栈 等待主机命令 注意: BOOT1=1 时进入 SRAM 启动 用于调试场景

2.4 实战技巧:如何判断当前是从哪个模式启动的

在实际调试中,你可能会遇到一个问题:我怎么知道芯片当前是从 Flash 启动的,还是从 BootLoader 启动的?

我一般用两种方法:

  • 看 PC 指针: 在调试器里暂停程序,查看 PC(程序计数器)的值。如果 PC 在 0x0800xxxx 范围内,说明是从 Flash 启动;如果在 0x1FFFxxxx(系统存储器地址),说明在 BootLoader 里。
  • 看串口输出: 如果芯片进入了 BootLoader,它会通过串口发送 0x7F。你用串口助手抓一下,看到 0x7F 就说明 BootLoader 在等你。
小技巧: 如果你不想每次都看 PC 指针,可以在用户程序的起始位置(Reset_Handler)里,把一个 GPIO 拉高。这样只要程序跑起来了,灯一亮,你就知道是从 Flash 启动的。调试时非常直观。

2.5 避坑指南:BootLoader 常见故障

最后,我总结几个我在项目中遇到过的 BootLoader 相关故障,希望能帮你少走弯路:

  • 故障 1:进入 BootLoader 后串口无响应
    我曾经遇到过,板子进入系统存储器模式后,串口发数据没反应。后来发现是串口引脚被其他外设占用了。BootLoader 使用的串口引脚是固定的(比如 USART1 的 PA9/PA10),如果这些引脚被复用成其他功能,BootLoader 就无法正常工作。
  • 故障 2:USB DFU 枚举失败
    前面提到了,H7 的 USB DFU 需要精确的 48MHz 时钟。如果你的板子没有外部晶振,或者晶振频率不对,USB 就无法枚举。解决办法:要么加外部晶振,要么用 HSI 并校准。
  • 故障 3:下载完成后程序不运行
    有时候用 BootLoader 下载完程序,复位后程序没跑起来。检查一下你的程序起始地址对不对。BootLoader 下载时,默认会把程序写入 Flash 的起始地址(0x08000000)。如果你的程序链接脚本里定义了别的地址,那肯定跑不起来。

嗯,这一章的内容就到这里。启动模式看似简单,但它是整个系统运行的基石。搞懂了 Boot 引脚和 BootLoader 的行为,你就能在芯片“不听话”的时候,知道从哪里下手去查。