嵌入式MCU烧录原理深度解析
MCU存储架构、SWD与串口两种烧录路径、Bootloader工作机制、MCUboot安全启动流程、常见问题排查,一文讲透烧录全链路。
一、原理简析
1.1 单片机存储"三件套"
单片机内部有三种存储器,各司其职:
- Flash:程序代码的长期仓库,断电不丢失,按扇区擦写
- RAM:运行时的临时工作台,断电即空,随机读写速度快
- ROM:出厂固化的系统存储器,内置Bootloader,用户不可改
烧录的本质就是:把二进制文件从PC写入Flash的指定地址。
flowchart TB
A["单片机存储系统"] --> B["Flash<br/>长期仓库"]
A --> C["RAM<br/>临时工作台"]
A --> D["ROM<br/>出厂配送中心"]
B --> E["存程序代码·断电不丢"]
C --> F["存变量堆栈·断电即空"]
D --> G["存Bootloader·出厂固化"]
style A fill:#E3F2FD
style B fill:#E8F5E9
style C fill:#FFF8E1
style D fill:#F3E5F5
style E fill:#E8F5E9
style F fill:#FFF8E1
style G fill:#F3E5F5
| 存储器 | 特点 | 用途 |
|---|---|---|
| Flash | 掉电不丢失,按扇区擦写 | 程序代码、常量 |
| RAM | 掉电丢失,随机读写 | 变量、堆栈 |
| ROM | 出厂固化,用户不可改 | Bootloader |
1.2 两种烧录路径
二进制文件进入Flash有两条路:
来自 linuxros.cn · linuxROS
flowchart TB
A["PC上的二进制文件"] --> B["路径一:SWD调试接口"]
A --> C["路径二:串口/USB"]
B --> D["ST-Link / J-Link"]
D --> E["直接操作Flash"]
C --> F["Bootloader中转"]
F --> E
E --> G["Flash中程序代码"]
style A fill:#E3F2FD
style B fill:#E8F5E9
style C fill:#FFF8E1
style D fill:#E8F5E9
style F fill:#FFF8E1
style E fill:#F3E5F5
style G fill:#E8F5E9
| 方式 | 接口 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| ST-Link/J-Link | SWD/SWIM | 速度快,可在线调试 | 需专用下载器 | 开发调试 |
| 串口/USB | UART/USB DFU | 无需下载器,成本低 | 需配置Boot引脚 | 量产、OTA升级 |
二、实操步骤
2.1 SWD方式烧录(开发调试首选)
使用ST-Link或J-Link通过SWD接口直接访问Flash,无需Bootloader参与:
# OpenOCD 烧录命令示例
openocd -f interface/stlink.cfg -f target/stm32f1x.cfg \
-c "program firmware.bin verify reset exit 0x08000000"
2.2 串口Bootloader烧录(量产常用)
利用ROM中出厂固化的Bootloader,通过串口协议完成烧录:
# stm32flash 开源工具烧录命令
stm32flash -w firmware.bin -v -g 0x08000000 /dev/ttyUSB0
关键前提:必须将BOOT0拉高,使CPU从系统存储器启动。
2.3 Boot引脚配置
| BOOT0 | BOOT1 | 启动区域 | 说明 |
|---|---|---|---|
| 0 | X | Flash | 正常运行用户程序 |
| 1 | 0 | 系统存储器 | 进入Bootloader模式 |
| 1 | 1 | SRAM | 调试用,极少使用 |
三、开源烧录工具选型
| 工具 | 接口 | 特点 |
|---|---|---|
| stm32flash | UART/I2C | 跨平台命令行,GPL-2.0,量产首选 |
| MCU-Flash-Tools | UART/USB | Python单脚本,支持STM32/CH32/AVR |
| stm32-dfu-bootloader | USB DFU | 支持固件验证、安全启动 |
| MCUboot | 多种 | 安全Bootloader,签名验证+OTA |
| wolfBoot | 多种 | 轻量安全启动,加密签名 |
四、核心流程图
串口Bootloader烧录完整流程
flowchart TB
A(["🔴 开始烧录"]) --> B["配置BOOT0=1<br/>BOOT1=0"]
B --> C["按下复位键"]
C --> D{"CPU读取<br/>Boot引脚"}
D -->|"BOOT0=1"| E["从ROM启动<br/>Bootloader"]
D -->|"BOOT0=0"| F["从Flash启动<br/>用户程序直接运行"]
subgraph BL["📡 Bootloader工作阶段"]
E --> G["初始化串口/USB外设"]
G --> H["等待上位机发送指令"]
H --> I["接收二进制数据包"]
end
subgraph FLASH["💾 Flash写入阶段"]
I --> J["擦除Flash目标扇区"]
J --> K["逐页写入Flash"]
K --> L{"CRC校验通过?"}
L -->|"是 ✅"| M["设回BOOT0=0"]
L -->|"否 ❌"| N{"重试次数超限?"}
N -->|"否"| J
N -->|"是"| O(["🔴 烧录失败"])
end
M --> P["复位启动"]
P --> Q(["🟢 用户程序运行"])
F --> Q
style A fill:#E3F2FD,stroke:#1565C0
style B fill:#E3F2FD,stroke:#1565C0
style C fill:#E3F2FD,stroke:#1565C0
style D fill:#FFF8E1,stroke:#F57F17
style E fill:#F3E5F5,stroke:#6A1B9A
style F fill:#C8E6C9,stroke:#2E7D32
style BL fill:#F3E5F5,stroke:#6A1B9A,stroke-dasharray:5 5
style G fill:#E1BEE7,stroke:#6A1B9A
style H fill:#E1BEE7,stroke:#6A1B9A
style I fill:#E1BEE7,stroke:#6A1B9A
style FLASH fill:#E3F2FD,stroke:#1565C0,stroke-dasharray:5 5
style J fill:#BBDEFB,stroke:#1565C0
style K fill:#BBDEFB,stroke:#1565C0
style L fill:#FFF8E1,stroke:#F57F17
style N fill:#FFF8E1,stroke:#F57F17
style M fill:#E8F5E9,stroke:#2E7D32
style P fill:#E8F5E9,stroke:#2E7D32
style Q fill:#C8E6C9,stroke:#2E7D32
style O fill:#FFEBEE,stroke:#C62828
MCUboot安全启动流程
MCUboot是Zephyr/Mynewt等RTOS官方推荐的安全Bootloader,支持签名验证和OTA升级:
flowchart TB
A(["🔴 上电复位"]) --> B["MCUboot启动<br/>初始化Flash与加密模块"]
B --> C["检查镜像元数据<br/>判断Swap类型"]
C --> D{"Slot1有<br/>待升级固件?"}
D -->|"有"| E["执行镜像交换<br/>Slot1→Slot0"]
D -->|"无"| F{"验证Slot0<br/>签名SHA-256"}
E --> F
F -->|"验证通过 ✅"| G["跳转到Slot0<br/>用户程序入口"]
F -->|"验证失败 ❌"| H(["🔴 启动失败<br/>系统挂起"])
G --> I(["🟢 用户程序运行"])
I --> J{"应用确认<br/>img_confirmed?"}
J -->|"是"| K(["✅ 升级永久生效"])
J -->|"否<br/>下次重启"| L["MCUboot自动<br/>回滚到旧版本"]
L --> G
style A fill:#E3F2FD,stroke:#1565C0
style D fill:#FFF8E1,stroke:#F57F17
style F fill:#FFF8E1,stroke:#F57F17
style J fill:#FFF8E1,stroke:#F57F17
style B fill:#F3E5F5,stroke:#6A1B9A
style C fill:#F3E5F5,stroke:#6A1B9A
style E fill:#E3F2FD,stroke:#1565C0
style G fill:#E8F5E9,stroke:#2E7D32
style I fill:#C8E6C9,stroke:#2E7D32
style K fill:#C8E6C9,stroke:#2E7D32
style L fill:#FFF9C4,stroke:#F57F17
style H fill:#FFEBEE,stroke:#C62828
五、常见问题解决
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 烧录后程序不运行 | BOOT0未切回0 | 烧录完成后将BOOT0接低电平再复位 |
| 串口无法连接Bootloader | 波特率不匹配 | stm32flash默认57600,Bootloader支持自动检测 |
| 链接后文件比源码大很多 | 链接器引入库文件 | 检查map文件,裁剪未使用的库函数 |
| Flash写入失败 | 扇区未擦除 | Flash必须先擦除再写入,按扇区操作 |
六、总结
烧录的本质是把二进制数据写入Flash指定地址,两条路径各有适用场景:SWD接口适合开发调试,串口Bootloader适合量产和OTA升级。开源生态提供了丰富的工具链——从轻量的stm32flash到支持安全启动的MCUboot,开发者可按需选用。