烧录与升级工具
本文介绍 fw-AD1NN_GP-MCU_SDK(AD15N 系列)的固件烧录与升级机制:PC 端 USB 升级工具如何通过 USB MSD(Mass Storage Device)私有 SCSI 命令与芯片交互、UART 升级通道的初始化与引脚配置,以及芯片侧 UPGRADE_STATE 状态机如何决定进入升级模式还是正常运行。
Purpose and Scope
本页覆盖 SDK 中与"烧录/升级"相关的芯片侧实现与工具链使用方法:
- USB 升级工具(强制升级工具)与芯片的通信协议 —— 私有 SCSI 命令
0xF4(FLASH 操作)与0xFC(掩码升级); - 升级入口状态
UPGRADE_STATE(USB 硬键 / USB 软键 / UART 键)与 NVRAM 启动状态记录机制; - UART 升级通道(
uart_update)的引脚配置、DMA 缓冲与初始化接口; - PC 端工具的获取方式与基本烧录流程(源自仓库
README.md)。
以下内容不属于本页范围,由其他目录页覆盖:文件系统(FAT/NORFS/SYDF)内部布局、外挂 Flash 驱动(norflash/)、量产工具 burner_1tuo2 的裸片烧写协议细节。
Overview
AD15N 系列芯片(CPU_SH55,以及兼容的 AD14N/SH54、AD17N/SH57、AD18N/CH58)支持两种主要的固件烧录/升级路径:
- USB 升级(强制升级工具):PC 上位机通过 USB 线与目标板连接。芯片在 USB Device(MSD + CDROM)模式下暴露私有 SCSI 命令接口,上位机借此直接读写内部 FLASH(
0xF4),或写入"软键升级"标志后复位(0xFC),使芯片重启后自动进入升级模式。这是开发阶段最常用的烧录方式。 - UART 升级(串口升级):通过 UART 引脚进入升级模式,
uart_update模块提供数据收发与 DMA 缓冲,常用于无 USB 连接或生产调试场景。
设计上,SDK 采用**"状态记录 + 复位跳转"**的策略:升级请求不是直接在运行时切换 FLASH 读写路径,而是通过 nvram_set_boot_state() 将意图写入 NVRAM,再触发 chip_reset();芯片复位后由 Boot 代码读取该状态,决定进入升级模式。这样升级模式运行在干净的启动环境中,避免与应用程序的复杂状态耦合。
flowchart TD
subgraph sg_PC["PC 上位机"]
USB_Tool["USB 升级工具 (强制升级)"]
Prod_Tool["生产烧写工具 (1拖2)"]
end
subgraph sg_Chip["目标芯片 (AD15N/SH55)"]
subgraph sg_USB["USB Device 栈"]
MSD["MSD (U盘) + CDROM"]
SCSI["private_scsi_cmd<br/>私有SCSI命令"]
FLASH_OP["scsi_flash_operations<br/>0xF4 FLASH读写"]
MASK_UP["go_mask_usb_updata<br/>0xFC 掩码升级"]
end
subgraph sg_UART["UART 升级通道"]
UART_INIT["uart_update_init<br/>引脚+DMA初始化"]
UART_DATA["uart_update_data_deal<br/>数据解析"]
end
NVRAM["NVRAM 启动状态<br/>nvram_set_boot_state"]
BOOT["Boot 代码<br/>读取状态决定启动路径"]
APP["应用程序固件"]
end
USB_Tool -->|"USB线 / SCSI CBW"| MSD
Prod_Tool -->|"USB线 / 批量烧写"| MSD
MSD --> SCSI
SCSI -->|"0xF4"| FLASH_OP
SCSI -->|"0xFC"| MASK_UP
MASK_UP -->|"写状态 + 复位"| NVRAM
NVRAM --> BOOT
BOOT -->|"UPGRADE_USB_SOFT_KEY"| MSD
BOOT -->|"正常运行"| APP
UART_Tool["串口调试/升级工具"] -->|"UART TX/RX"| UART_INIT
UART_INIT --> UART_DATA
架构说明:上位机工具(USB 升级工具、生产烧写工具)通过 USB 枚举为 MSD/CDROM 复合设备;芯片侧 private_scsi_cmd() 是私有命令分发入口,将 0xF4 路由到 FLASH 操作、0xFC 路由到掩码升级。掩码升级与 UART 升级都只是"设置状态 + 复位",真正的升级模式由 Boot 代码在复位后进入。
USB 升级机制
编译开关与入口
USB 升级代码位于 sdk/app/bsp/common/usb/device/msd_upgrade.c,受两级宏控制:外层 TCFG_PC_ENABLE(PC 模式总开关),内层 TCFG_PC_UPDATE(升级功能开关)。两者同时使能时,文件才参与编译:
#if TCFG_PC_ENABLE
#if TCFG_PC_UPDATE
...
#else
u32 private_scsi_cmd(const struct usb_device_t *usb_device, struct usb_scsi_cbw *cbw)
{
return FALSE;
}
#endif //PC_UPDATE_ENABLE
#endif
Source: msd_upgrade.c
当 TCFG_PC_UPDATE 未开启时,private_scsi_cmd() 退化为直接返回 FALSE 的空实现——设计意图是:不开启升级功能的量产固件不暴露任何私有命令,避免被异常指令干扰(安全与健壮性考量)。
升级状态枚举
typedef enum {
UPGRADE_NULL = 0,
UPGRADE_USB_HARD_KEY,
UPGRADE_USB_SOFT_KEY,
UPGRADE_UART_KEY,
} UPGRADE_STATE;
Source: msd_upgrade.c
四种状态分别对应:无升级请求(正常运行)、USB 硬键触发(如按住按键上电)、USB 软键触发(上位机发 0xFC 命令)、UART 键触发(串口升级)。该枚举值被写入 NVRAM,Boot 代码在复位后读取判断启动路径。
掩码升级流程(0xFC)
AT(.volatile_ram_code)
static void go_mask_usb_updata()
{
local_irq_disable();
/* ram_protect_close(); */
/* hw_mmu_disable(); */
nvram_set_boot_state(UPGRADE_USB_SOFT_KEY);
/* JL_CLOCK->PWR_CON |= (1 << 4); */
chip_reset();
/* cpu_reset(); */
while (1);
}
Source: msd_upgrade.c
关键设计点:
AT(.volatile_ram_code):把函数放入易失性 RAM 段执行,避免在掩码升级/复位场景下依赖可能被改写或未初始化的 Flash 代码路径,保证复位前最后一段代码稳定可执行;local_irq_disable():先关中断,防止复位过程中被中断打扰导致状态写入不完整;nvram_set_boot_state(UPGRADE_USB_SOFT_KEY):将升级意图写入 NVRAM(掉电保持),这是整个机制的核心——复位后 Boot 代码据此进入升级模式;chip_reset()+while(1):触发复位并停在此处兜底,若复位失败不会继续执行应用代码造成混乱。
私有 SCSI 命令分发(0xF4 / 0xFC)
u32 private_scsi_cmd(const struct usb_device_t *usb_device, struct usb_scsi_cbw *cbw)
{
void *fd = NULL;
u32 lba;
u8 ack[16];
switch (cbw->operationCode) {
#if TCFG_USB_MSD_CDROM_ENABLE
// FLASH操作接口
case 0xF4:
//log_info("res_upgrade\n");
scsi_flash_operations(usb_device, cbw);
break;
#endif
case 0xFC:
go_mask_usb_updata();
break;
default:
return FALSE;
}
return TRUE;
}
Source: msd_upgrade.c
该函数接收 USB 栈解析出的 SCSI CBW(Command Block Wrapper),按 operationCode 分发:
0xF4:FLASH 操作接口,仅在TCFG_USB_MSD_CDROM_ENABLE(CDROM 复合功能)开启时可用,转交scsi_flash_operations()执行具体的 FLASH 读写——这也是 USB 升级工具直接烧写固件的主通道(该函数实现位于 MSD 模块,本页未展开);0xFC:掩码升级,直接调用go_mask_usb_updata();- 其他命令码返回
FALSE,表示"不是我能处理的私有命令",交由标准 SCSI 处理流程,保证与标准 U 盘/CDROM 行为兼容。
UART 升级机制
UART 升级模块位于 sdk/app/bsp/common/uart_update/,提供串口升级通道的初始化与数据解析接口:
#ifndef _UART_UPDATE_
#define _UART_UPDATE_
#include "typedef.h"
#include "uart_dev.h"
#define DMA_BUF_LEN 32
#ifdef CPU_SH54
#define TCFG_UART_UPDATE_PORT IO_PORTA_09//SH54
#endif
#ifdef CPU_SH55
#define TCFG_UART_UPDATE_PORT IO_PORTB_09//SH55
#endif
void uart_update_data_deal(u8 *data, u8 len);
void uart_update_data_init(const uart_bus_t *ut);
void uart1_tx_rx_switch(u8 port, u8 status);
void uart_update_init(void);
#endif
Source: uart_update.h
要点:
DMA_BUF_LEN = 32:串口升级使用 DMA 收发,单次缓冲 32 字节,配合中断逐包处理升级数据;- 引脚差异:
TCFG_UART_UPDATE_PORT按芯片型号区分——SH54(AD14N)用IO_PORTA_09,SH55(AD15N)用IO_PORTB_09。硬件设计时必须核对目标板实际引脚; - 接口职责:
uart_update_data_init()绑定 UART 总线并初始化 DMA;uart_update_data_deal()处理每包升级数据;uart1_tx_rx_switch()用于切换 UART1 的 TX/RX 引脚复用;uart_update_init()为总入口(引脚配置 + 通道初始化)。
核心流程:USB 升级全过程
下图展示从 PC 端 USB 升级工具到芯片进入升级模式的完整时序:
sequenceDiagram
participant PC as USB 升级工具 (PC)
participant USB as USB Device 栈 (MSD/CDROM)
participant SCSI as private_scsi_cmd
participant NVRAM as NVRAM 启动状态
participant BOOT as Boot 代码
participant FLASH as 内部 FLASH
PC->>USB: 枚举 MSD/CDROM 设备
USB-->>PC: 设备就绪 (SCSI 命令可用)
PC->>SCSI: CBW opCode=0xF4 (FLASH 操作)
SCSI->>FLASH: scsi_flash_operations 读写
FLASH-->>SCSI: 数据/状态
SCSI-->>PC: CSW 响应
PC->>SCSI: CBW opCode=0xFC (掩码升级)
SCSI->>NVRAM: go_mask_usb_updata: nvram_set_boot_state(UPGRADE_USB_SOFT_KEY)
SCSI->>BOOT: chip_reset()
BOOT->>NVRAM: 读取 boot state
alt state == UPGRADE_USB_SOFT_KEY
BOOT->>USB: 进入升级模式,等待 0xF4 FLASH 写入
else state == UPGRADE_NULL
BOOT->>FLASH: 正常启动应用固件
end
流程解读:开发阶段烧录时,USB 升级工具先通过 0xF4 命令直接读写 FLASH 完成固件写入(首次烧录/裸片场景);0xFC 掩码升级用于让运行中的设备"主动"重启进入升级模式——设备先关中断、写 NVRAM 状态、复位,Boot 代码随后读取状态决定启动路径。两条通道合起来覆盖了"首次烧录 + 在线升级"两种典型场景。
配置选项
| 选项 | 类型 | 默认/取值 | 说明 |
|---|---|---|---|
TCFG_PC_ENABLE | 宏 | 0/1 | PC 模式总开关,关闭时整个 msd_upgrade.c 不参与编译 |
TCFG_PC_UPDATE | 宏 | 0/1 | USB 升级功能开关,关闭时 private_scsi_cmd() 返回 FALSE,不暴露私有命令 |
TCFG_USB_MSD_CDROM_ENABLE | 宏 | 0/1 | 决定 0xF4 FLASH 操作接口是否注册(需 CDROM 复合功能) |
TCFG_UART_UPDATE_PORT | IO 枚举 | IO_PORTA_09(SH54) / IO_PORTB_09(SH55) | UART 升级引脚,按芯片型号编译期确定 |
DMA_BUF_LEN | 宏 | 32 | UART 升级 DMA 单次收发缓冲长度(字节) |
说明:上述宏定义于工程配置头文件(如
usb_config.h及板级 config),具体数值随工程模板(*_voice_toy.cbp/*_mcu.cbp)而异。
API 参考
u32 private_scsi_cmd(const struct usb_device_t *usb_device, struct usb_scsi_cbw *cbw)
USB MSD 私有 SCSI 命令分发入口,供 USB 协议栈在处理非标准命令时回调。
参数:
usb_device(const struct usb_device_t *):当前 USB 设备实例,用于后续 FLASH 操作的数据传输;cbw(struct usb_scsi_cbw *):SCSI 命令块包装,含operationCode字段。
返回:
TRUE:命令已被本模块处理(0xF4/0xFC);FALSE:未识别的命令码,交给标准 SCSI 流程。
副作用:
0xFC会写入 NVRAM 并触发芯片复位,调用后不会返回;0xF4仅在TCFG_USB_MSD_CDROM_ENABLE开启时注册。
static void go_mask_usb_updata(void)
掩码升级动作:关中断 → nvram_set_boot_state(UPGRADE_USB_SOFT_KEY) → chip_reset() → while(1) 兜底。
注意: 该函数被标注 AT(.volatile_ram_code),位于易失性 RAM,复位前执行,永不返回。
void uart_update_init(void)
UART 升级总初始化入口(配置 TCFG_UART_UPDATE_PORT 引脚与 UART 通道)。
void uart_update_data_deal(u8 *data, u8 len)
处理一包 UART 升级数据(长度由 DMA 中断给出,受 DMA_BUF_LEN 约束)。
void uart_update_data_init(const uart_bus_t *ut)
绑定指定 UART 总线并初始化 DMA 收发通道。
void uart1_tx_rx_switch(u8 port, u8 status)
切换 UART1 TX/RX 引脚复用状态,用于升级模式与正常运行模式间复用同一组引脚。
失败模式与边界情况
0xFC后未进入升级模式:升级标志写在 NVRAM 后依赖chip_reset()生效。若复位前应用代码崩溃、NVRAM 写入失败(如掉电瞬间),设备可能直接正常启动。应对:重新执行掩码升级或改用 USB 硬键(UPGRADE_USB_HARD_KEY)进入升级模式——硬键方式不依赖 NVRAM 的运行时写入。- 裸片/首次烧录:全新芯片无任何固件或 NVRAM 状态,USB 软键(
0xFC)无从谈起,必须使用 USB 升级工具的直接 FLASH 操作(0xF4)或生产烧写工具(burner_1tuo2,代理商处获取)完成首次编程。这也是 README 中区分"USB 升级工具"与"生产烧写工具"的原因。 - 私有命令被误用:量产固件若意外开启
TCFG_PC_UPDATE,任何 USB 主机都可发送0xFC使设备复位进入升级模式(拒绝服务风险)。设计上通过编译开关隔离,量产配置应关闭该宏。 - 引脚冲突:
TCFG_UART_UPDATE_PORT与普通 UART/GPIO 功能共用引脚,若应用代码同时占用会导致升级通道异常。uart1_tx_rx_switch()的存在表明引脚在升级与正常运行间复用,切换时机错误会导致收发失败。 - DMA 缓冲溢出:
DMA_BUF_LEN=32固定缓冲,若上位机单包数据超过 32 字节且uart_update_data_deal未做分片处理,会产生丢包——实现中需要逐包解析,实际数据协议应遵守缓冲上限。
并发与一致性
- 中断与复位竞态:
go_mask_usb_updata()先local_irq_disable()再写 NVRAM、复位,正是为了避免中断服务程序在"写状态 → 复位"窗口内打断导致状态不一致; - USB 栈并发:
private_scsi_cmd()在 USB 中断/任务上下文中被调用,0xFC路径不依赖任何锁(直接复位),而0xF4的 FLASH 操作与文件系统(FAT/NORFS/SYDF)共享内部 Flash 控制器,若应用同时读写文件系统可能产生竞争——升级操作通常在专用模式下进行(见 Boot 状态机)。
操作指南(来自仓库 README)
- 准备 USB 升级工具(申请链接见 README.md),安装驱动并连接目标板;
- 用 Code::Blocks 打开对应工程(如
AD15N_mcu.cbp),Build → Build(Ctrl+F9)编译; - 编译成功后,将生成的固件通过 USB 升级工具烧录到目标板;
- 烧录前确保目标板已进入编程模式(USB 连接 / 按键进入);
- 量产场景使用生产烧写工具(代理商处获取)进行 1 拖 2 批量烧写。
# 编译示例(命令行方式,见 README 4.4 节)
cd sdk
make
# 编译产物通过 USB 升级工具烧录,工具获取见 README 3.3 节
Source: README.md
扩展点
- 新增私有 SCSI 命令:在
private_scsi_cmd()的switch (cbw->operationCode)中增加case分支即可扩展私有协议,返回TRUE表示已消费该命令; - 新增升级状态:扩展
UPGRADE_STATE枚举,配合nvram_set_boot_state()写入,由 Boot 侧对应处理(需同步修改 Boot 代码,本仓库sdk/app/bsp之外的 Boot 工程不在本页范围); - UART 升级协议适配:通过替换
uart_update_data_deal()的实现来适配自定义串口升级协议,无需改动 USB 路径。