Files
GateWay/.trae/documents/boot-ab-upgrade-plan.md
T

6.8 KiB
Raw Blame History

Boot A/B 分区升级改造计划

背景

当前 Boot 升级流程:收到 0x81 升级请求后复位 → 先整片擦除 App 区(0x8000~0x7C000, 58扇区) → 逐包(200字节)边收边写 → 收完才做 CRC32 整包校验 → 失败/超时仅清标志,但 App 区已被擦除,设备变砖只剩 Boot。

改造目标:App 区分 A/B 两区。新固件先下载到 B 区暂存,整包 CRC 校验成功后才搬运到 A 区;任何失败(CRC 错/下载超时/断电)都能回到 A 区原有 App,彻底消除变砖风险。RS485 协议(0x81/0x82/0x01/0x02 帧)不变,主机升级工具无需修改。

新 Flash 分区(512KB = 64 扇区 × 8KB)

分区 地址范围 扇区数 说明
Boot 0x00000~0x07FFF 4 (32KB) 不变,MAC 常量 0x4000 不受影响
A区(运行) 0x08000~0x41FFF 29 (232KB) App 链接基址仍为 0x8000
B区(暂存) 0x42000~0x7BFFF 29 (232KB) 升级包下载区
参数区 0x7C000~0x7DFFF 1 不变,boot_para_t 结构完全不变
备用 0x7E000~0x7FFFF 1 不变

App 当前实际 113KB,A/B 各 232KB 有约一倍余量。断电状态全部由 UpdateFlag + AppSize + Crc32Check + B区CRC实测 推导,无需新增参数字段。

新升级状态机

main() 决策(不变): AppFlag==START/BOOT_UPGRADE→跳A | UpdateFlag==UPDATE→BL_ERASE_FLASH | 其他→BL_IDLE

BL_ERASE_FLASH(改):
  AppSize非法(==0 或 >232KB) → AppFlag=START_FLAG/UpdateFlag=0 → BL_RESET(回老App)
  GetCrc32(B区, AppSize)==Crc32Check ?  ──是(断电续传:已下载完/搬运中断电)──→ BL_COPY_TO_A
                                       └─否→ 仅擦B区29扇区 → BL_GET_DATA(逐包写B区) → 收完 ↓

0x82 收完全部包(改):
  CRC32(B区,AppSize)==Crc32Check ?
    ├─是→ UpDateFlag=false → BL_COPY_TO_A
    └─否→ AppFlag=APP_START_FLAG / UpdateFlag=0 → 写参数 → BL_RESET (回老App, 原来是BL_IDLE变砖)

BL_COPY_TO_A(新增):
  按 ceil(AppSize/8K) 扇区数逐扇区: FeedDog → 擦A扇区 → 从B读8KB到RAM缓冲 → FlashWrite写A
  → CRC32(A区,AppSize)==Crc32Check ?
    ├─是→ AppFlag=APP_BOOT_UPGRADE_FLAG / UpdateFlag=0 → 写参数 → BL_RESET → 跳A
    └─否→ CopyRetryCnt<1 重试搬运; 仍失败→ AppFlag=0/UpdateFlag=0 → BL_IDLE

BL_GET_DATA_TIMEOUT(改): 重试3次耗尽 → AppFlag=APP_START_FLAG / UpdateFlag=0 → BL_RESET(回老App, 原来是BL_IDLE)

断电自愈覆盖:①下载中断电→重启 B 校验失败→重新下载;②搬运中断电→A 残缺但 B 仍有效→重启自动重搬;③擦 A 前提是 AppFlag=0 且 Boot 不会在 UpdateFlag 置位时跳转,无跳半擦区风险;④0x81 写参数中断电→参数区 0xFF→走 FirstRunFlag 分支跳 A,老 App 完好。

Boot 工程改动(d:\keilProject\hc32f460\git\hc32f460bootloader)

bsp.h(分区宏,L194-206):

  • APP_A_ADDRESS = FLASH_BASE + BOOT_SIZE (0x8000)、APP_BANK_SECTORS 29u、APP_BANK_SIZE = 29*8K (232KB)、APP_B_ADDRESS = APP_A_ADDRESS + APP_BANK_SIZE (0x42000)、APP_ADDRESS 保留别名指向 A 区、BOOT_PARA_ADDRESS = APP_B_ADDRESS + APP_BANK_SIZE (0x7C000 不变)

