杰理 SDK 文档中心
首页
首页
  • 概述与快速开始

    • SDK 总览与芯片能力
    • 环境搭建与编译构建
    • 烧录与固件升级
    • 文档与版本资源
  • 应用与示例方案

    • demo 示例工程
    • WiFi 摄像头方案 (wifi_camera)
    • WiFi 音箱方案 (wifi_soundbox)
    • WiFi 婴儿监护方案 (wifi_bbm)
    • 公共应用模块库
    • 示例代码库 (example)
  • 系统架构与平台

    • 总体架构与工程分层
    • 系统启动与运行框架
    • 芯片驱动与板级适配
    • 设备管理与文件系统
    • 系统工具库与算法
  • 音频子系统

    • 音频框架与处理节点
    • 音频编解码与音效
    • 播放器与录音器
    • 语音交互与 AI 唤醒
    • LE Audio 与蓝牙音频
    • 音频调试与歌词
  • 视频与显示子系统

    • 摄像头驱动与 ISP
    • 视频编码与图像处理
    • 显示与 GPU 加速
    • 屏幕镜像 (screen_mirror)
  • 无线连接与网络

    • 蓝牙协议栈 (双模蓝牙)
    • WiFi 协议栈与配网
    • 网络协议栈
    • 云平台与 IoT 协议
  • UI 子系统

    • LVGL 集成与应用
    • UI 工程与工具链
  • 配置系统

    • 功能配置
    • 板级配置
    • 网络与蓝牙配置
    • 音频配置与提示音
  • 工具与测试

    • 产测与射频测试工具
    • 固件升级与更新机制
    • 调试与日志工具
  • 硬件参考设计

    • 原理图参考设计
    • 芯片数据手册

固件升级与更新机制

AC792N SDK 的固件升级子系统是一套支持多传输通道(RCSP/APP、HTTP、网络)、多存储介质(NOR Flash、UFW 分区、扩展区)、带 CRC 校验与备份回滚的完整 OTA 更新框架。本文档从入口、核心流程、存储布局、校验算法到失败处理,系统性地解析该机制的实现。

Purpose and Scope

本页覆盖 AC792N SDK 中与"固件升级与更新"相关的完整能力:

  • 升级子系统的文件构成与分层(sdk/apps/common/update/ 目录)
  • NOR Flash 升级通路(norflash_update.c)的逐行实现剖析:loader 下载、写入、CRC16 回读校验
  • RCSP 升级通道(APP 通过蓝牙/串口下发升级包)与 TWS 双设备升级
  • 网络升级(http_update.c、net_update.c)与单备份升级(net_single_backup_update.c)
  • 压缩升级(compress_update)与扩展区文件更新(expand_zone_file_update.c、ex_flash_file_download.c)

不在本页范围:RCSP 协议本身的帧格式与蓝牙连接管理(属于蓝牙协议栈/Profile 范畴)、文件系统 nor_fs 的底层实现、网络协议栈细节。这些能力各有独立页面,本文仅在必要时引用其入口。

Overview

固件升级是嵌入式音频/IoT 设备(AC792N 芯片)生命周期管理中最关键的一环:设备出厂后需要在线修复缺陷、更新算法参数或增加功能。AC792N SDK 的升级机制采用**"下载即校验、校验即生效、失败可回退"**的设计原则:

  1. 多通道接入:升级请求既可以来自本地调试(loader 文件直接写入),也可以来自 APP 通过 RCSP 协议(蓝牙)、或通过 HTTP/网络远程拉取固件包。
  2. 分阶段落盘:升级包不直接覆盖运行区,而是先写入独立的升级区域(如 storage/nor_fs/C/loader.bin),全部写完后统一回读校验,校验通过才触发切换。
  3. CRC 完整性保证:写入完成后用 CRC16_with_initval 全量回读计算,与下发方提供的 loader_crc 比对,任何一位错误都会导致升级被拒绝,从而避免半砖设备。
  4. 备份与回滚:net_single_backup_update.c 等模块在升级前保留运行固件副本,升级失败时可恢复旧版本。

这套机制的核心价值在于:把"不可靠的无线传输"与"不可中断的固件运行区"解耦——传输可以断点重来、校验失败可以丢弃重下,而运行区只有在全部数据完整时才被切换,最大程度降低升级导致的设备变砖风险。

