uboot 升级协议与流程
本页面向 fw-Bootloader 中 uboot 的在线升级(OTA)机制,完整说明上位机与设备端之间的升级协议(数据包格式、回复状态、各操作指令及其参数结构)以及端到端升级流程(通道选择、触发方式、擦除/写入/校验时序)。
Purpose and Scope
本页覆盖的内容:
- uboot 升级协议的数据包格式(同步头、命令长度、指令、回复状态、CRC16)。
- 全部操作指令(
JL_SU_CMD_DEVICE_INIT/DEVICE_CHECK/ERASE/WRITE/FLASH_CRC/EX_KEY)的上位机参数与设备回复结构。 - 两种升级通道(串口、USB HID)与两种升级触发方式(I/O 口检测、SDK 软件复位)的配置原理。
- 升级主流程与 uboot 自身升级流程。
不属于本页范围、由兄弟页面承接的内容:
- uboot 工程的搭建、编译、调试打印等具体操作步骤,请参见 uboot 升级使用说明。
- 各芯片 OTA Loader 工程(如
ac792n-ota-loader下的sd_ota_update、usb_ota_update、usb_sec_ota_update等)属于另一组页面,本页仅在涉及传输通道时引用其目录结构。
协议正文与命令结构均提取自仓库文档 uboot升级协议流程v1.1.2.md,配置说明提取自 uboot升级使用说明v1.1.2.md。
Overview
在杰理(Jieli)方案中,fw-Bootloader 是设备上电后最先运行的引导程序(uboot)。它承担两件事:一是校验并跳转 APP,二是当需要在线升级时,直接与上位机(PC 工具)交互,把新的固件烧写到 Flash 对应区域。因为升级发生在 APP 尚未运行之前,uboot 升级通道是设备「变砖」风险最低、也最可靠的升级路径。
uboot 升级采用主机-从机命令/应答模型:
- 上位机(PC 工具,仓库中对应
pc_demo示例)主动发起每一个命令; - 设备端 uboot 收到命令后执行,并使用相同指令号回包,通过
rsp_status字段上报执行结果; - 全部命令包统一采用固定同步头 + 长度 + 指令 + 状态 + 参数 + CRC16 的帧格式,多字节字段为小端(little endian),校验使用 CRC-CCITT (XModem)。
整个升级流程被设计为四步循环:初始化(DEVICE_INIT)→ 校验设备(DEVICE_CHECK)→ 擦除(ERASE)→ 写入(WRITE)→ 校验(FLASH_CRC),其中擦除/写入按块循环,最终用 Flash CRC 与上位机本地数据比对来确认升级成功。EX_KEY(交换密钥)指令为安全升级预留了密钥协商能力。
协议文档中还包含两张流程图:主流程图(uboot_update_0.jpg)与 uboot 本身升级流程图(uboot_update_1.jpg),本页用 Mermaid 将其还原为可读的结构化描述。
Architecture
flowchart TD
subgraph sg_Host["上位机 (PC 工具)"]
Host["pc_demo 工具"]
end
subgraph sg_Transport["传输通道"]
UART["串口升级<br/>USB_MODE=0"]
USBHID["USB HID 升级<br/>USB_MODE=1"]
end
subgraph sg_Uboot["设备端 uboot (fw-Bootloader)"]
Main["main 入口 / 触发检测"]
Proto["升级协议处理<br/>JL_SU_CMD_* 指令解析"]
FlashDrv["Flash 驱动<br/>擦除 / 写入 / CRC"]
end
subgraph sg_Flash["存储介质"]
Flash[("SPI NOR Flash<br/>app 区 / uboot 区")]
end
Host --> UART
Host --> USBHID
UART --> Main
USBHID --> Main
Main --> Proto
Proto --> FlashDrv
FlashDrv --> Flash
架构要点:
- 上位机(Host):负责把待升级固件按协议分帧下发;
pc_demo中usb_hid子目录的main.cpp定义了 USB HID 模式下的usb_vid/usb_pid(见使用说明)。 - 传输通道:由编译宏
USB_MODE决定——USB_MODE=0走串口,USB_MODE=1走 USB HID;两套通道在协议层共用同一套JL_SU_CMD_*帧格式。 - 设备端 uboot:
main函数先做触发检测(I/O 电平或升级魔数),进入升级流程后由协议处理层解析命令并驱动 Flash 驱动执行擦除、写入与 CRC 校验。 - Flash 区域:
DEVICE_INIT回复中的upgrade_addr/upgrade_len描述目标区域(如app_dir_head、uboot_zone)的地址与长度,flash_alignsize用于对齐地址,保证擦除/写入符合 Flash 特性。
协议基础:数据包格式
所有命令包(无论上位机→设备还是设备→上位机)遵循统一帧格式,多字节数据低字节在前(小端),CRC16 采用 CRC-CCITT (XModem) 标准,回复使用与请求相同的指令号。
| Byte[0] | Byte[1] | Byte[3~2] | Byte[4] | Byte[5] | Byte[6~n-2] | Byte[n~n-1] |
|---|---|---|---|---|---|---|
| syncdata0 | syncdata1 | cmd_len | cmd | rsp_status | param(部分指令无) | crc16 |
| 固定 0xAA | 固定 0x55 | cmd+rsp_status+union 数据长度 | 操作指令 | 回复状态 | 指令参数 | 整包(不含 CRC 自身)的 CRC16 |
字段设计意图:
- syncdata0 / syncdata1(0xAA 0x55):帧同步头,接收方据此从字节流中定位帧起点;固定的魔数序列有效降低误同步概率。
- cmd_len:只统计
cmd + rsp_status + param的长度,不含同步头与 CRC,使接收方在收到 6 字节头之后即可确定需要继续收取多少字节。 - cmd / rsp_status:命令号与执行结果共用同一字节位,请求时
rsp_status通常置 0,应答时由设备写入状态值。 - param:联合体(union)形式的指令参数,不同指令长度与含义不同;无参数指令(如 ERASE/WRITE 的应答)可省略。
- crc16:覆盖整包其余字节,保证传输过程中命令与数据不被篡改或误码。
回复状态(rsp_status)
设备对每条命令通过 rsp_status 返回执行结果,取值定义如下(来源:协议文档第 49-61 行):
| 状态宏 | 数值 | 说明 |
|---|---|---|
JL_SU_CMD_SUSS | 0x0 | 命令执行成功 |
JL_SU_CMD_CRC_ERROR | 0x1 | CRC 错误 |
JL_SU_CMD_SDK_ID_ERROR | 0x2 | ID 错误(SDK/设备 ID 不匹配) |
JL_SU_CMD_OTHER_ERROR | 0x3 | 其他错误 |
enum {
JL_SU_CMD_SUSS, //命令成功
JL_SU_CMD_CRC_ERROR, //CRC 出错
JL_SU_CMD_SDK_ID_ERROR, //ID 错误
JL_SU_CMD_OTHER_ERROR, //其他错误
};
Source: uboot升级协议流程v1.1.2.md
上位机应在每次收发后检查该状态:非 JL_SU_CMD_SUSS 时需根据错误类型决定重试(CRC 类)或中止升级(ID 不匹配类)。
操作指令、参数及数据方向
协议共定义 6 条指令,取值 0xC0 ~ 0xC5。下面逐条说明其上位机参数结构与设备回复结构(结构体定义均来自协议文档)。
1. JL_SU_CMD_DEVICE_INIT(0xC0)——设备初始化
获取目标 file_name 区域在 Flash 中的地址、长度等信息,是升级流程的第一步。
- 方向:上位机 → 设备
| byte[0~15] | byte[16] | |
|---|---|---|
| 参数 | file_name[16] | mode |
| 说明 | 区域名字 | 读取模式:app_dir_head 设置 0,uboot_zone 设置 0 |
struct {
u8 file_name[16];
u8 mode;
} init;
Source: uboot升级协议流程v1.1.2.md
- 方向:设备 → 上位机
| byte[3~0] | byte[7~4] | byte[11~8] | byte[15~12] | |
|---|---|---|---|---|
| 参数 | upgrade_addr | upgrade_len | upgrade_eoffset | flash_alignsize |
| 说明 | 区域地址 | 区域长度 | 设备偏移长度 | 对齐长度 |
struct {
u32 upgrade_addr;
u32 upgrade_len;
u32 upgrade_eoffset;
u32 flash_alignsize;
} init;
Source: uboot升级协议流程v1.1.2.md
flash_alignsize 是后续所有擦除/写入地址对齐的依据:上位机必须按该值对齐地址与长度,否则可能触发 JL_SU_CMD_OTHER_ERROR。
2. JL_SU_CMD_DEVICE_CHECK(0xC1)——获取设备 PID/VID/sdkID
用于身份校验,防止把 A 设备的固件刷进 B 设备。
- 方向:上位机 → 设备
| byte[3~0] | |
|---|---|
| 参数 | sdk_id |
| 说明 | 上位机 sdk id |
struct {
u32 sdk_id;
} device_check;
Source: uboot升级协议流程v1.1.2.md
- 方向:设备 → 上位机
| byte[0~3] | byte[4~19] | byte[23~20] | |
|---|---|---|---|
| 参数 | vid | pid | sdk_id |
| 说明 | 设备 vid | 设备 pid | 设备 sdk id |
struct {
u8 vid[4];
u8 pid[16];
u32 sdk_id;
} device_check;
Source: uboot升级协议流程v1.1.2.md
上位机比对 sdk_id 不一致时,设备会以 JL_SU_CMD_SDK_ID_ERROR 拒绝后续写入。
3. JL_SU_CMD_ERASE(0xC2)——擦除指令
按 Flash 粒度(page/sector/block)擦除指定地址,为写入做准备。
- 方向:上位机 → 设备
| byte[3~0] | byte[7~4] | |
|---|---|---|
| 参数 | address | type |
| 说明 | 擦除地址 | 擦除类型 |
#define JL_ERASE_TYPE_PAGE 1 //page擦除
#define JL_ERASE_TYPE_SECTOR 2 //sector
#define JL_ERASE_TYPE_BLOCK 3 //block
struct {
u32 address;
u32 type;
} erase;
Source: uboot升级协议流程v1.1.2.md
- 设备回复参数:无,仅通过
rsp_status回复(方向:设备 → 上位机)。
4. JL_SU_CMD_WRITE(0xC3)——写指令
将固件数据写入 Flash 指定地址,是升级过程中最频繁的指令。
- 方向:上位机 → 设备
| byte[3~0] | byte[7~4] | byte[8~n] | |
|---|---|---|---|
| 参数 | address | data_length | data[0] |
| 说明 | 写地址 | 数据长度 | 数据 |
struct {
u32 address; // 烧写FLASH地址
u32 data_length; //烧写长度
u8 data[0]; // 烧写数据
} write;
Source: uboot升级协议流程v1.1.2.md
data[0] 是柔性数组用法:数据长度由帧头 cmd_len 决定,协议据此计算剩余数据字节数。上位机通常按 flash_alignsize 分块循环下发,每块等待一次 rsp_status 确认。
- 设备回复参数:无,仅通过
rsp_status回复(方向:设备 → 上位机)。
5. JL_SU_CMD_FLASH_CRC(0xC4)——获取设备 CRC
写入完成后,上位机请求设备对已写入区域按块计算 CRC16 并回传,用于与本地固件比对,确认升级结果。
- 方向:上位机 → 设备
| byte[3~0] | byte[7~4] | byte[11~8] | |
|---|---|---|---|
| 参数 | address | len | block_size |
| 说明 | 需要校验的地址 | 校验总长度 | 单块数据校验长度 |
struct {
u32 address;
u32 len;
u32 block_size;
} crc_list;
Source: uboot升级协议流程v1.1.2.md
- 方向:设备 → 上位机
| byte[0~n] | |
|---|---|
| 参数 | crc[0] |
| 说明 | crc 列表(由 len/block_size 个 crc16 组成) |
struct {
u16 crc[0];
} crc_list;
Source: uboot升级协议流程v1.1.2.md
分块校验的设计意图:单次传输整段 CRC 不可行且无法定位错误块,按 block_size 切块后,若某一块不匹配,上位机可精确重刷该块,而不是整包重传。
6. JL_SU_CMD_EX_KEY(0xC5)——交换密钥
为加密/安全升级预留的密钥协商指令。
- 方向:上位机 → 设备
| byte[3~0] | |
|---|---|
| 参数 | secret_key |
| 说明 | 密钥 |
struct {
u32 secret_key;
} ex_key;
Source: uboot升级协议流程v1.1.2.md
- 方向:设备 → 上位机:回复包含
rand(随机数)、data_length、data[0]等字段。注意:协议文档在rand/data_length/data之后即截断,密钥协商的完整应答结构(含随机数与密文数据如何使用)未在文档中完整展开,属于"实现细节未在源文档中提供"的部分。
核心流程
升级主流程(协议交互时序)
协议文档中的主流程图(uboot_update_0.jpg)描述的正是上位机主导的四阶段流程。用 Mermaid 时序图还原如下:
sequenceDiagram
participant Host as 上位机 PC 工具
participant Uboot as uboot 设备端
participant Flash as SPI NOR Flash
Host->>Uboot: C0 DEVICE_INIT (file_name, mode)
Uboot-->>Host: rsp_status + upgrade_addr/len/eoffset/alignsize
Host->>Uboot: C1 DEVICE_CHECK (sdk_id)
Uboot-->>Host: rsp_status + vid/pid/sdk_id
Note over Host,Uboot: sdk_id 不匹配则终止 (JL_SU_CMD_SDK_ID_ERROR)
loop 按 flash_alignsize 分块循环
Host->>Uboot: C2 ERASE (address, type)
Uboot-->>Host: rsp_status
Host->>Uboot: C3 WRITE (address, data_length, data)
Uboot-->>Host: rsp_status
end
Host->>Uboot: C4 FLASH_CRC (address, len, block_size)
Uboot-->>Host: rsp_status + crc 列表
Host->>Host: 逐块比对 CRC,确认升级结果
alt CRC 全部匹配
Host->>Uboot: 复位设备运行新固件
else 存在不匹配块
Host->>Uboot: 重刷对应块 / 报错
end
流程要点:
- DEVICE_INIT 必须最先执行——上位机只有拿到
upgrade_addr/upgrade_len/flash_alignsize才能规划擦除与写入的地址对齐策略。 - DEVICE_CHECK 做身份校验,
sdk_id不匹配时设备返回JL_SU_CMD_SDK_ID_ERROR,防止跨机型刷写。 - ERASE + WRITE 循环:先擦后写是 NOR Flash 的特性(只能 1→0,擦除后才能正确编程);按块执行便于出错时定点重试。
- FLASH_CRC 收尾:以设备侧计算出的 CRC 列表为最终依据,避免"上位机以为写成功、实际 Flash 数据不一致"的隐性故障。
uboot 自身升级流程
协议文档还单独给出了 uboot 本身升级的流程图(uboot_update_1.jpg)。与 APP 升级的区别在于:升级对象是 uboot 所在的 uboot_zone 区域,DEVICE_INIT 时通过 file_name 指定该区域即可复用同一套协议。其特殊性在于uboot 升级自身属于高风险操作——若升级过程中断电,引导程序可能不可用,因此工程上通常配合安全升级(如 ac792n-ota-loader 下的 sd_sec_ota_update、usb_sec_ota_update 等安全 OTA 工程)与密钥协商(EX_KEY)一起使用。
升级入口与触发方式流程
uboot 的 main 函数先判断是否进入升级流程,再按编译宏选择传输通道(来源:uboot升级使用说明v1.1.2.md):
flowchart TD
Start([上电 / 复位]) --> Boot["进入 uboot main"]
Boot --> Trigger{"触发方式"}
Trigger -->|"I/O 口检测"| IO["检测指定 I/O 电平状态"]
Trigger -->|"SDK 软件复位"| Magic["检测升级魔数<br/>USE_UPGRADE_MAGIC / nvram"]
IO -->|"电平有效"| Upgrade["进入升级流程"]
Magic -->|"魔数有效"| Upgrade
IO -->|"无效"| RunApp["校验并跳转 APP"]
Magic -->|"无效"| RunApp
Upgrade --> Trans{"升级通道"}
Trans -->|"USB_MODE=0"| UART["串口升级<br/>ut_device_mode 配置 TX/RX/波特率"]
Trans -->|"USB_MODE=1"| USB["USB HID 升级<br/>VID/PID 需与上位机一致"]
UART --> Proto["协议交互<br/>INIT/CHECK/ERASE/WRITE/CRC"]
USB --> Proto
Proto --> Verify{"CRC 校验"}
Verify -->|"通过"| Done["升级完成 / 复位运行新固件"]
Verify -->|"失败"| Fail["按 rsp_status 重试或报错"]
两种触发方式的取舍:
- I/O 口检测:由硬件(按键/拨码)决定是否升级,适合产线烧录,不依赖 APP 状态。
- SDK 软件复位触发:在 user.h 中使能
USE_UPGRADE_MAGIC宏,SDK 侧通过往nvram(extern u32 nvram_list[]、NV_RAM_LIST_ADDR)写入升级魔数后软复位,uboot 检测到魔数即进入升级流程——这是 OTA 升级(APP 内收到升级指令后重启进入 uboot)的典型实现。
升级模式与通道配置
uboot 升级支持串口与 USB HID 两种模式,通过编译宏切换(来源:uboot升级使用说明v1.1.2.md):
- 串口升级模式:在 Project build options → Compiler settings → #defines 中添加
USB_MODE=0;串口引脚与波特率由app/src/user.c中的ut_device_mode(tx, rx, bud)函数设置。 - USB HID 升级模式:在 #defines 中添加
USB_MODE=1;此时 uboot 工程的usb_vid、usb_pid必须与上位机(pc_demo\usb_hid\main.cpp)中的usb_vid、usb_pid保持一致,否则设备无法被枚举识别。
仓库中不同传输通道对应的 OTA Loader 工程分散在 ac791n-ota-loader 与 ac792n-ota-loader 目录下,例如 sd_ota_update(SD 卡升级)、usb_ota_update、usb_hid_ota_update、uart_user_update、usb_sec_ota_update / sd_sec_ota_update(安全升级)等,它们在协议层面共用本文档描述的 JL_SU_CMD_* 帧格式。
配置选项
| 配置项 | 位置 | 类型/取值 | 默认 | 说明 |
|---|---|---|---|---|
USB_MODE | 工程 Compiler settings → #defines | 0 / 1 | 无(按需定义) | 0 = 串口升级模式;1 = USB HID 升级模式 |
ut_device_mode(tx, rx, bud) | app/src/user.c | 函数调用 | 无 | 设置串口升级的 TX 脚、RX 脚与波特率 |
usb_vid / usb_pid | uboot 工程 USB 配置 | 数值 | 无 | USB HID 模式下必须与上位机 pc_demo\usb_hid\main.cpp 中的 VID/PID 一致 |
__DEBUG | 工程 Compiler settings → #defines | 宏 | 未定义 | 使能调试打印功能 |
isd_config.ini | 配置文件 | 引脚/波特率 | 无 | 方式一:配置调试打印脚与波特率(main 中调用 uart_init(uttx, ut_buad)) |
| 打印脚与波特率代码直设 | main 函数 | 引脚/波特率 | 无 | 方式二:代码中直接设置,例如 PA5、1000000 波特率 |
USE_UPGRADE_MAGIC | user.h | 宏 | 未定义 | 使能 SDK 软件复位触发升级(配合 nvram 魔数) |
| I/O 触发引脚 | main 函数 | I/O 电平检测 | 无 | I/O 口检测触发方式下,由电平状态决定是否进入升级流程 |
以上配置项位置与说明均来自 uboot升级使用说明v1.1.2.md。
API Reference:协议指令参考
| 指令 | 定义值 | 名称 | 上位机参数(请求) | 设备回复参数(应答) | 回复状态位 |
|---|---|---|---|---|---|
JL_SU_CMD_DEVICE_INIT | 0xC0 | 设备初始化 | file_name[16] + mode | upgrade_addr / upgrade_len / upgrade_eoffset / flash_alignsize(各 u32) | 成功/其他错误 |
JL_SU_CMD_DEVICE_CHECK | 0xC1 | 获取设备信息 | sdk_id (u32) | vid[4] / pid[16] / sdk_id (u32) | 成功/ID 错误 |
JL_SU_CMD_ERASE | 0xC2 | 擦除 | address (u32) + type (u32) | 无(仅状态) | 成功/其他错误 |
JL_SU_CMD_WRITE | 0xC3 | 写入 | address (u32) + data_length (u32) + data[] | 无(仅状态) | 成功/CRC 错误/其他错误 |
JL_SU_CMD_FLASH_CRC | 0xC4 | Flash CRC 校验 | address (u32) + len (u32) + block_size (u32) | crc[0](u16 列表,共 len/block_size 个) | 成功/其他错误 |
JL_SU_CMD_EX_KEY | 0xC5 | 交换密钥 | secret_key (u32) | rand (u16) / data_length (u16) / data[0](文档未完整展开) | 成功/其他错误 |
通用约定:
- 所有多字节字段小端传输;请求与应答使用相同
cmd指令号,以rsp_status区分方向与结果。 - 无参数应答仅回
syncdata0/1 + cmd_len + cmd + rsp_status + crc16,cmd_len = 2。 - 上位机收到
rsp_status非JL_SU_CMD_SUSS (0x0)时必须处理:0x1(CRC 错误)可重发当前包;0x2(SDK ID 错误)应中止升级;0x3(其他错误)按地址/参数对齐规则排查后重试。
Usage Examples
以下示例均为协议文档中的真实结构体定义,展示了各指令参数在代码中的形态。
示例 1:擦除类型宏与擦除参数
#define JL_ERASE_TYPE_PAGE 1 //page擦除
#define JL_ERASE_TYPE_SECTOR 2 //sector
#define JL_ERASE_TYPE_BLOCK 3 //block
struct {
u32 address;
u32 type;
} erase;
Source: uboot升级协议流程v1.1.2.md
示例 2:写指令参数(柔性数组承载固件数据)
struct {
u32 address; // 烧写FLASH地址
u32 data_length; //烧写长度
u8 data[0]; // 烧写数据
} write;
Source: uboot升级协议流程v1.1.2.md
示例 3:设备初始化应答(区域信息)
struct {
u32 upgrade_addr;
u32 upgrade_len;
u32 upgrade_eoffset;
u32 flash_alignsize;
} init;
Source: uboot升级协议流程v1.1.2.md
示例 4:设备信息校验应答
struct {
u8 vid[4];
u8 pid[16];
u32 sdk_id;
} device_check;
Source: uboot升级协议流程v1.1.2.md
示例 5:SDK 软件复位触发(上位机/SDK 侧写法)
使用说明文档给出的 SDK 侧触发框架(任意位置添加):
extern u32 nvram_list[];
#define NV_RAM_LIST_ADDR nvram_list
Source: uboot升级使用说明v1.1.2.md
其原理是:SDK 通过 NV_RAM_LIST_ADDR(即 nvram_list)写入升级魔数后执行软复位,uboot 侧检测到魔数即进入升级流程;需配合 user.h 中的 USE_UPGRADE_MAGIC 宏使用。魔数写入的具体值在文档截断处未展示,实现细节请以 uboot 工程源码为准。
故障模式与边界情况
1. CRC 错误(JL_SU_CMD_CRC_ERROR, 0x1)
- 触发场景:传输链路噪声(串口波特率不匹配、USB 线缆质量差)导致数据包被篡改;或写入后 Flash 数据与预期不符。
- 处理:上位机应重发当前命令包。CRC-CCITT (XModem) 为 16 位校验,对单包数据量有限的协议足够,但上位机仍应在连续多次 CRC 失败后中止升级,避免无限重试。
2. SDK ID 错误(JL_SU_CMD_SDK_ID_ERROR, 0x2)
- 触发场景:
DEVICE_CHECK时上位机传入的sdk_id与设备不符,或跨机型刷写固件。 - 处理:这是不可恢复错误,应直接终止升级并提示用户固件与设备不匹配。设计上把 ID 校验放在擦除/写入之前,是为了在破坏 Flash 内容前尽早拦截。
3. 地址/长度对齐问题(其他错误 0x3 的常见来源)
DEVICE_INIT返回的flash_alignsize必须用于后续所有ERASE/WRITE/FLASH_CRC的地址与长度对齐;未对齐的地址可能触发 Flash 驱动错误,设备回JL_SU_CMD_OTHER_ERROR。upgrade_eoffset(设备偏移长度)用于处理文件头/区域偏移,上位机计算实际烧写地址时必须计入该偏移。
4. USB HID 枚举失败
- 触发场景:
USB_MODE=1时 uboot 的usb_vid/usb_pid与上位机不一致。 - 处理:设备无法被枚举,协议无从开始。需同时修改 uboot 工程与
pc_demo\usb_hid\main.cpp的 VID/PID 保持一致。
5. 升级中途断电 / uboot 自身升级失败
- uboot 自身升级(
uboot_zone)失败可能导致引导程序不可用,属于最高风险路径。工程上通过安全升级工程(sd_sec_ota_update、usb_sec_ota_update)与EX_KEY密钥协商来降低风险;协议文档未给出断电恢复(如双备份引导)的细节,实现请以对应安全 OTA 工程源码为准。
6. 并发与多设备
- 协议为单主机-单设备、请求-应答串行模型,无并发窗口:上位机必须等待上一命令的应答后才能发送下一条。设备端天然串行处理,不存在并发写 Flash 的竞争问题;但上位机不应对同一设备并发下发命令,也不应对
rsp_status超时处理过短(Flash 擦除耗时随 type 增大,block 擦除明显慢于 page 擦除)。
性能与运维注意
- 写性能受擦除粒度限制:page(1)→ sector(2)→ block(3)擦除粒度递增、单次擦除耗时递增;上位机应在满足对齐要求的前提下选择尽量大的擦除类型以减少擦除次数。
- 分块 CRC 校验:
FLASH_CRC按block_size分块返回 CRC 列表,校验失败时可定点重刷失败块,避免整包重传——这是大固件升级场景下收敛重试成本的关键设计。 - 调试打印:使能
__DEBUG后可通过isd_config.ini或代码直设打印脚/波特率;产线模式下建议关闭打印以减小串口带宽占用与时间开销。 - 串口波特率:由
ut_device_mode(tx, rx, bud)决定,波特率越高升级越快,但受链路质量约束,需与上位机保持一致。
扩展点
- 新增指令:在
0xC0 ~ 0xC5之外扩展指令号,遵循同一帧格式(sync + len + cmd + rsp_status + param + crc16)即可复用现有传输与校验基础设施;参数结构沿用 union 方式扩展。 - 新区域升级:
DEVICE_INIT的file_name字段已抽象出区域概念(app_dir_head、uboot_zone),新增存储区域只需约定新的区域名字符串,无需改动协议。 - 安全升级:
EX_KEY密钥协商 +sd_sec_ota_update/usb_sec_ota_update安全工程构成加密升级扩展路径;标准协议不加密,安全升级在其上叠加密钥与密文数据。 - 通道扩展:传输层(串口/USB HID)与协议层解耦,
USB_MODE宏切换;新增通道(如 SD 卡,见sd_ota_update)可在不改变JL_SU_CMD_*帧格式的前提下接入。
测试情况说明
仓库内未发现针对本协议的自动化单元测试源码;协议正确性主要通过 fw-Bootloader/update_tools/win-uart/ 等上位机工具与真机联调验证。uboot升级使用说明v1.1.2.md 中引用的 pc_demo(含 usb_hid/main.cpp)是上位机侧的行为参考,其收发逻辑可作为协议实现的验证基准。
Related Links
- uboot 升级使用说明 v1.1.2 —— 工程选择、升级模式、调试与触发方式配置的完整操作手册。
- fw-Bootloader README —— Bootloader 工程总览。
- win-uart 升级工具 README —— Windows 串口升级工具说明。
- 相关 OTA Loader 工程:
ac791n-ota-loader/、ac792n-ota-loader/(含sd_ota_update、usb_ota_update、usb_hid_ota_update、uart_user_update、usb_sec_ota_update、sd_sec_ota_update等子工程)。 - 协议流程图原图:uboot_update_0.jpg(主流程)、uboot_update_1.jpg(uboot 自身升级流程)。