存储设备方案

概述

USB Mass Storage Class (MSC) 协议定义了 USB 大容量存储设备(如 U 盘、SD 卡读卡器等)的标准接口与传输规范。

Ameba 基于 USB-IF 官方发布的 MSC 协议标准,实现了完备的 USB 存储设备功能,支持通过 SCSI(Small Computer System Interface)命令集与主机进行交互,提供对存储介质的高效读写、擦除及状态查询能力。

Ameba USB MSC 设备

特性

  • 基于 BOT(Bulk-Only Transport)传输协议

  • 支持多种存储介质

    • RAM

    • SD 卡(SD 模式:使用 SDIO 接口)

    • SD 卡(SPI 模式:使用 SPI 接口)

    • 外置 flash

  • 支持 USB 热插拔

  • 支持描述符全定制

  • 支持配置速度模式等参数

应用场景

作为 USB 存储设备,Ameba 支持对多种存储介质的灵活访问与管理。可结合 Wi-Fi、蓝牙等无线连接技术,实现多样化的数据存储与交互方案,例如:

  • 个人存储与无线扩展:Ameba 可作为标准 U 盘或 SD/TF 读卡器,用于文件传输、系统启动盘制作及车载媒体播放。结合 Wi-Fi 或蓝牙功能,可升级为“无线 U 盘”,支持手机/PC 通过无线网络共享访问存储内容,打破传统有线连接的物理限制。

  • 多媒体设备数据桥接:在数码相机、行车记录仪或数字多媒体播放器(MP3/MP4)等应用中,Ameba 将设备内部存储或扩展卡模拟为通用 U 盘。连接 PC 后,用户可直接通过拖拽方式管理照片、视频及音乐文件。

  • 便捷固件升级:设备连接 PC 后被识别为一个存储盘符,用户只需将新的固件文件(如 .bin 或 .uf2 格式)拖入该盘符,设备即可自动校验并完成系统更新,极大地降低终端用户的维护门槛。

  • 智能工业数据记录仪:针对工业、农业或科研领域的数据采集需求,利用蓝牙功能进行低功耗的参数配置(如修改采样频率、文件命名规则)。当需要处理海量历史数据时,通过 USB 接口连接 PC 进行高速导出,兼顾配置的灵活性与传输的高效性。

协议简介

MSC 协议定义了在 USB 规范下实现存储设备的传输和控制功能,遵循该标准的常见设备有 U 盘、移动硬盘和读卡器等。

描述符结构

MSC 设备除了遵循标准的 USB 描述符规范(如设备描述符、配置描述符、端点描述符)外,还需通过接口描述符声明其使用的通信协议(如 SCSI),并通过批量端点封装传输指令与数据。

以下展示了基于 Bulk-Only Transport (BOT) 模式并使用 SCSI 命令集的标准 USB MSC 描述符结构:

Device Descriptor
└── Identifies basic device information

Configuration Descriptor
└── Contains total length of the entire configuration, power supply information, etc.
│
└── Interface Descriptor(Interface 0, Alternate Setting 0)
    ├── bInterfaceClass: 0x08 (Mass Storage)
    ├── bInterfaceSubClass: 0x06 (SCSI transparent command set)
    ├── bInterfaceProtocol: 0x50 (Bulk-Only Transport)
    └── bNumEndpoints: 2 (2 Endpoints)
        ├── Endpoint Descriptor(BULK IN)
        └── Endpoint Descriptor(BULK OUT)

Device Qualifier Descriptor
└── Device information while running in another speed mode

Other Speed Configuration Descriptor
├── Configuration information while running in another speed mode.
│
└── Interface Descriptor(Interface 0, Alternate Setting 0)
    ├── bInterfaceClass: 0x08 (Mass Storage)
    ├── bInterfaceSubClass: 0x06 (SCSI transparent command set)
    ├── bInterfaceProtocol: 0x50 (Bulk-Only Transport)
    └── bNumEndpoints: 2 (2 Endpoints)
        ├── Endpoint Descriptor(BULK IN)
        └── Endpoint Descriptor(BULK OUT)

协议文档

USB-IF 官方发布了 MSC 类基础协议及关于 BOT 传输协议规范。开发过程中请参考以下核心文档:

软硬件层级结构

下图展示了命令和数据在主机和设备之间所经过的软硬件层级。

../../../_images/usb_msc_arch_zh.svg