Architecture

flowchart TD
    subgraph sg_Channel["升级入口 / 传输通道"]
        RCSP["rcsp_update.c<br/>RCSP APP 升级(服务器)"]
        RCSP_TWS["rcsp_update_tws.c<br/>TWS 双设备升级"]
        RCSP_M["rcsp_update_master.c<br/>RCSP 升级客户端"]
        HTTP["http_update.c<br/>HTTP 下载升级"]
        NET["net_update.c<br/>网络升级"]
    end

    subgraph sg_Core["升级核心 / 调度与校验"]
        FS_UPDATE["fs_update.c<br/>文件系统更新"]
        UFW["norflash_ufw_update.c<br/>UFW 固件格式更新"]
        SINGLE_BACKUP["net_single_backup_update.c<br/>单备份升级"]
        EXPAND["expand_zone_file_update.c<br/>扩展区文件更新"]
    end

    subgraph sg_Storage["存储 / 落盘层"]
        NOR["norflash_update.c<br/>NOR Flash loader 写入与 CRC 校验"]
        EXFLASH["ex_flash_file_download.c<br/>外挂 Flash 下载"]
        NORFS[("nor_fs 文件系统<br/>storage/nor_fs/C/loader.bin")]
        UFW_ZONE[("UFW 固件分区")]
    end

    RCSP --> FS_UPDATE
    RCSP --> UFW
    RCSP_TWS --> UFW
    RCSP_M --> UFW
    HTTP --> NET
    NET --> SINGLE_BACKUP
    SINGLE_BACKUP --> NOR
    FS_UPDATE --> NOR
    UFW --> NOR
    EXPAND --> EXFLASH
    NOR --> NORFS
    UFW --> UFW_ZONE

架构解读:整个升级子系统呈"入口 → 核心 → 存储"三层结构。上层是各类传输通道(RCSP 是杰理私有协议,用于 APP 通过蓝牙/串口与设备交互;HTTP/网络用于远程 OTA);中层是升级策略实现,负责将下载的数据按目标格式(UFW 通用固件格式、文件系统更新、扩展区更新)组织;底层 norflash_update.c 是所有 NOR Flash 介质升级的公共落盘与校验单元——它把升级包以普通文件的方式写入 nor_fs 文件系统的 loader.bin,再用 CRC16 回读校验,是整套机制的"最后一道保险"。

升级子系统文件构成

固件升级相关代码集中分布在两个位置:sdk/apps/common/update/(通用更新核心)与 sdk/apps/common/third_party_profile/jieli/rcsp/(RCSP 协议升级通道)。官方文档入口位于 cache/V1.0.0/docs/html/_sources/SDK/system/通用组件/update/,其 index.rst.txt 通过 toctree 挂载了 compress_update.rst(压缩升级)章节。

文件角色归属层
update/norflash_update.cNOR Flash loader 写入 + CRC16 回读校验存储层(核心)
update/norflash_ufw_update.cUFW 通用固件格式升级升级核心
update/fs_update.c基于文件系统的固件更新调度升级核心
update/http_update.cHTTP 通道下载固件包入口/传输
update/net_update.c通用网络升级入口入口/传输
update/net_single_backup_update.c单备份(升级前保留旧固件)升级策略升级核心
update/expand_zone_file_update.c扩展区文件更新(参数/资源区)升级核心
update/ex_flash_file_download.c外挂 Flash 文件下载存储层
rcsp/server/functions/rcsp_update/rcsp_update.c/.hRCSP 升级服务器(APP 主控侧服务)入口/传输
rcsp/server/functions/rcsp_update/rcsp_update_tws.c/.hTWS 双耳联动升级入口/传输
rcsp/server/functions/rcsp_update/rcsp_ch_loader_download.c/.hRCSP loader 下载专用通道入口/传输
rcsp/client/rcsp_m_update/rcsp_update_master.c/.hRCSP 升级客户端(设备作为主控)入口/传输

说明:本表基于目录清单与文件名推断各模块职责;除 norflash_update.c 已逐行核实外,其余模块的职责描述来自命名与 RCSP 框架约定,细节以各文件源码为准。

NOR Flash 升级通路(norflash_update.c)逐行剖析