main.h:

  • 枚举加 BL_COPY_TO_A;BootLoader_t 加 uint32_t CopyRetryCnt

bsp.c:

  • FlashWrite/FlashRead 长度参数 uint8_t → int(8KB 整扇区 2048 字超出 uint8_t,评审发现 P2)

  • FlashWrite 尾部不足 4 字节时按 0xFF 补齐组字写入(防 AppSize 非 4 对齐时丢尾字节导致 CRC 永败)

  • EraseFlash() 参数化为擦指定起始地址+扇区数(现仅用于擦 B 区 29 扇区,不再碰 A 区)

main.c:

  • GetCrc32(int dwLen) → GetCrc32(uint32_t BaseAddr, int dwLen)(现有 L50 固定读 APP_ADDRESS)

  • AppWriteFlash 加基址参数,下载包写入 APP_B_ADDRESS + offset

  • AnalyzeData:

    • 0x81 分支:加 AppSize 合法性拒绝(>232KB 或 0 时忽略)

    • 0x82 分支:加状态守卫 Status 必须为 BL_GET_DATA/BL_WAIT_REV(评审发现 P1:防迟到包破坏搬运期 B 区);收完 CRC 改算 B 区,成功→BL_COPY_TO_A,失败→恢复 START_FLAG→BL_RESET

  • BootLoaderLoopHandler:BL_ERASE_FLASH 改为先校验 B 区(断电续传入口);新增 BL_COPY_TO_A case(调 CopyAppBtoA);BL_GET_DATA_TIMEOUT 耗尽改恢复 START_FLAG+BL_RESET

  • 新增 CopyAppBtoA():main.c 内实现,static uint32_t s_CopyBuf[2048](8KB),逐扇区 EFM_SectorErase(A)+读 B 缓冲+FlashWrite(A),每扇区 FeedDog,结尾 CRC32(A) 校验返回 bool

App 工程改动(d:\keilProject\HC32F460\Git\GateWay)

Project/GateWay/source/User/Inc/bsp.h(L19-29,评审 P0 关键):

  • FLASH_APP_SIZE 58→29 扇区后 FLASH_BINFO_BASE 会错算成 0x42000,必须改为: FLASH_APP_A_SIZE(29*8K) + FLASH_APPB_BASE = FLASH_APP_BASE + FLASH_APP_A_SIZE(0x42000) + FLASH_APPB_SIZE(29*8K) + FLASH_BINFO_BASE = FLASH_APPB_BASE + FLASH_APPB_SIZE(仍=0x7C000)、FLASH_CFG_BASE 连带仍=0x7E000

MDK/GateWay_app.sct:LR_IROM1/ER_IROM1 尺寸 0x74000 → 0x3A000(两个 target 共用此 sct,sct 优先于 uvprojx IROM)

MDK/GateWay.uvprojx:app_boot target(L1168) IROM Size 0x6c000→0x3a000(一致性,app_dubeg 的 0x0/0x80000 本就与 sct 不一致,不动)

source/User/Src/Update.c Update():加 AppSize==0 || AppSize>FLASH_APPB_SIZE 时报错返回,不置标志不复位

其余 App 代码(VTOR 动态基址、UpdateInit、升级异常检测、JumpToApp)均无需改动。

实施注意事项

  1. 编码:所有源文件含中文注释,一律 UTF-8。用 .NET 脚本(UTF8Encoding(false))做替换修改(本会话已验证 Edit 工具会把 GBK 污染写入),改后校验 git diff 行数与编码
  2. boot_para_t 结构、BOOT_PARA_ADDRESS、协议帧均不变 → 新旧 Boot/App 交叉兼容
  3. 编译验证:Boot(Keil MDK)+ App(app_boot target)分别编译通过,App 镜像 113KB < 232KB

验证清单

  1. 正常升级全流程:新固件运行、外部参数分区按 APP_BOOT_UPGRADE_FLAG 清理、传感器重新注册
  2. 下载中断电 → 重启回老 App → 可再次升级
  3. 下载超时(主机停发)→ 3 次重试后自动回老 App
  4. CRC 注入失败 → 回老 App
  5. 搬运中断电 → 重启自愈重搬 → 升级成功
  6. B 区残留旧镜像 + 新 0x81(不同 AppSize/CRC)→ 不误判、重新下载
  7. COPY 阶段发 0x81/0x82 → 均被拒绝,搬运不被干扰
  8. AppSize=0 / >232KB → 拒绝