存储设备方案
概述
USB Mass Storage Class (MSC) 协议定义了 USB 大容量存储设备(如 U 盘、SD 卡读卡器等)的标准接口与传输规范。
Ameba 基于 USB-IF 官方发布的 MSC 协议标准,实现了完备的 USB 存储设备功能,支持通过 SCSI(Small Computer System Interface)命令集与主机进行交互,提供对存储介质的高效读写、擦除及状态查询能力。
特性
基于 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 传输协议规范。开发过程中请参考以下核心文档:
MSC BOT 传输协议规范,请从 USB IF 官网下载 Mass Storage Bulk Only
BOT 传输过程中使用的 SCSI 命令集,请参考 https://www.t10.org/, 其中主要关注下面两个规范:
规范
说明
文档
SCSI Primary Commands (SPC)
所有 SCSI 设备通用的基本命令
SCSI Block Commands (SBC)
针对块存储设备(硬盘、U盘、SSD)的特定命令
软硬件层级结构
下图展示了命令和数据在主机和设备之间所经过的软硬件层级。
以读操作为例,当用户从 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 命令的执行结果(成功、失败或阶段错误)。
数据传输流程如下:
主机发起请求:主机 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 说明
应用示例
应用设计
本节概述 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 做存储介质时,需要格式化后才能正常使用。