norflash_update.c 是整个升级机制中与硬件最近的公共单元。它没有直接操作 flash 寄存器,而是复用 nor_fs 文件系统:把升级包(loader)当做一个普通文件写入,之后回读该文件计算 CRC16,与下发方携带的校验值比对。这种"文件化落盘 + 回读校验"的设计让上层通道(RCSP/网络/本地)完全不需要关心 flash 的擦写细节。

1. 升级区域参数结构

typedef struct _nor_fs_part {
    u32 update_file_addr;
    u32 update_area_start_addr;
    u32 update_area_end_addr;
} nor_fs_parm;

nor_fs_parm update_norfs_parm;

Source: norflash_update.c

该结构记录升级文件在 flash 中的绝对地址(update_file_addr)以及整个升级区域的起始/结束地址。它由全局变量 update_norfs_parm 持有,供上层通过 get_nor_update_param() 查询——设计意图是让上层(如 bootloader 或 RCSP 服务)在复位切换前能够拿到升级包的物理位置,从而决定从哪个地址启动新固件。

2. 打开升级文件(norflash_f_open)

#define NORFLASH_LOADER_PATH  "storage/nor_fs/C/loader.bin"

int norflash_f_open(u32 loader_len)
{
    fd = fopen(NORFLASH_LOADER_PATH, "w+");
    if (!fd) {
        r_printf("update fopen err\n");
        return -1;
    } else {
        g_printf("update fopen succ\n");
        update_norfs_parm.update_file_addr = nor_get_absolute_addr();
        update_norfs_parm.update_area_start_addr = nor_get_start_addr();
        update_norfs_parm.update_area_end_addr = update_norfs_parm.update_area_start_addr + nor_get_capacity();
        if (loader_len > nor_get_capacity) {
            return -2;
        }
    }
    return 0;
}

Source: norflash_update.c

要点:

  • 升级包固定写入 storage/nor_fs/C/loader.bin,"w+" 模式表示可写可读,为后续回读校验做准备。
  • 文件打开成功后,通过 nor_get_absolute_addr() / nor_get_start_addr() / nor_get_capacity() 三个查询函数填充升级区域参数。这三个函数由底层 norflash 驱动提供(本文件仅声明),实际返回的是当前 loader 存储分区的物理布局。
  • 边界检查 loader_len > nor_get_capacity 存在一个明显的笔误:nor_get_capacity 是函数地址而非调用(应为 nor_get_capacity()),因此该判断实际是"loader 长度大于函数地址则失败"。这是源码中可留意的缺陷,生产代码通常不会触发,但审计时应修正为函数调用。
  • 失败码约定:-1 表示打开文件失败(如文件系统未挂载、空间不足),-2 表示升级包超过升级区容量。

3. 写入回调(norflash_f_write)

static u16 norflash_f_write(void *buff, u32 addr, u32 len)
{
    if (fd) {
        return fwrite(fd, buff, len);
    }
    return 0;
}

Source: norflash_update.c

norflash_f_write 通过 register_loader_write_handler() 注册为 loader 写入回调。上层(如 RCSP 的 rcsp_ch_loader_download 或 UFW 升级流程)只调用统一的写处理器接口,无需感知底层是 nor_fs 文件系统还是裸 flash——这是典型的回调解耦设计:norflash_loader_start() 把静态写函数挂到全局处理器上,升级数据流便与具体存储实现解耦。注意 addr 参数在此实现中被忽略,因为文件系统内部自己维护写位置。

4. CRC16 回读校验(norflash_update_verify)

u32 norflash_update_verify(u32 loader_len, u32 loader_crc)
{
    u32 len = loader_len;
    u32 crc_temp = 0;
    if (fd) {
        fseek(fd, NOR_FS_SEEK_SET, 0);          //偏移到文件起始
        u16 r_len;

        u8 *temp_buf = malloc(512);
        if (NULL == temp_buf) {
            goto _ERR_RET;
        }
        u16 temp_buf_len = 512;

        while (len) {
            r_len = (len > temp_buf_len) ? temp_buf_len : len;

            fread(fd, temp_buf, r_len);
            crc_temp = CRC16_with_initval(temp_buf, r_len, crc_temp);

            len -= r_len;
        }

_ERR_RET:
        if (NULL != temp_buf) {
            free(temp_buf);
        }

        if (crc_temp == loader_crc) {
            return 0;
        }
    }
    return 1;
}

Source: norflash_update.c

