烧录与固件升级工具
AD23N 语音 MCU SDK 的固件烧录与升级能力总览:覆盖开发期首次烧录(USB 升级工具)、量产裸片烧写(一拖二/一拖八烧写器)、运行期 OTA 双备份升级与 UART 升级,并详解设备端升级 API 与配置要点。
Purpose and Scope
本页面向 AD23N SDK 使用者(嵌入式开发工程师、量产/测试工程师)说明如何把编译产物写入芯片以及芯片如何自我升级,是"从编译完成到设备运行新固件"这一环节的端到端参考。
本页覆盖:
- 开发期首次烧录(USB 升级工具 + 编程模式)的完整步骤与前置条件;
- 量产生产烧写(一拖二/一拖八烧写器)的使用场景与工具来源;
- 运行期 OTA 双备份升级(
dual_bank被动升级 API 的职责、调用时序、CRC 校验与 boot info 烧录); - UART 升级路径(
uart_update模块); - 与烧录相关的配置(
ISD_CONFIG.INI)与常见问题排查。
以下主题留给同目录其他页面(此处仅做交叉指引):
- 编译产物如何生成、
.cbp工程与 Makefile target 的选择,参见 编译指南 页面; - 应用层功能开关(
app_config.h)与ISD_CONFIG.INI之外的 SDK 配置,参见 配置说明 页面; - 芯片硬件特性(Flash 分区、电源、复位时序),参见 芯片用户手册(
doc/AD23N用户手册V1.1(杰理).pdf)。
Overview
AD23N 是杰理科技 32 位语音 MCU 系列,固件存放在内置 NOR Flash(支持外置 NOR Flash)。与其他 MCU 一样,芯片出厂或开发调试时需要通过烧录工具将固件写入 Flash;而产品交付后则依赖设备端升级能力(OTA/UART)让固件可以现场更新。
整个烧录与升级体系分为三条并行的路径,按使用阶段划分:
| 阶段 | 工具/路径 | 适用场景 | 关键入口 |
|---|---|---|---|
| 开发调试 | USB 升级工具(上位机) | 将编译产物烧入开发板 | 按住烧录键复位进入编程模式 |
| 量产 | 生产烧写工具(一拖二/一拖八) | 裸片/整板批量烧写 | 代理商提供的量产烧录上位机 |
| 运行期 | OTA 双备份升级 | 产品交付后的固件更新 | dual_bank_passive_update_* API 族 |
| 运行期 | UART 升级 | 串口通道固件更新 | uart_update 模块(uart_update.c/.h) |
SDK 中的固件升级实现集中在两处:
sdk/include_lib/update/code_v2/—— 以预编译头文件形式对外暴露的升级 API 契约(dual_bank_updata_api.h、update.h、dev_update.h、uart_update.h、update_loader_download.h);sdk/app/bsp/common/update/与sdk/app/bsp/common/uart_update/—— 设备端升级逻辑的源码实现(update.c、dev_update.c、uart_update.c/.h)。
这种"头文件即契约 + BSP 公共实现"的布局说明:升级能力被设计为与应用解耦的公共基础设施,任何基于该 SDK 的应用(如 mbox_flash)都可以直接调用升级 API 而无需关心 Flash 驱动细节。
Architecture
flowchart TD
subgraph sg_Host["PC 侧工具"]
USB_Tool["USB 升级工具<br/>(开发/调试烧录)"]
MassProd["生产烧写工具<br/>(一拖二 / 一拖八)"]
ISD_CFG["ISD_CONFIG.INI<br/>烧录配置"]
end
subgraph sg_Device["AD23N 目标板"]
subgraph sg_Boot["启动与编程模式"]
BOOT["Boot ROM / 编程模式入口"]
APP["应用固件 (mbox_flash)"]
end
subgraph sg_Update["设备端升级模块 (sdk/app/bsp/common)"]
UPDATE_C["update.c / dev_update.c<br/>升级核心逻辑"]
UART_U["uart_update.c/.h<br/>UART 升级通道"]
DUAL_BANK["dual_bank 双备份升级<br/>(include_lib/update/code_v2)"]
end
subgraph sg_Flash["NOR Flash 存储"]
BANK_A["Bank A (运行区)"]
BANK_B["Bank B (升级区)"]
BOOT_INFO["Boot Info"]
end
end
USB_Tool -->|"USB / UART 传输"| BOOT
MassProd -->|"批量烧写"| BOOT
ISD_CFG -.-> USB_Tool
APP -->|"触发升级"| UPDATE_C
UPDATE_C --> DUAL_BANK
UART_U --> UPDATE_C
DUAL_BANK -->|"写入/擦除/校验"| BANK_A
DUAL_BANK -->|"写入/擦除/校验"| BANK_B
DUAL_BANK -->|"烧录切换信息"| BOOT_INFO
架构说明
- PC 侧工具:USB 升级工具与生产烧写工具都是杰理官方上位机,通过 USB 或 UART 与目标板通信。
ISD_CONFIG.INI是烧录上位机读取的配置文件,控制烧录选项(详见 ISD 配置说明),仓库的doc/stuff/ISD_CONFIG.INI配置文件说明.pdf也提供了离线说明。 - 编程模式:目标板只有在特定条件下(按住烧录键复位/重新上电,或由升级工具拉入)才接受烧录,避免运行中的应用固件与烧录器争用 Flash。
- 设备端升级模块:
update.c/dev_update.c承载升级任务调度,uart_update提供串口数据通路,dual_bankAPI 负责把下载数据写入非易失存储(Flash)并完成校验与启动信息切换。运行期升级不依赖 PC 工具,由应用固件(或外部主机)把新固件数据流式喂给升级模块。 - 双备份(A/B)Flash 布局:同一片 NOR Flash 上划分运行区(Bank A)与升级区(Bank B),配合
Boot Info记录当前应启动哪个区,实现"升级失败不砖机"的可靠性设计(详见下文 OTA 章节)。
开发期首次烧录(USB 升级工具)
工具获取与安装
按 README.md 的说明,开发期烧录需要向杰理官方渠道申请 USB 升级工具(硬件 + 上位机软件),它负责把编译生成的固件烧入目标板:
| 工具 | 用途 | 获取方式 |
|---|---|---|
| USB 升级工具 | 将固件烧录到目标板 | 申请链接 · 使用文档 |
| 生产烧写工具 | 量产/裸片烧写 | 代理商处 · 一拖二使用文档 |
安装上位机后,将开发板通过 USB 或 USB 升级工具 连接到 PC,即可进入烧录流程。
首次烧录流程
README 的烧录与升级章节定义了首次烧录的标准步骤:
- 连接硬件:将开发板通过 USB 或者 USB 升级工具 连接到 PC;
- 进入编程模式:
- 方式一(USB):按住开发板上的烧录按键,然后复位或重新上电;
- 方式二(USB/UART):通过 USB 升级工具进入编程模式;
- 打开 USB 升级工具:启动烧录上位机;
- 选择固件:选择编译生成的固件文件;
- 开始烧录:点击下载按钮,等待烧录完成。
注意:烧录前请确保 USB 升级工具正确连接且目标板已进入编程模式。关于
ISD_CONFIG.INI配置详见 ISD 配置说明。
设计意图解读
- 为何需要"编程模式":芯片上电时 Boot ROM 先于应用运行;只有处于编程模式,Boot ROM 才会监听烧录协议(而不是跳转到应用)。这保证了烧录器与运行中的应用不会同时操作 Flash,避免写坏固件。
- 为何提供两种进入方式:开发板上有物理烧录按键时用方式一最直观;而通过升级工具拉入(方式二)无需人工按键,适合后续自动化与产线场景。
- 为何强调"先进入编程模式再烧录":USB 升级工具下载过程不可中断(掉线/断电会导致 Flash 写入不完整),README 在编译并烧录章节也特别提示:"编译前请确保 USB 升级工具正确连接且目标板已进入编程模式"。
与编译流程的衔接
编译与烧录的衔接点在工程文件与固件产物上:双击 AD23N_mbox_flash.cbp 打开工程 → Build → Build(Ctrl+F9)→ 编译成功后,使用 USB 升级工具烧录生成的固件(见 README.md#L142-L148)。即:CodeBlocks 负责产出固件,USB 升级工具负责把它写进 Flash,两者通过"固件文件"解耦。
量产生产烧写
量产场景与开发调试的关键差异是产能与裸片支持:
- 使用杰理生产烧写工具(一拖二 / 一拖八),一个上位机同时驱动多个烧录座/烧录头,成倍提升烧录吞吐;
- 支持裸片烧写(芯片未贴板时直接烧录),这是封装前工序的必需能力;
- 工具与使用文档均需通过代理商获取(README.md#L288-L290),量产线配置(如烧录座型号、校验选项)也由代理 FAE 配合完成。
量产烧写通常还会配合 ISD_CONFIG.INI 统一各产线的烧录参数(加密、校验、Flash 分区等),确保同一固件在所有产线烧出一致结果。
运行期 OTA 双备份升级
双备份(A/B)机制概述
README 在OTA 升级章节中明确:支持双备份固件升级。所谓双备份,即 Flash 上同时存在两个可启动的固件区(Bank A / Bank B),配合 Boot Info 记录"当前应启动哪个区":
- 升级时,新固件写入非运行区(更新 Bank),不影响当前运行的固件;
- 新固件写完后先校验(CRC),校验通过才烧录新的
Boot Info; - 下次复位后从新固件启动;若校验失败,则保留旧固件继续运行。
这种设计的核心价值是升级不砖机:即使升级过程中断电、传输中断或固件损坏,系统仍可回退到上一个完好的固件。代价是 Flash 容量占用翻倍,因此该机制通过预编译库的方式按需启用。
升级 API 契约(dual_bank)
设备端被动升级(passive update,即由外部主机/上位机主动推送数据)的 API 全部定义在 dual_bank_updata_api.h,调用方(应用或升级服务器代码)按固定时序使用:
| API | 职责 | 关键参数 |
|---|---|---|
get_dual_bank_passive_update_max_buf() | 查询临时缓冲最大大小 | 无 |
dual_bank_passive_update_init(fw_crc, fw_size, max_pkt_len, priv) | 初始化升级任务,登记新固件 CRC 与文件大小 | fw_crc:新固件 CRC;fw_size:固件总大小;max_pkt_len:单次编程最大长度 |
dual_bank_update_allow_check(fw_size) | 判断 Flash 剩余空间是否足够 | fw_size:新固件大小 |
dual_bank_update_write(data, len, write_complete_cb) | 将数据拷入临时缓冲并通知任务写非易失存储 | data/len:下载数据;write_complete_cb:编程完成回调(返回 0 表示无错) |
dual_bank_update_verify(crc_init_hdl, crc_calc_hdl, verify_result_hdl) | 计算已写入数据的 CRC 并与 init 时登记的 CRC 比对 | 三个回调均可传 NULL 走内部 CRC16-CCITT 实现 |
dual_bank_update_burn_boot_info(burn_boot_info_result_hdl) | 校验通过后烧录新固件的启动信息 | 回调 err == 0 表示烧录成功 |
dual_bank_passive_update_exit(priv) | 退出升级任务 | 无 |
dual_bank_update_read_data(offset, read_buf, read_len) | 读取升级区数据(用于外部 CRC 计算) | offset:相对升级区的偏移 |
dual_bank_passive_update_get_target_update_addr() | 获取待升级目标地址(init 之后有效) | 无 |
flash_update_clr_boot_info(type) | 擦除指定区的启动信息(慎用) | CLEAR_APP_RUNNING_BANK / CLEAR_APP_UPDATE_BANK |
dual_bank_update_verify_without_crc_new(verify_result_hdl) | 不带 CRC 参数的校验入口 | 结果回调 |
其中 dual_bank_updata_api.h 中对校验与启动信息切换的设计意图注释非常明确(第 35-48 行):先全量校验、再烧 boot info——即"数据完整"与"启动切换"是两个严格分离的阶段,只有数据完整才允许切换启动目标,这正是 A/B 升级可靠性的保证。
Core Flow:OTA 双备份升级时序
以下时序图基于 dual_bank_updata_api.h 的 API 调用契约绘制,展示了外部主机推送新固件到设备并完成切换的完整流程:
sequenceDiagram
participant H as 外部主机/上位机
participant A as 应用固件(升级任务)
participant DB as dual_bank 升级模块
participant F as NOR Flash (Bank B / Boot Info)
H->>A: 协商升级参数(固件大小、CRC)
A->>DB: get_dual_bank_passive_update_max_buf()
DB-->>A: 临时缓冲大小
A->>DB: dual_bank_passive_update_init(fw_crc, fw_size, max_pkt_len)
DB->>DB: 校验固件大小/分区空间
A->>DB: dual_bank_update_allow_check(fw_size)
DB-->>A: 允许/拒绝
loop 分片下载
H->>A: 固件数据包(data, len)
A->>DB: dual_bank_update_write(data, len, write_complete_cb)
DB->>F: 写入/擦除升级区
F-->>DB: 编程完成
DB-->>A: write_complete_cb(0=成功)
end
A->>DB: dual_bank_update_verify(NULL, NULL, verify_result_hdl)
DB->>F: 读取已写数据计算 CRC
F-->>DB: 数据
DB-->>A: verify_result_hdl(crc_res: 1=通过 0=失败)
alt CRC 校验通过
A->>DB: dual_bank_update_burn_boot_info(burn_boot_info_result_hdl)
DB->>F: 烧录新 Boot Info
F-->>DB: err=0
DB-->>A: 升级成功,待复位
else CRC 校验失败
A->>DB: 保留旧固件,上报失败
end
时序要点解读
- 初始化先行:
init阶段登记新固件的fw_crc与fw_size,升级模块据此规划写入范围并预留临时缓冲(max_pkt_len决定单次编程包大小上限,间接约束上位机分包粒度)。 - 空间预检:
dual_bank_update_allow_check必须在init之后调用,用于在开始写入前确认目标分区容量,避免写到一半才发现空间不足。 - 流式写入:
dual_bank_update_write每次接收一段数据,先拷入临时缓冲,再由内部任务异步写入非易失存储,通过write_complete_cb通知完成——这是"被动升级"(数据由外部推入)与"主动升级"(设备自己去拉取)的本质区别。 - 校验与切换分离:
dual_bank_update_verify支持传入自定义 CRC 回调,传 NULL 则使用内部 CRC16-CCITT 实现;只有verify_result_hdl返回通过后,才允许dual_bank_update_burn_boot_info烧录新启动信息。 - 收尾:全部完成后调用
dual_bank_passive_update_exit退出升级任务;dual_bank_update_get_target_update_addr可用于在写入前确认目标地址(如打印日志或做分区校验)。
UART 升级路径
除 OTA(数据经网络/主机下发)外,SDK 还提供 UART 升级通道,实现位于 sdk/app/bsp/common/uart_update/uart_update.c 与 uart_update.h,对外契约在 sdk/include_lib/update/code_v2/uart_update.h。
UART 升级与 USB 升级工具烧录的定位不同:
- USB 升级工具面向开发/量产,走 Boot ROM 编程模式,设备端无需运行应用;
- UART 升级面向运行期维护,走应用内升级任务,通过串口接收固件数据,再复用
update模块(update.c/dev_update.c)写入 Flash——适用于没有 USB 通路、只有串口的产品形态(如通过蓝牙/Wi-Fi 模块转发或产线串口治具)。
由于本仓库以预编译库形式提供升级实现,uart_update.c 与 update.c 的详细内部逻辑未在仓库中展开,具体调用方式以 code_v2 头文件契约为准;如需深入阅读,可直接查看 uart_update.h 与 update.h。
Usage Examples
以下代码片段全部提取自仓库真实源码,展示 OTA 双备份升级 API 的声明与用法契约。
示例 1:初始化升级任务并登记新固件 CRC 与大小
/* @brief:Initializes the update task,and setting the crc value and file size of new fw;
* @param fw_crc:crc value of new fw file
* @param fw_size:total size of new fw file
* @param priv:reserved
* @param max_ptk_len: Supported maxium length of every programming,it decides the max size of programming every time
*/
u32 dual_bank_passive_update_init(u32 fw_crc, u32 fw_size, u16 max_pkt_len, void *priv);
Source: dual_bank_updata_api.h
这是整个被动升级流程的第一个调用:在收到外部主机下发的固件元信息(CRC、大小)后立即调用,升级模块据此初始化任务并规划写入。
示例 2:流式写入下载数据并注册编程完成回调
/* @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
* @param write_complete_cb:callback for programming done,return 0 if no err occurred
*/
u32 dual_bank_update_write(void *data, u16 len, int (*write_complete_cb)(void *priv));
Source: dual_bank_updata_api.h
每次收到数据包就调用一次;内部先拷入临时缓冲,再由升级任务异步写 Flash。write_complete_cb 返回 0 表示本次编程成功,非 0 表示出错,调用方应据此决定是否中断升级。
示例 3:全量校验后烧录新固件启动信息
/* @brief: caculate all the data had flashed,and compare with the cre value intializeed when update init;
* @crc_init_hdl:if it equals NULL,use internal implementation(CRC16-CCITT Standard);otherwise,use user's customization;
* @crc_calc_hdl:if it equals NULL,use internal implementation(CRC16-CCITT Standard);otherwise,use user's customization;
* @verify_result_hdl:when the verification completed,this callback for result notification;
* if crc_res equals 1,crc verification passed,if 0,the verification failed.
*/
u32 dual_bank_update_verify(void (*crc_init_hdl)(void), u32(*crc_calc_hdl)(u32 init_crc, const void *data, u32 len), int (*verify_result_hdl)(int calc_crc));
Source: dual_bank_updata_api.h
三个回调全部传 NULL 时使用内部 CRC16-CCITT 实现,这是最常见的用法;若产品有自定义校验协议,可注入自己的 crc_init/crc_calc。校验结果通过 verify_result_hdl 通知(1=通过,0=失败),通过后调用:
/* @brief:After the new fw verification succeed,call this api to program the new boot info for new fw
* @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.
*/
u32 dual_bank_update_burn_boot_info(int (*burn_boot_info_result_hdl)(int err));
Source: dual_bank_updata_api.h
burn_boot_info_result_hdl 收到 err == 0 表示切换信息烧录成功,此后系统复位将从新固件启动。
示例 4:谨慎操作——擦除指定区的启动信息
enum {
CLEAR_APP_RUNNING_BANK = 0,
CLEAR_APP_UPDATE_BANK,
};
/* @brief:this api for erasing the boot info of specific bank,it should be called much carefully
* @param type:it decides which bank's boot info would be erased;
* clean the boot info of running bank and call system_reset,system will run the other bank if available;
*/
int flash_update_clr_boot_info(u8 type);
Source: dual_bank_updata_api.h
该 API 的头文件注释明确标注 "it should be called much carefully"(须非常谨慎):擦除运行区的 boot info 并复位后,系统会尝试从另一区启动——这是"回滚到上一个版本"的实现手段,通常只在固件损坏恢复或双区对调场景使用。
Configuration Options
烧录与升级相关的配置集中在两个层面:
烧录上位机配置(PC 侧)
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ISD_CONFIG.INI | 配置文件 | 由工具模板提供 | USB 升级工具/量产工具读取的烧录配置,含烧录选项、校验、加密等;仓库内离线说明见 doc/stuff/ISD_CONFIG.INI配置文件说明.pdf,在线文档见 ISD 配置说明 |
| 固件文件选择 | 文件路径 | 无 | 编译生成的固件文件(.cbp 工程 Build 产物),由用户在上位机中选择 |
设备端升级配置(固件侧)
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
max_pkt_len(dual_bank_passive_update_init 参数) | u16 | 由调用方指定 | 单次编程最大长度,决定每次 dual_bank_update_write 可接受的最大数据量,需与上位机分包策略匹配 |
fw_crc / fw_size(dual_bank_passive_update_init 参数) | u32 | 无 | 新固件的 CRC 值与总大小,用于写入前的空间规划与写入后的完整性校验 |
| CRC 实现选择 | 回调参数 | 内部 CRC16-CCITT | dual_bank_update_verify 传入 NULL 时使用内部实现,也可注入自定义 crc_init/crc_calc |
注:应用功能开关(如是否启用升级功能)在
sdk/app/src/mbox_flash/app_config.h中配置,属配置说明页面主题,此处不展开。
API Reference
以下为设备端双备份升级 API 的完整签名(均定义于 dual_bank_updata_api.h)。
u32 get_dual_bank_passive_update_max_buf(void)
获取升级过程中临时存储所需的缓冲大小,用于调用方分配缓冲或约束分包。
返回: 临时缓冲最大大小(字节)。
u32 dual_bank_passive_update_init(u32 fw_crc, u32 fw_size, u16 max_pkt_len, void *priv)
初始化被动升级任务,登记新固件的 CRC 与文件大小。
参数:
fw_crc(u32):新固件文件的 CRC 值;fw_size(u32):新固件文件总大小;max_pkt_len(u16):单次编程支持的最大长度,决定每次编程的数据量上限;priv(void *):保留参数。
返回: u32,0 表示成功。
u32 dual_bank_passive_update_exit(void *priv)
退出升级任务。
参数: priv (void *):保留参数。
u32 dual_bank_update_allow_check(u32 fw_size)
判断是否有足够空间存放新固件。必须在 dual_bank_passive_update_init 之后调用。
参数: fw_size (u32):新固件大小。
返回: 空间是否充足(0 表示允许)。
u32 dual_bank_update_write(void *data, u16 len, int (*write_complete_cb)(void *priv))
将下载数据拷入临时缓冲,并通知任务写入非易失存储。
参数:
data(void *):下载数据指针;len(u16):下载数据长度;write_complete_cb(int (*)(void *)):编程完成回调,返回 0 表示无错误。
返回: u32,0 表示成功接收。
u32 dual_bank_update_verify(void (*crc_init_hdl)(void), u32(*crc_calc_hdl)(u32 init_crc, const void *data, u32 len), int (*verify_result_hdl)(int calc_crc))
计算所有已写入数据的 CRC 并与 init 时登记的 CRC 比对。
参数:
crc_init_hdl(void (*)(void)):CRC 初始化回调,传 NULL 使用内部实现(CRC16-CCITT 标准);crc_calc_hdl(u32 (*)(u32, const void *, u32)):CRC 计算回调,传 NULL 使用内部实现;verify_result_hdl(int (*)(int)):校验结果通知回调,crc_res == 1表示通过,0表示失败。
u32 dual_bank_update_burn_boot_info(int (*burn_boot_info_result_hdl)(int err))
新固件校验成功后,烧录新固件的启动信息(Boot Info)。
参数: burn_boot_info_result_hdl (int (*)(int)):烧录结果回调,err == 0 表示成功,其他值表示失败。
int flash_update_clr_boot_info(u8 type)
擦除指定区的启动信息(谨慎使用)。
参数: type (u8):CLEAR_APP_RUNNING_BANK(0) 擦除运行区,CLEAR_APP_UPDATE_BANK(1) 擦除升级区。擦除运行区 boot info 后调用 system_reset,系统将尝试从另一区启动。
u32 dual_bank_update_read_data(u32 offset, u8 *read_buf, u32 read_len)
读取升级区数据(相对升级区的偏移),供调用方自行计算 CRC。
参数:
offset(u32):相对升级区的偏移;read_buf(u8 *):用户数据缓冲;read_len(u32):读取长度。
返回: 实际读取长度。
u8 dual_bank_update_verify_without_crc_new(int (*verify_result_hdl)(int calc_crc))
不携带 CRC 参数的校验入口,行为与 dual_bank_update_verify 的校验阶段一致,结果通过回调通知。
u32 dual_bank_passive_update_get_target_update_addr(void)
获取待升级目标地址,在 dual_bank_passive_update_init 之后使用有效。
Failure Modes, Edge Cases & Concurrency
以下分析基于头文件注释与 README 操作指引中的明确证据:
| 失败场景 | 表现 | 防护/处理 |
|---|---|---|
| 未进入编程模式就烧录 | 烧录失败或工具无法识别设备 | README 明确要求"先进入编程模式",并提供两种进入方式;工具连接/目标板状态是烧录前置条件 |
| 烧录过程中断电/断开 | Flash 写入不完整,固件损坏 | 双备份机制下旧固件区不受影响;开发期需保证连接稳定 |
| 升级数据写入出错 | write_complete_cb 返回非 0 | 调用方应中断升级流程,等待重试 |
| CRC 校验失败 | verify_result_hdl 收到 0 | 不烧录 Boot Info,保留旧固件继续运行——这是 A/B 升级的核心回退保证 |
| 新固件过大 | dual_bank_update_allow_check 拒绝 | 在写入前拦截,避免写一半失败;应检查固件大小与分区容量 |
| 误擦除运行区 Boot Info | 系统复位后切换启动区 | API 注释明示"must be called much carefully",仅用于回滚/恢复场景 |
分包与 max_pkt_len 不匹配 | 单次写入超限 | 上位机分包策略必须遵守 dual_bank_passive_update_init 登记的 max_pkt_len,可先用 get_dual_bank_passive_update_max_buf 查询缓冲上限 |
并发与异步注意:dual_bank_update_write 采用"拷贝到临时缓冲 + 后台任务写 Flash"的异步模型,write_complete_cb 是编程完成的同步信号——调用方应基于回调做流程推进,而不是在 write 返回后立即发送下一包(除非协议明确允许流水线)。Flash 擦写是慢操作,升级期间应避免应用其他任务同时访问目标分区。
Performance & Operational Notes
- 分包粒度:
max_pkt_len同时约束单次编程数据量与上位机分包大小,选择较大值可减少回调次数、提升吞吐,但会增大临时缓冲(get_dual_bank_passive_update_max_buf可查询)——需在 RAM 占用与传输效率间权衡。 - 擦写成本:NOR Flash 以扇区为单位擦除、以页为单位编程,升级写入速度受 Flash 擦写时序主导;
dual_bank_update_write内部异步化可让传输与擦写重叠,降低主机等待时间。 - 产线一致性:量产建议通过统一的
ISD_CONFIG.INI固化烧录参数,避免人工选择差异;一拖二/一拖八工具可摊薄单颗烧录时间。 - 升级窗口:OTA 升级建议在设备空闲/供电稳定时进行,避免升级中途进入低功耗模式或断电;双备份机制虽可回退,但频繁回退说明升级包质量或传输链路存在问题。
Extension Points
- 自定义 CRC:
dual_bank_update_verify允许注入crc_init_hdl/crc_calc_hdl,支持产品自定义校验协议(默认 CRC16-CCITT); - 结果回调:
write_complete_cb、verify_result_hdl、burn_boot_info_result_hdl均为调用方注册的回调,可在此接入日志、指示灯、上报服务器等业务逻辑; - UART 通道:
uart_update模块(sdk/app/bsp/common/uart_update/)可作为自定义传输层(蓝牙透传、串口透传等)接入升级任务的参考实现; - 启动区管理:
flash_update_clr_boot_info与CLEAR_APP_RUNNING_BANK/CLEAR_APP_UPDATE_BANK枚举为 OTA 对调、版本回滚提供设备端原语。
Tests
本仓库以预编译库(sdk/include_lib/、include_lib/liba/)形式发布升级组件,仓库内未包含升级模块的独立测试用例。测试与验证烧录/升级效果的可行手段包括:
- 首次烧录后检查设备是否正常运行(验证 USB 升级工具链路);
- 构造一个"升级到新固件 → 复位 → 确认启动新固件"的闭环,验证 OTA 流程与 Boot Info 切换;
- 在写入阶段人为断电,验证复位后仍能从旧固件启动(双备份回退验证);
- 修改
dual_bank_passive_update_init传入错误的fw_crc,验证verify_result_hdl收到失败且不烧录 Boot Info。
Related Links
- README.md — 烧录与升级章节:官方首次烧录/生产烧写/OTA 说明
- dual_bank_updata_api.h:双备份升级 API 契约
- dev_update.h:设备升级接口
- uart_update.h:UART 升级接口
- update.h:升级核心接口
- update_loader_download.h:升级引导下载接口
- 在线文档:USB 升级工具使用文档、一拖二烧写器、一拖八烧写器、ISD 配置说明
- 仓库离线资料:
doc/AD23N_SDK手册_v1.0.pdf、doc/stuff/ISD_CONFIG.INI配置文件说明.pdf、AD23N_SDK_发布版本信息.pdf - 交叉指引:编译产物的生成与工程选择见 编译指南;应用功能开关配置见 配置说明;芯片硬件与 Flash 分区细节见 AD23N 用户手册