以读操作为例,当用户从 U 盘读取一个文件时:

  • 主机端:

    • 应用请求:用户在应用程序(如文件管理器)中发起读文件请求。

    • 文件系统转换:文件系统将文件名与偏移量转换为逻辑块地址(LBA)读取请求,并生成标准的 SCSI READ 命令。

    • 协议封装:主机 MSC 类驱动将 SCSI 命令封装为 MSC 协议规定的命令包格式。

    • 硬件发送:USB 主机控制器驱动通过 USB 物理端口将数据包发送至总线。

  • 设备端:

    • 硬件接收:USB 设备控制器从物理端口接收数据包。

    • 协议解析:MSC 设备类驱动校验数据包完整性,并解析出内部封装的 SCSI 命令。

    • 介质访问:根据解析出的命令参数(如 LBA 地址和长度),从底层存储介质(如 SD 卡)读取对应数据。

    • 返回数据和状态:将读取的数据返回给主机,并回应命令执行状态。

BOT 传输流程

当 MSC 设备连接至主机并完成枚举后,若识别为支持 BOT 模式的大容量存储设备,后续的数据通信将仅通过批量端点进行。批量传输不受严格的时间限制,能够最大程度保证数据的完整性。

根据 MSC BOT 传输协议规范,所有的传输均遵循 "命令 (Command) -> 数据 (Data) -> 状态 (Status)" 的三段式流程:

  • CBW (Command Block Wrapper):由主机发往设备,包内封装了具体的 SCSI 命令(如 READ, WRITE, INQUIRY 等)。

  • Data (数据阶段):传输实际的文件或控制数据(传输方向取决于 SCSI 命令类型,对于某些无数据交互的命令,此阶段可省略)。

  • CSW (Command Status Wrapper):由设备发往主机,用于报告上一条 CBW 命令的执行结果(成功、失败或阶段错误)。

../../../_images/usb_msc_bot_flow_zh.svg

数据传输流程如下:

  • 主机发起请求:主机 MSC 类驱动将 SCSI 命令封装进 CBW 包,通过批量 OUT 端点发送给设备。

  • 设备解析与执行:设备接收 CBW 包并进行合法性检查,解析出 SCSI 命令。若 CBW 有效,设备将根据命令操作底层物理存储介质:

    • 写操作 (如 WRITE):通过批量 OUT 端点接收主机发送的数据流,并写入存储介质。

    • 读操作 (如 READ):从存储介质读取数据,通过批量 IN 端点回传给主机。

    • 无数据命令 (如 TEST UNIT READY):跳过数据阶段,直接进入状态阶段。

  • 设备反馈状态:数据传输完成后(或无需传输数据),设备通过批量 IN 端点发送 CSW 包,向主机报告命令执行结果。

  • 主机确认完成:主机解析接收到的 CSW 包,检查 bCSWStatus 字段以确认命令是否执行成功,从而结束本次操作。

类特定请求

MSC 设备的控制请求分为 标准请求(Standard Requests)类特定请求 (Class-Specific Requests)

本节主要介绍 MSC BOT 规范特有的 类特定请求,这些请求用于实现特有的存储功能,只有下面两个:

MSC 类特定请求

要求

描述

Bulk-Only Mass Storage Reset

必选

重置设备接口及其相关的端点

Get Max LUN

必选

查询该设备支持的最大逻辑单元数量

SCSI 命令

MSC BOT 规范下主要的 SCSI 命令如下:

SCSI 命令

要求

描述

INQUIRY

必选

主机发送的第一个命令,用于查询设备信息

REQUEST_SENSE

必选

当任何命令执行失败时,主机会发送此命令来获取详细的错误信息

TEST_UNIT_READY

必选

检查设备是否就绪

READ_CAPACITY(10)

必选

查询存储介质的容量

READ(10)

必选

核心的读数据命令

WRITE(10)

必选

核心的写数据命令

MODE_SENSE(6)

可选

查询设备的特定参数,如缓存策略、写保护状态等

ALLOW_MEDIUM_REMOVAL

可选

用来允许或禁止移除介质,实现“安全删除硬件”功能

START_STOP_UNIT

可选

用于加载/弹出介质

VERIFY(10)

可选

请求设备验证指定块的数据是否可读,但不传输数据

READ_FORMAT_CAPACITIES

可选

提供比 READ_CAPACITY 更详细的容量和格式信息

备注

详细 SCSI 标准可参考官方文档。

类驱动

驱动描述符结构

本节展示了驱动层定义的 MSC 类描述符结构体。这些结构体对应于 USB 协议规范中的标准描述符定义。

Device Descriptor
└── Identifies basic device information