这是升级安全的核心校验函数,设计意图值得细读:

  • fseek(fd, NOR_FS_SEEK_SET, 0) 把文件指针归零,从文件头开始回读——校验的是"实际写入 flash 的字节",而非内存中缓存的字节,能捕获写中断、坏块、部分擦除等真实存储错误。
  • 以 512 字节为块循环 fread + CRC16_with_initval 累积计算 CRC。crc_temp 作为初始值传入下一轮,实现跨块的链式 CRC——这与传输层分片下发、接收端边收边算的校验方式保持一致,双方使用同一初始值(通常为 0 或 0xFFFF)即可对齐。
  • 内存不足时跳转到 _ERR_RET,此时 crc_temp 仍为 0,与 loader_crc 相等才会误判通过;因此实际上下层保证 loader_crc 非 0,或该路径因返回 1(校验失败)被上层拒绝。这是一个理论上存在、实践中依赖上层约束的边界。
  • 返回值约定:0 = 校验通过,1 = 校验失败或文件句柄无效。上层只有在收到 0 时才允许提交升级(复位/切换分区)。

5. 收尾与启动入口

void norflash_update_close()
{
    if (fd) {
        fclose(fd);
    }
}

int  norflash_loader_start(u32 loader_len)
{
    register_loader_write_handler(norflash_f_write);
    return norflash_f_open(loader_len);
}

Source: norflash_update.c

  • norflash_loader_start() 是升级流程的标准入口:先注册写回调,再打开升级文件。返回 0 表示就绪,负值表示失败(见上文失败码)。
  • norflash_update_close() 负责释放文件句柄,必须在校验通过、提交升级前调用,否则 nor_fs 缓存可能未落盘。

6. 参数查询(get_nor_update_param)

int get_nor_update_param(void *buf)
{
    if (buf) {
        printf("file_addr:0x%x start_addr:0x%x end_addr:0x%x\n", update_norfs_parm.update_file_addr, update_norfs_parm.update_area_start_addr, update_norfs_parm.update_area_end_addr);
        memcpy(buf, (u8 *)&update_norfs_parm, sizeof(update_norfs_parm));
    }
    return sizeof(update_norfs_parm);
}

Source: norflash_update.c

该函数把升级区域的物理布局暴露给调用方(典型调用方是 bootloader 切换逻辑或调试工具),返回结构体字节数;buf 为 NULL 时仅返回长度,可用来先查询大小再分配缓冲区。

RCSP 升级通道(APP 蓝牙升级)

RCSP(杰理私有远程控制/服务协议)升级是消费类音频产品(耳机、音箱)最主要的升级方式:手机 APP 通过蓝牙把固件分包下发到设备。相关实现位于 sdk/apps/common/third_party_profile/jieli/rcsp/,按角色分为三组:

  • 服务器侧(设备作为被控端):rcsp/server/functions/rcsp_update/rcsp_update.c 处理 APP 的升级请求、版本协商、数据分片接收与写入调度;rcsp_ch_loader_download.c 是专门的 loader 下载通道,负责把 APP 下发的 loader 数据交给 norflash_loader_start() 注册的写回调;rcsp_update_tws.c 处理 TWS 双耳场景——主耳收到升级包后需要同步转发给副耳,保证两只耳机固件版本一致。
  • 客户端侧(设备作为主控去升级从设备):rcsp/client/rcsp_m_update/rcsp_update_master.c 用于设备主动向对端(如充电盒、另一只耳机)发起升级,是服务器侧的镜像角色。

RCSP 升级与底层 norflash_update.c 的衔接正是通过 register_loader_write_handler() 完成:RCSP 数据帧解析出 loader 字节流后,直接调用写处理器落盘,全程不感知 flash 细节;收包完成后调用 norflash_update_verify() 做整体 CRC 校验,再触发复位切换。

网络/HTTP 升级通道

flowchart LR
    subgraph sg_Net["网络升级路径"]
        HTTP["http_update.c"] --> NET["net_update.c"]
        NET --> SINGLE["net_single_backup_update.c<br/>单备份策略"]
        SINGLE --> NOR["norflash_update.c<br/>落盘 + CRC"]
    end
  • http_update.c:通过 HTTP 协议从服务器拉取固件包,适用于有 Wi-Fi 能力的设备(如本 SDK 中的 wifi_camera 应用)。负责建立连接、按块下载、断点续传等传输层工作。
  • net_update.c:网络升级的统一入口,封装下载完成后到写入/校验的衔接。
  • net_single_backup_update.c:实现"单备份"升级策略——升级前把当前运行固件备份到保留区,新固件写入后若校验失败或启动异常,可回滚到备份版本。该策略是无线 OTA 场景下降低变砖概率的关键设计。

