USB-SLAVE mstorage使用指南

1. 介绍

sifli USB控制器支持DEVICE、HOST等设备模式,其中DEVICE模式可支持CDC、MSTORAGE、HID、WINUSB等功能。本文主要说明USB作为MSTORAGE设备的使用流程:

  • 工作模式:作为DEVICE的MSTORAGE设备时,在PC上以可读写U盘形式呈现

  • 核心功能:PC可对U盘内文件进行增删改操作,拔出后修改会同步保留在HDK文件系统对应分区

2. USB配置

USB配置包括PINMUX引脚配置和menuconfig配置,需注意:

  • 时钟需运行在60MHz

  • 仅支持fatfs文件系统,暂不支持littlefs

2.1 PINMUX引脚配置

需将USB引脚配置为IO模式,代码如下:

#if defined(BSP_USING_USBD) || defined(BSP_USING_USBH)
    HAL_PIN_Set_Analog(PAD_PA01, 1);
    HAL_PIN_Set_Analog(PAD_PA03, 1);
#endif

特殊处理(563hdk)
563hdk的USB引脚与uart1引脚复用,而默认配置中uart1用于HCPU日志打印。若需正常使用USB,需修改如下配置:

  • 关闭“Enable UART1”

  • 开启“Enable UART4”和“Enable UART4 RX DMA”

  • 将HCPU日志打印切换为uart4(配置项:“the device name for console -> uart4”)

相关配置截图:

../_images/usb_uart_1.png ../_images/usb_uart_2.png

3. USB功能代码流程

3.1 核心流程

  1. 插入检测: callback(app_usb_function(USB_CONTROL_ENABLED)) → usb_start() → enabled_usb() → USB 驱动 → PC 挂载

  2. 拔出检测: callback(app_usb_function(USB_CONTROL_DISABLED)) → usb_stop() → disabled_usb() → USB 驱动 → HDK 卸载 → HDK 重新挂载

3.2 关键接口实现

3.2.1 核心控制函数app_usb_function

void app_usb_function(void *on_off)
{
    struct rt_device *usb_device;
    uint8_t usb_stat  = *(uint8_t *)on_off;
    LOG_I("%s %d", __func__, usb_stat);
    if (USB_CONTROL_ENABLED == usb_stat)
    {
#ifdef BSP_PM_FREQ_SCALING
        rt_pm_hw_device_start();                    //关闭自动降频
#endif
        usb_start();
    }
    if (USB_CONTROL_DISABLED == usb_stat)
    {
        usb_stop();
        usb_ota_to_msg_app();                       //上报OTA升级包事件
        usb_to_msg_gui(NULL, 0, USB_GUI_MSG_ID);    //上报USB事件ID
#ifdef BSP_PM_FREQ_SCALING
        rt_pm_hw_device_stop();                     //打开自动降频
#endif
    }
}

3.2.2 插入 / 拔出检测方式

USB检测可以根据使用情况分为USB检测引脚进行识别与USB充电接口识别;

  1. USB 检测引脚识别

//usb 检测引脚回调函数
static int irq_usb_service_callback(data_callback_arg_t *arg)
{
    if (MSG_SERVICE_DATA_NTF_IND == arg->msg_id)
    {
        pin_common_msg_t pin_msg;
        RT_ASSERT(arg != NULL);
        RT_ASSERT(sizeof(pin_common_msg_t) == arg->data_len);
        memcpy(&pin_msg, arg->data, arg->data_len);
        if (pin_msg.id == INSERT_DETE_USB_PIN)
        {
            rt_usb_irq_pin_enable(0);                                       //关闭中断
            rt_thread_mdelay(200);                                          //usb检测pin防抖延时
            int read_pin = rt_pin_read(INSERT_DETE_USB_PIN);
            if (read_pin == 1)
            {
                if (USB_CONTROL_DISABLED == usb_get_irq_state())
                {
                    LOG_I("%s USB_CONTROL_PIN_IN", __func__);
                    usb_set_irq_state(USB_CONTROL_ENABLED);
                    app_usb_function(&usb_pin_state);                       //调用USB协议
                }
            }
            else if (USB_CONTROL_ENABLED == usb_get_irq_state())
            {
                LOG_I("%s USB_CONTROL_PIN_OUT", __func__);
                usb_set_irq_state(USB_CONTROL_DISABLED);
                app_usb_function(&usb_pin_state);
            }
            rt_usb_irq_pin_enable(1);
        }
    }
    .......
}
  1. USB 充电接口识别

  • 收到充电事件:调用app_usb_function(USB_CONTROL_ENABLED)

  • 收到充电移除事件:调用app_usb_function(USB_CONTROL_DISABLED)

