拓冰建站拓冰建站
首页 / 资讯中心 / 正文

ESP32-P4 USB读卡器实战:TinyUSB MSC与SD卡驱动集成指南

1. 项目缘起与核心需求拆解1.1 为什么要在 ESP32-P4 上折腾 USB 读卡器第一次拿到 ESP32-P4 这块芯片的时候我盯着它的 USB 2.0 OTG 高速接口看了很久。市面上大多数 MCU 做 USB 设备要么只能做 HID 键鼠要么只能做 CDC 串口真正能把大容量存储设备MSC跑通的方案并不多。而 USB 读卡器这个实验本质上就是让 ESP32-P4 扮演一个“USB 存储设备”的角色插到电脑上之后电脑会把它识别成一个 U 盘而这个 U 盘背后的实际存储介质是开发板上插的那张 SD 卡。这个需求听起来简单但实际落地的时候涉及的东西一点都不少。你需要理解 USB 协议栈的分层结构、MSC 类的规范定义、TinyUSB 这个开源协议栈在 ESP-IDF 里的集成方式还要处理 SD 卡驱动和 USB 传输之间的数据搬运问题。更关键的是ESP32-P4 作为一颗相对较新的芯片它的 USB OTG 外设和之前 ESP32-S3 的 USB 外设并不完全一样很多在 S3 上能跑的代码直接搬到 P4 上会出各种奇怪的问题。我之所以花时间啃这个实验是因为它在实际产品里的应用场景非常明确。比如你做一个数据采集设备现场没有网络用户需要把采集到的数据导出来最直接的方式就是让设备变成一个 U 盘插上电脑就能拖文件。又比如你做一个工业相机或者音频录制设备SD 卡里的素材需要快速转移到 PC 上做后期处理USB 读卡器模式比走网络传输或者拆卡读卡器要方便得多。这个功能一旦跑通你的设备在易用性上就上了一个台阶。1.2 这个实验到底解决了什么问题从技术层面看这个实验解决的核心问题是如何让 ESP32-P4 通过 USB 接口把本地 SD 卡的内容以标准块设备的形式暴露给主机。主机这边不需要装任何驱动Windows、macOS、Linux 都能直接识别因为 MSC 协议是 USB 规范里定义好的标准类协议操作系统原生支持。从工程层面看它解决的是数据搬运的便利性问题。你不需要写上位机软件不需要配置网络服务不需要考虑文件系统兼容性只要设备插上 USB 线电脑上就多出来一个盘符用户可以像操作普通 U 盘一样操作它。这种“零门槛”的数据交互方式在很多嵌入式产品里都是刚需。从学习层面看这个实验是理解 USB 设备协议栈的绝佳入口。你会接触到描述符Descriptor的构造、端点Endpoint的配置、类请求Class Request的处理、SCSI 命令集的解析这些知识在别的 USB 实验里很难一次性全部覆盖到。1.3 适合谁来参考这篇内容如果你已经能用 ESP-IDF 点亮 LED、跑通串口打印并且对 FreeRTOS 的任务调度有基本概念那这篇内容就是为你准备的。你不需要事先懂 USB 协议我会在关键地方把原理讲清楚。但如果你连idf.py menuconfig都没用过建议先把 ESP-IDF 的环境搭建和基础工程编译流程过一遍否则后面配置项一多容易懵。另外如果你之前玩过 ESP32-S3 的 USB 实验那看这篇会更顺因为 TinyUSB 的 API 风格是一致的只是底层驱动和硬件配置有差异。我会特别标注 P4 和 S3 不一样的地方避免你踩我踩过的坑。2. 整体方案设计与技术选型考量2.1 为什么选 TinyUSB 而不是自己撸协议栈USB 协议栈这个东西自己从零写不是不行但性价比极低。USB 2.0 规范有六百多页光是描述符的类型就有设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符、类特定描述符等等每一种都有严格的字段定义和排列顺序。再加上枚举过程中的控制传输、地址分配、配置切换任何一个环节出错主机就直接不认你的设备。TinyUSB 是一个开源的跨平台 USB 协议栈它的设计目标就是“轻量、可移植、易集成”。它把 USB 设备控制器的底层操作抽象成了一组回调函数你只需要实现这些回调剩下的枚举流程、标准请求处理、端点管理它都帮你搞定了。对于 MSC 类设备TinyUSB 已经内置了完整的类驱动你只需要提供一个“读写扇区”的底层函数它就能把整个 U 盘的逻辑跑起来。ESP-IDF 从 v5.0 开始就把 TinyUSB 作为组件集成进来了ESP32-P4 的 USB OTG 驱动也是基于 TinyUSB 的。这意味着你不需要自己去移植协议栈直接在menuconfig里打开对应的选项然后在代码里调用 TinyUSB 的 API 就行。这个集成度比我早年用 STM32 自己移植 TinyUSB 要舒服太多了。2.2 MSC 类的两种实现路径RAM 盘 vs 真实存储TinyUSB 的 MSC 示例里默认提供的是一个基于 RAM 的虚拟磁盘数据存在内存里掉电就没了。这个方案适合验证协议栈是否跑通但实际项目里肯定要用真实存储。ESP32-P4 开发板上一般都有 SD 卡槽通过 SDMMC 接口或者 SPI 接口连接。我们要做的就是让 TinyUSB 的 MSC 类驱动去调用 SD 卡的读写函数把物理扇区的读写请求转发到 SD 卡上。这里有一个关键的设计决策扇区大小和存储容量的映射。SD 卡的物理扇区大小通常是 512 字节而 TinyUSB 的 MSC 驱动也是按 512 字节扇区来处理的所以这一层是对齐的不需要做额外的转换。但你要注意SD 卡的实际容量和你在 MSC 描述符里报告的容量必须一致否则主机会按照你报告的容量来格式化或者分区容量报错了会导致数据写入越界或者文件系统损坏。另一个决策点是是否启用写保护。如果你的应用场景只需要导出数据不需要主机往 SD 卡里写东西那可以在 MSC 配置里把写保护打开这样主机就只能读不能写避免误操作把卡里的数据搞坏。这个开关在 TinyUSB 的 MSC 配置结构体里有一个write_protect字段设置成true就行。2.3 ESP32-P4 的 USB OTG 外设有什么特殊之处ESP32-P4 的 USB OTG 外设支持 USB 2.0 高速480 Mbps和全速12 Mbps两种模式内部集成了 PHY不需要外接 USB PHY 芯片。这一点比某些需要外挂 PHY 的方案要省事。但要注意P4 的 USB 引脚是固定的不像 GPIO 那样可以随便映射。你在画板子或者接线的时候必须确认 D 和 D- 接在了正确的引脚上。另外P4 的 USB OTG 支持 Device 模式和 Host 模式我们这个实验用的是 Device 模式也就是让 P4 作为 USB 设备被电脑识别。在menuconfig里要选择USB Device模式而不是USB Host模式。如果你选错了枚举过程根本不会启动电脑上什么都不会出现。还有一个容易忽略的点USB 描述符里的 VID 和 PID。ESP-IDF 默认给了一个测试用的 VID/PID但如果你要做产品最好申请自己的 VID/PID或者在开源社区里找一个分配给你的。用默认的也不是不行但多个设备同时插到一台电脑上可能会冲突。3. 核心细节解析与实操要点3.1 TinyUSB 在 ESP-IDF 里的初始化流程TinyUSB 的初始化不是简单调一个函数就完事它涉及到几个层次的配置。最底层是 USB 设备控制器的硬件初始化这一层由 ESP-IDF 的usb_device驱动负责。中间层是 TinyUSB 的任务循环它需要在 FreeRTOS 里跑一个独立的任务来处理 USB 事件。最上层才是 MSC 类的初始化和回调注册。在 ESP-IDF 里你需要在menuconfig里打开Component config - TinyUSB Stack然后选择TinyUSB Task和MSC Class。这里有一个细节TinyUSB 的任务优先级和栈大小需要根据你的应用来调整。如果栈太小枚举过程中可能会栈溢出如果优先级太低USB 传输可能会丢包。我一般把优先级设成 5栈大小设成 4096 字节这个配置在大多数场景下都够用。初始化代码的顺序也很重要。你必须先初始化 SD 卡驱动确保存储介质可用然后再初始化 TinyUSB。如果顺序反了TinyUSB 在枚举的时候会去查询存储容量而这时候 SD 卡还没准备好主机就会看到一个容量为 0 的设备格式化都格不了。3.2 MSC 回调函数的实现要点TinyUSB 的 MSC 类驱动需要你实现几个回调函数最核心的是read_cb和write_cb。这两个函数的签名里包含了lun逻辑单元号、lba逻辑块地址、offset块内偏移和buf数据缓冲区。对于单张 SD 卡的情况lun永远是 0你不需要处理多逻辑单元。read_cb的实现逻辑是根据lba计算出 SD 卡上的字节偏移量然后调用 SD 卡的读函数把数据读到buf里。这里要注意SD 卡的读操作是按扇区对齐的lba乘以 512 就是字节偏移。如果你用的是 ESP-IDF 的sdmmc_read_sectors函数它直接接受扇区号作为参数用起来很方便。write_cb的逻辑类似只是方向反过来。但这里有一个坑写入操作的超时时间。SD 卡的写入速度比读取慢很多尤其是便宜的卡写入一个扇区可能要几十毫秒。如果你在write_cb里设置的超时时间太短写入会失败主机那边会报 I/O 错误。我一般把超时设成 1000 毫秒给足余量。还有一个细节是返回值。TinyUSB 的回调函数返回true表示操作成功返回false表示失败。如果你在read_cb里遇到了 SD 卡读取错误应该返回false这样主机会收到一个错误状态而不是拿到一堆垃圾数据。3.3 SD 卡驱动的配置与性能调优ESP32-P4 支持 SDMMC 接口相比 SPI 接口SDMMC 的读写速度要快得多。在menuconfig里你需要配置 SDMMC 的时钟频率、总线宽度和引脚映射。对于 USB 读卡器这个应用我建议把时钟频率设成 40 MHz总线宽度设成 4 线这样读写速度能跑到 20 MB/s 以上基本能满足大多数场景的需求。但高频时钟对信号质量的要求也高。如果你的 PCB 走线不好或者杜邦线太长40 MHz 可能会不稳定。这时候可以降到 20 MHz 试试如果稳定了再往上调。我在面包板上搭电路的时候20 MHz 是能稳定跑的最高频率再高就会偶尔出现读写错误。另外SD 卡的初始化过程也需要时间。有些卡上电后需要几百毫秒才能准备好如果你的代码在 SD 卡还没初始化完成的时候就启动了 USB 枚举主机会看到一个不可用的设备。我一般会在 SD 卡初始化之后加一个 500 毫秒的延时确保卡完全就绪。3.4 USB 描述符的定制化修改TinyUSB 默认提供的描述符里厂商名称、产品名称、序列号都是通用的字符串。如果你要做产品这些信息最好改成你自己的。修改的地方在tusb_config.h或者单独的描述符文件里。厂商名称和产品名称会显示在电脑的设备管理器里序列号则用于区分同一型号的多个设备。字符串描述符的编码格式是 UTF-16 LE不是普通的 ASCII。如果你直接写 ASCII 字符串进去电脑上显示出来的会是乱码。TinyUSB 提供了一个宏TUSB_DESC_STRING来帮你做转换你只需要把 ASCII 字符串传进去就行。还有一个容易忽略的点是最大电流声明。在配置描述符里有一个bMaxPower字段单位是 2 毫安。如果你声明的是 100 毫安就填 50。这个值不能超过主机端口能提供的最大电流否则主机会拒绝枚举。USB 2.0 标准端口一般能提供 500 毫安USB 3.0 端口能提供 900 毫安。我一般填 250 毫安留足余量。4. 实操过程与核心环节实现4.1 硬件连接与引脚确认ESP32-P4 的 USB 引脚是专用的不能随便映射。你需要查一下开发板的原理图确认 D 和 D- 接在了哪两个引脚上。一般来说P4 的 USB OTG 引脚是 GPIO 24 和 GPIO 25但不同开发板可能有差异。SD 卡的引脚则比较灵活可以通过 GPIO 矩阵映射到任意可用的引脚上。我用的开发板上SD 卡走的是 SDMMC 4 线模式CLK 接 GPIO 43CMD 接 GPIO 44D0 到 D3 分别接 GPIO 39 到 GPIO 42。这些引脚在menuconfig里配置好之后SD 卡驱动就能正常工作了。如果你用的是 SPI 模式的 SD 卡模块接线会更简单但速度会慢很多适合对性能要求不高的场景。接线的时候有一个细节USB 的 D 和 D- 不要接反。接反了不会烧芯片但电脑上什么都不会出现你会以为是软件问题查半天查不出来。我一般会在接好线之后先用万用表量一下确认 D 和 D- 没有短路也没有接反。4.2 menuconfig 关键配置项逐条说明打开idf.py menuconfig你需要关注以下几个配置项Component config - TinyUSB Stack - TinyUSB Task打开这个选项TinyUSB 会创建一个独立的任务来处理 USB 事件。Component config - TinyUSB Stack - MSC Class打开 MSC 类支持。Component config - TinyUSB Stack - MSC Class - MSC Vendor ID和MSC Product ID这里填你的 VID 和 PID。Component config - SDMMC配置 SDMMC 的时钟频率、总线宽度和引脚。Component config - USB Device选择TinyUSB作为 USB 设备栈。这里有一个坑TinyUSB 的任务栈大小。默认值是 2048 字节对于 MSC 类来说可能不够因为 MSC 的读写操作会用到较大的缓冲区。我建议改成 4096 字节如果还出现栈溢出就继续往上加。另一个坑是SDMMC 的 DMA 缓冲区。ESP32-P4 的 SDMMC 驱动支持 DMA但 DMA 缓冲区必须放在内部 RAM 里不能放在 PSRAM 里。如果你在menuconfig里把 DMA 缓冲区配置到了 PSRAMSD 卡读写会失败。这个选项在SDMMC - DMA Buffer Location里确保选的是Internal Memory。4.3 核心代码结构与关键函数解析整个工程的代码结构可以分为三部分SD 卡初始化、TinyUSB 初始化和 MSC 回调实现。SD 卡初始化部分我直接用了 ESP-IDF 提供的sdmmc_init和sdmmc_read_sectors函数没有自己写底层驱动。TinyUSB 初始化部分调用了tusb_init函数然后在 FreeRTOS 任务里循环调用tud_task。MSC 回调的实现是核心。read_cb函数里我先把lba转换成字节偏移然后调用sdmmc_read_sectors把数据读到buf里。如果读取成功返回true如果失败打印错误日志并返回false。write_cb函数类似只是调用的是sdmmc_write_sectors。这里有一个细节offset参数的处理。TinyUSB 的 MSC 回调里offset表示块内偏移对于 512 字节扇区来说offset通常是 0。但为了代码的健壮性我还是加上了offset的处理逻辑万一将来 TinyUSB 改了行为代码也不会崩。还有一个细节是并发访问的保护。如果你的应用里同时有别的任务在访问 SD 卡比如一个数据记录任务在往卡里写日志那 USB 读卡器的读写操作和这个任务之间需要加互斥锁。否则两个任务同时操作 SD 卡会导致文件系统损坏。我一般用一个SemaphoreHandle_t来做互斥在read_cb和write_cb的开头和结尾分别 take 和 give。4.4 编译、烧录与枚举验证编译的时候如果你用的是 VSCode 加 ESP-IDF 插件直接点编译按钮就行。但如果你在 VSCode 的终端里用idf.py build命令编译有时候会遇到环境变量没设置的问题。我一般会在 VSCode 的settings.json里配置好 ESP-IDF 的路径然后在终端里先执行export.sh或者export.bat再运行idf.py build。烧录完成后把开发板的 USB 口插到电脑上。如果是 Windows你会在设备管理器里看到一个“USB 大容量存储设备”然后在“此电脑”里会多出来一个盘符。如果是 macOS桌面上会直接出现一个磁盘图标。如果是 Linux用lsblk命令能看到多出来一个sdX设备。如果电脑上什么都没出现先检查 USB 线是不是只供电不传数据的那种。有些便宜的 USB 线只有电源线没有数据线插上去只能充电不能枚举。我踩过这个坑换了一根线就好了。如果线没问题再检查menuconfig里的 USB 模式是不是选成了Device以及 TinyUSB 的任务是不是正常启动了。5. 常见问题与排查技巧实录5.1 枚举失败电脑识别不到设备这是最常见的问题可能的原因有好几个。首先检查 USB 线确认是数据线而不是纯充电线。其次检查menuconfig里的 USB 模式必须是Device模式。然后检查 TinyUSB 的任务是否正常创建可以在任务里加一句日志打印看看任务有没有跑起来。如果这些都正常那可能是描述符的问题。用 USB 分析仪抓一下枚举过程的包看看主机发了什么请求设备回了什么。如果没有分析仪可以在 TinyUSB 的配置里打开调试日志把CFG_TUSB_DEBUG设成 2 或 3这样串口里会打印出详细的枚举过程。我遇到过因为字符串描述符编码错误导致枚举失败的情况打开调试日志之后一眼就看出来了。还有一个可能是供电不足。如果你的开发板上有其他大功耗外设比如屏幕或者 WiFi 模块USB 端口提供的 500 毫安可能不够用。这时候可以试试用带外部供电的 USB Hub或者把其他外设先关掉。5.2 读写错误能识别但打不开或复制文件失败设备能识别但打开盘符的时候提示“请插入磁盘”或者复制文件的时候报 I/O 错误这通常是 SD 卡读写出了问题。首先检查 SD 卡是不是 FAT32 格式如果是 exFAT 或者 NTFSESP-IDF 的 FATFS 驱动可能不支持。我一般会把 SD 卡格式化成 FAT32簇大小设成 4096 字节兼容性最好。如果格式没问题那可能是 SDMMC 的时钟频率太高了。把频率降到 20 MHz 试试如果稳定了再慢慢往上调。另外检查一下 SDMMC 的 DMA 缓冲区是不是在内部 RAM 里如果配置到了 PSRAM读写会失败。还有一个可能是扇区数量报告错误。在 MSC 的配置里你需要告诉 TinyUSB SD 卡的总扇区数。这个值必须和 SD 卡的实际容量一致。如果你用的是sdmmc_get_capacity函数获取的容量注意它的单位是字节需要除以 512 才是扇区数。我见过有人忘了除结果报告了一个巨大的容量主机格式化的时候直接卡死。5.3 写入速度慢复制大文件要等很久USB 读卡器的写入速度受限于 SD 卡的写入速度和 USB 的传输速度。如果你用的是低速 SD 卡写入速度可能只有几 MB/s复制一个几百 MB 的文件要等好几分钟。这时候可以换一张 Class 10 或者 UHS-I 的卡写入速度能提升到 20 MB/s 以上。另外TinyUSB 的 MSC 驱动里有一个CFG_TUD_MSC_BUFSIZE配置项它决定了每次读写操作的缓冲区大小。默认值是 512 字节也就是一个扇区。如果你把它改成 4096 或者 8192每次可以读写多个扇区效率会高很多。但要注意缓冲区越大占用的 RAM 也越多。ESP32-P4 有 768 KB 的 SRAM设成 8192 完全没问题。还有一个优化点是写入缓存。TinyUSB 的 MSC 驱动支持写入缓存但默认是关闭的。你可以在tusb_config.h里打开CFG_TUD_MSC_EP_BUFSIZE把它设成和CFG_TUD_MSC_BUFSIZE一样的值。这样写入操作会先缓存到内存里等积累到一定量再一次性写入 SD 卡减少 SD 卡的写入次数提升整体速度。5.4 常见问题速查表问题现象可能原因排查方法解决方案电脑识别不到设备USB 线是纯充电线换一根已知能传数据的线更换 USB 数据线电脑识别不到设备USB 模式配置错误检查 menuconfig 里的 USB Device 选项改为 Device 模式电脑识别不到设备描述符编码错误打开 TinyUSB 调试日志用 TUSB_DESC_STRING 宏转换字符串能识别但打不开SD 卡格式不支持在电脑上查看 SD 卡格式格式化为 FAT32能识别但打不开SDMMC 时钟太高降低时钟频率测试降到 20 MHz 再逐步上调复制文件报 I/O 错误扇区数量报告错误检查容量计算逻辑字节数除以 512 得到扇区数写入速度慢SD 卡本身速度慢用测速软件测试 SD 卡更换 Class 10 以上 SD 卡写入速度慢MSC 缓冲区太小检查 CFG_TUD_MSC_BUFSIZE增大到 4096 或 8192偶尔读写失败并发访问冲突检查是否有其他任务访问 SD 卡加互斥锁保护枚举过程中断供电不足检查开发板总功耗减少外设或使用外部供电5.5 几个我踩过的坑和独家技巧第一个坑是SD 卡热插拔。USB 读卡器模式下如果用户在电脑上点了“安全弹出”之后又把 SD 卡拔了换一张TinyUSB 这边是感知不到的它会继续用旧的容量信息去读写结果就是各种错误。我的做法是在 SD 卡检测引脚上接一个中断检测到卡拔出的时候先让 TinyUSB 断开连接等新卡初始化完成后再重新连接。这样电脑上会看到设备重新枚举容量信息也会更新。第二个坑是文件系统缓存。ESP-IDF 的 FATFS 驱动有缓存机制如果你在 USB 读卡器模式下往 SD 卡写数据同时另一个任务通过 FATFS 读同一个文件可能会读到缓存里的旧数据。我的做法是在 USB 读卡器的读写回调里直接操作物理扇区绕过 FATFS 的缓存。但这样做的代价是如果另一个任务正在写文件USB 读卡器这边看到的数据可能是不完整的。所以最好的方案还是加互斥锁确保同一时间只有一个任务在操作 SD 卡。第三个技巧是用 PSRAM 做读写缓冲区。ESP32-P4 支持外接 PSRAM容量可以到 16 MB 甚至更大。你可以把 TinyUSB 的 MSC 缓冲区放在 PSRAM 里这样就能设置一个很大的缓冲区比如 64 KB大幅提升读写效率。但要注意PSRAM 的访问速度比内部 SRAM 慢而且不能用于 DMA。所以正确的做法是DMA 缓冲区放在内部 SRAMTinyUSB 的 MSC 缓冲区放在 PSRAM两者之间用内存拷贝来搬运数据。第四个技巧是枚举指示灯。在 USB 枚举成功和断开的时候用板载 LED 给出不同的指示比如枚举成功时常亮断开时熄灭读写数据时闪烁。这样调试的时候一眼就能看出 USB 的状态不用每次都去电脑上看设备管理器。6. 性能优化与进阶扩展思路6.1 提升读写速度的几个关键参数如果你觉得默认配置下的读写速度不够快可以从以下几个参数入手调优。首先是 SDMMC 的时钟频率在信号质量允许的前提下尽量往高了设。ESP32-P4 的 SDMMC 最高支持 50 MHz但实际能跑多高取决于你的 PCB 走线和 SD 卡的质量。我一般从 40 MHz 开始试如果不稳定就降到 20 MHz。其次是 MSC 的缓冲区大小。前面提到过CFG_TUD_MSC_BUFSIZE默认是 512 字节你可以把它设成 4096 或 8192。这个值越大每次 USB 传输的数据量就越大协议开销占比就越小。但也不能无限大因为 TinyUSB 的端点缓冲区大小是有限的设太大了会分配失败。还有一个参数是USB 的传输模式。ESP32-P4 支持 USB 2.0 高速模式理论带宽是 480 Mbps。但 TinyUSB 的 MSC 驱动默认可能跑在全速模式12 Mbps下。你可以在menuconfig里检查一下 USB 的速度配置确保选的是 High Speed。如果选的是 Full Speed那速度上限就只有 12 Mbps也就是 1.5 MB/s 左右复制大文件会很慢。6.2 多逻辑单元与多分区的支持TinyUSB 的 MSC 类支持多个逻辑单元LUN也就是说你可以把多个存储介质同时暴露给主机电脑上会看到多个盘符。比如你的开发板上同时有 SD 卡和 SPI Flash你可以把 SD 卡作为 LUN 0把 Flash 的一个分区作为 LUN 1这样电脑上就会多出来两个盘。实现多 LUN 的关键是在回调函数里根据lun参数来区分不同的存储介质。read_cb和write_cb里都会传入lun你只需要写一个 switch 语句根据lun的值调用对应的读写函数就行。另外在 MSC 的配置结构体里你需要设置lun_count为实际的逻辑单元数量。这个功能在实际产品里很有用。比如你做一个数据记录仪SD 卡里存的是原始数据Flash 里存的是设备配置和日志用户插上 USB 之后两个盘都能看到方便分别处理。6.3 只读模式与写保护的应用场景如果你的应用只需要导出数据不需要主机往设备里写东西那强烈建议打开写保护。写保护有两种实现方式一种是在 MSC 配置里设置write_protect为true这样主机会把设备识别为只读根本不会发送写请求另一种是在write_cb里直接返回false这样主机发送写请求的时候会收到错误。第一种方式更彻底主机根本不会尝试写入用户体验也更好因为盘符上会显示一个只读的标记。第二种方式虽然也能阻止写入但主机可能会反复重试导致日志里一堆错误信息。写保护的应用场景很明确数据采集设备、工业相机、音频录制设备这些设备的数据只需要导出不需要回写。打开写保护可以避免用户误操作把设备里的数据删掉或者改掉。6.4 后续可以扩展的方向这个实验跑通之后你可以往几个方向继续深入。一个是USB 复合设备也就是让 ESP32-P4 同时作为 MSC 设备和 CDC 串口设备出现在电脑上。这样你既可以通过串口发送控制命令又可以通过 U 盘导出数据一个 USB 口搞定两件事。TinyUSB 支持复合设备你只需要在配置里同时打开 MSC 和 CDC 两个类就行。另一个方向是无线读卡器。在 USB 读卡器的基础上再加一个 WiFi 热点用户可以通过浏览器访问设备上的文件也可以通过 USB 导出。这样无论有没有 USB 线都能拿到数据。ESP32-P4 本身支持 WiFi加这个功能不难。还有一个方向是加密存储。在 SD 卡的读写回调里加上 AES 加密和解密这样即使 SD 卡被拔出来插到别的设备上里面的数据也是加密的无法直接读取。这个方案适合对数据安全有要求的场景比如医疗设备或者金融终端。我个人在实际操作中的体会是USB 读卡器这个功能看起来简单但细节特别多。每一个配置项背后都有它的道理每一个参数都影响最终的稳定性和性能。我建议你在跑通基本功能之后不要急着做产品先花时间把各种边界情况都测一遍比如大文件读写、长时间连续读写、热插拔、不同操作系统的兼容性。这些测试做完之后你对整个系统的理解会深很多后面做其他 USB 相关的功能也会更顺手。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门