压缩升级(compress_update)

官方文档目录 cache/V1.0.0/docs/html/_sources/SDK/system/通用组件/update/ 下的 index.rst.txt 通过 toctree 挂载了 compress_update.rst 章节,说明 SDK 支持压缩升级:固件包先压缩再传输,设备端解压后写入。压缩升级的价值在于显著减少无线传输时间与电量消耗——对蓝牙 BLE 这种低吞吐、短连接窗口的通道尤为重要。具体压缩算法与解压流程详见该文档章节:compress_update.rst。

Core Flow:一次完整升级的生命周期

以下时序图描述"APP 通过 RCSP 发起、设备经 NOR Flash 通路落盘"的典型升级流程(网络/HTTP 路径的差异仅在传输层):

sequenceDiagram
    participant APP as 手机APP (RCSP)
    participant RCSP as rcsp_update.c
    participant LOADER as rcsp_ch_loader_download.c
    participant NOR as norflash_update.c
    participant FS as nor_fs (loader.bin)

    APP->>RCSP: 升级请求(固件版本/长度/CRC)
    RCSP->>RCSP: 版本协商与合法性检查
    RCSP->>NOR: norflash_loader_start(loader_len)
    NOR->>NOR: register_loader_write_handler(norflash_f_write)
    NOR->>FS: fopen("storage/nor_fs/C/loader.bin","w+")
    FS-->>NOR: fd 就绪(0) / 失败(-1,-2)
    RCSP->>LOADER: 进入 loader 下载通道
    loop 分片下发
        APP->>LOADER: 数据分片(含片内CRC)
        LOADER->>NOR: norflash_f_write(buff, addr, len)
        NOR->>FS: fwrite(fd, buff, len)
    end
    APP->>RCSP: 下载完成 + loader_crc
    RCSP->>NOR: norflash_update_verify(loader_len, loader_crc)
    NOR->>FS: fseek 归零 + 512B分块回读
    NOR->>NOR: CRC16_with_initval 链式累积
    FS-->>NOR: 回读字节流
    NOR-->>RCSP: 0(通过) / 1(失败)
    alt 校验通过
        RCSP->>NOR: norflash_update_close()
        RCSP->>RCSP: 复位/切换启动新固件
    else 校验失败
        RCSP->>RCSP: 丢弃升级包, 保持旧固件运行
    end

流程要点:

  1. 先协商后下载:升级不是"拿到就写",而是先做版本协商,避免重复刷写相同版本、防止降级(是否允许降级由上层策略决定)。
  2. 写回调提前注册:norflash_loader_start() 在打开文件前先注册写处理器,保证任何分片数据到达时写入路径已就绪。
  3. 边收边写、整体校验:传输过程中不做全局校验(避免内存缓冲整包),全部落盘后一次性回读校验。这一设计把内存占用压到 512 字节级,代价是坏块只能在收包完成后被发现——因此上层必须支持"校验失败后重新发起升级"。
  4. 失败零成本回退:校验失败时设备仍运行旧固件,只是丢弃新包,不产生任何副作用;这是整套机制安全性的基石。

API Reference

以下 API 均来自 sdk/apps/common/update/norflash_update.c,是 NOR Flash 升级通路的公共接口(源码出处见各节签名后的链接)。

int norflash_loader_start(u32 loader_len)

升级流程标准入口:注册写回调并打开升级文件。

  • 参数:loader_len — 待写入 loader 的长度(字节)。
  • 返回:0 成功;-1 打开 storage/nor_fs/C/loader.bin 失败(文件系统未就绪/空间不足);-2 loader 长度超出升级区容量(受源码中 loader_len > nor_get_capacity 笔误影响,该分支实际很少触发)。

Source: norflash_update.c

int norflash_f_open(u32 loader_len)

打开升级文件并初始化升级区域参数。

  • 参数:loader_len — 待写入 loader 的长度(字节)。
  • 返回:0 成功;-1 fopen 失败;-2 长度超容量。
  • 副作用:填充全局 update_norfs_parm(update_file_addr、update_area_start_addr、update_area_end_addr)。

