生长中技术笔记
STM32F103 用内部 Flash 模拟 USB U 盘(HAL 库 MSC)
背景与适用场景
STM32F103 系列没有内置 SD 卡槽或 eMMC,但在需要"插入电脑即可读写文件"的场景(如固件 IAP 升级、日志导出、参数配置)中,模拟一个 USB 大容量存储设备(MSC)是成本最低的方案之一。本文以 STM32F103RCT6 为例,使用 HAL 库 + CubeMX 生成工程,将内部 Flash 的一段区域暴露为 USB 存储设备。
适用前提:
- 主控为 STM32F103 系列(带 USB OTG FS 外设),本文以 RCT6 为例。
- 应用代码能放在 Flash 起始区域,为存储区预留足够空间。
- 对内部 Flash 擦写寿命有基本认知。
CubeMX 配置
- 新建工程,芯片选 STM32F103RCTx,配置好系统时钟(通常 72 MHz)。
- 在 Connectivity 中启用 USB,模式选 Device,库选 Full Speed。
- 在 Middleware 中启用 USB_DEVICE,Class 选 Mass Storage Class。
- 生成工程。

修改 usbd_storage_if.c
生成工程后,打开 usbd_storage_if.c,将默认的 SD 卡操作替换为内部 Flash 读写。核心改动如下:
text
几点说明:
FLASH_START_ADDR设为0x08010000,即从 Flash 第 64 KB 处开始,共 64 个扇区 × 2 KB = 128 KB 用于存储。应用代码必须全部放在0x08000000 ~ 0x0800FFFF范围内。- 读取函数通过指针直接映射 Flash 地址,无需额外搬运(Cortex-M 可通过数据总线直接读取 Flash)。
- 写入函数先按页擦除,再以 4 字节(WORD)为单位编程。
代码中需要注意的问题
原文代码存在几处需要修正的地方,实际移植时请留意:
- 读取函数缺少返回值:
STORAGE_Read_FS末尾没有return USBD_OK;,调用方会拿到未定义值。 - 指针混用:函数内声明了
p = buf,但循环里写的是*(buf++),导致p未使用且buf指针被修改。应统一用p或直接用buf[i]索引。 - 写入函数返回
FAIL:应为return USBD_FAIL;,否则编译报错或行为未定义。 - 擦除粒度与页大小:
EraseInitStruct.NbPages = blk_len隐含假设每个 MSC 扇区(2 KB)恰好等于一个 Flash 页。实际页大小取决于存储地址落在 Flash 的哪个区域,需查阅对应数据手册确认。若扇区大小与页大小不一致,NbPages的计算需要相应调整。此外,MSC 协议中主机始终以整扇区为单位发起写请求,不存在"写半个扇区"的情况;但若应用层只想更新某扇区中的少量字节,仍需先读出整个扇区、修改后再整扇区写回,因为擦除会清除整页数据。
使用与验证
- 将程序烧录到开发板,通过 USB 连接电脑。
- 首次连接时,操作系统会提示"需要格式化",选择快速格式化(128 KB 容量通常会被格式化为 FAT12)。
- 格式化完成后,设备会显示为一个可读写的小容量 U 盘(约 128 KB)。
- 后续可通过 USB 拷贝固件文件到该"U 盘",再由板载 Bootloader 或 IAP 逻辑从
FLASH_START_ADDR读取并烧写到目标地址,实现免串口升级。

关键限制与风险
- Flash 擦写寿命:STM32F103 内部 Flash 数据手册保证的最小擦写次数为 10 000 次/页。频繁写入(如日志、频繁 IAP)会显著缩短寿命,不适合高写入频率场景。
- 擦除是页级操作:写入前必须整页擦除,擦除后该页所有位被置 1(读值为 0xFF)。若 MSC 层只写部分扇区,未覆盖的扇区内容会丢失,需要应用层自行做读-改-写保护。
- Flash 编程期间 CPU 阻塞:
HAL_FLASH_Program和HAL_FLASHEx_Erase是阻塞调用,期间 CPU 无法从 Flash 取指,USB 中断响应可能延迟,极端情况下主机可能判定设备超时。 - 存储区与应用区重叠风险:如果 IAP 升级时新固件大小超过 64 KB,会覆盖存储区;反之,存储区数据也可能被误擦。Bootloader 中需明确划分地址边界。
- 容量极小:128 KB 仅适合存放单个固件镜像或少量配置文件,不适合存储大文件。
- 首次必须格式化:Flash 出厂(或擦除后)为全 0xFF,没有文件系统结构,主机不会直接识别,必须经过一次格式化。
小结
用内部 Flash 模拟 MSC 是 STM32F103 上实现"U 盘"最轻量的方式,无需额外存储芯片,适合固件升级、参数备份等低频写入场景。实现时重点关注 Flash 擦写寿命、页级擦除的副作用以及存储区与应用区的地址隔离。
<!-- csdn-article-id: 111687764 -->本文最初于 2020/12/25 发布在 CSDN。
相关文章
评论
正在读取评论…