- 物联网
- 嵌入式
- 驱动开发
- 硬件开发
【免费下载链接】esp-iot-solution
Espressif IoT Library. IoT Device Drivers, Documentations and Solutions.
本文是 esp-iot-solution 仓库中 TinyUSB 应用指南 的深度拓展,围绕乐鑫 IoT 方案中 TinyUSB 协议栈的两种集成路线(esp_tinyusb高层封装与espressif/tinyusb原生组件)、芯片选型,以及 UAC 音频、UVC 视频两类典型 USB 设备应用展开。读完本文,你将掌握:如何在 ESP32-S2/S3/P4 上快速接入 USB CDC/MSC/HID/MIDI/DFU/网卡等类、如何通过tusb_config.h与描述符文件定制原生 TinyUSB 设备、以及如何基于 usb_device_uac 与 usb_device_uvc 组件实现 USB 声卡与 USB 摄像头,并理解 UAC 异步反馈、UVC 图像格式等底层原理。
TinyUSB 简介与芯片选型
TinyUSB 是一个开源的嵌入式 USB 主机/设备协议栈库,由 Adafruit 开发和维护,目标是为小型微控制器提供轻量级、跨平台、易于集成的 USB 功能。它支持多种 USB 设备类型,包括:
- HID(人机接口设备):键盘、鼠标、Surface Dial 旋钮等
- MSC(大容量存储):U 盘、无线磁盘
- CDC(通信设备类):虚拟串口、串口桥
- MIDI(音乐设备数字接口):电子乐器
- 以及DFU(固件升级)、ECM/NCM/RNDIS(USB 网卡)等类
在 esp-iot-solution 中,基于原生 TinyUSB 封装了若干上层组件(components/usb/目录下的usb_device_uac、usb_device_uvc、usb_stream、esp_tinyuf2等),覆盖音频、视频、U 盘、固件升级等场景。
在动手开发前,需要根据芯片的 USB 外设能力进行选型。下表整理了当前仓库支持矩阵:
| SoC | USB 1.1 Full Speed | USB 2.0 High Speed |
|---|---|---|
| ESP32-S2 | ✅ 支持 | — |
| ESP32-S3 | ✅ 支持 | — |
| ESP32-P4 | ✅ 支持 | ✅ 支持 |
选型要点:绝大多数 USB 设备(HID、MSC、CDC、UAC、UVC 等)在 Full Speed 下即可工作,ESP32-S2/S3 足以覆盖;若追求更高带宽(如大分辨率视频流、高采样率多声道音频),则需选择支持 High Speed 的 ESP32-P4。这一能力差异在源码中也有直接体现,例如 usb_device_uvc/tusb/tusb_config.h 中通过CONFIG_TINYUSB_RHPORT_HS区分:ESP32-P4 使能时定义CFG_TUSB_RHPORT1_MODE(High Speed 端口),其余芯片定义CFG_TUSB_RHPORT0_MODE并回退到 Full Speed。
TinyUSB 组件:两种集成路线
仓库将 TinyUSB 的接入方式划分为两个组件,分别面向"简单快速"与"深度定制"两类需求,二者不可混淆,务必按项目复杂度选择。
esp_tinyusb 组件(高层封装,简单应用首选)
esp_tinyusb组件封装了一系列 TinyUSB API,可方便地将 USBCDC-ACM、MSC、MIDI、HID、DFU、ECM/NCM/RNDIS类集成进自己的工程。使用步骤:
在工程目录下运行以下命令添加依赖:
idf.py add-dependency esp_tinyusb~1.0.0在
menuconfig中配置需要使用的 USB 类(打开对应 Kconfig 选项即可启用对应 Class)。在工程代码中调用 esp_tinyusb 提供的 API 完成初始化与数据收发。
esp-iot-solution 仓库examples/usb/device/下提供了丰富的落地示例,可直接参考:
- usb_dual_uvc_device:双摄 UVC 设备
- usb_uac:USB 声卡(UAC)
- usb_webcam:USB 摄像头(UVC,可将 ESP32-S3-EYE 变为 USB 摄像头)
- usb_extend_screen:USB 扩展屏
- usb_lcd_display:USB LCD 显示器
- usb_dongle、usb_hid_device、usb_msc_wireless_disk、usb_surface_dial、usb_uart_bridge
注意:esp_tinyusb 封装了许多 USB 类,使开发被支持的 USB 类非常容易,但这也意味着进行某些底层改动比较困难,仅适合简单的 USB 应用。
espressif/tinyusb 组件(原生封装,复杂应用首选)
espressif/tinyusb组件基于原生 tinyusb 仓库,将其代码封装为独立组件,方便在自己的工程中使用。该组件需要使用 ESP-IDF release/v5.0 及以上版本。
使用步骤:
在工程目录下添加依赖:
idf.py add-dependency "tinyusb~0.15.10"编写
tusb_config.h:该文件通过定义一系列宏来决定 TinyUSB 的配置,并反向提供给 espressif/tinyusb 组件。同时在 main 组件的CMakeLists.txt中添加以下代码,将配置头文件目录注入组件编译(可参考 原生 TinyUSB 开发指南 的工程目录结构):idf_component_get_property(tusb_lib espressif__tinyusb COMPONENT_LIB) target_include_directories(${tusb_lib} PRIVATE path_to_your_tusb_config)工程推荐目录结构为
main/下单独建tusb/目录,放置tusb_config.h、usb_descriptors.c、usb_descriptors.h,保证依赖关系简单:project_name | |-- main |-- CMakeLists.txt |-- idf_component.yml |-- main.c |-- tusb |-- tusb_config.h |-- usb_descriptors.c |-- usb_descriptors.h对应
main/CMakeLists.txt(放置于idf_component_register之后):# espressif__tinyusb 应匹配当前依赖的 tinyusb 名称 idf_component_get_property(tusb_lib espressif__tinyusb COMPONENT_LIB) target_include_directories(${tusb_lib} PUBLIC "${COMPONENT_DIR}/tusb") target_sources(${tusb_lib} PUBLIC "${COMPONENT_DIR}/tusb/usb_descriptors.c")关于反向依赖:工程依赖 tinyusb,又需要向 tinyusb 提供头文件,不可避免地存在反向依赖问题。目前官方做法是将 tinyusb 的关键文件作为源码直接编译进 main 组件解决。
编写
usb_descriptors.h:定义 USB 设备的描述符,包括设备描述符、配置描述符、接口描述符等。tinyusb 提供了大量描述符模板,优先复用预定义描述符,可方便地进行组装和长度计算。编写
usb_descriptors.c:实现向 tinyusb 提供描述符的回调函数,核心是三个弱函数:uint8_t const *tud_descriptor_device_cb(void); // 设备描述符 uint8_t const *tud_descriptor_configuration_cb(uint8_t index); // 配置描述符 uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid); // 字符串描述符注意:配置描述符的长度必须等于实际长度;各端点描述符的端点号要避免重复。仓库内可参考的现成实现:usb_device_uvc/tusb/usb_descriptors.c、usb_device_uvc/tusb/usb_descriptors.h 以及 usb_hid_device 示例中的
hid_device/usb_descriptors.c。
esp-iot-solution 中采用原生 TinyUSB 路线的示例包括:
- usb_hid_device:USB 键盘鼠标设备
- usb_msc_wireless_disk:USB 无线 U 盘
- usb_surface_dial:Windows Surface Dial HID 设备
- usb_uart_bridge:串口转 USB 设备
注意:espressif/tinyusb 提供了更多灵活性,可以更方便地定制 USB 设备,适合复杂的 USB 应用。若使用 ESP-IDF release/v4.4,可使用
leeebo/tinyusb_src组件(其作用与 espressif/tinyusb 相同,补全了对 v4.4 的支持)。
深入tusb_config.h:关键配置宏
TinyUSB 大部分功能通过宏开关控制。除上面提到的速率配置外,原生 TinyUSB 开发指南 还系统总结了以下关键宏:
系统设置类宏
CFG_TUSB_RHPORT0_MODE:定义连接到 USB PHY 的方式和速率,例如 USB 设备 + 全速:#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED)CFG_TUSB_RHPORT1_MODE:定义第二个 PHY 端口的方式和速率,例如 USB 设备 + 高速:#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED)(ESP32-P4 的 High Speed 场景使用)ESP_PLATFORM:使用 ESP-IDF 平台编译时需启用CFG_TUSB_OS:定义操作系统,使用 FreeRTOS 时设为OPT_OS_FREERTOS,也可不启用操作系统CFG_TUSB_OS_INC_PATH:在 ESP-IDF 中要求 include 路径添加freertos/前缀:#define CFG_TUSB_OS_INC_PATH freertos/CFG_TUSB_DEBUG:启用 tinyusb 日志打印等级(共三级,0 表示关闭)CFG_TUSB_DEBUG_PRINTF:定义日志打印函数,例如esp_rom_printfCFG_TUD_ENABLED:设为 1 启用 tinyusb device 功能CFG_TUSB_MEM_SECTION:将 tinyusb 内存分配到特定内存段CFG_TUSB_MEM_ALIGN:定义内存对齐方式,例如#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
设备与 Class 类宏
CFG_TUD_ENDPOINT0_SIZE:端点 0 的最大包大小- 每个 USB Class 都有独立宏,以 UVC 为例:
CFG_TUD_VIDEO(视频控制接口数量)、CFG_TUD_VIDEO_STREAMING(视频流接口数量)、CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE(视频流端点 buffer 大小)
仓库内可参考的完整tusb_config.h实例:usb_device_uvc/tusb/tusb_config.h(UVC)、usb_device_uac/tusb/tusb_config.h(UAC)。
初始化 USB PHY 与协议栈
static void usb_phy_init(void) { // Configure USB PHY usb_phy_config_t phy_conf = { .controller = USB_PHY_CTRL_OTG, .otg_mode = USB_OTG_MODE_DEVICE, .target = USB_PHY_TARGET_INT, }; usb_new_phy(&phy_conf, &s_uvc_device.phy_hdl); } static void tusb_device_task(void *arg) { while (1) { tud_task(); } } int main(void) { usb_phy_init(); bool usb_init = tusb_init(); if (!usb_init) { ESP_LOGE(TAG, "USB Device Stack Init Fail"); return ESP_FAIL; } xTaskCreatePinnedToCore(tusb_device_task, "TinyUSB", 4096, NULL, 5, NULL, 0); }若使用外部 USB PHY,需改用相应外部 PHY 初始化流程。此外还可通过实现tud_mount_cb/tud_umount_cb/tud_suspend_cb/tud_resume_cb等设备层弱函数,获取设备插入、拔出、暂停、恢复等事件。
USB Device 应用一:USB 音频(UAC)
TinyUSB 支持 USBUAC 2.0标准,用于通过 USB 传输音频数据,主要特点:
- 最高支持 32 位 / 384 kHz 的音频流
- 兼容 USB 1.1 Full Speed 与 USB 2.0 High Speed
- 延迟更低
UAC 传输方式与三种同步机制
UAC仅支持 USB 传输中的同步传输,因此 UAC 音频设备的数据端点都是同步端点。同步传输不进行重传、延迟低;但由于主机与从机的传输时钟不同步,可能产生短暂静音/爆音,由此产生三种同步方式:
- SYNC 同步:将输出时钟与每个 Frame 的 SOF 包同步
- 自适应:根据主机传输数据的速率调整输出的采样率
- ASYNC 异步:相比前两者多了一个反馈端口,从机根据主机当前的速率告知主机后续的发送速率,从而完成数据的补发或少发,不需要再适应主机的发送频率
ASYNC 异步传输的反馈端点
通过启用宏CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP实现反馈速率的计算。TinyUSB 提供了多种反馈数据计算方式,其中基于 FIFO 的反馈计算(AUDIO_FEEDBACK_METHOD_FIFO_COUNT)最为简单且实用,需要实现以下虚函数完成设置:
void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param) { (void)func_id; (void)alt_itf; // Set feedback method to fifo counting feedback_param->method = AUDIO_FEEDBACK_METHOD_FIFO_COUNT; feedback_param->sample_freq = s_uac_device->current_sample_rate; ESP_LOGD(TAG, "Feedback method: %d, sample freq: %d", feedback_param->method, feedback_param->sample_freq); }工作原理:UAC Class 内部维护一块软件 FIFO,大小由CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ定义。将此内存大小设置为 10ms 的数据量,UAC 驱动内部便拥有一块缓冲区,并通过反馈端点将 FIFO 水位维持在二分之一处:数据缺失时主机在一包中多发数据,数据盈余时主机少发数据。
应用建议:每一次新音频传输开始前(例如超过 100ms 没有数据到来即视为新音频),先让 UAC 内部 FIFO 缓冲一半缓冲区大小的数据再开始播放,这样可以保证 I2S 一直有数据可取,不会产生爆音和噪声;配合反馈端点,软件 FIFO 大小会持续维持在稳定水平。
仓库配套:usb_device_uac 组件
esp-iot-solution 将 UAC 能力封装为 usb_device_uac 组件,支持 ESP32-S2/S3/P4,可从主机侧收发音频,最多 8 个扬声器通道、4 个麦克风通道,采样率可配置,特性包括:
- 通过 UAC 接口进行音频收发
- 支持设置收发音频的间隔
- 支持调节音量与设置静音
- 支持同步传输的反馈端点
组件当前不支持:动态配置 MIC/SPK 采样率;无法同时兼容 Windows 与 Linux(若需在 macOS 上使用,请启用宏UAC_SUPPORT_MACOS)。
添加依赖:
idf.py add-dependency "espressif/usb_device_uac=*"关键 Kconfig 配置(见 Kconfig.uac):
UAC_SPEAKER_CHANNEL_NUM:扬声器通道数,范围 0~8,默认 1UAC_MIC_CHANNEL_NUM:麦克风通道数,范围 0~4,默认 1UAC_SAMPLE_RATE:采样率,默认 48000- 位深选择:16-bit、24-bit(24 槽位打包)、24-bit(32 槽位)、32-bit(32 槽位),
UAC_BYTES_PER_SAMPLE随之自动缩放(2/3/4 字节) UAC_SPK_INTERVAL_MS:SPK 首次读取 UAC 数据的间隔,并使 SPK FIFO 容纳 n 毫秒数据,默认 10msUAC_MIC_INTERVAL_MS:MIC 写入 UAC 数据的间隔,批量取数可降低从端延迟,默认 10msUAC_SPK_NEW_PLAY_INTERVAL:距上次收到音频数据超过该毫秒数即视为新一次播放,默认 100msUAC_SUPPORT_MACOS:在全速设备上支持 macOS 的 16.16 到 10.14 格式转换USB_DEVICE_UAC_AS_PART:启用后需在工程中自行编写tusb_config.h与usb_descriptors.c(适用于需要并入复合设备场景,此时需在配置中额外提供spk_itf_num与mic_itf_num)
编程接口:初始化入口为uac_device_init(),配置结构体 usb_device_uac.h 定义如下回调:
uac_output_cb_t output_cb:UAC 数据输出回调(主机→设备,扬声器方向),传 NULL 则禁用输出uac_input_cb_t input_cb:UAC 数据输入回调(设备→主机,麦克风方向),传 NULL 则禁用输入uac_set_mute_cb_t set_mute_cb:设置静音回调,传 NULL 则忽略静音请求uac_set_volume_cb_t set_volume_cb:设置音量回调,传 NULL 则忽略音量请求cb_ctx:用户自定义回调上下文skip_tinyusb_init:为 true 时跳过 TinyUSB 与 USB PHY 的初始化(用于复合设备等场景)
完整示例见 examples/usb/device/usb_uac。
USB Device 应用二:USB 视频(UVC)
TinyUSB 支持 USBUVC 1.5标准,用于通过 USB 传输视频数据,可传输多种视频格式,包括未压缩的 YUV 格式,以及压缩的 MJPEG、H264、H265 等。
传输方式
- 当视频流接口(Video Streaming)传输视频时,其传输端点可为同步传输或批量传输端点
- 当视频流接口传输静态图像时,传输类型为批量传输端点
图像格式:Format 与 Frame
UVC 能传输多种视频格式,这些图像格式通过视频描述符的Format和Frame定义:
| 图像类型 | Format | Frame |
|---|---|---|
| MJPEG | FORMAT_MJPEG:0x06 | FRAME_MJPEG:0x07 |
| YUV2 / NV12 / M420 / I420 | FORMAT_UNCOMPRESSED:0x04 | FRAME_UNCOMPRESSED:0x05 |
| H264 | FORMAT_H264:0x13 | FRAME_H264:0x14 |
| H265 | FORMAT_FRAME_BASED:0x10 | FRAME_FRAME_BASED:0x11 |
其中Frame based格式比较特殊,可以存储任意按帧存储的图像格式(如 MJPG、H264、H265 等),通过GUID 字段来表示具体的图像格式。
双摄摄像头
UVC 设备中,一个物理摄像头对应一个 VC(Video Control)描述符,一个 VC 描述符可以挂多个 VS(Video Streaming)描述符,表示该摄像头可传输多种格式图像。但在某些特殊硬件上有两个物理摄像头,此时需要两个 VC 描述符:
USB Descriptor | |-- Video Control | |-- Video Streaming | |-- Video Control |-- Video Streaming仓库配套:usb_device_uvc 组件
usb_device_uvc 是面向 ESP32-S2/ESP32-S3 的 UVC 设备驱动,支持向 USB 主机流式传输 JPEG 帧,用户可通过回调函数将摄像头或任意设备包装为 UVC 标准设备。特性:
- 支持通过 UVC 视频流接口传输视频
- 同时支持同步(isochronous)与批量(bulk)两种模式
- 支持多种分辨率与帧率
- 支持传输两个摄像头的画面
注意:若在 Kconfig 中启用了
UVC_SUPPORT_TWO_CAM且需要多摄像头切换,请将UVC_CAM1_XFER_MODE与UVC_CAM2_XFER_MODE均设置为同步(Isochronous)模式。
添加依赖:
idf.py add-dependency "espressif/usb_device_uvc=*"关键实现:驱动通过tud_video_n_streaming()启动视频流、tud_video_n_frame_xfer()传输一帧图像,并通过弱函数tud_video_frame_xfer_complete_cb()检查是否传输完成;视频流端点 buffer 大小由宏CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE定义。双摄场景下的描述符组装可参考 usb_device_uvc/tusb/usb_descriptors.c 与 uvc_frame_config.h。完整示例见 examples/usb/device/usb_webcam(将 ESP32-S3-EYE 变为 USB 摄像头)与 examples/usb/device/usb_dual_uvc_device(双摄 UVC 设备)。
小结与进一步阅读
本文梳理了 esp-iot-solution 中 TinyUSB 的两条技术路线:需要快速交付且功能标准时选用esp_tinyusb高层封装;需要深度定制(如复合设备、自定义描述符)时选用espressif/tinyusb原生组件。在设备类层面,UAC 提供了声卡能力(重点理解同步/自适应/异步三种同步机制与基于 FIFO 的反馈端点),UVC 提供了摄像头能力(重点理解 Format/Frame 描述符与双 VC 结构)。
可继续深入阅读仓库内的相关资料:
- 原生 TinyUSB 开发指南:
tusb_config.h宏全集、描述符文件写法、PHY 与协议栈初始化、Class 回调实现 - USB Device 方案综述:仓库 USB 设备应用全景
- USB Host 方案:从设备视角切换到主机视角
- USB OTG 外设介绍 与 USB PHY 介绍:硬件底层
- 组件源码:usb_device_uac、usb_device_uvc、esp_tinyuf2
- 示例集:examples/usb/device(UAC/UVC/HID/MSC/串口桥/扩展屏/U 盘等)
- 物联网
- 嵌入式
- 驱动开发
- 硬件开发
【免费下载链接】esp-iot-solution
Espressif IoT Library. IoT Device Drivers, Documentations and Solutions.
相关推荐
esp-iot-solution USB Device UAC 组件详解:基于 TinyUSB 的 USB 音频设备驱动实践
esp iot solution USB Device UAC 组件详解:基于 TinyUSB 的 USB 音频设备驱动实践 usb_device_uac 是
物联网嵌入式驱动开发硬件开发基于 TinyUSB 的 ESP32 USB 设备开发指南:组件选型、原生移植与 UAC/UVC 音视频应用(esp-iot-solution)
基于 TinyUSB 的 ESP32 USB 设备开发指南:组件选型、原生移植与 UAC/UVC 音视频应用(esp iot solution) TinyUSB
物联网嵌入式驱动开发硬件开发基于 esp-iot-solution 的 ESP-IDF Native TinyUSB 开发指南:从工程结构到 UVC 设备实战
基于 esp iot solution 的 ESP IDF Native TinyUSB 开发指南:从工程结构到 UVC 设备实战 TinyUSB 是一套开源的
物联网嵌入式驱动开发硬件开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考