在USB使用时频率需要一直保持在60MHz运行,因此系统不能降频也不能进行睡眠,所以在检测到USB插入时候系统需要进入IDLE模式再开启USB使能进行USB协议检测,只有当USB协议检测成功后PC才能识别到当前U盘

3.2.3 USB 启动与停止

  • 启动 USB(usb_start)

void usb_start(void)
{
    usb_timer_start();                                  //开启定时器;500ms检测一次USB协议
    if (!usb_flag)
    {
        rt_kprintf("%s rt_pm_request\n", __func__);
        rt_pm_request(PM_SLEEP_MODE_IDLE);              //系统进入IDLE模式
        usb_flag = 1;
    }
    enabled_usb();                                      //使能USB协议
}

在拔出USB后需要对将加载到PC上的分区进行卸载后再重新再HDK上进行加载;

  • 停止 USB(usb_stop)

void usb_stop(void)
{
    udevice_t usb_device;
    disabled_usb();
    if (USB_IN == device_status_get(USB_CONNECT_STATUS))    //成功在PC上加载过分区
    {
        usb_unmount_fs();                                   //先卸载分区
        usb_mount_fs();                                     //重新加载分区
    }
    if (usb_flag)
    {
#ifdef RT_USING_PM
        LOG_I("%s rt_pm_release", __func__);
        rt_pm_release(PM_SLEEP_MODE_IDLE);                  //退出IDLE
#endif
        usb_flag = 0;
    }
    device_status_set(USB_CONNECT_STATUS, USB_OUT);         //设置USB模块状态
    usb_device = rt_usbd_find_device(usb_dcd);
    RT_ASSERT(usb_device);
    usb_device->state = USB_STATE_NOTATTACHED;              // 重置USB协议状态
}
  • 使能 USB(enabled_usb)

static void enabled_usb(void)
{
    struct rt_device *usb_device;
    usb_device = rt_device_find("usb_reg");
    if (usb_device)
    rt_device_control(usb_device, RT_DEVICE_OFLAG_OPEN, NULL);
}
  • 关闭 USB(disabled_usb)

static void disabled_usb(void)
{
    struct rt_device *usb_device;
    usb_device = rt_device_find("usb_reg");
    if (usb_device)
        rt_device_control(usb_device, RT_DEVICE_OFLAG_CLOSE, NULL);
}

3.2.4 USB 协议检测

通过定时器超时函数检测 USB 协议是否识别成功(5 秒内未识别则关闭 USB):

/**
功能:定时检测USB协议
使用:开了一个定时器去定时检测USB协议是否识别成功
*/
static udcd_t               usb_dcd;
int app_usb_init(void)
{
    usb_dcd = (udcd_t)rt_device_find("usbd");
    RT_ASSERT(usb_dcd);
}

static uint8_t usb_handler(void)
{
    static uint8_t usb_protocol_flag = 0;
    uint8_t usb_intput;
    udevice_t usb_device;
    usb_device = rt_usbd_find_device(usb_dcd);                  //usb_dcd是一个全局变量的USB设备指针,在初始化时去find该设备并赋值给usb_dcd
    RT_ASSERT(usb_device);
    rt_kprintf("%s,state=%d\n", __func__, usb_device->state);
    if (USB_STATE_CONFIGURED == usb_device->state)              //USB协议识别成功
    {
        device_status_set(USB_CONNECT_STATUS, USB_IN);          //设置USB 的状态
        usb_protocol_flag = 0;
        usb_to_msg_gui(NULL, 0, USB_GUI_MSG_ID);                //上报USB识别成功的状态
        usb_timer_stop();                                       //USB协议识别成功就关闭当前定时器
    }
    else                                                        //USB没有检测到协议
    {
        usb_protocol_flag ++;
        if (10 == usb_protocol_flag)                            //5s内没有识别到协议
        {
            usb_stop();                                         //关闭USB模块
            usb_timer_stop();                                   //关闭当前定时器
            usb_protocol_flag = 0;
        }
    }
    return 0;
}