Configuration Descriptor
└── Contains total length of the entire configuration, power supply information, etc.
│
└── Interface Descriptor(Interface 0, Alternate Setting 0)
    ├── bInterfaceClass: 0x08 (Mass Storage)
    ├── bInterfaceSubClass: 0x06 (SCSI transparent command set)
    ├── bInterfaceProtocol: 0x50 (Bulk-Only Transport)
    └── bNumEndpoints: 2 (2 Endpoints)
        ├── Endpoint Descriptor(BULK IN)
        └── Endpoint Descriptor(BULK OUT)

Device Qualifier Descriptor
└── Device information while running in another speed mode

Other Speed Configuration Descriptor
├── Configuration information while running in another speed mode.
│
└── Interface Descriptor(Interface 0, Alternate Setting 0)
    ├── bInterfaceClass: 0x08 (Mass Storage)
    ├── bInterfaceSubClass: 0x06 (SCSI transparent command set)
    ├── bInterfaceProtocol: 0x50 (Bulk-Only Transport)
    └── bNumEndpoints: 2 (2 Endpoints)
        ├── Endpoint Descriptor(BULK IN)
        └── Endpoint Descriptor(BULK OUT)

支持设备描述符全定制,参考 设备描述符定制

端点配置

端点

数量

描述

控制 IN/OUT 端点

1

处理 USB 主机发送的控制请求

批量 IN 端点

1

向 USB 主机发送数据

批量 OUT 端点

1

从 USB 主机接收数据

类特定请求实现

驱动已实现了 MSC 的两个类特定请求(Class-Specific Requests)

MSC 类特定请求

说明

Bulk-Only Mass Storage Reset

重置 BOT 传输状态,准备接收下一个 CSW

Get Max LUN

驱动当前只支持一个逻辑单元

SCSI 命令实现

驱动已实现的 MSC BOT 规范下的 SCSI 命令如下,开发者可参考现有实现来扩展其他命令。

SCSI 命令

说明

INQUIRY

返回常量 MSC 设备信息

REQUEST_SENSE

当任何命令执行失败时,主机会发送此命令来获取详细的错误信息

TEST_UNIT_READY

指示设备是否准备好接收下一个命令

READ_CAPACITY(10)

返回存储介质的容量

READ(10)

读存储介质

WRITE(10)

写数据到存储介质

MODE_SENSE(6)

返回常量 MSC 设备参数

ALLOW_MEDIUM_REMOVAL

MSC 驱动不支持移除介质,直接返回成功

START_STOP_UNIT

MSC 驱动不支持加载/弹出介质,介质一旦初始化即可使用

VERIFY(10)

检查指定块的地址是否在合理范围

READ_FORMAT_CAPACITIES

提供比 READ_CAPACITY 更详细的容量和格式信息

MODE_SENSE(10)

返回比 MODE_SENSE 更多的常量 MSC 设备参数

MODE_SELECT(6)

不允许主机修改其内部参数,驱动直接返回成功

MODE_SELECT(10)

同 MODE_SELECT(6) 实现

READ(12)

同 READ(10) 实现

WRITE(12)

同 WRITE(10) 实现

API 说明

驱动 API

应用示例

应用设计

本节概述 MSC 驱动的完整使用流程,涵盖驱动加载、热插拔管理和驱动卸载等核心环节。

加载驱动

定义配置结构体和回调函数,随后调用初始化接口,对底层存储初始化并加载 USB 设备核心驱动及 MSC 类驱动。

static usbd_config_t usbd_msc_cfg = {
    .speed = CONFIG_USBD_MSC_SPEED,
    .isr_priority = INT_PRI_MIDDLE,
};

static usbd_msc_cb_t usbd_msc_cb = {
    .status_changed = usbd_msc_cb_status_changed  /* USB status change callback. */
};

int ret = 0;
ret = usbd_msc_disk_init();    /* Initializes the underlying storage disk. */
if (ret != HAL_OK) {
    return;
}

ret = usbd_init(&usbd_msc_cfg);     /* Initialize USB device core driver with configuration. */
if (ret != HAL_OK) {
    usbd_msc_disk_deinit();
    return;
}

ret = usbd_msc_init(&usbd_msc_cb);  /* Initializes the MSC device class. */
if (ret != HAL_OK) {
    usbd_msc_disk_deinit();
    usbd_deinit();
    return;
}

USB 热插拔事件处理

通过注册 status_changed 回调函数来检测 USB 连接状态变化(连接/断开)。

参考 设备连接状态检测 , 获取更多细节。

备注

