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

113 lines
6.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 → 拒绝