ota差分升级流程
·
差分升级全流程说明:通路验证 → OTA 收包 → 写分区/解包
差分升级 = 设备端把「当前运行的 old 镜像 + 收到的差分包」合并出 new 镜像写入备份槽。
整条链路分三个阶段,下面按数据流顺序描述。
一、验证通路:蓝牙 / 天眼 / 总线
三条通道互不干扰,各自"硬件收字节 → 进队列 → 帧解析 → 进协议栈"。
1.1 蓝牙(USART1,PA9/PA10,DMA)
手机 App ──蓝牙──> BLE 模块 ──UART──> USART1 (DMA 接收)
│
boot_ble.c: Bsp_Usart_Init() 初始化 USART1+DMA
▼
usart2_queue(环形缓冲,boot_ble.c)
▼
ota_updata.c: ble_receive_frame_decode() 帧解析
· 帧头/命令/长度/校验和校验
· 控制帧(20B):开始升级/包长度/CRC 指令
· 数据包:bt_serial 序号 + bt_length + 数据
▼
update_ble() 蓝牙升级状态机(见第二节)
1.2 天眼(CMC,UART4,PA0/PA1,中断)
天眼/云端 ──> 主控转发 ──UART──> UART4
│
boot_cmc.c: comm_CMC_Init() 初始化 UART4
▼
QUEUE_RX_BUF_CMC(boot_cmc.c 队列缓冲)
▼
boot_cmc.c: CMCAnalysisData_Boot() 帧解析(返回收到字节数)
▼
FRAME_RX_BUF_CMC(完整帧缓冲)
▼
app_updata.c: update_app_bycmc() CMC 升级状态机
1.3 总线(RS485,USART2,PA2/PA3,9600,9bit 偶校验)
仪表总线/其他节点 ──RS485──> USART2
│
boot_rs485.c: Bsp_Rs485_Init() 初始化(9600/9bit/偶校验)
▼
Bsp_Rs485_ReceiveOK() 一帧收齐判定
▼
RS485_BUF.str / strlen(帧数据)
▼
app_updata.c: update_app() RS485 升级状态机
1.4 通路验证方式(上板前可先自测)
- PC 模拟器(无硬件):
./tools/ota_sim/ota_sim.exe old_os new_os out/diff/ota_diff.cpio
已验证整条解析/重建链路(1KB 分包模拟通道到达 → 两条 COMPARE MATCH)。 - 真机逐通道:RTT 看对应日志
- 蓝牙:
[MAIN][BLE] queue: N -> M bytes processed - 天眼:
[MAIN][CMC] data received: N bytes, calling update_app_bycmc - 总线:
[MAIN][RS485] data received: N bytes, calling update_app - 收到开始指令后:
[OTA-CH] open: staging …,说明通路与协议栈已接通。
- 蓝牙:
二、走 OTA 收包(协议状态机)
三条通道的帧解析结果统一进入升级协议状态机(同事协议,ACK/重传原样保留):
UP_Start → UP_Receive → UP_CRC → UP_OK(成功)/ UP_ERROR(失败)
2.1 开始升级(各通道的第一条指令)
| 通道 | 开始指令 | 处理位置 | 动作 |
|---|---|---|---|
| 蓝牙 | 0x0A + 子命令 0x01(开始)/ 0x02(总长度 programlen) |
ota_updata.c update_ble() |
回 ACK;ota_base==NIU_DB(仪表)时 ota_channel_open(programlen) —— 擦除 app 分区暂存区(0x08EC0000),登记总长度 |
| 天眼 | CMC CMD 0x05(含 programlen) |
app_updata.c update_app_bycmc() |
同上:**ota_channel_open(programlen)** |
| 总线 | 0x11(含 programlen,4 字节大端) |
app_updata.c update_app() |
同上:**ota_channel_open(programlen)** |
open 失败(超长/擦除失败)→ 直接置 UP_ERROR,不进入收包。
2.2 数据包(边收边落盘,不驻留 RAM)
| 通道 | 数据指令 | 处理 |
|---|---|---|
| 蓝牙 | 0x0A 子命令 0x03(每包带序号 bt_serial) |
累积到 updata_buff[2048],凑满 1KB(PACKSIZE)调 flash_writes() |
| 天眼 | CMC CMD 0x06(带包序号 packnum) |
直接调 flash_writes(flash_addr, data, words) |
| 总线 | 0x12 数据帧(1KB/包) |
调 flash_writes(flash_addr, data, words) |
关键桥接(app_updata.c flash_writes() 一处分流):
- 地址落在 APP 备份区(仪表镜像数据)→ **
ota_channel_write()**:按 256B 页缓冲,
凑满一页写一页到 app 分区暂存区,s_written累计,每 16KB 打印进度 - 其它地址(素材区)→ 原 flash 写入逻辑不变
每包收到后按协议回 ACK/NACK(send_ble_ota_data_ack / RS485 应答帧 / CMC 应答),
发送端重传机制原样生效。60 秒无数据 → 超时复位(Delay_TIME),看门狗全程喂。
2.3 收包完成(CRC 校验 → 触发重建)
| 通道 | CRC 指令 | 处理 |
|---|---|---|
| 蓝牙 | 0x0A 子命令 0x04(远端 CRC) |
本地算 CRC16_Ble(OTA_CHANNEL_STAGE_ADDR, programlen)(暂存区全量校验)对比 |
| 天眼 | CMC CMD 0x07 |
本地 CRC16(OTA_CHANNEL_STAGE_ADDR, programlen) 对比 |
| 总线 | 0x13 CRC 帧 |
同上 |
- CRC 一致 → **
ota_channel_finish()**(进入第三节:解包+差分重建)→UP_OK - CRC 不一致 → **
ota_channel_abort()**(清空暂存区)→UP_ERROR,回 NACK
下发端注意:payload 从"全量镜像"换成差分包 cpio(out/diff/ota_diff.cpio,
约 518KB),协议帧格式、包长、CRC 算法全部不变。
三、写分区、解包(ota_channel_finish 内部)
ota_channel_finish()(mh1000_ota/ota_channel.c)按序完成五步:
3.1 校验与冲刷
- 检查
s_written == s_total(收包完整性,缺包直接失败清空) - 把最后一页(不足 256B 以 0xFF 补齐)写进暂存区
3.2 流式读回解包(CPIO 解析)
app 分区暂存区(0x08EC0000)
│ 4KB 块流式读回(ch_flash_read,QSPI 内存映射)
▼
ota_parse_pack_feed() CPIO 070702 容器状态机
├─ ota_info.bin → ota_parse_manifest() 解析 [file] 段
│ (os_a.patch:os_a; 映射 + [diff] 段分区表)
│ → hook: ota_diff_on_manifest() 缓存 diff manifest
└─ os_a.patch → 按 [file] 映射找到目标分区 os_a
→ staging 层 accumulate(malloc 517KB,仅此一个文件)
→ ota_diff_apply() ← 进入 3.3
3.3 差分重建与写分区(HDiffPatch)
ota_diff_apply("os_a", patch_data, 517KB, old_size, new_size, cache 128KB)
│
├─ 目标分区解析:os_a(源)→ 配对槽 os_b
├─ 擦除 os_b 整个分区(4KB sector 循环)
├─ old 流:QSPI 内存映射随机读 os_a——源分区保持只读不破坏
├─ diff 流:patch 数据在 RAM(staging accumulate)
├─ new 流:256B 页缓冲,凑满一页 FLASH_ProgramPage 烧写 os_b
│ (每 64KB 打印 burn 进度)
└─ patch_decompress_with_cache():old+patch 流式重组 → 逐页写入 os_b
峰值 RAM ≈ patch 517KB + cache 128KB ≈ 650KB
3.4 完成与清场
- 校验写入字节数 == new_size(写短即失败)
- 清空 app 分区暂存区(防残留旧包)
- 打印
[OTA-CH] upgrade applied OK, reboot to switch A/B slot - 返回 0 → 状态机
UP_OK→ 系统重启(NVIC_SystemReset)→ bootloader 切到 os_b 跑新系统
3.5 失败路径(任意一步出错)
ota_channel_abort():清空暂存区 + 复位状态- 状态机
UP_ERROR:回 NACK,不重启不切槽——设备继续跑 os_a 当前版本
(A/B 机制天然回退,下次可重新发起升级)
附:全程数据流总览
手机/天眼/总线(payload=差分包 cpio)
│ 蓝牙 USART1 ┃ 天眼 UART4 ┃ 总线 USART2
▼
帧解析(boot_ble / boot_cmc / boot_rs485)
▼
协议状态机(update_ble / update_app_bycmc / update_app)
│ 开始指令 → ota_channel_open(总长)
│ 数据包 → flash_writes 分流 → ota_channel_write(页写 app 分区暂存)
│ CRC 指令 → ota_channel_finish() / abort()
▼
ota_channel_finish:
暂存区流式读回 → CPIO 解包(ota_parse_pack)
→ os_a.patch 累积(staging)
→ ota_diff_apply:old(os_a 0x08040000) + patch ─HDiffPatch─> new
→ 逐页烧写 os_b(0x083E0000)
▼
UP_OK → 重启 → bootloader 切槽 → 新系统启动(失败则继续跑旧系统)
更多推荐
所有评论(0)