STM32模拟U盘实现方案

AI助手已提取文章相关产品:

利用STM32F103RC的USB将W25Q64模拟成U盘

在工业控制、智能设备和自动化测试场景中,我们常常遇到一个看似简单却颇具挑战的问题:如何让一台没有传统存储接口的嵌入式设备,像U盘一样被PC直接识别并读写?更进一步,如果这个“U盘”还能预装固件、记录日志、甚至支持批量配置分发,那它的价值就远不止一个移动硬盘那么简单。

这正是虚拟U盘技术的魅力所在。通过微控制器模拟标准USB大容量存储设备(Mass Storage Class, MSC),我们可以把外挂Flash芯片伪装成一个即插即用的可移动磁盘。而今天要讲的方案—— 用STM32F103RC驱动W25Q64实现虚拟U盘 ——不仅成本极低,而且兼容性极强,是很多量产项目中的“隐形功臣”。

为什么选STM32F103RC?

你可能会问:现在有那么多带USB的MCU,为什么要用这款“老将”?毕竟它属于早已发布的STM32F1系列,主频72MHz,128KB Flash、20KB RAM,在当下动辄几百MHz主频、内置高速SRAM的新平台面前显得有些寒酸。

但正是这种“够用就好”的定位,让它在性价比敏感的应用中依然坚挺。更重要的是, 它原生支持USB 2.0全速设备模式(12Mbps) ,配合成熟的HAL库或LL库,可以快速搭建MSC应用。不需要额外PHY芯片,也不需要复杂的PCB布局——PA11/PA12引脚直连D+/D-即可。

不过这里有个关键细节容易被忽略: STM32F103RC内部没有集成D+上拉电阻 。这意味着你必须通过一个GPIO控制外部1.5kΩ电阻连接到3.3V电源,才能向主机宣告“我来了”。代码里通常是这样操作:

// 模拟上拉,触发枚举
HAL_GPIO_WritePin(USB_PULLUP_PORT, USB_PULLUP_PIN, GPIO_PIN_SET);

一旦这一步没做,PC根本不会发现设备的存在,调试时只能看到“未知USB设备”或者干脆无反应。

W25Q64:小身材,大用途

作为存储介质,W25Q64是一款典型的SPI Flash芯片,容量8MB(64Mbit),采用SOP8封装,仅需CS、SCK、MOSI、MISO四根信号线即可通信。它的优势在于价格低廉(几元人民币)、非易失性、擦写寿命高达10万次,非常适合用于存放固件镜像或运行日志。

但要注意,Flash不是RAM,不能随意写入。每次写之前必须先擦除,且最小擦除单位是4KB扇区,而写入单位是256字节页。如果你试图在一个未擦除的区域写数据,结果只会是“掩码叠加”,旧数据并不会消失。

因此,在实现文件系统时必须做好抽象层管理。比如当主机下发一个512字节的写请求时,我们的处理流程应该是:

  1. 锁定对应的LBA地址;
  2. 读取整个4KB扇区到缓冲区;
  3. 在缓冲区中修改目标512字节;
  4. 擦除原扇区;
  5. 将更新后的4KB数据重新写回。

虽然听起来繁琐,但在实际应用中可以通过合理设计簇大小来减少这类操作频率。

协议栈是怎么跑起来的?

当你把板子插入PC,背后其实是一场精密的“对话”。整个过程从USB枚举开始,然后进入SCSI命令交互阶段。PC并不关心你是真U盘还是假U盘,它只认标准协议。

首先,设备发送自己的描述符,声明自己是一个 大容量存储类设备(bInterfaceClass = 0x08) 。接着,主机发起一系列SCSI Inquiry命令获取设备信息,例如厂商名、产品型号、是否可移除等。这些都可以在 USBD_Storage_fopsTypeDef 结构体中自定义:

static int8_t USER_GetInquiryData(uint8_t lun, uint8_t *data) {
    const char info[] = "STMicroelectronics";
    memcpy(data, info, strlen(info));
    return USBD_OK;
}

确认身份后,主机会查询容量:

int8_t USER_GetCapacity(uint8_t lun, uint32_t *block_num, uint16_t *block_size) {
    *block_num = 16384;   // 8MB / 512B per sector
    *block_size = 512;
    return USBD_OK;
}

这里的512字节扇区是硬性要求——无论底层Flash物理页多大,对外都必须模拟成标准块设备。这也是FatFs能正常工作的前提。

接下来就是真正的读写操作了。每当用户拖拽文件到设备,Windows就会发出 WRITE(10) 命令,携带LBA地址和数据长度。STM32接收到后,调用注册的 USER_Write 函数,最终落地到SPI Flash。