Source: norflash_update.c

static u16 norflash_f_write(void *buff, u32 addr, u32 len)

loader 写入回调(经 register_loader_write_handler 注册)。

  • 参数:buff 数据指针;addr 目标地址(当前实现忽略,由文件系统维护写位置);len 写入长度。
  • 返回:实际写入字节数;文件句柄无效时返回 0。

Source: norflash_update.c

u32 norflash_update_verify(u32 loader_len, u32 loader_crc)

回读升级文件并链式计算 CRC16,与下发方校验值比对。

  • 参数:loader_len 总长度;loader_crc 期望 CRC 值。
  • 返回:0 校验通过;1 校验失败(含文件句柄无效、内存分配失败路径)。
  • 实现:512 字节分块 fread + CRC16_with_initval 累积;内存不足时 goto _ERR_RET,此时 crc_temp 为 0,依赖上层保证 loader_crc != 0 避免误判。

Source: norflash_update.c

void norflash_update_close()

关闭升级文件句柄,必须在提交升级(复位切换)前调用以确保缓存落盘。

Source: norflash_update.c

int get_nor_update_param(void *buf)

导出升级区域物理布局参数。

  • 参数:buf 输出缓冲区(nor_fs_parm 结构,16 字节);可为 NULL。
  • 返回:sizeof(nor_fs_parm)。

Source: norflash_update.c

void register_loader_write_handler(u32(*hdl)(const void *, u32, u32))

注册 loader 写入回调(由底层驱动/其他模块实现),norflash_loader_start 内部调用,也是 RCSP 等上层通道与存储层解耦的钩子。

Source: norflash_update.c

Configuration Options

配置项类型默认值说明
NORFLASH_LOADER_PATH宏(路径字符串)"storage/nor_fs/C/loader.bin"loader 升级包在 nor_fs 中的落盘路径;"w+" 模式打开,支持写入后回读校验
update_norfs_parm.update_file_addru32由 nor_get_absolute_addr() 填充升级文件在 flash 中的绝对地址
update_norfs_parm.update_area_start_addru32由 nor_get_start_addr() 填充升级区域起始地址
update_norfs_parm.update_area_end_addru32start + nor_get_capacity()升级区域结束地址(= 起始 + 容量)
CRC 分块大小常量(局部)512 字节回读校验时的临时缓冲区大小,即每次 fread/CRC 累积的块长

Source: norflash_update.c

Failure Modes, Edge Cases & Concurrency

已核实的失败路径

失败场景表现处理
fopen 失败(文件系统未挂载/空间不足)norflash_f_open 返回 -1,打印 update fopen err上层应终止升级流程并保持旧固件
loader 长度超容量返回 -2终止升级;注意该判断在源码中因笔误(nor_get_capacity 未加括号)实际几乎不生效
CRC 校验失败norflash_update_verify 返回 1丢弃升级包,设备继续运行旧固件,等待重试
回读时内存分配失败malloc(512) 返回 NULL,跳 _ERR_RETcrc_temp 保持 0,若 loader_crc 恰为 0 会误判通过——上层应保证下发 CRC 非 0
文件句柄无效时写入norflash_f_write 返回 0上层应检测写入字节数不足并中止

边界与一致性

  • 写地址语义:norflash_f_write 忽略 addr 参数,位置由 nor_fs 文件系统内部维护;调用方若依赖绝对地址写入(如裸 flash 直写场景)需使用其他通路(norflash_ufw_update.c 等)。
  • 校验与切换的原子性:CRC 通过后立即 close + 复位,切换期间不可被打断;get_nor_update_param 供 bootloader 在复位后定位新固件入口。
  • 并发模型:本模块通过全局 fd 与全局 update_norfs_parm 工作,不支持多线程/多任务并发升级。升级期间应保证只有一个写者(RCSP 或网络通道二选一),且写回调注册是覆盖式(后注册者生效)。TWS 场景下主/副耳的升级由 rcsp_update_tws.c 串行编排,避免双通道竞争同一 fd。

传输层与存储层的一致性保证

设计上,"边收边写 + 整体回读校验"意味着传输层的分片 CRC 只保证单包无错,全量正确性由最终的 norflash_update_verify 兜底。因此任何"最后一包已确认"都不能作为升级成功的依据——必须以 norflash_update_verify == 0 为唯一成功判据。

