基于 STM32F407ZG、LAN8720A、FreeRTOS、LwIP 和 Qt 6 实现的嵌入式固件升级系统,支持 TCP/IP 网络升级 与 UART 恢复升级。
系统采用 Bootloader + Run APP + A/B 候选镜像槽 + 双 Flag 副本 架构。Run APP 通过 TCP 接收新固件并写入非活动候选槽;Bootloader 负责安装、CRC 校验、试运行确认、失败回滚和 APP 跳转。UART 通道由 Bootloader 独立提供,在 APP 无法正常运行时仍可恢复设备。
PC 端提供 Qt 图形化 IAP 上位机,支持固件选择、版本管理、UART/TCP 连接、分包发送、ACK/NACK、超时重传、进度显示及升级后重连。
Qt IAP 主界面,当前设备未连接、尚未选择固件。
当前状态:第一版核心功能已完成,已完成 TCP/UART 升级、自动安装、Trial 确认、失败回滚及传输中断后重新升级等流程验证,详见测试与验证。
项目覆盖从 PC 发送 BIN、设备接收与安装,到新版本试运行和异常恢复的完整升级流程。网络用于正常维护,独立串口入口用于恢复;新固件只有通过试运行确认,才会成为下一次回滚所依赖的可靠版本。
| 平台 | 配置 |
|---|---|
| MCU / 网络硬件 | STM32F407ZG,1 MB 内部 Flash;LAN8720A,RMII |
| 固件 | STM32 HAL、FreeRTOS、LwIP Netconn API |
| 嵌入式工具链 | Keil MDK、Arm Compiler 6(AC6) |
| PC 工具 | Qt 6.5、C++、CMake;Widgets / SerialPort / Network |
- 双通道升级:TCP 网络主通道 + Bootloader UART 恢复通道。
- A/B 候选镜像槽:运行区与候选区物理隔离,自动选择非活动槽。
- Bootloader 安装与回滚:Trial Confirm 确认新版本,失败恢复可靠旧版本。
- 可靠状态管理:双 Flag 副本 + Sequence + CRC,启动时选择有效的新副本。
- 协议可靠性:ACK/NACK + 超时重传 + 重复 DATA/END 幂等应答。
- TCP 字节流鲁棒性:处理拆包、粘包、半包、连续多帧和异常前导字节。
- Flash 安全写入:写入失败不推进序号与长度,尾部缓存于 END 阶段刷新。
- Qt 升级流程自动化:发送、复位等待、自动重连及稳定在线观察。
flowchart LR
PC[PC / Qt IAP] -->|TCP| APP[Run APP / FreeRTOS + LwIP]
PC -->|UART 恢复| BOOT[Bootloader]
APP -->|接收与校验| SLOT[A/B 候选镜像槽]
BOOT -->|接收与校验| SLOT
SLOT -->|安装或回滚| RUN[Run 区]
BOOT -->|校验并跳转| RUN
APP <-->|升级状态| FLAG[双 Flag 副本]
BOOT <-->|安装状态| FLAG
| 模块 | 职责 |
|---|---|
| Qt IAP | 组帧、发送、应答匹配、重传、进度与重连管理 |
| Run APP | TCP 接收、非活动槽写入、Pending 提交、Trial 确认 |
| Bootloader | 启动决策、安装、回滚、跳转和 UART 恢复 |
| Firmware Update Core | 共享协议、Flash、缓存、Flag 与 CRC 源码,分别编译进 APP 和 Bootloader |
flowchart LR
B[Bootloader<br/>64 KB] --> F0[Flag Copy 0<br/>64 KB]
F0 --> R[Run APP<br/>256 KB]
R --> A[Slot A<br/>256 KB]
A --> S[Slot B<br/>256 KB]
S --> F1[Flag Copy 1<br/>128 KB]
地址递增顺序如上,图形宽度不代表容量比例。
| 区域 | 地址范围 | 容量 | 用途 |
|---|---|---|---|
| Bootloader | 0x08000000 ~ 0x0800FFFF |
64 KB | 启动、安装、回滚、UART 恢复 |
| Flag Copy 0 | 0x08010000 ~ 0x0801FFFF |
64 KB | 升级状态副本 0 |
| Run APP | 0x08020000 ~ 0x0805FFFF |
256 KB | 当前实际执行的 APP |
| Slot A | 0x08060000 ~ 0x0809FFFF |
256 KB | 候选或可靠备份镜像 A |
| Slot B | 0x080A0000 ~ 0x080DFFFF |
256 KB | 候选或可靠备份镜像 B |
| Flag Copy 1 | 0x080E0000 ~ 0x080FFFFF |
128 KB | 升级状态副本 1 |
APP 固定链接到 Run 区: Link Address = 0x08020000,IROM Size = 0x00040000,VTOR Offset = 0x00020000。
A/B 是镜像存储槽位。APP 始终从 Run 区执行,不需要为两个槽分别维护两套业务源码。
Qt → TCP / LwIP → Run APP → 非活动槽 → 整包 CRC → Pending
→ END ACK → 复位 → Bootloader 安装 → Trial
START 选择目标槽并擦除,DATA 按序写入,END 刷新缓存、校验镜像并提交 Pending。Run APP 不擦写正在执行的 Run 区。
Qt → USB-UART → Bootloader → 候选槽 → 校验 / Pending → 安装与启动
UART 入口不依赖 FreeRTOS、LwIP 或 Run APP,适用于 APP 损坏或网络不可用时的恢复。
SOF | CMD | TOTAL_LEN | DATA | RESERVE | CRC16
| 命令 | 作用 |
|---|---|
| START | 声明镜像大小、CRC、版本;设备选择目标槽 |
| DATA | 传输带包序号的固件数据,每包最多 256 字节 |
| END | 核对包数与长度、校验镜像、提交 Pending |
| ACK | 返回成功结果和对应附加值 |
| NACK | 返回错误码及状态、期望序号等信息 |
详细字节布局、命令码、ACK/NACK 返回值、错误码及重传规则见 doc/protocol.md。
| 状态机 | 主流程 |
|---|---|
| APP 升级会话 | IDLE → RECEIVING → READY;致命处理错误进入 ERROR |
| Bootloader 安装 | IDLE → PENDING → INSTALLING → TRIAL_READY → TRIAL_RUNNING;随后确认或回滚 |
新会话从 IDLE 开始;ACK 丢失时允许满足条件的重复 START、DATA、END 重应答。TCP 接收中断后,APP 释放会话,下一次从 START 重新升级。
安装、试运行和回滚状态写入 Flag。安装或回滚中复位时,Bootloader 根据持久化状态重新执行相应操作。
镜像接收完成后先试运行,通过确认再成为可靠版本。
active_slot:最后一次通过确认的可靠镜像,回滚时从这里恢复。pending_slot:待安装或正在试运行的候选镜像。
flowchart TD
INSTALL[候选镜像安装到 Run 区] --> REQUEST[Bootloader 写入 TRIAL_REQUEST]
REQUEST --> TRIAL[启动 APP / 约 3 秒试运行]
TRIAL -->|APP 写入 CONFIRMED 并复位| CHECK[Bootloader 校验确认口令与 Run 镜像]
CHECK -->|通过| COMMIT[pending_slot 成为 active_slot]
TRIAL -->|未确认便复位| OLD{存在可靠 active_slot?}
OLD -->|是| ROLLBACK[旧镜像恢复到 Run 区]
OLD -->|否 / 首次安装| RECOVERY[放弃候选 / 留在 Bootloader 等待恢复]
首次安装没有可靠旧镜像时,不会把失败候选当成回滚源。当前 Trial 以运行时间和 IWDG 为基础,尚未覆盖网络、外设和所有业务任务的联合健康确认。
| 风险 | 对应措施 |
|---|---|
| 状态写入中断 | Flag 双副本、结构 CRC、Sequence 选取 |
| 残缺镜像被误用 | 擦除候选槽前先使旧镜像信息失效 |
| 数据写入失败 | 写入/缓存处理成功才推进计数;END 刷新尾部缓存并校验整包 |
| ACK 丢失与重复发送 | 原帧重传,重复 DATA/END 不重复写入或提交 |
| 不合适的固件版本 | 拒绝版本 0 和低版本,允许同版本重装 |
| 新 APP 无法确认 | 保留可靠旧镜像,由 Bootloader 回滚 |
| Flash 耗时触发复位 | 擦写前后喂狗,START 超时与看门狗配置需覆盖擦除耗时 |
CRC 用于检测数据损坏;固件真实性验证仍列在后续优化中。
采用 Netconn API,STM32 作为 TCP Server。逐字节接收器按帧头和长度组帧;断线后清理连接与未完成会话,当前不支持断点续传。
| 配置 | 当前值 |
|---|---|
| PC | 192.168.10.1/24 |
| STM32 / TCP Server | 192.168.10.2:5000 |
| 并发范围 | 单客户端、单升级会话 |
Qt 提供串口扫描与 TCP 连接、BIN 大小/CRC 显示、四段式版本输入、自动分包、进度与错误日志。每帧等待应答,超时后重发原帧。
TCP 升级结束后等待设备复位,每秒尝试重连,最多 30 次;连接后持续观察 5 秒,再显示稳定在线。传输中断则清空进度,提示重新开始。
稳定在线观察与 Bootloader 的 Trial 确认是不同机制。当前 Qt 尚未读取设备实际运行版本和回滚结果进行最终核验。
以下为第一版已完成的流程验证汇总;运行截图与原始串口日志尚未归档。
| 验证场景 | 已记录结果 |
|---|---|
| 网络与协议接入 | LAN8720A/RMII/LwIP Ping、TCP 双向通信、START/DATA/END 应答 |
| TCP 字节流 | 拆包、粘包、半包、连续多帧和异常前导字节处理 |
| 完整 TCP 升级 | BIN 接收、Flash 写入、整包 CRC、非活动槽选择和 Pending 提交 |
| 安装与 Trial | 候选镜像安装、试运行确认、复位启动 |
| Trial 失败 | 恢复最后可靠版本 |
| 中途断线 | 保留可靠镜像,APP 释放会话,Qt 清空进度,可重新升级 |
| Qt 自动重连 | 设备重新上线及稳定连接观察 |
| UART 恢复 | 独立接收、校验、安装与启动流程 |
验证文档列出各场景的观察点及 Qt 主界面、TCP 完成、Trial/Rollback、UART 恢复四组证据的归档要求。精确掉电注入与长时间压力测试仍属后续工作。
bootloder_02/
├─ Boot_Loader/ # 启动、UART 恢复、安装与回滚
├─ A-APP-1/ # 主 Run APP,含 FreeRTOS/LwIP/TCP IAP
├─ B-APP-1/ # 不同版本构建与升级验证
├─ Common/FirmwareUpdate/ # boot_protocol / flash / cache / flag / crc
├─ QT_IAP/ # Qt 6 上位机
└─ doc/ # 协议、验证说明、架构图与设计流程
编译:
- Keil MDK + AC6 打开 Boot_Loader.uvprojx,Bootloader 地址保持
0x08000000。 - 打开 A-APP-1.uvprojx,按 Flash 规划设置 Run 地址/容量/VTOR,保留 IWDG 和
App_TrialInit(),生成 BIN。 - Qt Creator 打开 CMakeLists.txt,使用 Qt 6.5 或兼容工具链及 Widgets、SerialPort、Network 模块,构建
PC_IAP。
TCP 使用: PC 与设备同网段,确认 Ping 与端口连接;选择按 Run 地址链接的 BIN,填写版本并升级;等待设备复位和 Qt 重连观察。
UART 使用: 连接 Bootloader 升级串口,在 Qt 选择串口与波特率;复位设备并在启动等待窗口内发起升级。当前等待窗口约 3 秒;无法准备有效 Run 镜像时会留在 Bootloader 等待恢复。
遇到 L6050U 代码量授权限制时,先核对 Build Output 首行的实际 AC6 版本,确认没有误用旧工具链。
- 当前边界:单客户端;传输中断从 START 重来;Trial 以运行时间和看门狗为主。
- 结果核验:增加设备版本、Trial/回滚结果查询,完善 Qt 最终确认。
- 验证补充:归档运行截图和日志,补 Flag 写入瞬间掉电、循环升级与网络抖动测试。
- 后续能力:多任务健康确认、断点续传、固件签名、设备认证和传输保护。
- Bootloader 每次复位先运行,通常只需初次烧写;安装与回滚由它统一决策。
- APP 接收镜像到非活动槽,Bootloader 再安装到固定 Run 区。
- A/B 表示镜像槽位,公共升级源码分别编译进 APP 和 Bootloader。
- 关键状态先持久化,再执行可能被掉电打断的 Flash 操作。