3.2.5 USB 功耗控制(拔出后)

在USB拔出后为了节约功耗需要将USB功能禁用并pinmux配置为如下

void BSP_USB_Power_Down(void)
{
#if defined(BSP_USING_USBD) && defined(BSP_USING_USBH)
    HAL_PIN_Set(PAD_PA01, GPIO_A1, PIN_PULLUP, 1);
    HAL_PIN_Set(PAD_PA03, GPIO_A3, PIN_PULLUP, 1);
#endif
}

4. USB OTA升级

通过 USB 实现 OTA 升级(支持资源差分、代码差分、全量升级)。具体实现为在通过usb数据线在pc上挂载系统指定的分区后将需要升级的文件放入指定的文件夹中,在拔出USB后会直接检测指定的文件夹里是否含有文件,如果有就执行系统升级,并上报GUI进行界面显示。

使用USB作为OTA的流程为:拔出USB callback(app_usb_function(USB_CONTROL_DISABLED)) → usb_read_ota_dir(检测是否有OTA文件) → usb_to_msg_gui(向UI上报OTA事件) → usb_ota_all_file(执行升级)

4.1 升级目录与配置

####4.1.1 升级目录定义

/**
 * @brief  OTA check ID
 */
enum
{
    USB_OTA_CHECK_ROOT      = 0X01,
    USB_OTA_CHECK_DYN       = 0X02,
    USB_OTA_CHECK_MUSIC     = 0X04,
    USB_OTA_CHECK_UDISK     = 0X08,
    USB_OTA_CHECK_MISC      = 0X10,
    USB_OTA_CHECK_HCPU      = 0X20,

    USB_OTA_MOVING_IDLE     = 0XFF,
    USB_OTA_MOVING_ING      = 0xC0,
    USB_OTA_MOVING_SUCC     = 0x3F,
};

/**
 * @brief  OTA Upgrade Directory Contains resources and code
 */
#define USB_DIFF_RES_PATH                   "/udisk/diff_res"
#define USB_DIFF_CODE_PATH                  "/udisk/diff_code"
#define USB_DIFF_RES_ROOT_PATH              "/udisk/diff_res/root"          /**<root Resource upgrade directory           */
#define USB_DIFF_RES_DYN_PATH               "/udisk/diff_res/dyn"           /**<dyn Resource upgrade directory           */
#define USB_DIFF_RES_MUSIC_PATH             "/udisk/diff_res/music"         /**<music Resource upgrade directory           */
#define USB_DIFF_RES_MISC_PATH              "/udisk/diff_res/misc"          /**<misc Resource upgrade directory           */
#define USB_DIFF_RES_UDISK_PATH             "/udisk/diff_res/udisk"         /**<udisk Resource upgrade directory           */
#define USB_DIFF_CODE_HCPU_PATH             "/udisk/diff_code/hcpu"         /**<hcpu code upgrade directory           */

4.1.2 升级目录与 ID 映射

/**
 * @brief  Directory and corresponding ID for USB upgrade
 */
struct usb_diff_msg
{
    char  *path;                                                            /**升级文件的目录*/
    char  *dev_name;                                                        /**升级文件的device设备名*/
    uint16_t id;                                                            /**check id*/
};
static struct usb_diff_msg usb_ota_msg[] =
{
    {USB_DIFF_RES_ROOT_PATH,        "root",          USB_OTA_CHECK_ROOT},
    {USB_DIFF_RES_DYN_PATH,         "dyn",           USB_OTA_CHECK_DYN},
    {USB_DIFF_RES_MUSIC_PATH,       "music",         USB_OTA_CHECK_MUSIC},
    {USB_DIFF_RES_UDISK_PATH,       "udisk",         USB_OTA_CHECK_UDISK},
    {USB_DIFF_RES_MISC_PATH,        "misc",          USB_OTA_CHECK_MISC},