建议使用信号量通知专用任务线程进行处理,避免在中断上下文中执行耗时操作。

static u8 msc_usb_attach_status;
static rtos_sema_t msc_usb_status_changed_sema;

static usbd_msc_cb_t usbd_msc_cb = {
    .status_changed = usbd_msc_cb_status_changed   /* USB status change callback. */
};

/* Callback executed in ISR context */
static void usbd_msc_cb_status_changed(u8 old_status, u8 status)
{
  RTK_LOGS(TAG, RTK_LOG_INFO, "USB status change: %d -> %d\n", old_status, status);
  msc_usb_attach_status = status;
  rtos_sema_give(msc_usb_status_changed_sema);
}

/* Thread handling the state machine */
static void msc_usb_hotplug_thread(void *param)
{
  int ret = 0;

  UNUSED(param);

  for (;;) {
    if (rtos_sema_take(msc_usb_status_changed_sema, RTOS_SEMA_MAX_COUNT) == RTK_SUCCESS) {
      if (msc_usb_attach_status == USBD_ATTACH_STATUS_DETACHED) {
        RTK_LOGS(TAG, RTK_LOG_INFO, "DETACHED\n");
        /* Clean up resources */
        usbd_msc_deinit();
        ret = usbd_deinit();
        if (ret != 0) {
          break;
        }
        usbd_msc_disk_deinit();
        RTK_LOGS(TAG, RTK_LOG_INFO, "Free heap: 0x%x\n", rtos_mem_get_free_heap_size());

        /* Re-initialize for next connection */
        usbd_msc_disk_init();
        ret = usbd_init(&msc_cfg);
        if (ret != 0) {
          break;
        }
        ret = usbd_msc_init(&msc_cb);
        if (ret != 0) {
          usbd_deinit();
          break;
        }
      } else if (msc_usb_attach_status == USBD_ATTACH_STATUS_ATTACHED) {
        RTK_LOGS(TAG, RTK_LOG_INFO, "ATTACHED\n");
      } else {
        RTK_LOGS(TAG, RTK_LOG_INFO, "INIT\n");
      }
    }
  }
  RTK_LOGS(TAG, RTK_LOG_ERROR, "Hotplug thread fail\n");
  rtos_task_delete(NULL);
}

卸载驱动

在不再需要存储功能或系统关机时,按加载的反序释放资源。

/* De-initializes the underlying storage disk. */
usbd_msc_disk_deinit();
/* Deinitialize MSC device class driver. */
usbd_msc_deinit();
/* Deinitialize USB device core driver. */
usbd_deinit();

运行方式

本节介绍了一个完整的 USB 大容量存储(MSC)应用示例,该示例演示了如何通过 MSC 协议栈,将 Ameba 开发板配置为 USB 存储设备。

当开发板连接至 USB 主机(如 PC)时,系统将其识别为可移动磁盘。主机可以通过 USB 接口直接读写开发板上的存储介质(如 SD 卡), 支持 USB 热插拔。

该示例代码路径: {SDK}/example/usb/usbd_msc ,可为开发者设计自定义 USB 存储产品提供完整的设计参考。

备注

慎用 SD 卡热插拔功能,在传输过程中热插拔,有造成文件系统损坏、数据丢失的风险。

配置与编译

  • 编译与烧录

    在 SDK 根目录下执行以下命令以配置环境,选择目标 SoC,编译工程,然后将生成的 Image 文件烧录至开发板。

    # Initialize environment (required for every new terminal)
    source env.sh or env.bat(Windows system)
    
    # Select Target SoC (replace xxx with your specific SoCs)
    ameba.py soc xxx
    
    ameba.py build -a usbd_msc -p
    
  • Menuconfig 配置确认

    若编译失败,请执行 ameba.py menuconfig,确认已选择 USBD MSC 和存储介质。

    - Choose `CONFIG USB --->`:
    
      [*] Enable USB
          USB Mode (Device)  --->
      [*] MSC
          Select storage media (RAM)  --->
    

结果验证

  • 启动设备

    重启开发板,观察串口日志,应显示如下启动信息:

    [MSC-I] USBD MSC demo start
    
  • 连接主机

    用 USB 线缆将开发板连接到 PC(或其他支持 USB MSC 的主机设备)。

  • 系统识别

    PC 端文件管理器中应自动弹出一个新的可移动磁盘盘符。用户可以双击打开该盘符,对其中的文件进行读写操作,验证数据传输是否正常。

    备注

    使用 RAM 做存储介质时,需要格式化后才能正常使用。