1. 项目概述:一块开发板如何听懂鼠标在说什么
硬件工程师和嵌入式开发者应该都有这种体会:做产品时,USB Host功能总是既让人期待又让人头疼。期待是因为它能直接让设备连接U盘、鼠标、键盘、游戏手柄这类标准外设,产品瞬间就“活”了;头疼是因为USB协议栈本身是一套很繁琐的东西,枚举、端点配置、HID报告解析,哪一环没弄明白,代码就跑不起来。
最近我在调DNESP32P4开发板的USB Host功能,手上正好有一个USB鼠标,于是翻开了正点原子的《DNESP32P4开发指南_V1.0》第四十八章,把这一章的“USB鼠标(Host)实验”从原理到底层代码完整梳理了一遍。这篇博文就是这次调试过程的全记录,适合正在学ESP32-P4、准备做USB Host相关项目的开发者参考,也适合那些之前只用过ESP32-S3、第一次碰P4的USB外设功能的朋友。
先说结论:这个实验的本质,就是让ESP32-P4开发板通过USB接口识别并读取一个标准USB鼠标的移动数据和按键数据。ESP32-P4内部集成了一个支持Host和Device双模式的USB OTG控制器,硬件上再配一个USB-A座子,插上鼠标就能干活。实验代码把USB协议栈跑起来,完成设备枚举后,主机就能周期性地拿到鼠标上报的位移量(dx/dy)和按键状态(左键/右键/中键)。开发板拿到这些原始数据后,再通过串口打印出来,方便观察调试。
这个实验适合谁?我梳理了一下:
- 刚接触ESP32-P4、想摸清这颗芯片USB外设能力的开发者;
- 在嵌入式设备上做USB HID类设备接入的工程师(鼠标只是HID里最简单的一类,搞懂了它,键盘、游戏手柄、触摸板都是同一个套路);
- 准备做固件升级、数据采集、人机交互设备的创客和产品开发者。
我自己踩过几次坑之后,比较深的一个体会是:USB Host实验看着只是“插个鼠标读数据”,但它背后牵出来的知识链条非常长——从USB物理层的D+/D-差分信号,到协议层的令牌包、枚举过程,再到HID层的报告描述符解析,一环扣一环。这篇文章不会只在代码层面过一遍,我会把每个关键节点背后的原理也拆开讲清楚,尽量做到你看完不仅能跑通这个实验,还能自己改代码去适配别的HID设备。
2. 方案选型与整体设计思路:为什么ESP32-P4做USB Host值得折腾
2.1 ESP32-P4的USB控制器到底“强”在哪里
ESP32-P4是乐鑫新一代高性能MCU,和之前大家熟悉的ESP32、ESP32-S3相比,定位有明显的区别。P4没有Wi-Fi和蓝牙,但它把算力、接口资源和多媒体能力都做了大幅提升,其中一个很关键的升级就是USB。P4内置了一个完整的USB 2.0 OTG控制器,最高支持480Mbps的高速模式,这在MCU圈子里的确是比较能打的存在。
具体到USB协议栈的支持,这个控制器可以工作在Host模式,也能工作在Device模式。做Host时,它可以连接USB鼠标、键盘这类低速/全速设备,也可以连接U盘这类批量传输设备。对比一下之前用ESP32-S3做USB Host的体验:S3的USB OTG控制器走的是内置的USB Serial/JTAG或USB OTG外设,资源上多少有些局限,尤其是当你要同时跑Wi-Fi协议栈和USB协议栈时,内存和CPU都会比较紧张。而P4由于不集成射频前端,CPU主频可以更高,内存也更大,留给USB Host协议栈的余量就宽裕很多,跑起枚举和各种类设备的驱动都会更从容。
2.2 Host模式与Device模式的选择逻辑
很多刚接触USB开发的朋友容易搞混一个问题:我的开发板插上鼠标,为什么是“Host模式”?这里关键在于USB总线架构中角色是固定的——主机(Host)负责提供电源、发起通信、管理总线;设备(Device)是被动响应的一方。
在这个实验里,开发板就是主机,鼠标就是设备。开发板通过USB接口的VBUS引脚给鼠标供电,然后通过D+/D-两根差分信号线和鼠标通信。整个通信过程的节奏都由开发板来控制:开发板先发令牌包,询问鼠标“你是谁、需要多少带宽、有几个端点”,鼠标再一一回复。所以在写程序时,我们的思维必须切换成“主机思维”——不是等待数据,而是主动去枚举设备、发起中断传输、读取数据。
如果你之前做过USB Device方面的开发(比如用STM32模拟一个USB键盘),那这里最大的思维转变是:Device模式下的代码核心是“响应中断、填充端点缓冲区”;Host模式下的代码核心则是“发起传输、解析返回数据、管理设备状态机”。
2.3 为什么选择官方USB协议栈而不是裸写寄存器
这里想多说一句关于方案选型的问题。USB Host要实现的功能,如果全部用寄存器去操作,代码量会非常恐怖。就拿枚举来说,主机需要经历设备复位、获取设备描述符、设置地址、获取配置描述符、设置配置等一长串流程,每一步都要正确地构造SETUP包、处理控制传输的各个阶段。就算你有能力写完这段逻辑,后面还要面对HID类设备的报告描述符解析,工作量足够让人自闭。
所以这个实验采用了乐鑫官方ESP-IDF提供的USB Host协议栈。这个协议栈基于底层硬件驱动,把枚举、标准请求、端点管理等复杂逻辑封装成了中间层API,应用层只需要调用几个关键函数就能完成设备的发现。
采用官方协议栈还有个好处是维护性好。ESP-IDF更新频率快,官方会把USB Host底层的bug修复、性能优化都同步进来,你的应用代码基本不受影响。相比之下,自己写驱动或者移植第三方协议栈,后期维护成本会高得多。
3. 核心概念解析:USB鼠标通信的底层逻辑
3.1 从点击到数据:鼠标数据是“流”还是“包”
在拆代码之前,先把USB鼠标通信的几个底层概念理清楚。如果不明白这些,看代码时很容易一头雾水。
USB鼠标属于HID(Human Interface Device,人机交互设备)类,传输方式默认为中断传输(Interrupt Transfer)。注意这个“中断”和我们平时说的MCU外部中断完全是两码事。USB的中断传输指的是:主机按照固定的时间间隔,主动向设备发起IN事务,询问设备有没有新数据。设备没有数据时返回NAK,有数据时返回DATA包。
所以鼠标的数据并不是“流”,而是“包”。主机每隔固定的时间(通常鼠标是8ms或10ms)去取一次数据,每次取回的是一个固定长度的报告(Report)。一个典型的鼠标报告是4个字节:
- 第0字节:按键状态,bit0代表左键,bit1代表右键,bit2代表中键;
- 第1字节:X轴位移量,带符号,正数向右,负数向左;
- 第2字节:Y轴位移量,带符号,正数向下,负数向上;
- 第3字节:滚轮数据(有滚轮的鼠标才有)。
这里特别要注意的是位移量的单位不是像素,而是“count”(计数单位)。一个count到底对应屏幕上几个像素,由操作系统和鼠标DPI(每英寸点数)共同决定。在嵌入式场景下,我们拿到原始count值后,完全可以自定义映射关系——比如一个count对应LED点阵屏上的一个点,或者对应电机转动的步数。
3.2 HID设备枚举时发生了什么
当鼠标插上开发板的USB口,主机侧自动触发一连串事件。这个过程叫枚举(Enumeration),也是USB设备接入时最核心的流程。虽然代码被官方协议栈封装好了,但原理必须清楚:
- 主机检测到D+线被拉高(全速设备)或D-线被拉高(低速设备),知道有新设备接入;
- 主机向地址0发送复位信号,让设备进入默认状态;
- 主机向地址0发送GET_DESCRIPTOR请求,获取设备描述符(Device Descriptor),里面包含设备使用的USB版本、设备类型、端点0的最大包大小等信息;
- 主机发送SET_ADDRESS请求,给设备分配一个唯一地址(比如地址1);
- 主机向新地址发送GET_DESCRIPTOR请求,完整读取设备描述符和配置描述符(Configuration Descriptor);
- 主机根据配置描述符中的接口信息加载对应的类驱动。这里鼠标会暴露一个HID接口,接口下有一个中断IN端点;
- 主机发送SET_CONFIGURATION请求,让设备进入配置状态,设备开始正常工作。
如果你用USB分析仪或者带日志的USB协议栈观察这个过程,会发现枚举阶段主机和设备之间会来回交换十几个甚至几十个包。这就是为什么有时候鼠标插上去要“卡”一下才能用,枚举流程没跑完之前,设备是不会正常工作的。
3.3 HID报告描述符与Boot Protocol
鼠标的枚举过程中,有一个关键请求是GET_HID_REPORT_DESCRIPTOR。主机通过这个请求拿到设备的HID报告描述符,它是一份二进制格式的“数据说明书”,告诉主机:这个鼠标报告有多少个字节、每个字节的每个bit代表什么含义、数据范围是多少。
标准鼠标的报告描述符大致就是说“我有一个输入报告,共4字节,其中1字节是按键位图,3字节是带符号的相对位移值”。主机解析完这份描述符之后,才知道如何去解释后续中断传输拿到的数据。
这里有个简化方案值得提一下:USB HID规范定义了一套Boot Protocol(引导协议)。在这个协议下,鼠标的报告格式被固定为上面说的“3字节+1字节”传统格式(按键、X、Y),不需要解析报告描述符就能直接使用。很多不带操作系统的主机端(比如KVM切换器、BIOS设置界面)就是这么做的。本实验如果只是获取鼠标移动和按键的基础数据,Boot Protocol就足够用了。不过为了兼容更多设备,最好还是支持正常协议,因为现在很多高DPI鼠标在正常工作模式下会输出更高字节数的报告,额外包含滚轮、侧键,甚至RGB灯效控制数据。
4. 开发环境准备与硬件连接
4.1 需要准备的物料清单
开始动手之前,先确认一下手上有没有这些东西:
- DNESP32P4开发板一块。这是正点原子出的ESP32-P4核心板,板上集成了USB Type-C转串口电路,方便通过串口查看调试信息;
- USB鼠标一个。建议先用最普通的有线鼠标(光电鼠标即可),暂时不要用无线鼠标或者带RGB灯效的游戏鼠标——虽然原理上都能用,但无线鼠标可能涉及接收器协议配对,游戏鼠标可能上报额外的多字节报告,会增加排查问题的难度;
- USB-A公对公转接线,或者USB-A转USB-C转接头(看开发板的USB Host接口形态而定)。DNESP32P4开发板上有专门的USB Host接口,需要看丝印确认是USB-A母座还是USB-C接口;
- 一根Type-C数据线,用于开发板供电和串口调试。
4.2 确认开发板上的USB Host接口
拿到DNESP32P4开发板后,先做的第一件事不是写代码,而是确认USB Host接口的位置和引脚定义。可以把开发板翻过来,看丝印上有没有标USB_HOST或者USB_A的字样。P4芯片的USB_OTG引脚(D+和D-)会被引出到一组引脚上,一般和USB Device接口的引脚是分开的。
我自己用的是标准版本的DNESP32P4,板上的USB Host接口做的是USB-A母座,就在板子边沿,插鼠标非常方便,不需要额外转接线。接线的时候你根本不会搞错方向——USB-A接口形状决定了鼠标插头只能从一个方向插进去,这点倒是比Type-C友好不少。
4.3 IDF环境与工具链
代码编译和烧录需要完整的ESP-IDF开发环境。我用的版本是ESP-IDF v5.3(P4系列需要IDF 5.2以上才能完整支持,建议直接用最新稳定版)。
安装方式官方文档写得很清楚,Linux、Windows、macOS都有对应的脚本,这里不赘述。装完IDF之后,用idf.py set-target esp32p4指定目标芯片,然后ids.py menuconfig打开配置界面,按实验代码的说明启用相应的USB Host配置项。
一个比较重要的配置点是:如果代码中用到了USB Host协议栈,必须在menuconfig的Component config -> USB -> USB Host中使能对应的选项。这个选项下面还可以配置轮询间隔的最小值、并发传输的任务优先级等参数,初次接触时保持默认就好,后面遇到性能问题再来调。
5. 代码架构与关键实现:一步步解析USB Host鼠标实验
5.1 整体代码流程
打开实验代码工程,可以看到整个程序的核心逻辑并不复杂,大致分为这几步:
- 初始化USB Host协议栈;
- 注册USB Host客户端(Client),并指定HID类的匹配条件;
- 启动USB Host主任务,等待设备插入;
- 设备插入后,枚举由协议栈自动完成,客户端回调函数被触发;
- 客户端解析HID报告描述符,找到中断IN端点;
- 创建数据传输任务,周期性发起中断传输去读鼠标数据;
- 在回调中处理接收到的鼠标报告,解析按键和位移,并通过串口打印。
对比一下裸金属开发(比如STM32用CubeMX生成USB Host代码),ESP-IDF这套流程的好处是抽象清晰:设备接入、枚举、配置等底层细节都被协议栈内部消化掉了,应用层只需要关注“回调函数进来之后应该干什么”。
5.2 代码中的关键骨架与核心函数
我用伪代码的形式把核心骨架整理出来,方便对照自己的理解:
#include "usb/usb_host.h" #include "usb/hid_host.h" // HID主机事件回调 static void hid_host_event_cb(const hid_host_event_t *event, void *arg) { if (event->event == HID_HOST_EVENT_DEVICE_CONNECTED) { // 设备连接:这里可以创建设备处理任务 ESP_LOGI(TAG, "HID device connected"); } else if (event->event == HID_HOST_EVENT_DEVICE_DISCONNECTED) { // 设备断开:清理资源 ESP_LOGI(TAG, "HID device disconnected"); } } // 初始化HID主机 static void hid_host_init(void) { const hid_host_driver_config_t hid_host_config = { .callback = hid_host_event_cb, }; ESP_ERROR_CHECK(hid_host_driver_install(&hid_host_config)); } void app_main(void) { // 1. 安装USB主机驱动 const usb_host_config_t host_config = { .skip_phy_setup = false, .intr_flags = ESP_INTR_FLAG_LEVEL1, }; ESP_ERROR_CHECK(usb_host_install(&host_config)); // 2. 初始化HID主机类驱动 hid_host_init(); // 3. 启动USB主机处理任务(必须在IDF菜单中使能动态分配) usb_host_handle_t usb_host_handle = NULL; // 此处可开启一个专用任务,调用usb_host_main_loop()维护主机状态机 // 主循环 while (1) { // 可以在这里处理自己的业务逻辑 vTaskDelay(pdMS_TO_TICKS(1000)); } }当然,这只是一个精简骨架。实际工程里,在HID Host事件回调中接到设备连接事件后,还需要进一步获取设备信息,配置中断传输,并启动一个专用的读数据任务。
这里有一个值得注意的设计点:ESP-IDF的HID Host库把整个流程分成了两层——底层是通用USB Host协议栈,上层是HID类驱动。底层负责物理传输和枚举,上层负责HID协议的解析和中断传输的调度。这种分层设计对开发者来说其实很友好:如果你只做鼠标,用上层的HID Host库就够了;如果某天你要接一个UVC摄像头或者音频设备,底层USB Host协议栈还在,只需要注册对应的类驱动即可,不需要动底层代码。
5.3 中断传输与数据接收回调
鼠标的数据读取核心在于中断传输。在HID设备枚举完成后,代码会拿到端点的地址和最大包长度。P4的USB主机控制器会按照设备的轮询间隔(bInterval字段)周期性地发起IN事务,设备有数据就返回,没有就NAK。ESP-IDF的HID Host库会自动处理这套时序,应用层面只需要准备好缓冲区,挂接一个接收完成回调。
写回调函数时我建议直接采用“解析但不处理”的原则:回调函数里只做数据解析,把解析后的坐标、按键信息通过队列或者全局变量传给业务逻辑任务,不要在回调函数里做耗时操作,更不要在回调里打印大段日志。USB中断传输的回调上下文对时间较为敏感,若在回调中做阻塞操作,轻则影响枚举和后续数据接收,重则触发看门狗。
我实际测试时,如果在回调里用ESP_LOG打印每一包鼠标数据,短期内看不出问题,但跑久了就会感觉系统响应变卡。改成在回调里只做解析、把结果通过FreeRTOS队列发给主任务去打印后,整个系统稳定很多。
5.4 鼠标报告解析的代码逻辑
上报数据的解析是这个实验最直观也是最有成就感的部分。以下是接收缓冲区的处理逻辑(以普通三键鼠标为例):
typedef struct { uint8_t buttons; // bit0左键, bit1右键, bit2中键 int8_t dx; // X位移 int8_t dy; // Y位移 int8_t wheel; // 滚轮 } mouse_data_t; void parse_mouse_report(const uint8_t *data, size_t len, mouse_data_t *out) { if (len < 3) { return; } out->buttons = data[0]; out->dx = (int8_t)data[1]; out->dy = (int8_t)data[2]; out->wheel = (len >= 4) ? (int8_t)data[3] : 0; }这里有一个很容易犯的错:data[1]和data[2]是位移量,如果向左移动,这一个字节会被解释成负数。在C语言里,直接用int8_t强制转换就能正确处理,如果用uint8_t,你会发现往左移动时得到的不是负数,而是128到255的大正数,这就会导致方向判断出错。
6. 实操演示:串口打印数据与实时监控
6.1 编译烧录与启动日志
代码准备好之后,编译烧录的流程和普通ESP32项目一样:
idf.py build idf.py -p /dev/ttyUSB0 flash monitor烧录完成后,按下开发板上的复位键(EN按键),串口监视器里可以看到启动日志。正常的情况下,日志会显示USB主机驱动安装成功、HID Host驱动注册成功,然后进入等待状态。这时候插上USB鼠标,日志中应该会出现设备连接的事件,并打印出设备描述符中的厂商ID(VID)、产品ID(PID)等信息。
如果你在插入鼠标之后,日志里没有任何反应,说明USB Host驱动没有正常跑起来,或者硬件连接有问题。先检查电源和USB座子的连接,再看menuconfig里的USB Host是否确实打开,最后确认鼠标是标准HID设备(可以在电脑上测试一下这只鼠标能不能用)。
6.2 观察到的实际输出
设备正常枚举之后,移动鼠标,串口终端就会持续输出类似这样的信息:
I (5230) usb_hid_mouse: mouse connected, VID=0x046d, PID=0xc077 I (6150) usb_hid_mouse: buttons=0x01 dx=3 dy=-2 wheel=0 I (6230) usb_hid_mouse: buttons=0x01 dx=1 dy=0 wheel=0 I (6310) usb_hid_mouse: buttons=0x00 dx=0 dy=1 wheel=0 I (6480) usb_hid_mouse: buttons=0x00 dx=0 dy=0 wheel=-1每一行就是一次中断传输拿到的报告。移动鼠标时dx和dy会不断变化,按下按键时buttons会相应变化。仔细观察可以发现,即使鼠标静止不动,很多时候也会上报全零的数据包——这是正常的,主机一直在周期性地询问,设备每次都会回复当前状态。
滚轮事件会体现在第4字节(wheel)上,向上滚动为正数,向下为负数。需要注意,不同鼠标的报告格式在whell字节上可能会存在差异。
6.3 用绘图方式直观理解鼠标数据的意义
如果只是看串口数字,很难直观理解dx、dy和鼠标实际移动的对应关系。我建议在PC端写一个简单的Python脚本,通过串口读取开发板转发的鼠标数据,然后实时绘图,把dx、dy映射成屏幕上一个点的运动轨迹。这样你动动鼠标,屏幕上就能看到开发板“画”出来的线条,对验证代码正确性很有帮助,也能直观感受到USB中断传输的实时性。
代码很简单,用pyserial读串口,把dx、dy累加,再用matplotlib渲染即可。这个“USB鼠标流量绘图”的思路,本质上就是把HID数据流变成可视化轨迹,对理解数据链路非常有帮助。
7. 常见问题与排查思路:参考官方资料时的避坑指南
7.1 鼠标插入后设备无响应
这是做完这个实验最可能遇到的问题。排查思路按以下优先级来:
- 硬件确认:鼠标本身是否正常?用电脑测一下。USB线是否有问题?换一根试试。开发板USB Host口的供电是否足够?有些功耗高的无线鼠标可能需要额外供电;
- 协议栈是否安装:代码中是否调用usb_host_install()?menuconfig中的USB Host选项是否打开?可以编译一份官方自带的HID Host例子程序烧录进去对比测试;
- 设备类型是否匹配:鼠标是否是标准HID设备?某些“多媒体鼠标”使用了厂商自定义的协议,没有标准的HID报告描述符,自然枚举不出HID接口;
- 日志查看:打开IDF的详细日志级别(menuconfig -> Log output -> Default log verbosity),设为Info或者Debug,重新烧录看协议栈的输出,一般能定位到具体卡在哪个环节。
7.2 枚举成功但读不到数据
设备能枚举说明控制传输没问题,读不到多半是中断传输初始化配置的问题。重点检查两处:一是是否拿到了正确的端点地址和最大包长度,二是中断传输回调是否正常注册。另外,注意USB鼠标如果长时间没有移动,有些鼠标会自动进入休眠状态,需要点击一下按键才能唤醒,这可能也会造成“读不到数据”的错觉。
7.3 鼠标移动方向反了或数据跳变
方向反了的问题很简单:不同系统对Y轴正负方向定义不同,根据需求把dy取个反即可。数据跳变则要检查你的解析代码是否把带符号的位移量当成了无符号数处理。注意int8_t和uint8_t的区别——这个坑在这类实验里几乎必踩一次。另外,如果坐标值在不移动鼠标时也无规律跳变,多半是电源干扰或者USB数据线质量不好,可以换一根屏蔽好的USB线再测试。
7.4 热插拔导致系统死机
USB设备的热插拔本身就是USB规范设计的应用场景,但嵌入式系统的USB Host在热插拔时,要处理的异常情况比冷启动时多得多——比如设备刚插入的瞬态电流导致VBUS电压跌落、设备在枚举中途被拔出导致状态机卡死等。
解决办法是在代码里为USB Host中断分配独立的高优先级任务,并在设备断开事件回调中及时释放资源、复位状态机。ESP-IDF官方例子中已经考虑了大部分进入DISCONNECTED事件的资源清理逻辑,尽量不要删减这些保护代码。
7.5 手边没有正点原子官方资料时的通用排查方法
这里多说一句,有时候大家在写实验的时候会找很多网络资料,但网络上各种来源混杂的环境描述可能会有些不一致。我的建议是:遵循“官方为主,书籍为辅”的原则。先读乐鑫官方ESP-IDF的USB Host文档和HID Host示例代码,再结合开发板配套指南里的接线和配置说明。如果出现差异,以乐鑫官方文档和ESP-IDF源码为准,因为芯片底层行为不会变,变的最多是板级设计。
8. 进阶玩法与项目扩展思路
8.1 不止是鼠标:HID键盘、触摸板和游戏手柄
跑通了鼠标,基本就打通了HID Host的整条路径。换个HID设备,核心流程还是那几步:枚举、加载类驱动、读取报告、解析描述符。
做键盘时,报告是按键码数组(8字节或更多,包含修饰键和控制键),需要对照USB HID键值表翻译成ASCII码。做触摸板时,数据格式复杂一些,报告描述符要解析得仔细,坐标精度和触控点数都需要处理。做游戏手柄时,关键是多轴数据和按键矩阵的解析,协议上还是HID这一套。
8.2 组合键的识别与节能唤醒
鼠标实验扩展一下,还可以实现组合键识别(同时按下多键触发特定动作)。对于HID规范,每个报告里按键位图各个bit是独立的,解析时用位与操作就能同时检测多个按键状态。做低功耗产品时,还可以配置成鼠标长时间无动作后进入休眠,有动作时通过中断唤醒MCU。
8.3 把鼠标数据转发到上位机或云端
开发板解析到鼠标数据之后,数据流向其实完全由你决定。你可以通过UART把坐标数据发给PC,也可以在板内把数据转成MQTT消息发到云端,甚至可以作为人机交互输入控制屏幕上的菜单。这些扩展本质上就是把“主机枚举并读取HID报告”这一步能力变成你的基础设施,上面想搭什么业务都行。
9. 实际调试过程中的几点个人体会
这个实验做完,我最大的感受是:USB协议栈虽然抽象层级多,但用ESP-IDF的HID Host库后,代码复杂度被大幅收敛,真正需要应用开发者关心的就剩下“拿到报告之后怎么解析”这一件事。相比当年在STM32上手动实现HID Host那个复杂程度,P4这套方案对产品落地已经相当友好了。
另外,在调试环境的选择上,建议有条件的朋友准备一个USB协议分析仪。没有也没关系,ESP-IDF的日志系统配合枚举到设备描述符的详细打印,其实已经能解决绝大部分问题。我调试过程中一半以上的问题,都是通过打开详细日志、看枚举卡在哪一步来定位的。这个思路在所有USB Host开发项目中通用。
如果你手头没有DNESP32P4开发板,用的是别的ESP32-P4板卡,代码移植也不麻烦,只要确认芯片型号是ESP32-P4、USB引脚引出正确,编译烧录即可。
最后给一个小提醒:做USB Host实验时,鼠标一定不要用Type-C接口的,优先选USB-A接口的传统有线鼠标,这样省去转接产生的变量,排查问题时能少走很多弯路。等代码完全跑通了,再去折腾无线鼠标、高报告率电竞鼠标这些“增强型设备”,会顺手很多。