    {USB_DIFF_CODE_HCPU_PATH,       "code_pang",     USB_OTA_CHECK_HCPU},
};

4.2 升级核心流程

OTA升级流程如下:

  • 系统资源升级:发现有升级文件 → 使用读取文件系统的方式读取需要升级的文件 → 将读取的文件写入到相应的资源地址 → 写入kvdb标志用于开机检测 → 删除文件系统文件 → 重启

  • 代码升级:发现有升级文件 → 使用读取文件系统的方式读取需要升级的文件 → 将读取的文件写入到emmc一段空白地址处 → 写入env标志位用于OTAmanager执行 → 写入kvdb标志用于开机检测 → 删除文件系统文件 → 重启

4.2.1 检测升级文件并上报

/**
功能:向UI发送消息接口
*/
void usb_ota_to_msg_app(void)
{
    int ota_file_page_num = 0, file_len = 0, page_num = 0;
    for (int i = 0; i < USB_OTAD_PATH_FILE_NUM; i ++)//遍历udisk上的所有文件夹
    {
        file_len = usb_read_ota_dir(usb_ota_msg[i].path, NULL); //读取指定目录是都有文件
        page_num = file_len / USB_OTA_PACKAGE_SIZE;             //计算升级的包数 512字节一包
        if (file_len % USB_OTA_PACKAGE_SIZE) page_num++;        //计算余数
        ota_file_page_num += page_num; /*Calculate the total number of packages to upgrade*/
    }
    if (ota_file_page_num)
    {
        usb_to_msg_gui(NULL, ota_file_page_num, USB_GUI_OTA_ID);/*Send the total number of packages that need to be upgraded to the GUI*/
        LOG_I("%s page num %d", __func__, ota_file_page_num);
        usb_ota_all_file(usb_ota_msg, ota_file_page_num);/*Execute USB upgrade resources and code*/
    }
}

4.2.2 执行全量升级(usb_ota_all_file)

/*
    功能:ota所有文件
    参数:ota_msg->所有的升级目录信息;ota_page_max:总包数(512一个包)
*/
void usb_ota_all_file(struct usb_diff_msg ota_msg[], uint32_t ota_page_max)
{
    uint32_t bin_size = 0;
    ota_page_count = 0;
    usb_ota_write_flag(0x00);                                               //将check id 全部设置为0
    for (int i = 0; i < USB_OTAD_PATH_FILE_NUM; i ++)
    {
        LOG_I("%s,path=%s", __func__, ota_msg[i].path);
        bin_size = usb_ota_file_transfer(ota_msg[i], ota_page_max);         //执行单个文件的升级
    }
    usb_ota_write_flag(usb_ota_read_flag() >> 0x2);                         //写入升级完成的标志 00xx xxxx:(7,8位:00升级完成;11升级中)
    usb_ota_jump(usb_ota_get_type(), bin_size);                             //重启跳转
}
/*
    功能:ota文件搬移与标志位写如
    参数:ota_msg:需要升级的文件信息;page_max:总包数(512一个包)
    返回:如果有执行代码(HCPU)的升级就返回该文件大小,没有就返回0.
*/
/*Performing an upgrade of a single file*/
static uint32_t usb_ota_file_transfer(struct usb_diff_msg ota_msg, uint32_t page_max)
{
    uint32_t size = 0, ota_hcpu_size = 0;
    uint8_t usb_check = 0;
    char file_name[USB_DEVICE_NAME_LEN] = {0};
    memset(file_name, 0x00, USB_DEVICE_NAME_LEN);
    usb_check = usb_ota_read_flag();                                                //获取check ID
    size = usb_read_ota_dir(ota_msg.path, file_name);
    if (size)
    {
        LOG_I("%s ota_msg.path=%s,name=%s", __func__, ota_msg.path, file_name);
        usb_check |= USB_OTA_MOVING_ING;/*Upgrading*/
        usb_ota_write_flag(usb_check);/*Write upgrade flag*/
        if (RT_EOK == usb_ota_moving_file(ota_msg, file_name, page_max))            //进行文件的搬移
        {
            LOG_I("%s moving succ !!!", __func__);
            usb_check |= ota_msg.id;/*file upgrad succ*/
            usb_ota_write_flag(usb_check);/*Write upgrade flag*/
            if (0 == strcmp(ota_msg.dev_name, "code_pang"))
            {
                usb_ota_set_type(USB_OTA_TYPE_HCPU);
                ota_hcpu_size = size;
            }
            else usb_ota_set_type(USB_OTA_TYPE_RES);
        }
        else
        {
            LOG_I("%s moving fail !!!", __func__);
            usb_check |= (!ota_msg.id);/*file upgrad fail*/
            usb_ota_write_flag(usb_check);/*Write upgrade flag*/
        }
    }
    else
    {
        usb_check |= ota_msg.id;
        usb_ota_write_flag(usb_check);/*Write upgrade flag*/
    }
    return ota_hcpu_size;
}

