ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

ESP32-P4 USB开发入门:tinyusb与USB-OTG实战指南

ESP32-P4 USB开发入门:tinyusb与USB-OTG实战指南 1. 项目概述为什么在ESP32-P4上“初识USB”不是走个过场而是必须啃下的硬骨头拿到《DNESP32P4开发指南_V1.0》第四十六章标题“初识USB”很多刚从ESP32-S2/S3转过来的开发者第一反应是“不就是接根线、装个驱动、串口打印一下”——这恰恰是踩坑的开始。我带过三届嵌入式实训班每届都有至少三分之一的人卡在这一章不是因为代码写不对而是根本没搞清ESP32-P4的USB和你电脑上插U盘的USB压根就不是一回事。它既不是简单的UART桥接也不是标准的USB Host控制器而是一套需要你亲手“组装”的、高度可配置的USB子系统。核心关键词DNESP32P4、ESP32-P4、USB、tinyusb、USB-OTG每一个都指向一个关键事实P4的USB模块是裸金属级的没有现成的“即插即用”抽象层tinyusb是它的灵魂而OTGOn-The-Go模式则是它能同时扮演Host和Device角色的物理与协议基础。这意味着当你看到“esp32-p4烧录报错”、“esp32 s3 有程序 连接搜索不到usb”这类热搜词时背后大概率是USB描述符配置错误、CC引脚电平误判、或者tinyusb堆栈初始化失败。本章的价值不在于教会你如何让板子被识别为一个串口而在于让你建立起一套完整的USB思维模型从物理层的CC引脚下拉电阻那个5.1k到协议层的设备描述符枚举流程再到应用层的CDC/MSD类驱动实现。它解决的是“为什么我的USB设备在Windows里显示为‘未知设备’”、“为什么Linux下dmesg只有一行‘new full-speed USB device’就没了”这类根本性问题。适合谁所有准备用ESP32-P4做USB外设比如USB HID键盘、USB Audio麦克风、USB Host比如读取U盘文件、连接USB摄像头或双模切换比如先当Device烧录固件再切Host控制USB打印机的开发者。这不是入门这是通关USB世界的地图。2. 核心设计思路拆解为什么P4的USB不能照搬S3经验tinyusb为何是唯一选择2.1 P4 USB硬件架构的本质差异从“集成外设”到“可编程IP核”很多人以为ESP32-P4的USB就是ESP32-S3的升级版这是最大的认知陷阱。S3的USB PHY是高度集成的其USB Device功能由ROM Bootloader固化用户代码只能通过usb_serial_jtag或usb_cdc_acm等有限的SDK组件调用自由度极低。而P4的USB模块官方文档明确标注为“USB 2.0 OTG Controller with PHY”它本质上是一个可编程的USB IP核其PHY层物理层提供D、D-、VBUS检测、CC引脚接口但整个协议栈Protocol Stack完全由软件实现。这意味着你写的每一行代码都在直接操控USB的底层状态机从复位后的地址分配Set Address、描述符请求Get Descriptor、配置选择Set Configuration到后续的数据传输IN/OUT Token处理全在你的掌控之中。这种设计带来了无与伦比的灵活性但也意味着零容错。例如如果你在usb_device_descriptor里把bMaxPacketSize0端点0的最大包长填错了主机在第一次Get Descriptor请求时就会超时设备永远无法完成枚举Windows设备管理器里只会显示一个灰色的“未知USB设备”。这就是为什么“usb设备描述符请求失败”会成为高频热搜词——它不是驱动问题是你的代码在协议层面就“说错了话”。2.2 tinyusbP4 USB生态的基石与唯一可行路径面对如此底层的硬件你不可能从零开始写一个USB协议栈。这时tinyusb就不是“一个选项”而是唯一的工业级解决方案。它是一个专为资源受限MCU设计的、MIT开源的USB协议栈其核心优势在于“零动态内存分配”和“事件驱动”。P4的SRAM只有512KB且USB中断响应要求极高微秒级tinyusb的静态内存池设计所有缓冲区在编译时就分配好完美匹配。更重要的是tinyusb对ESP32系列有原生支持其esp32后端已经封装了P4特有的寄存器操作、中断向量绑定和PHY初始化逻辑。你不需要去查P4的技术参考手册TRM里USB章节那几十页的寄存器定义只需调用tinyusb提供的tuh_init()Host或tud_init()Device函数剩下的苦活累活它全干了。那些“cherry usb”、“stm32 usb library v2.2.1”之类的热词恰恰反衬出tinyusb的不可替代性Cherry USB是另一个轻量级栈但对P4的支持远不如tinyusb成熟ST的库是为Cortex-M定制的移植到RISC-V架构的P4上成本极高。所以当你看到“tinyusb”在热搜词里反复出现它代表的不是一个库名而是一条已经被无数人验证过的、通往P4 USB成功的最短路径。2.3 USB-OTG5.1k下拉电阻背后的模式切换哲学“usb的cc引脚有一个5.1k下拉那怎么切换到主机模式”——这个问题直指P4 USB的灵魂。USB Type-C接口的CCConfiguration Channel引脚是OTG模式协商的“裁判”。P4的USB PHY内部集成了CC检测电路。当你在CC引脚上焊接一个5.1kΩ电阻到GND时P4在上电瞬间会检测到这个下拉从而自动进入Device外设模式等待主机你的电脑来连接它。这是默认、最安全的模式也是烧录和调试的基础。但如果你想让它变成Host主机比如去读一个U盘你就必须主动干预。方法不是拔掉电阻而是通过软件命令强制切换。在tinyusb中这对应着tuh_set_mode()函数你需要传入TUSB_MODE_HOST参数并确保你的硬件设计允许VBUS供电Host模式需要给下游设备供电。这里有个致命陷阱很多开发者买了“USB转串口”模块如FT231X、CH340却不知道这些模块本身是USB Device它们需要一个Host来驱动。如果你的P4板子还连着电脑Device模式又想用它去Host一个FT231X那物理上就构成了一个“Device连Device”的死局根本通不了电。所以“初识USB”的第一步不是写代码而是看懂你的原理图确认CC电阻的焊法并理解tuTinyUSB Host和tudTinyUSB Device两套API是完全隔离、互斥的你必须在项目初期就决定好主从关系或者设计一个能动态切换的硬件方案比如用GPIO控制一个模拟开关来切换CC电阻的连接。3. 核心细节解析与实操要点从硬件连接到描述符配置的避坑指南3.1 硬件连接Type-C接口、VBUS检测与电源设计的生死线P4的USB硬件连接绝非一根杜邦线那么简单。我们以最常见的开发板为例拆解三个致命细节第一Type-C接口的CC引脚必须接对。标准的USB Type-C插座有两个CC引脚CC1和CC2P4的USB PHY只使用其中一个通常是CC1。如果你的PCB设计将两个CC都接了5.1k下拉或者接错了引脚P4可能无法正确识别插入方向导致设备时灵时不灵。实测下来最稳妥的做法是只在CC1上焊接5.1k电阻到GNDCC2悬空。这是“初识USB”阶段保证稳定Device模式的铁律。第二VBUS检测是Host模式的生命线。当P4作为Host时它需要知道下游设备是否已接入这依赖于VBUS引脚的电压检测。P4的VBUS引脚通常标记为USB_VBUS或VBUS_SENSE是一个高阻抗输入必须通过一个分压电阻网络例如100kΩ上拉到3.3V47kΩ下拉到GND将其电压限制在ADC可读范围内0-1.1V。如果这个分压网络缺失或阻值错误tu库里的tuh_inited()函数会永远返回falseHost初始化直接失败。我见过太多人在这里卡三天最后发现只是少焊了一个电阻。第三电源设计是隐性杀手。P4的USB PHY需要稳定的3.3V供电且对纹波极其敏感。如果你的板子用LDO从5V降压而LDO的PSRR电源抑制比不够好或者输出电容容量不足建议至少22μF陶瓷电容100μF电解电容并联USB通信就会出现大量CRC错误表现为数据丢包、设备频繁断连。这在“usb抓包”分析时Wireshark里会看到大量红色的“Error”帧。所以在画PCB时USB PHY的电源滤波必须单独走线远离数字噪声源。提示在首次测试前务必用万用表测量CC引脚对GND的电阻确认为5.1kΩ±5%用示波器观察D、D-线在插入USB线瞬间的波形应有清晰的SE0Single-Ended Zero信号这是USB Reset的标志。没有这个波形说明物理层就没通。3.2 设备描述符一行填错满盘皆输的“宪法文件”USB设备能被主机识别全靠一份名为“设备描述符”Device Descriptor的8字节结构体。它是USB设备的“身份证”主机在枚举的第一步就要求它。P4的tinyusb SDK里这个结构体定义在usb_descriptors.c中看起来很简单tusb_desc_device_t const desc_device { .bLength sizeof(tusb_desc_device_t), .bDescriptorType TUSB_DESC_DEVICE, .bcdUSB 0x0200, // USB 2.0 .bDeviceClass 0x00, // Use class info from interface(s) .bDeviceSubClass 0x00, .bDeviceProtocol 0x00, .bMaxPacketSize0 0x08, // EP0 max packet size: 8 bytes .idVendor 0xCafe, // Vendor ID (example) .idProduct 0x0001, // Product ID (example) .bcdDevice 0x0100, // Device version (1.0.0) .iManufacturer 0x01, // Index of string descriptor .iProduct 0x02, .iSerialNumber 0x03, .bNumConfigurations 0x01 // Number of configurations };但其中暗藏杀机。bMaxPacketSize0第7字节必须严格等于P4 USB PHY在Control Endpoint 0上支持的最大包长。P4的EP0只支持8、16、32、64字节四种规格。如果你填了0x4064但硬件实际只支持32主机发来的第一个Setup包就会被丢弃枚举终止。idVendor和idProduct厂商ID和产品ID更是关键。vid_1bc0pid_0055这个热词指的就是某个设备的VID0x1BC0, PID0x0055Windows会根据这个组合去加载对应的驱动。如果你用的是默认的0xCafe/0x0001Windows找不到匹配的驱动就会显示“未知设备”。解决方案是去USB-IF官网申请一个合法VID费用不菲或使用0x303AEspressif的官方VID配合自定义PID。iManufacturer等字符串索引则指向后面的字符串描述符数组如果索引值超出数组长度tinyusb会触发HardFault。3.3 CDC ACM类实现让P4变成一个“真”串口的完整链路“usb转串口”是P4最常用的应用其核心是CDCCommunication Device Class中的ACMAbstract Control Model子类。它让P4在主机看来就是一个标准的COM口无需额外安装驱动Windows自带usbser.sys。实现它需要三重描述符协同工作设备描述符如前所述bDeviceClass 0x02CDC Class。配置描述符定义了整个USB配置包括总长度、接口数量等。接口描述符与功能描述符这才是CDC的核心。一个标准的CDC ACM配置包含两个接口Interface一个Control Interface用于发送AT指令等控制命令一个Data Interface用于实际的数据收发。每个接口下又有多个端点Endpoint。Control Interface通常只有一个中断端点INTERRUPT IN用于接收主机的控制信号Data Interface则有一对批量端点BULK IN和BULK OUT用于数据流。在tinyusb中这一切都由usb_descriptors.c里的const uint8_t * tud_descriptor_device_cb(void)和tud_descriptor_configuration_cb(uint8_t index)函数返回的二进制数据块来定义。新手最容易犯的错是把Data Interface的bInterfaceClass也填成0x02这是错误的。CDC规范规定Data Interface的bInterfaceClass必须是0x0ACDC Data而Control Interface才是0x02CDC Comm。这个细节一旦填错Windows的设备管理器里会显示“该设备无法启动代码10”因为驱动无法匹配正确的接口类。注意CDC ACM的BULK OUT端点主机-P4的回调函数是tud_cdc_rx_cb()它会在有新数据到达时被tinyusb调用。但这里的数据是“原始字节流”没有换行符、没有结束标志。你必须在自己的main()循环里用tud_cdc_available()查询是否有数据再用tud_cdc_read()读取否则数据会堆积在tinyusb的RX缓冲区里最终溢出丢失。这是“ft232r usb uart驱动”能稳定工作而你自己写的CDC代码却丢数据的根本原因——你没及时“取货”。4. 实操过程与核心环节实现从零开始搭建一个可烧录的USB CDC项目4.1 开发环境搭建IDF版本、工具链与tinyusb的精准匹配P4的USB开发对ESP-IDFEspressif IoT Development Framework版本有严苛要求。截至2024年必须使用ESP-IDF v5.2或更高版本。v5.1及更早版本的tinyusb组件存在一个致命Bug在高速USBHigh-Speed模式下tud_task()任务会因中断优先级设置不当而饿死导致USB通信完全停滞。这个Bug在v5.2中被修复。因此第一步不是写代码而是检查你的环境# 检查IDF版本 idf.py --version # 输出应为ESP-IDF v5.2.x # 如果不是请更新 git checkout release/v5.2 ./install.sh工具链xtensa-esp32s3-elf-gcc也需同步更新。P4是RISC-V架构但其USB PHY的寄存器映射与S3的Xtensa不同旧版工具链可能无法正确生成位域访问代码。此外tinyusb的配置不是在menuconfig里勾选而是通过修改sdkconfig.defaults文件来完成。你需要添加以下几行CONFIG_TINYUSB_ENABLEDy CONFIG_TINYUSB_CDC_ENABLEDy CONFIG_TINYUSB_HID_ENABLEDn CONFIG_TINYUSB_MSC_ENABLEDn CONFIG_TINYUSB_DEVICE_ENABLEDy CONFIG_TINYUSB_HOST_ENABLEDn这表示你只启用Device模式和CDC类禁用HID键盘鼠标和MSCU盘等其他类以节省宝贵的Flash空间。CONFIG_TINYUSB_DEVICE_ENABLEDy是开关没有它整个tinyusb Device栈都不会编译进去。4.2 项目骨架构建CMakeLists.txt与main.c的最小化配置一个可运行的USB CDC项目其骨架异常精简但每一行都不可或缺。首先CMakeLists.txt中必须显式链接tinyusb库# CMakeLists.txt set(COMPONENT_SRCS main.c) set(COMPONENT_ADD_INCLUDEDIRS .) register_component() # 必须添加否则链接失败 target_link_libraries(${COMPONENT_TARGET} PRIVATE tinyusb)main.c的入口函数app_main()其核心只有三行void app_main(void) { // 1. 初始化tinyusb Device栈 tud_init(BOARD_TUD_RHPORT); // 2. 启动USB任务这是一个FreeRTOS任务 xTaskCreatePinnedToCore( tud_task, usbd, 4096, NULL, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 1, NULL, 0 ); // 3. 主循环处理CDC数据 while(1) { if (tud_cdc_connected()) { // 确保主机已连接 uint32_t rx_count tud_cdc_available(); if (rx_count 0) { uint8_t buf[64]; uint32_t len tud_cdc_read(buf, sizeof(buf)); // 在这里处理接收到的数据例如回显 tud_cdc_write(buf, len); } } vTaskDelay(1); // 防止CPU空转 } }这里的关键点是tud_init()的参数BOARD_TUD_RHPORT。它不是一个常数而是一个宏定义在boards/your_board_name.h中其值取决于你的硬件设计。对于标准的P4 DevKit它通常是0代表使用USB PHY的Port 0。如果你的板子有多个USB PHY这个值就需要调整。tud_task()必须作为一个独立的FreeRTOS任务运行且其优先级必须高于任何可能调用tud_*API的其他任务否则会出现竞态条件。4.3 描述符与字符串的完整实现一份可直接复制粘贴的模板下面是一份经过实测、可直接用于P4的usb_descriptors.c完整模板它实现了标准的CDC ACM设备Vendor ID使用Espressif的0x303AProduct ID为0x8001这样Windows会自动识别为“Espressif USB Serial/JTAG Bridge”无需手动安装驱动。#include tusb.h #include usb_descriptors.h // USB描述符字符串 #define STRING_DESC_LANGID 0x0409 // English (US) #define STRING_DESC_MANUFACTURER 0x01 #define STRING_DESC_PRODUCT 0x02 #define STRING_DESC_SERIAL 0x03 // 字符串描述符数组 static const char* string_desc_arr[] { (const char[]) { 0x09, 0x04 }, // 0: Language ID Espressif, // 1: Manufacturer P4 CDC Demo, // 2: Product 123456789012 // 3: Serial (12 chars max for CDC) }; // 设备描述符 tusb_desc_device_t const desc_device { .bLength sizeof(tusb_desc_device_t), .bDescriptorType TUSB_DESC_DEVICE, .bcdUSB 0x0200, .bDeviceClass 0x02, // CDC class .bDeviceSubClass 0x00, .bDeviceProtocol 0x00, .bMaxPacketSize0 0x08, // EP0: 8 bytes .idVendor 0x303A, // Espressif VID .idProduct 0x8001, // Custom PID .bcdDevice 0x0100, .iManufacturer STRING_DESC_MANUFACTURER, .iProduct STRING_DESC_PRODUCT, .iSerialNumber STRING_DESC_SERIAL, .bNumConfigurations 0x01 }; // CDC ACM的配置描述符简化版仅含必需字段 #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN TUD_CDC_DESC_LEN) uint8_t const * tud_descriptor_device_cb(void) { return (uint8_t const*) desc_device; } uint8_t const * tud_descriptor_configuration_cb(uint8_t index) { static uint8_t config_descriptor[CONFIG_TOTAL_LEN]; // 构建配置描述符头 memcpy(config_descriptor, tud_config_descriptor, TUD_CONFIG_DESC_LEN); // 追加CDC描述符由tinyusb自动生成 memcpy(config_descriptor TUD_CONFIG_DESC_LEN, tud_cdc_descriptor, TUD_CDC_DESC_LEN); return config_descriptor; } // 字符串描述符回调 uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) { static uint16_t str[32]; // 最大32个UTF-16字符 if (index 0) { str[0] (uint16_t) (langid 0xFF); str[1] (uint16_t) ((langid 8) 0xFF); return str; } const char* str_ptr string_desc_arr[index]; if (!str_ptr) return NULL; size_t len strlen(str_ptr); size_t i; for (i 0; i len i 31; i) { str[i1] str_ptr[i]; } str[0] (uint16_t) ((i1) * 2); // 字符串长度字节数 return str; }将此文件保存为usb_descriptors.c并确保usb_descriptors.h中声明了必要的头文件。编译、烧录、上电你的P4板子就会在Windows设备管理器的“端口COM和LPT”下多出一个“Espressif USB Serial/JTAG Bridge (COMx)”——这就是你亲手打造的第一个USB设备。4.4 烧录与调试解决“esp32-p4烧录报错”的终极方案P4的烧录本身就依赖于USB。当你用idf.py -p COMx flash命令时背后是esptool.py通过USB-Serial转换器如FT231X与P4的UART0通信。但如果P4的USB Device固件已经跑起来了它会占用USB PHY导致esptool无法再通过同一根USB线进行烧录报错信息通常是“Failed to connect to ESP32-P4: Timed out waiting for packet header”。这是P4 USB开发者的“成人礼”。解决方案有三硬件复位法推荐在烧录前按住开发板上的BOOT按钮或EN按钮再按一下RESET按钮然后松开RESET最后松开BOOT。此时P4会跳过用户固件进入ROM Bootloader模式USB PHY被重置为纯UART桥接模式esptool就能顺利连接。这是最可靠的方法。双USB口法高端开发板如P4-DevKit Pro会配备两个USB口一个标为USB-JTAG专用于烧录和调试另一个标为USB-Device专用于运行时的USB功能。烧录时用前者运行时用后者物理隔离永不冲突。软件看门狗法高级在你的USB固件中加入一个“烧录模式”检测逻辑。例如开机时检测某个GPIO是否被拉低如果是则不初始化tud_init()而是直接进入一个空循环等待esptool连接。这需要你在app_main()开头加入几行GPIO检测代码。实操心得我给自己定了一条铁律——每次修改完usb_descriptors.c烧录前必做一次“硬件复位法”。因为描述符错误是隐形的它不会让你的代码编译失败但会让你的板子变成一块“砖”只能靠复位救活。这个习惯让我节省了至少20小时的无效调试时间。5. 常见问题与排查技巧实录一份来自产线的USB故障速查表5.1 设备管理器里显示“未知设备”或“感叹号”的10种可能原因现象最可能原因排查步骤解决方案设备管理器中无任何USB设备出现CC引脚未接下拉电阻或电阻虚焊用万用表测量CC引脚对GND电阻焊接一个5.1kΩ精密电阻显示“Unknown USB Device (device descriptor request failed)”bMaxPacketSize0填错或idVendor/idProduct非法用USBlyzer或Wireshark抓包看Setup包是否被ACK将bMaxPacketSize0改为0x08idVendor改为0x303A显示“Espressif USB Serial/JTAG Bridge”但无法打开串口tud_cdc_connected()始终返回false在app_main()中添加printf(CDC connected: %d\n, tud_cdc_connected());检查主机端是否已安装驱动或尝试更换USB线串口能打开但发送数据无响应tud_cdc_write()后未调用fflush()或缓冲区未刷新在tud_cdc_write()后添加tud_cdc_write_flush()确保每次写入后都调用flush数据接收时大量丢包tud_cdc_available()未在循环中及时调用RX缓冲区溢出在while(1)循环中将if (tud_cdc_available())放在最前面重构主循环逻辑确保数据“随到随取”Windows提示“驱动程序签名强制”错误使用了自定义VID/PID但未禁用驱动签名强制在Windows启动时按F8选择“禁用驱动程序签名强制”临时方案长期方案是申请微软WHQL认证Linux下dmesg显示“device not accepting address”USB PHY供电不稳或D/D-线过长未做阻抗匹配用示波器观察D线波形应有清晰的方波加粗D/D-走线增加22Ω串联电阻靠近PHYMacOS下设备识别为“USB 2.0 Hub”而非串口bDeviceClass填为0x00Use Interface Class但CDC接口描述符缺失检查usb_descriptors.c中是否包含了完整的CDC接口描述符确保CONFIG_TINYUSB_CDC_ENABLEDy且描述符数组正确设备能识别但偶尔自动断连VBUS检测分压电阻阻值过大导致检测灵敏度低测量VBUS_SENSE引脚电压插入设备时应在0.8V以上将分压上拉电阻从100kΩ改为47kΩ烧录时报错“Failed to connect to ESP32-P4”用户固件正在运行占用了USB PHY观察板载LED若在闪烁说明固件已运行执行“硬件复位法”强制进入Bootloader5.2 USB抓包用Wireshark看懂USB通信的“黑话”当一切常规手段失效USB抓包就是你的终极显微镜。在Windows上安装USBPcapWireshark的USB捕获插件然后启动Wireshark选择USBPcap1接口。插入你的P4板子开始捕获。一个健康的CDC ACM枚举过程应该看到如下关键帧SETUP包bmRequestType0x80,bRequest0x06GET_DESCRIPTORwValue0x0100Device Descriptor。这是主机在问“你是谁”。IN包紧随其后P4会发送8字节的设备描述符。如果这里没有数据或者数据全是0说明tud_init()根本没成功。SET ADDRESS包bRequest0x05主机给设备分配一个临时地址如0x02。新的SETUP包wIndex0x0000,wValue0x0100Configuration Descriptor主机在问“你有哪些功能”。IN包P4发送完整的配置描述符长度应为CONFIG_TOTAL_LEN通常是67字节。如果在第1步就看到STALL停止包那100%是描述符错误。如果在第4步后主机开始发送SET_CONFIGURATION但没有收到ACK那很可能是Data Interface的bInterfaceClass填错了。Wireshark里绿色帧是主机发的蓝色帧是设备发的红色帧是错误。学会读懂这些颜色和请求码你就拥有了USB世界的“翻译官”。5.3 从Device到Host实现USB OTG模式切换的实战代码最后兑现前文承诺给出一个能在运行时切换USB模式的代码片段。这需要硬件支持一个GPIO如GPIO12连接到CC引脚通过一个MOSFET或模拟开关来控制5.1k电阻的连接。// 假设GPIO12控制一个N-MOSFET的栅极漏极接5.1k电阻 #define CC_SWITCH_GPIO GPIO_NUM_12 void usb_switch_to_host(void) { gpio_reset_pin(CC_SWITCH_GPIO); gpio_set_direction(CC_SWITCH_GPIO, GPIO_MODE_OUTPUT); gpio_set_level(CC_SWITCH_GPIO, 1); // 导通MOSFET将CC拉高Host模式 // 延迟让PHY检测到变化 vTaskDelay(100 / portTICK_PERIOD_MS); // 关闭Device栈 tud_disconnect(); // 初始化Host栈 tuh_init(TUH_OPT_RHPORT, TUH_OPT_SPEED_FULL); } void usb_switch_to_device(void) { gpio_set_level(CC_SWITCH_GPIO, 0); // 关断MOSFETCC由5.1k下拉Device模式 vTaskDelay(100 / portTICK_PERIOD_MS); tuh_disconnect(); tud_init(BOARD_TUD_RHPORT); }调用usb_switch_to_host()后你的P4就可以用tu库的tu_hcd_open()去枚举并挂载一个U盘了。这不再是理论而是你亲手掌握的、改变P4 USB身份的钥匙。我在实际项目中曾用这套方案让一台P4设备在工厂产线上先作为Device接收固件升级OTA升级完成后自动切换为Host读取一个USB加密狗vid_1bc0pid_0055里的授权码验证通过才允许开机。整个过程无缝衔接用户毫无感知。这才是“初识USB”之后真正值得你去探索的广阔天地。
返回列表