Files
GateWay/.trae/documents/cat1-remote-upgrade-plan.md
T
YuanHongbin f206b8f7dd feat(ota): Cat1远程升级联调进度(App侧收发链路打通, 待整包实测)
- Update.c/h: NetUpgrade模块(0x7D流式组帧/0x81会话/断点续传/CRC32/TX队列)
- spiflash.c/h: 固件暂存区(扇区4038~4095)+进度区(4037), 日志回绕点前移
- CatOneTask.c: +LIPURC异步分发(循环解析同批多URC); 释放CatOneRev_Sem保REV_DATA快速返回; 升级应答帧发完即回WAIT_SEND
- CatOneTask.c: USART1专用Cat1(上电恒连), 调试日志走RS485 Ch1, CAT1日志常开
- Public.c/h: AsciiToHex长度参数uint8->uint16(修432字符帧截断)
- bsp.c/h, main.c, board.c, DebugCmd.c: APP_UPDATE_SPI_FLAG及配套适配
- 联调修复: sscanf锚点/窗口差值判断/AsciiToHex截断/URC被AT状态机吞帧/信号量缺失
2026-09-24 17:14:02 +08:00

86 lines
7.1 KiB
Markdown
Raw 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.
# Cat1 远程升级方案设计(待评审)
## 总体架构
App 扮演"接收端"角色(与 Boot 在 RS485 升级中的角色相同),服务器扮演升级主机:
```
服务器(升级主机) --TCP--> 合宙Cat1模组 --USART1--> App(RT-Thread)
│ ├─ 收0x7D帧(0x81/0x82) → 固件分包写外部SPI Flash暂存区
│←────────── 0x01 ACK(带进度) ──────────────┤
│ └─ 收完→CRC32校验→置UpdateFlag=APP_UPDATE_SPI_FLAG→复位
复位
└→ Boot: 检测APP_UPDATE_SPI_FLAG
→ SPI Flash逐扇区读 → 写内部B区 → CRC32(B)
→ 搬运B→A → CRC32(A) → 擦SPI Flash暂存区 → 置完成标志 → 复位跳App
```
**升级协议完全复用 RS485 的 0x7D 帧**:帧头 0x7D + DevMac(6) + Cmd(1) + PayloadLen(1) + Payload + CRC16(Modbus),命令 0x81 升级请求{PackageNum, AppSize, AppCrc32} / 0x82 数据{PackageIndex, PackageNum, DataLen, Data[200]} / 0x01 ACK / 0x02 请求数据。服务器端协议不变。
## 关键决策与理由
1. **Boot 不驱动 Cat1**:AT 注网 30 秒起、Boot 仅 32KB 分区(当前 Code 16KB)、模组 TCP 连接态在 MCU 复位后不可控。收包/重试/续传等复杂逻辑全部放 App(RTOS 资源丰富),Boot 只做"从外部 Flash 搬运到 B 区"这一简单确定性动作。
2. **固件暂存外部 SPI Flash**:16MB 尾部划 58×4KB=232KB 暂存区(扇区 4038~4095),日志环形写入回绕点同步从 16MB 末尾前移到扇区 4038。
3. **复用 UpdateFlag 机制**:新增标志值 `APP_UPDATE_SPI_FLAG`(建议 0xA5A55A5A,与现有 START=0x55AA5A5A / UPDATE=0xA5A5AA55 / BOOT_UPGRADE=0x5A5A55A5 不冲突),`boot_para_t` 结构不变,RS485 本地升级路径完全不受影响。
4. **依赖 TCP 可靠有序**:应用层丢包仅发生在断连/模组重启,因此用"断点续传 + 超时重发请求"而非滑动窗选择重传,状态机与 Boot 版本同构,服务器实现简单。
## 升级流程(含 Cat1 特化处理)
1. 服务器 → `0x81{PackageNum, AppSize, AppCrc32}`
2. App 校验 DevMac/合法性(AppSize==0 或 >232KB 拒绝)→ 回 `0x01 ACK`:
- 载荷建议扩展为 `{Status, ResumedIdx}`:`0x20`=全新开始(App 擦暂存区)、`0x21`=断点续传(已收 ResumedIdx 包,服务器从该包续发)。**注意:原 0x01 载荷 1 字节(0x20),扩展为 3 字节需服务器端同步适配(新版本服务器)**
3. 服务器从 ResumedIdx 起发 `0x82{PackageIndex, ...}`:
- **流控(推荐窗口式)**:服务器连发 W 包(建议 W=8~16)→ App 校验 PackageIdx 顺序写入暂存区(乱序/重复包丢弃)→ App 主动发 `0x02{期望包号}` → 服务器继续。停等式(每包一问一答)在 Cat1 上太慢:1160 包 × RTT≈2~5 分钟+,窗口式约 30~60 秒
- TCP 保证有序,App 端只需顺序校验 + 期望包号判定
4. App 收完 → `CRC32(暂存区, AppSize)`:
- 通过 → 回 `0x01{0x30 完成}` → 写 boot_para_t{AppSize, Crc32Check, UpdateFlag=APP_UPDATE_SPI_FLAG} → 200ms → 复位
- 失败 → 回 `0x01{0x31 CRC错}` → 不复位,清暂存区等待服务器重发(可多次)
5. Boot 检测 `APP_UPDATE_SPI_FLAG` → 逐 8KB:读外部 2×4KB 扇区 → 擦写内部 B 区 1×8KB 扇区 → `CRC32(B)` → `CopyAppBtoA()`(现有)→ `CRC32(A)` → **擦外部暂存区(防残留误判,同 RS485 路径 B 区擦除教训)** → 置 `APP_BOOT_UPGRADE_FLAG` → 复位跳 App
6. App 启动(`APP_BOOT_UPGRADE_FLAG`)→ 擦外部参数分区 → 上线后服务器可查版本确认升级成功
### 超时与重试参数(Cat1 特化,均为 App 侧状态机)
| 参数 | Boot RS485 值 | Cat1 版建议 | 理由 |
|------|--------------|------------|------|
| 等包超时 | 500ms | **3 秒** | Cat1 RTT + 模组缓冲 |
| 重试次数 | 3 | **5** | 偶发断连需模组重连 |
| 断连恢复 | — | 模组重连(现有逻辑)后服务器重发 0x81,ACK 带进度续传 | TCP 断开后应用层无从补偿 |
| 整体超时 | — | 30 分钟无进展 → 放弃升级(清标志,不复位) | 防止无限等待 |
### 断电自愈矩阵(Cat1 路径)
| 断电/异常时机 | 恢复行为 |
|--------------|---------|
| App 收包中断连/断电 | 暂存区+进度保留 → 重连后 0x81 续传 |
| App 置标志后、复位前 | Boot 正常走 SPI Flash→B→A |
| Boot 搬运中(写B区)断电 | UpdateFlag 仍为 SPI_FLAG → B 区 CRC 有效则重搬;无效则清标志回老 App |
| 搬运完、擦暂存区中断电 | 参数未写 → B CRC 判定重搬(B 仍在);参数已写 → 跳新 App |
## 改动清单
### App(GateWay 工程)
| 文件 | 改动 |
|------|------|
| `spiflash.h/c` | 新增 `FLASH_FW_STASH_BASE`(扇区 4038 起)+ StashErase/StashWrite/StashRead;`AddLog` 回绕点 16MB→扇区 4038;新增 CRC32(与 Boot GetCrc32 同算法 0xEDB88320) |
| `CatOneTask.c`(或新文件) | Cat1 通道增加 0x7D 帧组帧解析(`+LIPURC` hex→二进制后按 0x7D 组帧,与现有 0x7A 链路并存,按帧头区分);升级接收状态机(移植 Boot AnalyzeData+BL 状态机,超时/重试参数按上表) |
| `Update.c` | 新增 `UpdateFromStash()`:写 boot_para_t(APP_UPDATE_SPI_FLAG + AppSize + Crc32Check)→ 复位 |
| `bsp.h`(App) | `APP_UPDATE_SPI_FLAG` 宏;`boot_para_t` 不动 |
| `main.h`(App) | 版本号随迭代递增 |
### Boot(HC32F460BootLoader 工程)
| 文件 | 改动 |
|------|------|
| `bsp.c/h` | SPI Flash 驱动:与 App 相同总线(需确认外部 Flash 挂载的 SPI 外设与引脚,Boot 复用),W25Q 写使能/扇区擦/页读命令;约 +2~3KB(Boot 当前 16KB,32KB 分区内余量足够) |
| `main.c/h` | `APP_UPDATE_SPI_FLAG` 宏 + main() 决策分支(SPI_FLAG → 新状态 `BL_STASH_TO_B`);`BL_STASH_TO_B`:逐 8KB 外部读→B 区写→CRC(B) 通过进 `BL_COPY_TO_A`(现有),失败清标志回老 App;`BL_COPY_TO_A` 成功分支追加"擦外部暂存区" |
### 服务器端(不在本仓库,需同步开发)
- 实现 0x81/0x82 升级主机逻辑:窗口流控(W 包一批)、解析 0x02 期望包号、断点续传(ACK 的 ResumedIdx)、完成/失败状态处理
## 风险与待确认项
1. **调试口与 Cat1 共用 USART1(PB08 方向切换)**:升级期间必须保证 UART 方向锁定在 Cat1 侧(`CAT1_ON()`),升级状态机运行期间禁止调试打印切向(`U_DBG_LOG` 若触发 `DBG_ON()` 会破坏 TCP 数据)。实施时需审查 DEBUG=1 下所有升级路径的打印。
2. **外部 Flash 挂载总线待确认**:读 spiflash.c 确认 SPI 外设号/引脚/QSPI 模式,Boot 驱动按此实现。
3. **日志区历史数据**:回绕点前移到扇区 4038(约 15.77MB),已存历史日志超出的部分会被覆盖——现网日志增长慢,实际影响小,但需知晓。
4. **0x01 ACK 载荷扩展**(1→3 字节)需服务器端配合,若服务器不改动则退化为停等式流控(慢但兼容)。
5. **升级流量**:232KB/次,Cat1 资费可忽略。