4.2.4 重启跳转(usb_ota_jump)

/*
    功能:跳转以重启
    参数:flag:升级的类型;len:执行代码升级的文件大小
*/
void  usb_ota_jump(uint32_t flag, uint32_t len)
{
    if (flag == USB_OTA_TYPE_HCPU)
    {
        /*jump to ota manager code*/
        LOG_I("%s,USB_OTA_CODE_HCPU drv_reboot,file_size=%d", __func__, len);
        dfu_ctrl_env_t *env = dfu_ctrl_get_env();
        env->prog.state = DFU_CTRL_USB_INSTALL;                                 //写入OTA manager的标志
        env->prog.FW_version = len;                                             //HCPU 代码大小
        dfu_ctrl_update_prog_info(env);                                         //保存env
        drv_reboot();                                                           //重启
    }
    else if (flag == USB_OTA_TYPE_RES)
    {
        /*Just restart the resource file*/
        LOG_I("%s,USB_OTA_RES drv_reboot", __func__);
        drv_reboot();
    }
}

4.2.5 开机检测升级结果

/**
 * check ID:是由一个uint8_t的数以位作为每个文件的标志组成;
 * check ID:uint8_t ID = 0x3f ;   1111 1111 :1-6位依次为文件 ROOT DYN MUSIC MISC UDISK HCPU的升级成功标志 1-升级成功 0-升级失败;7-8位 11-升级中;00升级完成,
 * 在usb_ota_file_transfer()接口中没有升级此文件时候也会将该文件的标志置为1;因此0x3f是升级完成,在升级完成后会将check ID置为0xff。
 * 
*/
static void dfs_check_res_integrity(void)
{
    uint8_t usb_ota_stater = 0;
    usb_ota_stater = usb_ota_read_flag();
    LOG_I("%s usb_ota_stater=%p", __func__, usb_ota_stater);
    switch (usb_ota_stater)
    {
    case USB_OTA_MOVING_IDLE:
    {
        LOG_I("%s IDLE", __func__);
    }
    break;
    case USB_OTA_MOVING_SUCC:                                       //升级成功
    {
        LOG_I("%s Resource updated !", __func__);
        usb_ota_write_flag(USB_OTA_MOVING_IDLE);                    //写入0xff
    }
    break;
    default :
    {
        LOG_I("%s Upgrade failed, please upgrade again !", __func__);
        usb_ota_to_msg_app();                                       //为其他值视为升级失败,就重新进入升级流程
    }
    break;

    }
}

4.3 OTA Manager(代码升级专用)

仅当升级 HCPU 代码时进入,负责将 emmc 中的升级文件写入 APP 运行地址。(只升级资源就直接覆盖后重启即可)。

4.3.1 重置处理函数

