USB的MSD模式和FatFS在操作SD卡,让他们互斥就好了

一、总原则

MSD 模式下,MCU 的角色是"块级透明桥",不是文件系统代理。

  • 它不解析 FAT、不认文件名、不知道什么是"文件"
  • 它只做一件事:把 PC 的"读/写第 N 个扇区"翻译成 SD 卡的 CMD17/CMD18/CMD24
  • 文件系统的事全在 PC 那边(Windows 的 FAT 驱动)

所以"U 盘"= 把 SD 卡的扇区原样透传给电脑。


二、USB 插入 → 是否进入 MSD 模式

                USB 插入
                   │
        ┌──────────┴──────────┐
        │                     │
   VBUS 存在               PC 发起枚举
   (PB5 变高)          GET_DESCRIPTOR → SET_ADDRESS
        │                     → GET_DESCRIPTOR(配置)
        │                     │
   只说明"有电"          SET_CONFIGURATION  ← 握手成功的唯一标志
   充电头也满足                │
   → 不能作判据          ┌─────┴─────┐
        │             是         不是
        │              │           │
        └──► ✗     进入 MSD      ✗ 不进 MSD
                     模式          本机照常拍照

关键:判据是"协议握手",不是"有没有电"。

充电头只会给 VBUS,永远不会发 SET_CONFIGURATION,所以插充电器时本机照常工作,不进 MSD。


三、进入 MSD 模式(握手成功的那一刻)

这一步必须在中断里、几毫秒内完成,因为主机的第一条 SCSI 命令随后就到:

① 置模式标志  →  "本机禁访"立即生效
② 让 MSC 能报"有介质" → 用开机时就已缓存的容量
③ 不动本机 FatFs  →  卸载是 I/O,太慢,会错过主机第一条命令

四、PC 读 SD 卡的完整流程

PC(Windows FAT 驱动)
   │  ① READ CAPACITY(10)
   ▼
USB1 控制器 ──中断──► USB-ESL 协议栈 ──► InterfaceMSD (BOT/SCSI)
   │                                          │
   │                                          ├─ ② 用缓存的 BlockCount 回答
   │  ③ READ(10)  LBA=N, count=M              │
   ▼                                          ▼
                                        usbMSDDiskRead()
                                              │ ④ 打上"USB 正在用总线"标记
                                              ▼
                                        disk_read(0, buf, N, M)
                                              ▼
                                        SD_SPI_ReadSectors()
                                              │ ⑤ CMD17/CMD18 → SPI4
                                              ▼
                                           SD 卡

要点:

点说明
全程同步发生在 USB 中断里MCU 就是个搬运工,没有任务调度参与
中断可能很长一次 READ10 能读几十个块 → 所以 USB 中断优先级要低于图像采集
数据不经过文件系统buf 与扇区直接对拷,MCU 完全不知道这是 FAT 还是目录还是照片

五、PC 写 SD 卡的流程

同一条链,只是命令换成写:

WRITE(10) → usbMSDDiskWrite() → disk_write() → CMD24/CMD25 → SD 卡

六、MSD 模式下本机能做什么

操作允许原因
拍照(写文件)✗两边 FAT 缓存会互相覆盖
格式化✗同上
读文件 / 刷新图库✗只读也会拿到过期目录,会看到不存在的文件
显示卡的总容量✅来自 CSD 缓存,不读 FAT
显示剩余容量⚠️ 只能显示进入前的旧值算剩余要挂载 FAT

禁的是"本机挂载文件系统",不是"本机不能碰卡"。 物理上 MCU 随时能读扇区;不让它读,是因为它的 FAT 缓存已经和 PC 改过的实际内容不一致了。


七、退出 MSD 模式

判据:PB5 物理拔出(去抖后)。 为什么不看协议事件:

  • 本机是自供电设备,拔线时 PC 侧不一定发任何事件(reset / deconfig 都可能没有)
  • PC 上点"弹出" ≠ 拔线,不应该退出

退出动作(顺序不能反):

① 清模式标志        ← 先退出模式,USB 侧立刻拒绝新访问
② f_mount(NULL)     ← 丢弃被 PC 改过的陈旧 FAT/目录缓存
③ disk_initialize() ← 卡可能被 PC 动过,重跑 SD 协议初始化
④ 用本机唯一的 FATFS 对象重挂 + 刷新图库

②③④ 顺序的原因:必须先退出模式再挂载。反过来(还在模式内就挂载)挂载过程中的磁盘读会被自己的互斥层拒绝,永远挂不上。


八、互斥的两层(概念)

层粒度解决什么
模式层整卷决定"这一段时间归谁"——本机所有磁盘调用一律拒绝
事务层单次 SPI 事务防止一次 CMD17/CMD24 被另一端插进来打断

模式层保证不会两个主人同时挂同一个 FAT;事务层保证单条 SD 命令不被切碎。


九、完整状态图

        ┌──────────────────────────────────────────────┐
        │            本机模式(LOCAL)                  │
        │  FatFs 挂载中 · 可拍照 · 可格式化 · 可看图库   │
        └──────────────────────────────────────────────┘
             │                              ▲
   USB 插入并用真主机                     PB5 物理拔出
   完成 SET_CONFIGURATION                 (去抖 ~300ms)
             │                              │
             ▼                              │
        ┌──────────────────────────────────────────────┐
        │             MSD 模式(USB)                   │
        │  PC 全权限读写扇区 · 本机禁止读写 SD          │
        │  容量显示停在进入前的旧值                      │
        └──────────────────────────────────────────────┘

   插充电器:只有 VBUS,永远不握手 → 留在"本机模式"
   PC 上点"弹出":不退出 → 要物理拔线