Performance & Operational Notes

  • 内存占用极低:CRC 回读校验仅需 512 字节临时缓冲区,加上文件句柄与参数结构,整个 NOR 升级通路的 RAM 开销不超过 1KB——这对音频设备紧张的 RAM 预算至关重要,也是选择"边收边写、整体校验"而非"整包缓冲"的根本原因。
  • I/O 模式:写入为流式 fwrite,校验为 512 字节分块顺序 fread。顺序读写对 nor_fs 友好,但每块一次文件系统调用;若升级包达数百 KB,校验阶段会成为 CPU/I/O 热点,可考虑增大分块(如 4KB)换取吞吐,代价是 RAM 占用上升。
  • CRC 计算成本:CRC16_with_initval 为逐字节查表/移位实现,属轻量计算;但对大固件包全量计算仍需毫秒级时间,建议在校验期间关闭不必要的定时任务或延长看门狗,防止校验被误判为死机。
  • 运维建议:OTA 服务器应随包下发 loader_crc(与设备端 CRC16_with_initval 使用相同初始值),并支持断点重传;设备端日志(update fopen succ / update fopen err)可作为升级失败的快速定位线索。
  • 升级区容量规划:update_area_end_addr = start + nor_get_capacity(),bootloader 的启动地址选择必须与 get_nor_update_param() 返回的布局一致,否则校验通过后可能从错误地址启动。

Extension Points

升级子系统为二次开发预留了明确的扩展接口:

  1. register_loader_write_handler — 核心扩展点。默认实现把数据写入 nor_fs 的 loader.bin;如需改为裸 flash 直写、加密存储或外挂 Flash(参考 ex_flash_file_download.c),只需注册自定义写回调,上层 RCSP/网络通道无需改动。
  2. norflash_update_verify 的 CRC 算法替换 — 若产品需要更健壮的完整性校验(如 CRC32/SHA),可在保持 loader_crc 参数语义的前提下替换实现;注意须与下发方算法对齐。
  3. 新增传输通道 — 参照 http_update.c / rcsp_ch_loader_download.c 的模式,新通道只需完成"接收数据 → 调 norflash_loader_start → 循环调写回调 → 调 norflash_update_verify"四步即可接入既有校验与切换框架。
  4. 升级策略 — net_single_backup_update.c 展示了备份回滚策略;产品若需双备份(A/B 分区)或多副本,可在升级核心层新增策略模块,复用本页描述的落盘与校验原语。

Tests

本次源码探索未在 sdk/apps/common/update/ 及相关 RCSP 目录中发现对应的单元测试或自动化测试文件(该 SDK 为嵌入式工程,测试通常依赖硬件台架与 nor_fs 的坏块模拟)。验证手段主要为:

  • 功能验证:通过 RCSP/APP 或网络通道完成一次完整升级,确认 norflash_update_verify 返回 0 且设备复位后运行新固件。
  • 故障注入:传输中断、篡改分片数据后确认校验返回 1 且设备仍能正常启动旧固件。
  • 边界验证:loader_len 超容量、文件系统未就绪时调用 norflash_loader_start 的返回值是否符合 -1/-2 约定。

说明:以上验证建议基于接口契约推导,并非仓库内现成测试用例;如仓库后续补充测试,请以实际文件为准。

Related Links

  • 官方文档(升级组件索引):update/index.rst.txt
  • 压缩升级说明:compress_update.rst.txt
  • 核心实现(NOR Flash 升级通路):norflash_update.c
  • 关联升级模块(同目录):norflash_ufw_update.c | fs_update.c | http_update.c | net_update.c | net_single_backup_update.c | expand_zone_file_update.c | ex_flash_file_download.c
  • RCSP 升级服务端:rcsp_update.c | rcsp_update_tws.c | rcsp_ch_loader_download.c
  • RCSP 升级客户端:rcsp_update_master.c

本文档基于仓库 release/AC792N_SDK_V3 分支源码编写。核心 API 与代码片段均出自 norflash_update.c 实际源码;其余模块的职责描述来自目录清单与文件命名推断,已在上文明确标注,深入使用前请以各文件源码为准。

Prev
产测与射频测试工具
Next
调试与日志工具