02 Bootloader 升级与写入笔记
项目定位
本页用于集中整理 Bootloader、APP 分区、bin 写入、跳转、备份回滚和后续 OTA 扩展。当前资料来源包括三份工程:
| 工程 | 原始来源 | Wiki 原始资料目录 | 作用 |
|---|---|---|---|
| GD Bootloader | D:\Users\Desktop\GD_BT\GD_BL | raw/projects/gd32-bootloader-gd-bl | GD32F470 Bootloader,负责启动、升级判断、APP 备份、跳转 |
| GD APP | D:\Users\Desktop\GD_BT\GD_APP | raw/projects/gd32-bootloader-gd-app | GD32F470 APP 侧升级触发,写下载区并复位进入 BL |
| ARM Bootloader | D:\Users\Desktop\Low_enegy-bootloader\ARM_bl | raw/projects/stm32-arm-bootloader | 自编 STM32G431 Bootloader,重点验证串口写入、256 字节包、跳转和后续 OTA 思路 |
当前优先目标:把“bin 如何写入 Flash”整理清楚。OTA、SD 卡、外部 Flash、CAN、BLE/WiFi 等方式先作为扩展方案记录,后续逐个补实现。
当前 Flash 分区
GD32F470 方案
来自 GD_BL/APP/Bootloader/bootloader_port_config.h 与 GD_APP/APP/Bootloader/bootloader.c:
| 区域 | 地址 | 大小 | 用途 |
|---|---|---|---|
| Bootloader | 0x08000000 | 48 KB | 上电入口,升级管理与跳转 |
| 参数区 | 0x0800C000 | 4 KB | BootParam、备份参数、升级日志、用户配置、校准数据 |
| APP 主区 | 0x0800D000 | 76 KB | 当前运行应用 |
| APP 备份区 | 0x08020000 | 128 KB | 保存上一版 APP,用于升级失败回滚 |
| 下载区 | 0x08073000 | 52 KB | APP 侧先写入新固件,复位后由 BL 搬运/校验 |
| Flash 页 | 4 KB | - | 擦除单位 |
STM32G431 方案
来自 ARM_bl/MyApp/bootloader.h、bootloader.c、bootloader_save.c:
| 区域 | 地址 | 用途 |
|---|---|---|
| Bootloader | 0x08000000 | 上电入口 |
| APP | 0x08010000 | 应用程序向量表和代码区 |
| 接收缓冲 | BL_RX_BUF_SIZE = 1024 | UART DMA 接收缓冲 |
| 固件包 | DOWNLOAD_LEN = 256 | 当前串口写入的固定包长 |
| Flash 基址 | 0x08000000 | 计算页号 |
已有写入方式一:STM32 直接串口写 Flash
位置:raw/projects/stm32-arm-bootloader/MyApp/bootloader.c
流程:
ERASE
-> 擦除 APP 区非空页
WRITE
-> s_bl_write_enable = 1
-> HAL_FLASH_Unlock()
PC 发送固件二进制数据
-> HAL_UARTEx_RxEventCallback 收到一段写一段
-> 按 8 字节 doubleword 写 Flash
-> 如果 Size < 256,认为是最后一包并停止写入
JUMP
-> 检查 APP MSP
-> 清外设/中断/SysTick
-> SCB->VTOR = APP_JUMP_ADDR
-> __set_MSP(app_msp)
-> __enable_irq()
-> 跳转 APP Reset_Handler特点:
- 协议简单,适合最小验证。
- 不需要提前声明总大小。
- 最后一包依赖
Size < 256判断结束。 - 风险是串口 IDLE 分包不一定等于 PC 端发送包边界,后续需要加帧头、长度、序号、CRC 或使用 ringbuffer 方案。
已有写入方式二:STM32 WRITE:<size> + 256 字节包
位置:raw/projects/stm32-arm-bootloader/MyApp/bootloader_save.c
流程:
WRITE:<firmware_size>
-> 记录 g_bl_total_size
-> g_bl_written_size = 0
-> g_bl_write_addr = 0x08010000
-> 进入 BL_WRITE_READY / BL_WRITE_WRITING
PC 按 256 字节发送 bin
-> UART DMA 收到数据后写入 ringbuffer
-> bootloader_proc 周期检查 ringbuffer.itemCount
-> 满 256 字节就读取 256 字节
-> bootloader_write_256bytes(data)
-> 内部按 64 字节块 / 8 字节 doubleword 写 Flash
-> 不足 8 字节尾包补 0xFF 对齐
写入达到总大小
-> HAL_FLASH_Lock()
-> BL_WRITE_DONE特点:
- 这是当前更适合扩展的写入框架。
WRITE:<size>明确总长度,避免用串口分包大小判断结束。- 256 字节是传输层包长,Flash 实际仍按 8 字节 doubleword 写入。
- 后续可以在 256 字节包上增加
seq、crc16/crc32、ACK/NACK 和重传。
已有写入方式三:GD APP 写下载区,复位交给 BL 升级
位置:raw/projects/gd32-bootloader-gd-app/APP/Bootloader/bootloader.c
函数:bootloader_write_and_reset(const uint8_t *bin, uint32_t bin_len, uint32_t app_crc32, uint32_t app_version)
流程:
APP 收到或准备好新 bin
-> 读取 BootParam
-> 计算当前 APP CRC,保存为备份 CRC
-> 擦除下载区 0x08073000
-> 将 bin 写入下载区
-> 设置 updateFlag = 0x5A
-> 设置 updateStatus = 0x01
-> 写入 appSize / appCRC32 / appVersion
-> 保存 BootParam
-> 软件复位
Bootloader 启动
-> 读取参数区
-> 检查更新标志
-> 校验下载区
-> 备份当前 APP
-> 将下载区搬运到 APP 主区
-> 跳转 APP 或失败回滚特点:
- APP 负责“接收新固件”和“写下载区”。
- Bootloader 负责“最终替换 APP”和“回滚保护”。
- 这比直接在 Bootloader 中边收边写 APP 更安全,适合 OTA。
- 当前
updateMode = 0x01,可以后续扩展为 UART、SD、CAN、BLE/WiFi、外部 Flash 等模式。
GD Bootloader 关键机制
位置:raw/projects/gd32-bootloader-gd-bl/APP/Bootloader/bootloader.c
核心点:
- 启动时读取
BOOT_CONFIG_ADDR = 0x0800C000的参数区。 BootParam和BootParam_Reserved记录当前 APP 与备份 APP 状态。- APP 版本不一致时执行
Backup_App()。 Backup_App()对 APP 主区计算 CRC32,再擦除备份区并复制 APP。- 跳转前检查 APP 栈地址和入口地址,再设置
SCB->VTOR、__set_MSP()并调用 Reset_Handler。 - 启动失败时累加
bootFailCount,可以作为后续回滚条件。
写入方式扩展清单
后续所有写入方式最终都应收敛到同一个内部接口:
source -> receive buffer -> packet verify -> write download area -> verify full image -> set BootParam -> reset -> BL upgrade建议优先级:
| 方式 | 适用场景 | 建议落点 | 状态 |
|---|---|---|---|
| UART 256B 包 | 有线调试、最小闭环 | STM32 bootloader_save.c,GD 可复用协议 | 已有雏形 |
| UART 帧协议 | 可靠串口升级 | 在 256B 包外加帧头、长度、序号、CRC、ACK/NACK | 待补充 |
| YMODEM/XMODEM | 通用串口工具传 bin | Bootloader 或 APP 接收端 | 待补充 |
| SD 卡 / FatFS | 现场插卡升级 | GD 工程已有 FatFS/SD 组件,可从 /firmware/app.bin 读取 | 待补充 |
| 外部 SPI Flash | 大固件缓存、断点续传 | APP 先写外部 Flash,BL 再搬运到内部 Flash | 待补充 |
| CAN 升级 | 多节点车控/工业设备 | 按节点 ID 分发,包长适配 CAN/CAN-FD | 待补充 |
| BLE/WiFi OTA | 无线升级 | APP 下载 bin 到下载区或外部 Flash,BL 只做校验与替换 | 待补充 |
| 双 APP A/B | 高可靠升级 | 固定 A/B 两个应用槽,BootParam 记录 active slot | 待补充 |
统一 256 字节包建议
当前 STM32 已经用 256 字节包作为写入单元。建议后续统一成下面的逻辑,而不是直接裸发 256 字节:
PacketHeader
magic 2B 固定头,例如 0xA55A
version 1B 协议版本
type 1B DATA / START / END / ABORT
seq 4B 包序号
offset 4B 写入偏移
length 2B 数据长度,最大 256
crc16 2B 当前包校验
payload 0~256B接收端策略:
START:声明固件总大小、整包 CRC32、目标地址、版本号。DATA:按seq/offset写入下载区,不直接覆盖正在运行的 APP 主区。END:计算整包 CRC32,通过后设置BootParam.updateFlag。ABORT:退出写入状态,擦除未完成下载区或标记无效。- 任意包错误:返回 NACK,PC 端重发当前包。
OTA 扩展方向
OTA 不建议让 Bootloader 直接处理复杂网络协议。推荐分层:
APP 网络层
-> 下载 app.bin
-> 校验长度和 CRC
-> 写入下载区或外部 Flash
-> 写 BootParam(updateFlag/updateMode/appSize/appCRC32/appVersion)
-> 软件复位
Bootloader
-> 只做可信的 Flash 搬运、CRC 校验、备份和回滚原因:
- Bootloader 越小越安全,网络协议、TLS、断点续传放在 APP 更容易维护。
- Bootloader 不依赖复杂外设栈,减少升级失败概率。
- APP 可以支持多种来源,Bootloader 只关心“下载区是否有一个完整可信的 bin”。
待补充任务
- 给 STM32 256B 写入协议补
START/DATA/END/ACK/NACK帧格式。 - 给 GD 下载区写入补一页“APP 下载 bin 后如何设置 BootParam”。
- 补充 SD 卡读取
/firmware/app.bin写下载区流程。 - 补充 OTA 方案:APP 下载、断点续传、整包 CRC、失败回滚。
- 统一 BootParam 字段说明:
updateFlag、updateMode、updateStatus、appCRC32、backupCRC32。 - 给跳转流程补检查清单:MSP、Reset_Handler、VTOR、NVIC、SysTick、DMA、
__enable_irq()。