uint8_t dfu_ctrl_reset_handler(void)
{
    dfu_ctrl_env_t *env = dfu_ctrl_get_env();
    RT_ASSERT(env->mode == DFU_CTRL_OTA_MODE);
    uint16_t state = env->prog.state;

    uint8_t status = DFU_ERR_GENERAL_ERR;
    uint8_t is_jump = 0;
    LOG_I("dfu_ctrl_reset_handler %d", state);
    // dfu_ota_bootloader_ram_run_set(DFU_RAM_STATE_UPDATE_FAIL);

    if (HAL_Get_backup(RTC_BAKCUP_OTA_FORCE_MODE) == 1)
    {
        HAL_Set_backup(RTC_BAKCUP_OTA_FORCE_MODE, 0);
        dfu_flash_addr_reset(0);
        dfu_ctrl_boot_to_user_fw();
    }
    ...........
    switch (state)
    {
 
    case DFU_CTRL_USB_INSTALL:                                  //USB升级标志
    {
#ifdef OTA_EMMC
        env->callback(DFU_APP_RESET_IND, NULL);                 //打开UI
        LOG_I("%s moving addr=%p,bin_size=%d\n", __func__, HCPU_FLASH_CODE_START_ADDR, env->prog.FW_version);
        uint8_t dfu_emmc_code_upgrade(dfu_ctrl_env_t *env, uint32_t size);
        uint8_t dfu_upgrade_result = dfu_emmc_code_upgrade(env, env->prog.FW_version);//进行code升级
        switch (dfu_upgrade_result)
        {
        case DFU_USB_FIND_FAIL:                                 //升级失败
        {
            LOG_I("%s find fail!\n", __func__);
            env->prog.state = DFU_CTRL_IDLE;                    //写入标志位 回退之前的app,重启
            env->prog.FW_version = 0;
            dfu_ctrl_update_prog_info(env);
            drv_reboot();
        }
        break;
        case DFU_USB_FAIL:                                      //在搬移过程中失败
        {
            LOG_I("%s fail!,env->prog.FW_version=%d\n", __func__, env->prog.FW_version);
            if (env->prog.FW_version == 0) env->prog.FW_version = 1;
            else if (env->prog.FW_version == 5)                 //重启超过5次就不在重启了
            {
                LOG_I("%s while(1)\n", __func__);
                while (1) {;}
            }
            else env->prog.FW_version += 1;
            dfu_ctrl_update_prog_info(env);
            drv_reboot();
        }
        break;
        }
#else
        is_jump = 1;
#endif
    }
    break;
    ...........
    return 0;
}

4.3.2 EMMC 代码升级实现

/**
 * 功能:code升级
 * 参数:env;size:文件大小
 * 返回:升级结果
*/
uint8_t dfu_emmc_code_upgrade(dfu_ctrl_env_t *env, uint32_t size)
{

    uint32_t page_num = size / DFU_EMMC_PAGE_MAX;
    uint32_t emmc_offset = 0, nor_offset = 0, end_page_size = size % DFU_EMMC_PAGE_MAX;
    if (end_page_size) page_num ++ ;

    rt_device_t dev = rt_device_find("code_pang");
    LOG_I("%s page_num=%d,dev=%p,name=%s", __func__, page_num, dev, dev->parent.name);
    env->callback(DFU_APP_DL_END_AND_INSTALL_START_IND, NULL);
    if (dev)
    {
        if (RT_EOK == rt_device_open(dev, RT_DEVICE_FLAG_RDWR))
        {
            int res = 0;
            while (page_num --)
            {
                memset(code_buf, 0x00, DFU_EMMC_PAGE_MAX);
                res = rt_device_read(dev, emmc_offset, (void *)code_buf, DFU_EMMC_PAGE_MAX / 512);//读取emmc里面的code
                if (res > 0)
                {
                    rt_flash_erase(HCPU_FLASH_CODE_START_ADDR + (nor_offset * DFU_EMMC_PAGE_MAX), DFU_EMMC_PAGE_MAX);//擦除nor
                    uint32_t wr_size = rt_flash_write(HCPU_FLASH_CODE_START_ADDR + (nor_offset * DFU_EMMC_PAGE_MAX), code_buf, DFU_EMMC_PAGE_MAX);//将代码写入nor上的app地址
                    if (wr_size != DFU_EMMC_PAGE_MAX)
                    {
                        LOG_I("%s write fail!,nor_offset=0x%x,ADDR=0x%x wr_size=%d", __func__, nor_offset, HCPU_FLASH_CODE_START_ADDR + (nor_offset * DFU_EMMC_PAGE_MAX), wr_size);
                        return DFU_USB_FAIL;
                    }
                }
                else
                {
                    rt_device_close(dev);
                    LOG_I("%s read fail!,emmc_offset=0x%x,ADDR=0x%x res=%d", __func__, emmc_offset, HCPU_FLASH_CODE_START_ADDR, res);
                    return DFU_USB_FAIL;
                }
                emmc_offset += (DFU_EMMC_PAGE_MAX / 512);
                nor_offset  += 1;
                dfu_install_progress_ind(nor_offset * DFU_EMMC_PAGE_MAX, size);//向UI发送进度
            }
        }
    }
    else
    {
        LOG_I("%s find code_pang fail!", __func__);
        rt_device_close(dev);
        return DFU_USB_FIND_FAIL;
    }
    LOG_I("%s emmc code moving sucss!", __func__);
    env->callback(DFU_APP_INSTALL_COMPLETD_IND, NULL);
    env->callback(DFU_APP_TO_USER, NULL);
    env->prog.state = DFU_CTRL_IDLE;
    env->prog.FW_state = DFU_CTRL_FW_INSTALLED;
    env->prog.FW_version = 0;
    env->is_force_update = 0;
    dfu_ctrl_update_prog_info(env);//保存状态
    dfu_ctrl_boot_to_user_fw();//重启
    return DFU_USB_EOK;
}

4.4 USB OTA使用流程

  1. 插入USB,pc上正确识别到U盘。并且能正常打开里面的目录,目录如下:

#define USB_DIFF_RES_PATH                   "/udisk/diff_res"
#define USB_DIFF_CODE_PATH                  "/udisk/diff_code"
#define USB_DIFF_RES_ROOT_PATH              "/udisk/diff_res/root"          /**<root 图库 字体 等相关资源                   */
#define USB_DIFF_RES_DYN_PATH               "/udisk/diff_res/dyn"           /**<dyn 资源升级目录                           */
#define USB_DIFF_RES_MUSIC_PATH             "/udisk/diff_res/music"         /**<music 资源升级目录                         */
#define USB_DIFF_RES_MISC_PATH              "/udisk/diff_res/misc"          /**<misc 资源升级目录                          */
#define USB_DIFF_RES_UDISK_PATH             "/udisk/diff_res/udisk"         /**<udisk 资源升级目录                         */
#define USB_DIFF_CODE_HCPU_PATH             "/udisk/diff_code/hcpu"         /**<hcpu 代码升级目录  hcpu已经包含了lcpu代码   */
  1. 将需要升级的文件放至对应的目录里面去

../_images/usb_ota_1.png ../_images/usb_ota_2.png
  1. 拔出 USB:自动检测并执行升级

  • 仅资源升级:显示 copy 进度 → 完成后重启至主界面

../_images/usb_ota_res_1.png ../_images/usb_ota_res_2.png ../_images/usb_ota_master.png
  • 含代码升级:copy 完成 → 重启至 OTA manager 安装 → 完成后重启至主界面

../_images/usb_ota_code_1.png ../_images/usb_ota_code_2.png ../_images/usb_ota_code_3.png ../_images/usb_ota_master.png

5. 常见报错

5.1 检测不到 USB 协议

  • 检查 USB 时钟是否为 60MHz(在bsp_init.c或brv_io.c中确认)

在bf0_hal_pcd.c中

HAL_RCC_DisableModule(RCC_MOD_USBC);
  • 检查是否有代码关闭 USB 模块(如HAL_RCC_DisableModule(RCC_MOD_USBC);)

  • 确认 USB 引脚连接正确

5.2 PC 识别到 MSTORAGE 设备但无 U 盘

  • 检查 USB 协议通信是否正常,可使用 wireshark 抓取 USB 协议包分析

  • 确认 menuconfig 中分区配置正确(分区名或 DHARA 映射名)