IAP OTA空中升级,usb cdc 升级







USB Type-C CDC IAP 固件升级说明
1. 功能范围
本功能通过 Type-C 接口现有的 USB CDC 虚拟串口升级应用固件,不再需要为每次升级飞线连接 UART0。
适用范围和边界:
-
设备必须能够正常启动当前应用;
-
USB CDC 必须已经枚举,并能被电脑识别为 COM 口;
-
升级数据是 `_firmware/ota_firmware.bin`,不是约 24 MB 的完整 `.fw` 量产包;
-
接收完成后由现有 OTA App 校验镜像,再设置 OTA 标志并重启;
-
Bootloader 从 OTA 临时分区执行升级;
-
本功能不能替代救砖通道。如果应用损坏、无法启动或 USB CDC 无法枚举,仍然需要 UART0 或专用 ADFU 工具。
2. 原理
2.1 为什么不能直接通过 shell 传输固件
USB CDC 默认与日志、命令回显、Shell 提示符共用同一条数据流。如果把 1 MB 以上的固件直接作为文本命令输入,会出现以下问题:
-
日志和命令回显会污染固件数据;
-
Shell 的命令行长度和字符解析机制不适合二进制数据;
-
无法可靠判断每一块固件是否完整写入;
-
USB 断线后无法恢复偏移和校验状态。
因此本实现增加了独立二进制帧协议。Shell 只负责执行一次:
```text
app fwupdate start
```
进入 IAP 后,CDC 输入不再交给 Shell,应用日志也不再镜像到 USB。
2.2 二进制帧格式
请求帧头固定为 16 字节,小端序:
| 字段 | 长度 | 说明 |
| --- | --- | --- |
| magic | 2 | `A5 5A` |
| version | 1 | 协议版本,当前为 1 |
| type | 1 | BEGIN / DATA / END / ABORT |
| payload_len | 2 | 负载长度,最大 1024 |
| reserved | 2 | 保留 |
| offset | 4 | DATA 在完整固件中的偏移 |
| payload_crc32 | 4 | 当前负载的 CRC32 |
消息类型:
| 类型 | 值 | 说明 |
| --- | --- | --- |
| READY | 0 | 设备进入 IAP,回复中包含最大负载长度 |
| BEGIN | 1 | 开始升级,负载包含镜像长度、整包 CRC32、版本和标志 |
| DATA | 2 | 固件数据块 |
| END | 3 | 数据发送完成,请求设备校验镜像 |
| ABORT | 4 | 取消本次升级 |
设备回复同样为 16 字节,固定以 `5A A5` 开头,包含状态、已接收偏移和当前累计 CRC32。
2.3 数据路径
```text
PC USB CDC
-> 二进制帧解析
-> 校验帧 CRC32
-> 查询 AP OTA 流剩余空间
-> ota_backend_apota_write_data()
-> OTA App 写入临时分区
-> OTA App 校验 AOTA 镜像
-> END 检查完整长度及整包 CRC32
-> END 成功回复完成 USB 发送
-> 设置 REC_OTA_FLAG / SOC_PSTORE_TAG_OTA_UPGRADE
-> 重启
-> Bootloader 执行 OTA 恢复
```
DATA 帧采用逐帧 ACK。设备空间不足时回复 `BUSY`,上位机以相同偏移重试,因此不会把半个数据块写入 OTA 流。
AP OTA 环形缓存为 2556 字节,长度和剩余空间按缓存计数查询;流写入返回本次接收的字节数。OTA 层一次可读取 4096 字节,由后端分次取走并累计,及时释放缓存空间。旧 AP 传输接口仍返回缓存总量。
屏幕进度界面与蓝牙 OTA 共用。进度通知和能力查询不代表镜像校验完成,只有 `REPORT_IMAGE_VALID` 发布最终结果。USB 使用额外的提交等待:镜像有效后还要等整包 CRC 正确及 END 回复发完,OTA App 才设置升级标志并重启。
2.4 中断和失败处理
-
单帧 CRC 错误:回复错误,上位机重发同偏移数据;
-
OTA 流空间不足:回复 `BUSY`,上位机延迟后重试;
-
BEGIN 后设备最多等待 12 秒,直到 OTA App 真正打开后端流再恢复成功;
-
OTA 流打开后若空间暂时不足,上位机按实际无进展时间等待,默认 30 秒,可用 `--busy-timeout` 调整;
-
OTA 流提前关闭:立即报告 OTA 错误,并输出已接收偏移及错误码,不再持续返回 BUSY;
-
整包 CRC 错误:END 被拒绝,不设置 OTA 标志;
-
OTA 镜像校验失败:不进入 Bootloader 升级;
-
超过 30 秒未收到 BEGIN,或超过 60 秒未收到 DATA:自动退出 IAP;
-
USB 拔出:清理当前 OTA 会话并恢复 Shell 日志模式;
-
PC 升级失败:关闭串口前发送二进制 ABORT,设备取消提交并恢复 Shell;
-
升级失败时旧应用仍可继续启动,重新连接后可再次执行升级。
3. 源码修改
| 文件 | 修改内容 |
| --- | --- |
| `src/main/usb_iap.c` | 二进制 IAP 协议、帧缓存、CRC、OTA 流写入和校验等待 |
| `src/main/usb_iap.h` | USB IAP 对外接口 |
| `src/main/usb_serial_runtime.c` | CDC 二进制独占模式、日志屏蔽、IAP 数据分流和断线清理 |
| `src/main/system_app_shell.c` | `app fwupdate start/status/abort` 命令 |
| `src/user_comm/ota_transmit/ap_ota_backend.c` | OTA 流空间查询、整帧写入和校验结果等待接口 |
| `src/user_comm/ota_transmit/ap_ota_stream.c` | 环形缓存空间、部分读取、整帧写入及关闭保护 |
| `framework/ota/libota/ota_upgrade.c` | 尊重后端提交失败,取消时禁止进入 OTA_DONE |
| `src/main/CMakeLists.txt` | 编译 USB IAP 和复用 AP OTA 流 |
| `Kconfig` | 新增 `CONFIG_APP_USB_IAP`,ATS3089P 默认开启 |
| `tools/firmware/usb_iap_update.py` | PC 端升级工具 |
重要协议、CDC 分流和 OTA 写入代码均带 `//zk+` 中文注释。
4. 设备端操作
4.1 确认 USB 串口已启用
首次可以通过原 UART 或已经可用的 CDC 执行:
```text
app usbserial boot on
app usbserial on
app usbserial status
```
正常状态应包含:
```text
enabled=1 configured=1
```
4.2 手动进入 IAP
```text
app fwupdate start
```
设备成功进入后,PC 工具会收到二进制 READY 帧。
4.3 查询状态
普通 UART 可执行:
```text
app fwupdate status
```
输出示例:
```text
USB IAP: active=1 received=524288 total=1443328
```
4.4 取消升级
通过普通 UART 执行:
```text
app fwupdate abort
```
IAP 激活期间 USB CDC 被二进制协议独占,因此不能再通过同一个 COM 口输入这个文本命令。
5. PC 端升级
5.1 安装依赖
```text
python -m pip install pyserial
```
5.2 生成 OTA 固件
正常编译和打包后,使用构建目录中的:
```text
application/bt_watch/outdir/ats3089p_dev_watch/_firmware/ota_firmware.bin
```
不要选择:
```text
ats3089p_dev_watch_<日期>.fw
```
完整 `.fw` 是整包量产镜像,不适用于应用层 IAP。
5.3 执行升级
```text
cd
python application/bt_watch/tools/firmware/usb_iap_update.py ^
--port COM14 ^
--firmware application/bt_watch/outdir/ats3089p_dev_watch/_firmware/ota_firmware.bin
```
默认参数:
-
每帧 1024 字节;
-
版本号为 `0xffffffff`,跳过应用层版本比较;
-
普通帧超时 15 秒;
-
DATA 无进展超时 30 秒,`--busy-timeout 60` 可调整为 60 秒;
-
END 校验等待最多 45 秒。
升级成功时工具显示:
```text
升级包已通过设备校验,设备正在重启进入 Bootloader 升级。
```
设备重启后,Bootloader 会完成 OTA 写入。升级期间不要断电或拔线。
6. 构建与验证
构建:
```text
cd application\bt_watch\outdir\ats3089p_dev_watch
ninja
```
随后在 `\zephyr` 下执行打包,才能更新 `_firmware/ota_firmware.bin`:
```text
python -B build.py -p
```
设备运行旧版 IAP 时,修改电脑脚本不能修复设备端缓存问题。需要先使用 UART0 或现有可靠烧录通道写入修复后的应用,再通过 CDC 验证后续升级。
检查配置:
```text
CONFIG_APP_USB_IAP=y
```
PC 端协议自检:
```text
python -m py_compile application/bt_watch/tools/firmware/usb_iap_update.py
python -m unittest discover -s application/bt_watch/tools/firmware/tests -p test_usb_iap_update.py
```
真实 C 源码的主机回归测试(需要本机 gcc、clang 或 TinyCC,也可用 `--compiler` 指定路径):
```text
python application/bt_watch/tools/firmware/tests/run_ap_ota_native.py
```
该测试编译工程的 AP OTA 后端、环形流及通用 stream 源码,覆盖缓存回绕、4 KiB 分片读取、跳过填充、校验结果复位和提交授权;不访问串口。实板仍需验证 USB 拔插、闪存写入和重启。
实板验证步骤:
-
确认 Type-C CDC 能正常收发 Shell;
-
执行 `app fwupdate start`;
-
运行 PC 升级工具;
-
观察进度到 100%;
-
确认设备重启并运行新固件;
-
使用 `app fwupdate status` 或版本命令确认升级结果;
-
额外测试拔线中断、错误 CRC 文件和重复升级。
7. 限制
-
USB CDC IAP 依赖当前应用正常运行,不能用于应用完全损坏后的首次恢复;
-
OTA 包必须是 `ota_firmware.bin`;
-
传输期间不要切换 U 盘、USB 声卡或关闭 USB 串口;
-
当前实现只升级应用 OTA 分区,不修改 Bootloader;
-
Bootloader 或分区表损坏时仍需 UART0/ADFU 专用烧录设备。