烧录与固件升级
本文档介绍 AC792N SDK 中固件烧录(Flashing)与升级(OTA/Update)机制的完整实现:从应用层升级参数的组织与传递,到双备份(Double Bank)Flash 编程接口,再到 UART、网络、NorFlash、USB/SD 等各升级通道的接入方式。
Purpose and Scope
本文档覆盖 AC792N SDK 固件升级子系统的核心实现,包括:
- 应用层升级框架(
sdk/apps/common/update/update.c)中的升级参数结构、CRC 校验与结果持久化机制; - 双备份升级引擎的对外 API(
sdk/include_lib/update/db_updata_api.h)及其数据流; - 各升级通道(UART、HTTP 网络、NorFlash、TWS、USB MSD、SD 卡、文件系统、测试盒)的定位与协作关系。
以下主题由其他页面覆盖,本文档不展开:USB 设备协议栈本身的实现(见 USB 相关目录项)、网络协议栈细节、fs_update.c 所依赖的文件系统内部实现。本文档侧重"升级机制本身如何工作",而不是某个具体外设驱动的细节。
Overview
AC792N SDK 是一套面向蓝牙/WiFi 音频与摄像头类产品的嵌入式固件 SDK。固件升级能力是量产烧录与售后 OTA 的共同基础,其设计目标是:
- 多通道统一:无论固件包来自串口(UART)、U 盘(MSD)、SD 卡、网络(HTTP)还是 TWS 对耳,应用层最终都通过同一套参数填充与跳转机制进入升级流程;
- 断电安全:升级参数(
UPDATA_PARM)存放在固定 RAM 地址,通过 CRC16 保护;升级结果持久化到 Flash 参数区,重启后可判断上次升级是否成功; - 双备份可回退:核心升级引擎采用双 Bank 结构(
db_update_*API),新固件写入备份 Bank,校验通过后再烧录启动信息(boot info),从而在升级失败时仍能回退到旧固件; - Loader 引导:应用通过
update_mode_api_v2()等接口把升级类型、参数与跳转地址写入UPDATA_PARM,随后跳转到独立 Loader(LOADER.BIN/ota.bin/ UFW 包)完成实际的 Flash 编程。
升级包在文件系统侧以 *.UFW(见 update.c)为标识;Loader 名称固定为 LOADER.BIN。
Architecture
下图展示升级子系统的整体架构:应用层、升级框架、双备份引擎与各传输通道之间的关系。
flowchart TD
subgraph sg_App["应用层 (sdk/apps/common)"]
APP["业务应用 (音频/摄像头)"]
UFW["UFW 升级包 (/*.UFW)"]
end
subgraph sg_Frame["升级框架 update.c"]
MODE_API["update_mode_api_v2()"]
PARAM["UPDATA_PARM 参数块 + CRC16"]
RESULT["update_result_get/set()"]
BOOTCHK["update_success_boot_check()"]
HWCLOSE["update_close_hw()"]
end
subgraph sg_Channels["升级传输通道"]
UART["uart_update.c"]
NET["net_update.c / http_update.c"]
NOR["norflash_update.c / norflash_ufw_update.c"]
USB_SD["usb msd_upgrade / sd_upgrade_impl"]
TWS["update_tws.c / update_tws_new.c"]
FS["fs_update.c / expand_zone_file_update.c"]
TESTBOX["testbox_update.c"]
end
subgraph sg_Engine["双备份升级引擎 (db_update_*) 与 Loader"]
DB_API["db_update_init/write/verify"]
LOADER["LOADER.BIN / ota.bin"]
BANK_A["Bank A (当前固件)"]
BANK_B["Bank B (目标固件)"]
BOOTINFO["Boot Info 区域"]
end
APP --> MODE_API
APP --> UFW
MODE_API --> PARAM
PARAM --> RESULT
RESULT --> BOOTCHK
MODE_API --> HWCLOSE
PARAM -.->|"跳转 Loader"| LOADER
UART --> DB_API
NET --> DB_API
NOR --> DB_API
USB_SD --> DB_API
TWS --> DB_API
FS --> DB_API
TESTBOX --> DB_API
DB_API --> BANK_A
DB_API --> BANK_B
DB_API --> BOOTINFO
LOADER --> DB_API
架构要点说明:
- 应用层通过
update_mode_api_v2(type, priv_param_fill_hdl, priv_update_jump_handle)发起升级,这是所有升级方式的统一入口(见 update.c); - 升级框架负责把升级类型、结果、CRC 写入
UPDATA_PARM参数块,该参数块位于固定的 RAM 地址(UPDATA_FLAG_ADDR),用于应用与 Loader 之间的"握手"; - 传输通道负责把新固件数据搬运到目标介质(SD 卡文件、网络缓存、NorFlash、U 盘等),最终都调用双备份引擎或 Loader 完成 Flash 编程;
- 双备份引擎(
db_update_*,见 db_updata_api.h)以"目标 Bank + Boot Info"为核心:新固件先写入备用 Bank,校验通过后再切换启动信息,保证可回退。
核心概念
UPDATA_PARM 参数块
UPDATA_PARM 是应用与 Loader 之间传递升级信息的共享结构,存放在 UPDATA_FLAG_ADDR 指向的 RAM 中,其字段包括:
| 字段 | 作用 |
|---|---|
parm_type | 升级类型标识(如 NORFLASH_UPDATA、SD0/SD1/USB 等) |
parm_result | 升级结果(UPDATA_NON / UPDATA_READY / UPDATA_SUCC 等) |
parm_crc | 对整个参数块(跳过 crc 字段自身)计算的 CRC16 |
magic | 参数魔数 UPDATE_PARAM_MAGIC,用于合法性识别 |
ota_addr | Loader/OTA 代码地址(来自 succ_report.loader_saddr) |
parm_priv | 私有参数区,不同升级类型填充各自专属参数 |
参数块填充后必须重新计算 CRC16,见 update.c:
//fill common content \ private content \ crc16
static void update_param_content_fill(int type, UPDATA_PARM *p, void (*priv_param_fill_hdl)(UPDATA_PARM *P))
{
if (support_norflash_update_en) {
p->parm_type = NORFLASH_UPDATA; //uboot通过该标识从外挂flash读取ota.bin
*((u16 *)((u8 *)p + sizeof(UPDATA_PARM) + 32)) = (u16)type; //将实际的升级类型保存到UPDATA_PARM后
} else {
p->parm_type = (u16)type;
}
p->parm_result = (u16)UPDATA_READY;
p->magic = UPDATE_PARAM_MAGIC;
p->ota_addr = succ_report.loader_saddr;
//支持loader放到外挂flash里ota_addr为0
if (0 == p->ota_addr && !support_norflash_update_en) {
log_error("ota addr err\n");
return;
}
if (priv_param_fill_hdl) {
priv_param_fill_hdl(p);
}
p->parm_crc = CRC16(((u8 *)p) + 2, sizeof(UPDATA_PARM) - 2); //2 : crc_val
}
设计意图:CRC16 覆盖除 crc 字段以外的全部参数,Loader 在跳转前重新计算比对,可发现参数被踩踏或未正确初始化的情况。
support_norflash_update_en分支说明当 Loader 放在外挂 NorFlash 时,parm_type固定为NORFLASH_UPDATA,真实类型被保存在参数块之后 32 字节偏移处——这是一种向后兼容的"类型透传"方案。
升级结果机制与启动检查
升级结果的读写围绕 UPDATA_PARM 的 parm_result 字段展开,并在每次上电时被检查。这是"升级是否成功"这一关键状态在应用与 Loader 之间传递的唯一通道。
读取与清理结果
update_result_get() 在系统启动早期被调用:它读取 UPDATA_FLAG_ADDR 处的参数块,用 CRC16 校验(从 parm_crc 之后开始计算),校验通过后把 parm_result 存入全局标志 g_updata_flag,并将 magic 高 16 位合并进去,随后立即清零参数块,防止重复消费(见 update.c):
u16 update_result_get(void)
{
u16 ret = UPDATA_NON;
if (!UPDATE_SUPPORT_DEV_IS_NULL()) {
UPDATA_PARM *p = UPDATA_FLAG_ADDR;
u16 crc_cal;
crc_cal = CRC16(((u8 *)p) + 2, sizeof(UPDATA_PARM) - 2); //2 : crc_val
if (crc_cal && crc_cal == p->parm_crc) {
ret = p->parm_result;
}
g_updata_flag = ret;
g_updata_flag |= ((u32)(p->magic)) << 16;
memset(p, 0x00, sizeof(UPDATA_PARM));
}
return ret;
}
写入结果
update_result_set(result) 写入升级结果。它特别处理了 UPDIFF_FLASH_UPDATA(差分升级)与 COMBAK_FLASH_UPDATA(组合备份升级)两种类型——这两种类型不写结果,因为其流程由 Loader 完全接管(见 update.c):
void update_result_set(u16 result)
{
if (!UPDATE_SUPPORT_DEV_IS_NULL()) {
UPDATA_PARM *p = UPDATA_FLAG_ADDR;
if (p->parm_type == UPDIFF_FLASH_UPDATA || p->parm_type == COMBAK_FLASH_UPDATA) {
log_info("update updiff/combak\n");
return;
}
/* memset(p, 0x00, sizeof(UPDATA_PARM)); */
p->parm_result = result;
p->parm_crc = CRC16(((u8 *)p) + 2, sizeof(UPDATA_PARM) - 2);
}
}
启动期检查函数
| 函数 | 行为 | 用途 |
|---|---|---|
update_success_boot_check() | 若结果为 UPDATA_SUCC 且类型是 SD0/SD1/USB 升级,返回 true | 避免成功升级后重复升级 |
device_is_first_start() | 判断 DEVICE_FIRST_START、DEVICE_UPDATE_KEY_ERR 位或结果等于 UPDATA_SUCC | 识别首次启动/升级后的首次启动 |
update_result_deal() | 结果非 UPDATA_NON 时进入 while(1) 死循环并持续喂狗(wdt_clear()) | FPGA 环境除外;正常环境等待复位/关机处理 |
vm_need_recover() | 结果低 16 位为 UPDATA_SUCC 时返回 true | 通知 VM(参数区)需要恢复 |
关键常量定义(见 update.c):
#define LOADER_NAME "LOADER.BIN"
#define DEVICE_UPDATE_KEY_ERR BIT(30)
#define DEVICE_FIRST_START BIT(31)
设计意图:
DEVICE_FIRST_START与DEVICE_UPDATE_KEY_ERR作为g_updata_flag的高位标志,与低 16 位的升级结果并存于一个 32 位变量,使启动检查逻辑只需一次读操作即可完成多种判断,同时保持与 Loader 的位级兼容。
升级发起流程:update_mode_api_v2
update_mode_api_v2() 是应用层发起升级的统一入口,流程为:申请参数内存 → 清零 → 填充公共参数与私有参数 → 写回 RAM 参数区 → 关闭外设硬件 → 跳转 Loader。其前半段如下(见 update.c):
void update_mode_api_v2(UPDATA_TYPE type, void (*priv_param_fill_hdl)(UPDATA_PARM *p), void (*priv_update_jump_handle)(int type))
{
u16 update_param_len = sizeof(UPDATA_PARM) + UPDATE_PRIV_PARAM_LEN;
UPDATA_PARM *p = malloc(update_param_len);
if (p) {
memset((u8 *)p, 0x00, sizeof(UPDATA_PARM));
#if 0 //兼容新的ota loader
if (support_vm_data_keep) {//单备份升级,parm_priv参数存vm_addr和vm_len到ota.bin的,传给net_ota.bin网络升级使用
u32 vm_addr = 0, vm_len = 0;
vm_reverse_addr_size_get(&vm_addr, &vm_len);
memcpy(p->parm_priv, &vm_addr, sizeof(vm_addr));
memcpy(p->parm_priv + sizeof(vm_addr), &vm_len, sizeof(vm_len));
}
#endif
update_param_content_fill(type, p, priv_param_fill_hdl);
...
跳转前的硬件收尾由 update_before_jump_common_handle() 完成:关闭 UI、local_irq_disable() 关闭中断、hwi_all_close() 关闭所有硬件中断、wifi_det_close() 关闭 WiFi 检测(见 update.c):
static void update_before_jump_common_handle(UPDATA_TYPE up_type)
{
dev_update_close_ui();
local_irq_disable();
hwi_all_close();
#ifdef CONFIG_SUPPORT_WIFI_DETECT
wifi_det_close();
#endif
/*跳转的时候遇到死掉的情况很可能是硬件模块没关导致,加上保护可以判断哪个异常,保护的地址根据不同SDK而定*/
/* u8 inv = 0; */
/* mpu_set(1, (u32)&test_pro_addr, (u32)test_pro_addr, inv, "0r", DBG_FM); */
}
设计意图:跳转 Loader 前必须把中断与硬件模块全部关闭,否则 Loader 运行期间残留的中断/外设可能破坏 Flash 编程时序。注释中提到的 MPU 保护(被注释)是调试异常跳转时的可选手段。
update_close_hw() 则按名称过滤关闭除指定设备外的所有升级目标硬件驱动——例如 USB 升级时保留 USB 外设、关闭其余外设(见 update.c):
void update_close_hw(void *filter_name)
{
const struct update_target *p;
list_for_each_update_target(p) {
if (memcmp(filter_name, p->name, strlen(filter_name)) != 0) {
printf("close Hw Name : %s\n", p->name);
p->driver_close();
}
}
}
双备份升级引擎 API(db_update_*)
双备份(Double Bank)升级引擎通过 sdk/include_lib/update/db_updata_api.h 暴露,是 AC792N 支持"升级失败可回退"的核心。其典型生命周期为:
db_update_init(max_pkt_len)— 初始化升级任务,设定每次编程的最大包长;db_update_allow_check(fw_size)— 校验目标 Bank 是否有足够空间容纳新固件;db_update_write(data, len, write_complete_cb)— 将下载数据拷贝到临时缓冲并异步通知任务写入非易失存储(每次不超过max_pkt_len);db_update_verify(verify_result_hdl)— 校验整个升级过程是否正确;db_update_burn_boot_info(bak_addr, bak_size, burn_boot_info_result_hdl)— 校验通过后烧录新的启动信息,使系统下次从新 Bank 启动;db_update_exit()— 退出升级任务。
辅助接口:db_update_local_crc_info() 读取本地固件 CRC;db_update_db_package_crc_info(fst_pkt, fst_len, dcrc) 解析升级包首包(至少 128 字节)中的头信息得到远端 CRC;db_update_target_base() 获取目标 Bank 基址与大小;db_update_erase_advance() / db_update_erase_all() 提前或整体擦除升级区;db_update_retry() 允许不重新传输数据包而重试上次升级;db_update_break_last_update_param() 打断上次升级参数。
完整的 API 原型见 db_updata_api.h:
/* @brief:Initializes the update task,and setting the crc value and file size of new fw;
* @param max_ptk_len: Supported maxium length of every programming,it decides the max size of programming every time
*/
int db_update_init(u16 max_pkt_len);
/* @brief:copy the data to temporary buffer and notify task to write non-volatile storage
* @param data:the pointer to download data
* @param len:the length to download data, no larger than max_pkt_len
* @param write_complete_cb:callback for programming done,return 0 if no err occurred
*/
int db_update_write(void *data, u16 len, int (*write_complete_cb)(int err));
/* @brief:After the new fw verification succeed,call this api to program the new boot info for new fw
* @param bak_addr:bakup area for firmware-patch process
* @param bak_size:bak area size
* @param burn_boot_info_result_hdl:this callback for error notification
* if err equals 0,the operate to burn boot info succeed,other value means to fail.
*/
int db_update_burn_boot_info(u32 bak_addr, u32 bak_size, int (*burn_boot_info_result_hdl)(int err));
/* @brief:Verify the db update process is correct
* @param verify_result_hdl:this callback for error notification
*/
int db_update_verify(int (*verify_result_hdl)(int ret));
/* @brief:Try to update again without tranfer package
* @param bak_addr:bakup area for firmware-patch process
* @param bak_size:bak area size
* @param try_result_hdl:this callback for error notification
* ret equals 1 mean can update, 0 mean can not ,other mean error.
*/
int db_update_retry(u32 bak_addr, u32 bak_size, int (*try_result_hdl)(int ret));
CRC 信息结构体定义(见 db_updata_api.h):
/* @brief:struct for firmware crc16&crc32
*/
struct crc_info {
u32 crc32;
u16 crc16;
};
设计意图:
db_update_retry的存在说明引擎具备"断点续传"能力——传输中断后不必重新下载整个固件包,只要目标 Bank 内数据仍完整(CRC 可验),即可直接烧录启动信息完成升级。这对网络升级场景的弱网重试尤为重要。
升级通道矩阵
SDK 在 sdk/apps/common/update/ 下为每种传输介质提供独立实现,全部汇入同一套参数/跳转机制:
| 通道模块 | 文件 | 典型场景 |
|---|---|---|
| UART 升级 | uart_update.c / uart_update_master.c | 产线烧录、售后串口工具 |
| 网络升级 | net_update.c / http_update.c / net_single_backup_update.c | 云端 OTA(HTTP 下载固件包) |
| NorFlash 升级 | norflash_update.c / norflash_ufw_update.c / ex_flash_file_download.c | Loader 置于外挂 NorFlash 的架构 |
| USB 升级 | sdk/apps/common/usb/device/msd_upgrade.c | U 盘(MSD)拖固件升级 |
| SD 卡升级 | sd_upgrade_impl.h(实现层) | SD 卡固件包升级 |
| TWS 升级 | update_tws.c / update_tws_new.c | 真无线耳机主耳→副耳透传升级 |
| 文件系统升级 | fs_update.c / expand_zone_file_update.c | 基于 FS 的扩展区/文件更新 |
| 测试盒升级 | testbox_update.c | 产测盒通信升级 |
Core Flow:一次完整升级的时序
下图以"网络/串口下载 → 双备份引擎写 Bank B → 校验 → 烧 Boot Info → 重启"为主线,展示一次典型升级的完整时序:
sequenceDiagram
participant APP as 业务应用
participant U as update.c 框架
participant CH as 传输通道 (UART/HTTP/USB/SD)
participant DB as db_update_* 引擎
participant FL as Flash (Bank A/B + Boot Info)
participant LD as Loader
APP->>U: 调用 update_mode_api_v2(type, priv_fill, jump)
U->>U: 填充 UPDATA_PARM + CRC16,写入 UPDATA_FLAG_ADDR
U->>U: update_close_hw / 关中断 / hwi_all_close
U->>LD: 跳转 Loader (LOADER.BIN / ota.bin)
LD->>DB: db_update_init(max_pkt_len)
DB->>FL: 读取本地 CRC (db_update_local_crc_info)
CH->>DB: 传输固件数据 (db_update_write, 每包 ≤ max_pkt_len)
DB->>FL: 写入 Bank B(异步,write_complete_cb 回调)
CH->>DB: 数据传完
DB->>FL: db_update_verify 校验 Bank B CRC
alt 校验通过
DB->>FL: db_update_burn_boot_info 烧录新 Boot Info
FL-->>DB: 成功 (err == 0)
DB->>LD: 结束,复位重启
LD->>APP: 从 Bank B 启动,update_result_get 读到结果
else 校验失败
DB-->>CH: verify_result_hdl 报错
CH->>CH: 可选 db_update_retry / 上报失败
FL-->>LD: 保持旧 Boot Info,仍从 Bank A 启动
end
流程要点:
- 应用层只负责组织参数与跳转,不直接操作 Flash——实际编程发生在 Loader 上下文;
- 数据先入 RAM 临时缓冲,再由引擎任务异步写入非易失存储,
write_complete_cb在每包编程完成后回调,便于通道层做流控; - 校验通过前不碰 Boot Info,这是"升级失败仍可启动"的根因;校验失败时旧 Bank 的启动信息原封不动;
- 升级结果经
UPDATA_PARM.parm_result持久化,下次启动由update_result_get()消费并清零。
失败模式、边界情况与并发
升级结果非预期
update_result_get()中 CRC16 不匹配时ret保持UPDATA_NON,参数块被清零,系统按"无升级记录"处理——避免脏数据触发错误分支;update_result_deal()对非UPDATA_NON/0 的结果进入死循环并喂狗,等待复位。源码注释明确提示"关机要慎重,要设置开机键",即量产中该路径依赖外部复位手段。
空间不足
db_update_allow_check(fw_size) 在写入前校验目标 Bank 容量;若新固件大于 Bank 大小,写入不得开始。调用方必须先用 db_update_target_base() 查询 Bank 基址/大小并做容量判断。
并发与异步编程
db_update_write() 是"拷贝到临时缓冲 + 异步编程"模型,len 不得超过 db_update_init 传入的 max_pkt_len,否则会破坏缓冲边界。通道层必须串行调用写接口,不能并发写多个包;编程完成信号依赖 write_complete_cb,编程期间 Flash 处于忙状态,任何并发读(如 db_update_read)都应与写互斥。
升级中断与重试
- 传输中断:目标 Bank 数据可能不完整,但引擎支持
db_update_retry()基于已写数据的 CRC 判断能否直接完成; - 需要强制重新开始:调用
db_update_break_last_update_param()打断上次升级参数,再走完整流程; - 擦除时序:大面积擦除耗时较长,
db_update_erase_advance()通过erase_state_pause回调上报擦除进度/暂停点,便于上层维持看门狗或 UI 提示,避免擦除期间被复位。
启动边界标志
DEVICE_FIRST_START(BIT31)与 DEVICE_UPDATE_KEY_ERR(BIT30)是 g_updata_flag 中的特殊状态位:前者标识首次启动(触发 VM 恢复等初始化),后者标识升级密钥错误(如差分升级鉴权失败)。两者都使 device_is_first_start() 返回 true,引导系统走"首次启动"路径。
性能与运维注意事项
- 包长即吞吐:
db_update_init(max_pkt_len)的max_pkt_len决定每次编程的数据量,直接影响写 Flash 的吞吐;增大包长可减少编程任务切换开销,但受 RAM 缓冲与通道 MTU 限制; - 擦除是耗时大头:升级区擦除常比写入更慢,
db_update_erase_advance/db_update_erase_all允许把擦除提前到下载之前(后台并行),以缩短用户感知的升级时间; - 喂狗义务:
update_result_deal()与擦除回调路径中都涉及wdt_clear(),说明升级流程必须主动喂狗,防止长 Flash 操作触发看门狗复位; - 产线烧录:UART/测试盒通道面向产线,强调确定性时序;OTA(HTTP)通道面向售后,依赖
db_update_retry提供弱网容错; - 调试手段:跳转死机多为外设未关闭,可用被注释的
mpu_set(...)保护地址辅助定位异常。
扩展点
- 新增传输通道:仿照
sdk/apps/common/update/下既有通道(如uart_update.c),实现数据获取后调用db_update_write/verify/burn_boot_info即可接入双备份引擎,或直接复用update_mode_api_v2走 Loader 流程; - 私有参数扩展:通过
update_mode_api_v2的priv_param_fill_hdl回调向UPDATA_PARM.parm_priv写入通道专属参数;若 Loader 位于外挂 NorFlash,注意update_param_priv_fill()会把 NorFlash 参数放在parm_priv前面,私有参数须追加在其后; - 特殊升级类型:
UPDIFF_FLASH_UPDATA(差分)与COMBAK_FLASH_UPDATA(组合备份)不经过update_result_set,由 Loader 全权接管,适合需要自定义回滚策略的升级; - UI/状态接入:
led_update_start()/led_update_finish()与dev_update_close_ui()为升级过程的指示与收尾预留了钩子(当前为空实现),可在此接入 LED/屏显升级状态。
Related Links
- 升级框架主实现:update.c
- 双备份升级 API 头文件:db_updata_api.h
- 升级适配层:upgrade_adapter.h
- SD 卡升级实现:sd_upgrade_impl.h
- USB MSD 升级:msd_upgrade.c
- UART 升级:uart_update.c
- 网络升级:net_update.c / http_update.c
- NorFlash 升级:norflash_update.c / norflash_ufw_update.c
- TWS 升级:update_tws.c / update_tws_new.c
- SDK 总览:README.md
Configuration Options(编译期/运行时配置)
升级子系统的主要开关来自 app_config.h / 宏定义与 SDK 全局配置,源码中可直接观测到的包括:
| 配置/符号 | 类型 | 默认行为 | 说明 |
|---|---|---|---|
UPDATE_SUPPORT_DEV_IS_NULL() | 宏 | 依据目标设备 | 判断升级支持设备是否存在;结果读写前必须判空 |
support_norflash_update_en | 常量 | 0 | 使能外挂 NorFlash 上的 Loader/OTA;使能时 parm_type 固定为 NORFLASH_UPDATA,真实类型偏移保存 |
support_vm_data_keep | 常量 | 0 | 单备份升级时是否在 parm_priv 中携带 VM 地址/长度(当前代码中该分支被 #if 0 禁用,兼容新 ota loader 用) |
CONFIG_FPGA_ENABLE | 宏 | 关 | FPGA 验证环境跳过 update_result_deal 的死循环等待 |
CONFIG_SUPPORT_WIFI_DETECT | 宏 | 关 | 跳转 Loader 前调用 wifi_det_close() 关闭 WiFi 检测 |
THIRD_PARTY_PROTOCOLS_SEL & RCSP_MODE_EN | 宏 | 关 | 使能 RCSP(杰理私有协议)时引入 rcsp_cfg.h 相关逻辑 |
UPDATE_PARAM_MAGIC | 常量 | — | UPDATA_PARM.magic 的魔数值,Loader 侧用于参数合法性识别 |
UPDATE_PRIV_PARAM_LEN | 常量 | — | 私有参数区长度,update_mode_api_v2 按 sizeof(UPDATA_PARM) + UPDATE_PRIV_PARAM_LEN 申请内存 |
说明:以上符号中部分为 SDK 全局宏(由构建系统注入),本文档基于
update.c/db_updata_api.h源码中的直接引用列出;具体数值请以对应产品的app_config.h与构建配置为准。
API Reference 摘要
应用层升级框架(update.c)
| 函数 | 签名要点 | 说明 |
|---|---|---|
update_mode_api_v2 | (UPDATA_TYPE type, void (*priv_param_fill_hdl)(UPDATA_PARM*), void (*priv_update_jump_handle)(int)) | 统一升级入口:填参数 → 跳 Loader |
update_result_get | u16 update_result_get(void) | 读取并清零升级结果(带 CRC16 校验) |
update_result_set | void update_result_set(u16 result) | 写入升级结果(差分/组合备份类型除外) |
update_success_boot_check | bool update_success_boot_check(void) | 成功升级后的首次启动检查 |
device_is_first_start | bool device_is_first_start(void) | 首次启动/升级密钥错误判断 |
update_result_deal | int update_result_deal(void) | 处理非预期结果(喂狗等待复位) |
update_close_hw | void update_close_hw(void *filter_name) | 关闭除指定名称外的全部升级目标硬件 |
update_param_priv_fill | (UPDATA_PARM *p, void *priv, u16 priv_len) | 填充私有参数,NorFlash 模式下追加在 NorFlash 参数之后 |
vm_need_recover | bool vm_need_recover(void) | VM 恢复判断(结果为 UPDATA_SUCC 时) |
双备份升级引擎(db_updata_api.h)
| 函数 | 参数 | 返回 | 说明 |
|---|---|---|---|
db_update_init | u16 max_pkt_len | int | 初始化升级任务,设定每次编程最大包长 |
db_update_exit | 无 | int | 退出升级任务 |
db_update_local_crc_info | struct crc_info *lcrc | int | 获取本地固件 CRC16/CRC32 |
db_update_db_package_crc_info | u8 *fst_pkt, u32 fst_len, struct crc_info *dcrc | int | 解析升级包首包(≥128 字节)头部得到远端 CRC |
db_update_allow_check | u32 fw_size | int | 校验目标 Bank 空间是否足够(须在 init 之后调用) |
db_update_write | void *data, u16 len, int (*write_complete_cb)(int err) | int | 拷贝数据到临时缓冲并异步编程,len ≤ max_pkt_len |
db_update_read | void *data, u32 len, u32 offset | int | 从目标 Bank 按偏移读取数据 |
db_update_burn_boot_info | u32 bak_addr, u32 bak_size, int (*burn_boot_info_result_hdl)(int err) | int | 校验通过后烧录新 Boot Info,err==0 表示成功 |
db_update_target_base | u32 *bank_base, u32 *bank_size | int | 获取目标 Bank 基址与大小 |
db_update_verify | int (*verify_result_hdl)(int ret) | int | 校验双备份升级过程是否正确 |
db_update_retry | u32 bak_addr, u32 bak_size, int (*try_result_hdl)(int ret) | int | 不重新传输数据包重试升级;ret==1 可升级、0 不可、其他为错误 |
db_update_break_last_update_param | 无 | int | 打断上次升级参数,强制重新开始 |
db_update_erase_advance | int (*erase_state_pause)(int ret) | int | 提前擦除升级区,回调上报擦除进度(ret==1 结束、0 擦除中) |
db_update_erase_all | 无 | void | 整体擦除升级区 |
回调语义约定(源自头文件注释):write_complete_cb/burn_boot_info_result_hdl/verify_result_hdl 中 err/ret == 0 表示成功,非 0 表示失败;try_result_hdl 中 ret == 1 表示可升级、0 表示不可、其他值表示错误。
总结
AC792N SDK 的烧录与升级能力建立在"应用层参数握手 + Loader 执行 + 双备份引擎编程"的三段式架构上:update.c 负责把升级类型、结果与 CRC 组装进 UPDATA_PARM 并跳转 Loader;db_update_* 系列 API 负责把新固件安全写入备份 Bank,校验通过后才切换 Boot Info;UART、HTTP、NorFlash、USB/SD、TWS 等通道各自负责数据搬运,最终汇聚到同一引擎。这套设计在保证多通道统一的同时,通过 CRC 保护、双 Bank 回退与断点重试,为产线与 OTA 场景提供了断电安全的升级保障。