ESP IoT Solution 中 USB 主机 CDC 驱动(iot_usbh_cdc)使用指南:从热插拔匹配到数据收发
【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution
iot_usbh_cdc是 ESP IoT Solution 提供的一个简化版 USB 主机 CDC(Communications Device Class)驱动组件,它让 ESP32 系列芯片能以 USB 主机身份连接并通信各类 CDC 设备(如 4G 模块、USB 串口、AT 指令设备等)。本文基于 USB 主机 CDC 文档 并结合仓库源码,完整讲解驱动的安装配置、设备匹配、端口打开、数据收发、描述符支持范围与典型注意事项,帮助你快速在自己的应用中集成 USB CDC 主机功能。
组件概览与核心特性
iot_usbh_cdc组件位于仓库的 components/usb/iot_usbh_cdc 目录,核心源文件为 iot_usbh_cdc.c,对外头文件为 iot_usbh_cdc.h。根据组件 README.md 与源码实现,该组件具备以下特性:
- 支持标准 USB CDC 设备(如 USB 转串口、CDC-ACM 设备);
- 支持厂商自定义(Vendor Specific)的 CDC 设备描述符(如手机/模块的 "Mobile AT Interface");
- 支持 CDC 多接口(multiple interface),可在同一设备上打开多个端口;
- 支持通过 USB HUB 连接多个 USB 设备;
- 内置热插拔事件处理:设备连接/断开时自动回调通知;
- 可选启用内部环形缓冲区(ringbuffer)简化数据收发,降低应用层对时序的依赖;
- 驱动内部可直接完成 USB Host Driver 协议栈的初始化,也可跳过初始化与其他 USB 驱动共存。
从源码结构看,组件内部维护了三类核心对象(见 iot_usbh_cdc.c):全局驱动对象usbh_cdc_obj_t(持有 USB Host client 句柄、事件组、互斥锁及设备/端口链表)、设备对象usbh_cdc_dev_t(封装设备句柄与控制传输)、端口对象usbh_cdc_port_t(封装通知/数据端点传输与收发环形缓冲区)。
添加组件依赖
在使用组件管理器(IDF Component Manager)的工程中,执行以下命令即可将组件加入依赖,CMake 阶段会自动下载:
idf.py add-dependency "espressif/iot_usbh_cdc=*"也可以使用create-project-from-example直接基于官方示例模板创建工程:
idf.py create-project-from-example "espressif/iot_usbh_cdc=*:usb_cdc_basic"仓库自带的完整可运行示例位于 examples/usb/host/usb_cdc_basic,此外还有面向 4G 模块的 examples/usb/host/usb_cdc_4g_module 可作为实战参考。
安装驱动并初始化 USB Host Driver 协议栈
安装驱动使用usbh_cdc_driver_install,参数为usbh_cdc_driver_config_t配置结构。原文档给出的最小配置如下:
/* 安装 USB CDC 驱动,并由驱动内部初始化好 USB Host Driver 协议栈 */ usbh_cdc_driver_config_t config = { .task_stack_size = 1024 * 4, .task_priority = 5, .task_coreid = 0, .skip_init_usb_host_driver = false, }; usbh_cdc_driver_install(&config);各配置字段的含义与取值范围(依据 iot_usbh_cdc.h 与 Kconfig):
| 配置字段 | 含义 | 说明 |
|---|---|---|
task_stack_size | 驱动任务栈大小(字节) | 示例中使用1024 * 4,需满足驱动内部usbh_cdc_client_task的运行需求 |
task_priority | 驱动任务优先级 | 示例中使用 5;Kconfig 中USBH_TASK_BASE_PRIORITY默认值为 5 |
task_coreid | 驱动任务绑定的核心 | 设为-1表示不指定核心(tskNO_AFFINITY),示例中使用 0;Kconfig 中USBH_TASK_CORE_ID默认 0,范围 -1~1 |
skip_init_usb_host_driver | 是否跳过 USB Host Driver 初始化 | false时由驱动内部初始化协议栈;true时跳过,适用于其他驱动已初始化的情况 |
关键点:skip_init_usb_host_driver的使用场景。当你的应用中还有其他 USB 驱动(例如 USB MSC、UVC、RNDIS 等)也需要使用 USB Host Driver 协议栈时,应将此配置项置为true以避免重复初始化。从源码看(iot_usbh_cdc.c),当该选项为false时,驱动会创建名为usb_lib的任务,该任务内部通过usb_host_lib_info检查协议栈是否已被其他模块安装,若未安装则以usb_host_install完成初始化;若已安装则直接复用。整个安装流程还包括:分配驱动对象、创建事件组与互斥锁、创建usbh_cdc客户端任务、注册 USB Host client(usb_host_client_register)。
驱动安装失败的返回值(定义在 iot_usbh_cdc.h):
ESP_ERR_INVALID_ARG:配置参数为 NULL 或参数非法;ESP_ERR_INVALID_STATE:驱动已被安装(重复调用);ESP_ERR_NO_MEM:内存分配失败;ESP_FAIL:任务创建失败或 USB Host 初始化失败。
注册设备事件回调与设备匹配列表
设备热插拔通过usbh_cdc_register_dev_event_cb注册回调实现。该函数接收三个参数:设备匹配列表dev_match_id_list、事件回调event_cb、用户数据user_data。
static void device_event_cb(usbh_cdc_device_event_t event, usbh_cdc_device_event_data_t *event_data, void *user_ctx) { switch (event) { case CDC_HOST_DEVICE_EVENT_CONNECTED: { ESP_LOGI(TAG, "Device connected: dev_addr=%d, matched_intf_num=%d", event_data->new_dev.dev_addr, event_data->new_dev.matched_intf_num); /* 在此处根据应用需要打开一个或多个端口 */ } break; case CDC_HOST_DEVICE_EVENT_DISCONNECTED: ESP_LOGI(TAG, "Device disconnected: dev_addr=%d, dev_hdl=%p", event_data->dev_gone.dev_addr, event_data->dev_gone.dev_hdl); break; default: break; } } const static usb_device_match_id_t match_id_list[] = { { .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT, .idVendor = USB_DEVICE_VENDOR_ANY, .idProduct = USB_DEVICE_PRODUCT_ANY, }, { 0 } }; usbh_cdc_register_dev_event_cb(match_id_list, device_event_cb, NULL);关于dev_match_id_list的规范(原文档明确说明):
- 数组不能位于栈上,必须是静态或全局存储,因为驱动内部会保存该指针供后续匹配使用(见 iot_usbh_cdc.c);
- 必须以空项结尾(示例中的
{ 0 }),驱动遍历列表时以此判断结束; - 数组中的每一项都是一组匹配条件,满足任一条件即触发回调;
- 使用
USB_DEVICE_VENDOR_ANY(值为 0)与USB_DEVICE_PRODUCT_ANY(值为 0)可匹配所有设备;指定具体idVendor/idProduct则可精确匹配特定厂商与产品。
更丰富的匹配条件
通过match_flags可以组合更多匹配维度,完整的标志位定义在 usbh_helper.h:
| 匹配标志 | 含义 |
|---|---|
USB_DEVICE_ID_MATCH_VENDOR | 匹配设备描述符的 idVendor |
USB_DEVICE_ID_MATCH_PRODUCT | 匹配设备描述符的 idProduct |
USB_DEVICE_ID_MATCH_DEV_BCD | 匹配设备版本号 bcdDevice |
USB_DEVICE_ID_MATCH_DEV_CLASS/DEV_SUBCLASS/DEV_PROTOCOL | 匹配设备描述符中的类/子类/协议 |
USB_DEVICE_ID_MATCH_INT_CLASS/INT_SUBCLASS/INT_PROTOCOL | 匹配接口描述符中的类/子类/协议 |
USB_DEVICE_ID_MATCH_INT_NUMBER | 匹配接口编号 |
USB_DEVICE_ID_MATCH_ALL | 匹配所有字段(0xFFFF) |
对应的匹配结构体usb_device_match_id_t与匹配辅助函数(usbh_match_device、usbh_match_interface、usbh_match_id_from_list等)也在 usbh_helper.h 中提供,可被其他 USB 驱动复用。若想匹配任意设备,可直接使用组件导出的预置列表宏ESP_USB_DEVICE_MATCH_ID_ANY——官方示例 usb_cdc_basic_main.c 正是这样使用的。
回调中的事件数据与死锁警示
事件数据联合体usbh_cdc_device_event_data_t定义在 iot_usbh_cdc.h:
- 连接事件携带:
dev_addr(设备地址)、matched_intf_num(匹配到的接口编号,-1 表示无效)、device_desc(设备描述符指针)、active_config_desc(活动配置描述符指针); - 断开事件携带:
dev_addr与dev_hdl(设备句柄)。
重要限制:device_event_cb回调运行在 USB 任务上下文中,不允许在其中调用usbh_cdc_write_bytes等与设备通信的函数,否则会导致死锁。正确的做法是在回调中创建一个新任务,或通过事件组/队列发送事件到应用任务,由应用任务执行实际的设备通信。官方示例中正是采用这一模式:连接回调内只做usbh_cdc_port_open和创建cdc_task(见 usb_cdc_basic_main.c),数据收发全部放在独立任务中完成。
驱动还提供了usbh_cdc_unregister_dev_event_cb用于注销回调;usbh_cdc_driver_uninstall调用时也会自动清理所有已注册的回调。
打开 CDC 端口
当设备匹配成功并触发CDC_HOST_DEVICE_EVENT_CONNECTED事件后,即可在回调中通过usbh_cdc_port_open打开设备的一个或多个端口进行通信。usbh_cdc_port_config_t是端口配置的核心结构(定义见 iot_usbh_cdc.h):
usbh_cdc_port_handle_t cdc_port = NULL; usbh_cdc_port_config_t cdc_port_config = { .dev_addr = event_data->new_dev.dev_addr, .itf_num = 0, .in_transfer_buffer_size = 512, .out_transfer_buffer_size = 512, .cbs = { .notif_cb = NULL, .recv_data = NULL, .closed = NULL, .user_data = NULL, }, }; usbh_cdc_port_open(&cdc_port_config, &cdc_port);各字段说明:
| 字段 | 含义 |
|---|---|
dev_addr | CDC 设备的设备地址,取自事件数据new_dev.dev_addr |
itf_num | 要打开的接口编号(对应设备配置描述符中的 bInterfaceNumber) |
in_ringbuf_size | 接收环形缓冲区大小;设为 0 表示不使用接收 ringbuffer |
out_ringbuf_size | 发送环形缓冲区大小;设为 0 表示不使用发送 ringbuffer |
in_transfer_buffer_size | IN 传输缓冲区大小(接收),不能为 0,源码中会校验(见 iot_usbh_cdc.c) |
out_transfer_buffer_size | OUT 传输缓冲区大小(发送),不能为 0 |
cbs | 端口事件回调集合(见下文) |
flags | 端口配置标志,如USBH_CDC_FLAGS_DISABLE_NOTIFICATION |
端口回调集合usbh_cdc_port_event_callbacks_t提供三个可选回调(均可置 NULL):
closed:端口关闭时回调;recv_data:收到数据时回调,通知应用有数据就绪;notif_cb:收到 CDC 通知(如串口状态、网络连接状态、速率变化)时回调,参数为iot_cdc_notification_t *notif,其中bNotificationCode可取USB_CDC_NOTIFY_NETWORK_CONNECTION、USB_CDC_NOTIFY_SERIAL_STATE、USB_CDC_NOTIFY_SPEED_CHANGE等(定义见 iot_usbh_cdc_type.h),Data负载可解析为usb_cdc_serial_state_t位域结构查看 DCD/DSR/break/ring 及各类错误位;user_data:透传给以上回调的用户数据指针。
官方示例展示了recv_data与notif_cb的典型用法(usb_cdc_basic_main.c):接收回调中先usbh_cdc_get_rx_buffer_size查询数据量,再usbh_cdc_read_bytes读取;通知回调则分别处理网络连接变化、链路速率和串口状态事件。示例还演示了在同一设备上打开多个接口端口(通过EXAMPLE_BULK_ITF_NUM控制同时打开接口 0 和接口 1),这正是组件多接口能力的直接体现。
从源码实现看,usbh_cdc_port_open的完整流程包括:校验参数、检查端口是否重复打开、按地址查找/新建设备对象、解析接口描述符(cdc_parse_interface_descriptor)、按需创建收发 ringbuffer、分配并配置三个 USB 传输(通知 INTR 端点、BULK IN 端点、BULK OUT 端点)、claim 数据接口(必要时同时 claim 通知接口)、提交 BULK IN 与 INTR IN 轮询传输,最后通过usb_host_client_unblock唤醒客户端任务(见 iot_usbh_cdc.c)。
数据收发:接收、发送与环形缓冲区
端口打开后,主机会自动从 CDC 设备接收 USB 数据到 USB 缓冲区,应用层有两种获取数据的方式:
- 轮询方式:调用
usbh_cdc_get_rx_buffer_size查询接收缓冲区中已就绪的数据大小,然后调用usbh_cdc_read_bytes读取; - 回调方式:注册
recv_data回调,数据就绪时驱动自动通知。
/* 查询并读取接收数据 */ size_t data_len = 0; usbh_cdc_get_rx_buffer_size(cdc_port, &data_len); if (data_len > 0) { data_len = data_len > sizeof(buf) ? sizeof(buf) : data_len; usbh_cdc_read_bytes(cdc_port, buf, &data_len, 0); }in_ringbuf_size的作用:当in_ringbuf_size大于 0 时,驱动会创建内部接收 ringbuffer,IN 传输完成的数据先缓存到 ringbuffer 中,应用层随时可取,接收时机灵活;从源码可见,ringbuffer 满时新数据会被丢弃并打印告警(iot_usbh_cdc.c),因此应根据实际吞吐量合理设置大小。当in_ringbuf_size为 0 时不使用 ringbuffer,此时建议在recv_data回调中直接调用usbh_cdc_read_bytes(文档与头文件注释均如此建议),且读取长度必须等于内部传输缓冲区大小,否则返回ESP_ERR_INVALID_ARG(见 iot_usbh_cdc.h)。
发送数据使用usbh_cdc_write_bytes:
usbh_cdc_write_bytes(cdc_port, (uint8_t *)buff, len, pdMS_TO_TICKS(100));当out_ringbuf_size大于 0 时,数据会先写入内部发送 ringbuffer,由驱动后台逐步提交到 OUT 传输;当其为 0 时,数据直接通过 OUT 传输发送到设备,此时可通过ticks_to_wait实现阻塞式写入。官方示例即以usbh_cdc_write_bytes周期性地向 4G 模块发送AT\r\n指令(usb_cdc_basic_main.c)。
此外,组件还提供以下辅助 API:
usbh_cdc_flush_rx_buffer/usbh_cdc_flush_tx_buffer:清空收发缓冲区(仅 ringbuffer 模式支持,非 ringbuffer 模式返回ESP_ERR_NOT_SUPPORTED);usbh_cdc_send_custom_request:向设备发送自定义控制请求(支持 IN/OUT 双向),可用于读取字符串描述符、设置线路编码等标准请求之外的场景;usbh_cdc_desc_print:打印设备与配置描述符,便于调试;usbh_cdc_get_dev_handle:获取底层 USB 设备句柄,可配合usb_host_get_device_descriptor、usb_host_get_active_config_descriptor使用;usbh_cdc_port_get_intf_desc:获取解析后的通知接口与数据接口描述符。
热插拔异步性的注意事项
由于 USB 设备热插拔是异步的,设备可能在运行时的任何时刻被拔出。调用usbh_cdc_write_bytes、usbh_cdc_read_bytes等函数前必须确认设备仍连接且端口已打开,否则这些函数会返回错误(通常为ESP_ERR_INVALID_STATE)。从源码看,当设备断开(USB_HOST_CLIENT_EVENT_DEV_GONE)时,驱动会自动关闭该设备上的所有端口并回调CDC_HOST_DEVICE_EVENT_DISCONNECTED事件(iot_usbh_cdc.c),应用应在此事件中清理状态、置空端口句柄。官方示例使用事件组USB_DEV_DISCONNECTED_BIT通知发送任务退出循环,避免对已断开设备继续写入(usb_cdc_basic_main.c)。
关闭端口与卸载驱动
使用usbh_cdc_port_close关闭已打开的端口并释放其资源。关闭流程(见 iot_usbh_cdc.c)包括:置位to_close标志、注销端口回调、重置相关端点、释放 claim 的接口、释放传输与 ringbuffer,并且当该设备上所有端口都已关闭时,自动关闭底层 USB 设备并释放设备对象。
使用usbh_cdc_driver_uninstall可完全卸载驱动并释放所有资源。需要注意:
- 当仍有设备连接时无法直接卸载,会返回
ESP_ERR_INVALID_STATE(源码中会检查cdc_devices_list是否为空,见 iot_usbh_cdc.c),必须先关闭所有设备; - 卸载过程中会通过事件组
CDC_TEARDOWN通知客户端任务退出并等待CDC_TEARDOWN_COMPLETE,随后注销 USB Host client、释放驱动对象,超时会返回ESP_ERR_NOT_FINISHED。
支持的 CDC 设备描述符
驱动支持以下标准 CDC 设备描述符布局(典型 CDC-ACM 设备,含 IAD 描述符 + 通知接口 + 数据接口):
------------------- IAD Descriptor -------------------- bLength : 0x08 (8 bytes) bDescriptorType : 0x0B (Interface Association Descriptor) bFirstInterface : 0x00 (Interface 0) bInterfaceCount : 0x02 (2 Interfaces) bFunctionClass : 0x02 (Communications and CDC Control) bFunctionSubClass : 0x06 bFunctionProtocol : 0x00 iFunction : 0x07 (String Descriptor 7) Language 0x0409 : "" ---------------- Interface Descriptor ----------------- bLength : 0x09 (9 bytes) bDescriptorType : 0x04 (Interface Descriptor) bInterfaceNumber : 0x00 (Interface 0) bAlternateSetting : 0x00 bNumEndpoints : 0x01 (1 Endpoint) bInterfaceClass : 0x02 (Communications and CDC Control) bInterfaceSubClass : 0x06 (Ethernet Networking Control Model) bInterfaceProtocol : 0x00 (No class specific protocol required) iInterface : 0x07 (String Descriptor 7) Language 0x0409 : "" ----------------- Endpoint Descriptor ----------------- bLength : 0x07 (7 bytes) bDescriptorType : 0x05 (Endpoint Descriptor) bEndpointAddress : 0x87 (Direction=IN EndpointID=7) bmAttributes : 0x03 (TransferType=Interrupt) wMaxPacketSize : 0x0020 Bits 15..13 : 0x00 (reserved, must be zero) Bits 12..11 : 0x00 (0 additional transactions per microframe -> allows 1..1024 bytes per packet) Bits 10..0 : 0x20 (32 bytes per packet) bInterval : 0x09 (256 microframes -> 32 ms) ---------------- Interface Descriptor ----------------- bLength : 0x09 (9 bytes) bDescriptorType : 0x04 (Interface Descriptor) bInterfaceNumber : 0x01 (Interface 1) bAlternateSetting : 0x00 bNumEndpoints : 0x00 (Default Control Pipe only) bInterfaceClass : 0x0A (CDC-Data) bInterfaceSubClass : 0x00 bInterfaceProtocol : 0x00 iInterface : 0x00 (No String Descriptor) ---------------- Interface Descriptor ----------------- bLength : 0x09 (9 bytes) bDescriptorType : 0x04 (Interface Descriptor) bInterfaceNumber : 0x01 (Interface 1) bAlternateSetting : 0x01 bNumEndpoints : 0x02 (2 Endpoints) bInterfaceClass : 0x0A (CDC-Data) bInterfaceSubClass : 0x00 bInterfaceProtocol : 0x00 iInterface : 0x00 (No String Descriptor) ----------------- Endpoint Descriptor ----------------- bLength : 0x07 (7 bytes) bDescriptorType : 0x05 (Endpoint Descriptor) bEndpointAddress : 0x0C (Direction=OUT EndpointID=12) bmAttributes : 0x02 (TransferType=Bulk) wMaxPacketSize : 0x0200 (max 512 bytes) bInterval : 0x00 (never NAKs) ----------------- Endpoint Descriptor ----------------- bLength : 0x07 (7 bytes) bDescriptorType : 0x05 (Endpoint Descriptor) bEndpointAddress : 0x83 (Direction=IN EndpointID=3) bmAttributes : 0x02 (TransferType=Bulk) wMaxPacketSize : 0x0200 (max 512 bytes) bInterval : 0x00 (never NAKs)该布局的特征:IAD 将两个接口(通知接口 + 数据接口)关联为一个功能;通知接口含 1 个 IN 中断端点(用于接收 CDC 通知,如串口状态、网络状态);数据接口的激活备选设置(bAlternateSetting = 1)含 1 个 IN 批量端点和 1 个 OUT 批量端点用于数据传输。
驱动同时支持部分厂商自定义的 CDC 设备描述符,典型例子如下(一个接口内同时包含中断、IN 批量、OUT 批量三种端点,接口类为 0xFF 厂商特定,常见于手机 Modem 的 "Mobile AT Interface"):
---------------- Interface Descriptor ----------------- bLength : 0x09 (9 bytes) bDescriptorType : 0x04 (Interface Descriptor) bInterfaceNumber : 0x03 (Interface 3) bAlternateSetting : 0x00 bNumEndpoints : 0x03 (3 Endpoints) bInterfaceClass : 0xFF (Vendor Specific) bInterfaceSubClass : 0x00 bInterfaceProtocol : 0x00 iInterface : 0x09 (String Descriptor 9) Language 0x0409 : "Mobile AT Interface" ----------------- Endpoint Descriptor ----------------- bLength : 0x07 (7 bytes) bDescriptorType : 0x05 (Endpoint Descriptor) bEndpointAddress : 0x85 (Direction=IN EndpointID=5) bmAttributes : 0x03 (TransferType=Interrupt) wMaxPacketSize : 0x0010 Bits 15..13 : 0x00 (reserved, must be zero) Bits 12..11 : 0x00 (0 additional transactions per microframe -> allows 1..1024 bytes per packet) Bits 10..0 : 0x10 (16 bytes per packet) bInterval : 0x09 (256 microframes -> 32 ms) ----------------- Endpoint Descriptor ----------------- bLength : 0x07 (7 bytes) bDescriptorType : 0x05 (Endpoint Descriptor) bEndpointAddress : 0x82 (Direction=IN EndpointID=2) bmAttributes : 0x02 (TransferType=Bulk) wMaxPacketSize : 0x0200 (max 512 bytes) bInterval : 0x00 (never NAKs) ----------------- Endpoint Descriptor ----------------- bLength : 0x07 (7 bytes) bDescriptorType : 0x05 (Endpoint Descriptor) bEndpointAddress : 0x0B (Direction=OUT EndpointID=11) bmAttributes : 0x02 (TransferType=Bulk) wMaxPacketSize : 0x0200 (max 512 bytes) bInterval : 0x00 (never NAKs)从描述符解析源码(iot_usbh_descriptor.c)可以印证支持的判定逻辑:标准 CDC 设备通过“设备类为每接口/通信类且接口类为 0x02(CDC 控制)”或“复合设备使用 IAD 且bFirstInterface匹配、bInterfaceCount为 2”来识别;厂商自定义类型则解析接口内的中断、IN 批量、OUT 批量三类端点。相关类型常量(通信类子类、协议码、CDC 请求码、功能描述符结构等)统一定义在 iot_usbh_cdc_type.h。
多设备与 USB HUB 场景下的注意事项
组件支持通过 USB HUB 连接多个 USB 设备(驱动内部会跳过 HUB 设备本身,见 iot_usbh_cdc.c)。但需注意:
在使用 USB HUB 连接多个设备时,如果出现主机的 channel 不够用的情况,可以通过
usbh_cdc_port_config_t的flags打开USBH_CDC_FLAGS_DISABLE_NOTIFICATION标志位,强制禁用中断(通知)端点,从而减少一个 channel 的使用。如果设备本身就没有中断端点,则该标志无效。
启用方式:
usbh_cdc_port_config_t cdc_port_config = { .dev_addr = event_data->new_dev.dev_addr, .itf_num = 0, .in_transfer_buffer_size = 512, .out_transfer_buffer_size = 512, .flags = USBH_CDC_FLAGS_DISABLE_NOTIFICATION, /* 禁用通知端点,节省一个 channel */ .cbs = { 0 }, };从源码看,该标志在usbh_cdc_port_open中会将解析出的通知端点强制置空(iot_usbh_cdc.c),从而跳过中断端点的 claim 与轮询传输分配;代价是notif_cb通知将不再工作。
Kconfig 可调参数
组件提供若干 Kconfig 编译期配置(见 Kconfig),可在menuconfig的 "IoT USB Host CDC" 菜单下调整:
| 配置项 | 默认值 | 作用 |
|---|---|---|
USBH_TASK_CORE_ID | 0 | 内部 USB Host 库任务绑定的核心(范围 -1~1,-1 不指定) |
USBH_TASK_BASE_PRIORITY | 5 | 内部 USB Host 库任务基础优先级 |
USBH_CDC_CONTROL_TRANSFER_BUFFER_SIZE | 256 | 控制传输缓冲区大小(字节) |
USBH_CDC_IN_EP_RETRY_COUNT | 3 | IN 端点传输失败后的重试次数,超过后停止传输 |
完整实战:从安装到 AT 指令通信
综合上述 API,一个典型的 USB CDC 主机应用(以连接 4G 模块发送 AT 指令为例)的骨架如下:
/* 1. 安装驱动 */ usbh_cdc_driver_config_t driver_cfg = { .task_stack_size = 1024 * 4, .task_priority = 5, .task_coreid = 0, .skip_init_usb_host_driver = false, }; usbh_cdc_driver_install(&driver_cfg); /* 2. 注册设备事件回调,匹配所有设备(可改为指定 VID/PID) */ usbh_cdc_register_dev_event_cb(ESP_USB_DEVICE_MATCH_ID_ANY, device_event_cb, NULL); /* 3. 在连接回调中打开端口,并在独立任务中收发数据 */ static void device_event_cb(usbh_cdc_device_event_t event, usbh_cdc_device_event_data_t *event_data, void *ctx) { if (event == CDC_HOST_DEVICE_EVENT_CONNECTED) { usbh_cdc_port_config_t port_cfg = { .dev_addr = event_data->new_dev.dev_addr, .itf_num = 1, /* 数据接口编号 */ .in_ringbuf_size = 2048, /* 启用接收 ringbuffer */ .out_ringbuf_size = 2048, /* 启用发送 ringbuffer */ .in_transfer_buffer_size = 512, .out_transfer_buffer_size = 512, .cbs = { .recv_data = recv_cb, /* 数据就绪通知 */ .notif_cb = notif_cb, /* CDC 通知 */ .user_data = NULL, }, }; usbh_cdc_port_open(&port_cfg, &g_port); /* 打开端口 */ xTaskCreate(data_task, "cdc_task", 4096, NULL, 5, NULL); /* 独立任务通信 */ } else if (event == CDC_HOST_DEVICE_EVENT_DISCONNECTED) { g_port = NULL; /* 清理状态 */ } } /* 4. 应用任务中发送数据 */ static void data_task(void *arg) { char cmd[] = "AT\r\n"; while (g_port) { usbh_cdc_write_bytes(g_port, (uint8_t *)cmd, strlen(cmd), pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(1000)); } vTaskDelete(NULL); } /* 5. 接收回调中读取数据 */ static void recv_cb(usbh_cdc_port_handle_t port, void *arg) { uint8_t buf[256]; size_t len = 0; usbh_cdc_get_rx_buffer_size(port, &len); if (len > 0) { len = len > sizeof(buf) ? sizeof(buf) : len; usbh_cdc_read_bytes(port, buf, &len, 0); ESP_LOGI(TAG, "recv %d bytes", len); } }完整的可编译示例请参考 examples/usb/host/usb_cdc_basic(主逻辑见 usb_cdc_basic_main.c,含 ESP32-S3 USB-OTG 引脚模式切换的硬件初始化代码)以及 4G 模块场景的 examples/usb/host/usb_cdc_4g_module。组件的单元测试(含设备事件、读写、多接口等用例)位于 components/usb/iot_usbh_cdc/test_apps,可作为 API 行为边界的补充参考。
小结
iot_usbh_cdc将 ESP-IDF 底层 USB Host 协议栈的 client 注册、设备枚举、接口 claim、传输轮询等繁琐流程封装为简洁的安装—注册—打开—收发四步模型,同时通过匹配列表、事件回调、可选 ringbuffer 与端口回调,兼顾了热插拔灵活性、多设备扩展性与易用性。实际开发中重点把握三件事:其一,dev_match_id_list必须是静态数组且以空项结尾;其二,设备事件回调中禁止直接通信,应转移到独立任务;其三,正确评估 ringbuffer 是否启用以及USBH_CDC_FLAGS_DISABLE_NOTIFICATION在 HUB 多设备场景下的取舍,即可稳定地将 USB CDC 设备接入 ESP32 应用。
【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考