FatFs:嵌入式文件系统的“瑞士军刀”

说到文件系统,大多数人第一反应是FAT。没错,尽管它古老,但它几乎被所有操作系统原生支持,无需安装驱动。对于8MB的小容量设备,使用FAT12或FAT16最为合适。

我们采用ChaN开发的 FatFs R0.14 ,这是一个轻量级、模块化、高度可移植的开源库,专为资源受限系统设计。它不依赖操作系统,只需实现几个底层接口就能跑起来。

核心文件是 diskio.c ,其中最关键的是五个函数:

DSTATUS disk_initialize(BYTE pdrv);
DSTATUS disk_status(BYTE pdrv);
DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count);
DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count);
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff);

它们构成了FatFs与硬件之间的桥梁。比如 disk_ioctl(GET_SECTOR_COUNT) 返回总扇区数, CTRL_SYNC 用于确保写操作完成。

特别提醒一点: 不要在中断上下文或USB回调中直接调用SPI读写 !否则可能导致时序冲突或死锁。建议将数据拷贝到缓冲区后再异步处理,必要时加入互斥锁保护共享资源。

实际开发中的坑与对策

1. PC不识别设备?

最常见的原因是 USB描述符配置错误 。尤其是 bDeviceClass bInterfaceClass 字段:

  • bDeviceClass = 0x00 (指定由接口决定)
  • bInterfaceClass = 0x08 (MSC类)
  • bInterfaceSubClass = 0x06 (SCSI透明命令集)
  • bInterfaceProtocol = 0x50 (Bulk-Only Transport)

任何一个出错,主机都会拒绝加载MSC驱动。

2. 写入失败或文件损坏?

检查是否遗漏了 扇区擦除步骤 。Flash特性决定了“先擦后写”是铁律。另外,编译器优化等级过高可能打乱SPI时序,建议对关键函数加 __attribute__((optimize("O1"))) 降级优化。

3. 插拔几次后变砖?

多半是电源问题。USB总线供电能力有限,而W25Q64在编程时瞬态电流可达8mA以上。若系统电源滤波不足,会造成MCU复位或Flash写入异常。推荐在VCC处并联10μF电解电容 + 0.1μF陶瓷电容。

4. 文件系统频繁崩溃?

可以考虑预先烧录一个干净的FAT12镜像进Flash起始位置。使用工具如 fatgen103.pdf 规范生成MBR、DBR、FAT表和根目录,确保首次插入即能被正确识别。也可以启用FatFs的 _USE_MKFS 宏,在初始化时自动格式化。

如何提升稳定性与用户体验?

  • 加入DMA传输 :SPI使用DMA可大幅降低CPU占用率,尤其在连续读写大文件时效果明显。
  • 设置写保护开关 :通过GPIO检测物理跳线或软件标志位,防止误删关键数据。
  • 支持热插拔检测 :监听USB断开事件,及时释放资源,避免下次连接失败。
  • 统一VID/PID :使用ST官方默认值(0x0483/0x5740)可提高兼容性,避免杀毒软件误报。
  • 日志分区隔离 :将用户数据区与系统日志区分开,便于维护和恢复。

它能在哪些地方发光?

这个方案已经在多个实际场景中证明了自己的价值:

  • 产线烧录工具 :工人只需将模块插入PC,自动弹出U盘,里面放着对应机型的固件包,双击即可安装。
  • 仪器数据导出 :医疗设备运行过程中持续记录日志到Flash,关机后拔下核心板,就像取出一张U盘。
  • 教学实验平台 :学生不仅能学会SPI、USB协议,还能深入理解MBR、BPB、FAT表等底层结构,比单纯调API有意义得多。

甚至有人把它做成“伪装U盘”,插入电脑后自动执行特定任务(当然要合法合规使用)。

还能怎么升级?

未来优化方向还有很多:

  • 引入简单的 磨损均衡算法 ,延长Flash寿命;
  • 使用 TinyUSB 替代HAL库的USB堆栈,代码更精简,资源占用更低;
  • 增加RTC模块,为每个文件打上时间戳,打造微型数据记录仪;
  • 改造成复合设备,同时具备U盘 + 虚拟串口功能,方便调试;
  • 支持双分区:一个只读区放固件,一个可读写区供用户存配置。

这种将普通MCU变身标准外设的设计思路,充分体现了嵌入式开发中“以软补硬”的智慧。用不到十元的成本,换来跨平台即插即用的能力,不仅是技术上的胜利,更是工程性价比的典范。当你亲手做出第一个能被Windows自动识别的“自制U盘”时,那种成就感,或许才是驱动无数工程师深夜敲代码的最大动力。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

您可能感兴趣的与本文相关内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值