ESP32
树莓派搭建espidf 开发环境
esp-matter 开发记录
WLED 笔记
esp32s3 作为 USB HOST UAC 读取 CM108A声卡
ESP-IDF USB Host UAC 支持情况
USB Host 与 Device 模式说明
ESP32-S3 UAC设备工作原理分析
espressif/usb_host_uac 组件完整使用指南
USB UAC Host 使用指南
本文档使用 MrDoc 发布
-
+
首页
espressif/usb_host_uac 组件完整使用指南
## 概述 `espressif/usb_host_uac` 是 Espressif 官方提供的 USB Host Audio Class (UAC) 组件,可以让 ESP32-S3/ESP32-P4 作为 USB Host 读取 USB 声卡(如 CM108A)的音频数据。 **组件版本**: 1.3.3 **组件地址**: https://components.espressif.com/components/espressif/usb_host_uac --- ## 快速开始 ### 1. 添加组件依赖 在 `main/idf_component.yml` 中添加: ```yaml dependencies: idf: ">=5.1" espressif/usb_host_uac: version: "1.3.3" ``` ### 2. 配置 sdkconfig 在 `sdkconfig.defaults` 或 `sdkconfig.defaults.esp32s3` 中添加: ``` # USB Host Configuration CONFIG_USB_HOST_ENABLE=y CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE=256 CONFIG_USB_HOST_HW_BUFFER_BIG=y CONFIG_USB_HOST_HW_BUFFER_BIG_EXTRA=y ``` ### 3. 包含头文件 ```c #include "usb/usb_host.h" #include "usb/uac_host.h" ``` --- ## API 详解 ### 核心数据结构 #### uac_host_driver_config_t - UAC 驱动配置 ```c typedef struct { bool create_background_task; // 创建后台任务处理 USB 事件 size_t task_priority; // 后台任务优先级 size_t stack_size; // 后台任务栈大小 BaseType_t core_id; // 任务运行的核心 ID uac_host_driver_event_cb_t callback; // 驱动事件回调(必需) void *callback_arg; // 回调用户参数 } uac_host_driver_config_t; ``` #### uac_host_device_config_t - UAC 设备配置 ```c typedef struct { uint8_t addr; // USB 设备地址 uint8_t iface_num; // UAC 接口号 uint32_t buffer_size; // 音频缓冲区大小 uint32_t buffer_threshold; // 缓冲区阈值 uac_host_device_event_cb_t callback; // 设备事件回调 void *callback_arg; // 回调用户参数 } uac_host_device_config_t; ``` #### uac_host_stream_config_t - 音频流配置 ```c typedef struct { uint8_t channels; // 声道数 uint8_t bit_resolution; // 位深度 uint32_t sample_freq; // 采样率 uint16_t flags; // 控制标志 } uac_host_stream_config_t; ``` ### 事件类型 #### uac_host_driver_event_t - 驱动事件 ```c typedef enum { UAC_HOST_DRIVER_EVENT_RX_CONNECTED, // RX 设备(麦克风)已连接 UAC_HOST_DRIVER_EVENT_TX_CONNECTED, // TX 设备(扬声器)已连接 } uac_host_driver_event_t; ``` #### uac_host_device_event_t - 设备事件 ```c typedef enum { UAC_HOST_DEVICE_EVENT_RX_DONE, // RX 完成:接收缓冲区数据超过阈值 UAC_HOST_DEVICE_EVENT_TX_DONE, // TX 完成:发送缓冲区数据低于阈值 UAC_HOST_DEVICE_EVENT_TRANSFER_ERROR, // 传输错误 UAC_HOST_DRIVER_EVENT_DISCONNECTED, // 设备已断开 } uac_host_device_event_t; ``` --- ## 初始化流程 ### 完整初始化步骤 ```c /* 步骤 1: 创建事件队列(用于在任务间传递事件) */ QueueHandle_t event_queue = xQueueCreate(10, sizeof(s_event_queue_t)); /* 步骤 2: 创建 USB Host 任务 */ xTaskCreatePinnedToCore(usb_lib_task, "usb_events", 4096, arg, priority, NULL, core); /* 步骤 3: 创建 UAC Host 任务 */ xTaskCreatePinnedToCore(uac_lib_task, "uac_events", 4096, NULL, priority, &handle, core); /* 步骤 4: 在 usb_lib_task 中初始化 USB Host */ const usb_host_config_t host_config = { .skip_phy_setup = false, .intr_flags = ESP_INTR_FLAG_LOWMED, }; usb_host_install(&host_config); /* 步骤 5: 在 uac_lib_task 中安装 UAC Host 驱动 */ uac_host_driver_config_t uac_config = { .create_background_task = true, .task_priority = 5, .stack_size = 4096, .core_id = 0, .callback = uac_host_lib_callback, .callback_arg = NULL }; uac_host_install(&uac_config); ``` --- ## 设备连接流程 ### 当 USB 声卡连接时 ```c /* 1. 接收驱动事件回调 */ static void uac_host_lib_callback(uint8_t addr, uint8_t iface_num, const uac_host_driver_event_t event, void *arg) { if (event == UAC_HOST_DRIVER_EVENT_RX_CONNECTED) { // 麦克风设备连接了! } } /* 2. 打开 UAC 设备 */ uac_host_device_handle_t dev_handle = NULL; const uac_host_device_config_t dev_config = { .addr = addr, .iface_num = iface_num, .buffer_size = 16000, .buffer_threshold = 4000, .callback = uac_device_callback, .callback_arg = NULL, }; uac_host_device_open(&dev_config, &dev_handle); /* 3. 获取设备信息 */ uac_host_dev_info_t dev_info; uac_host_get_device_info(dev_handle, &dev_info); /* 4. 获取音频参数 */ uac_host_dev_alt_param_t alt_params; uac_host_get_device_alt_param(dev_handle, 1, &alt_params); /* 5. 配置并启动音频流 */ uac_host_stream_config_t stream_config = { .channels = alt_params.channels, .bit_resolution = alt_params.bit_resolution, .sample_freq = alt_params.sample_freq[0], }; uac_host_device_start(dev_handle, &stream_config); /* 6. 打印设备参数(可选) */ uac_host_printf_device_param(dev_handle); ``` --- ## 读取音频数据 ### 方法 1: 事件驱动方式(推荐) ```c /* 在设备事件回调中处理 */ static void uac_device_callback(uac_host_device_handle_t handle, const uac_host_device_event_t event, void *arg) { if (event == UAC_HOST_DEVICE_EVENT_RX_DONE) { // 数据准备好了! uint32_t bytes_read = 0; uac_host_device_read(handle, buffer, buffer_size, &bytes_read, 0); if (bytes_read > 0) { // 处理音频数据 process_audio_data(buffer, bytes_read); } } } ``` ### 方法 2: 轮询方式 ```c /* 在任务中循环读取 */ while (1) { uint32_t bytes_read = 0; esp_err_t ret = uac_host_device_read(dev_handle, buffer, buffer_size, &bytes_read, pdMS_TO_TICKS(10)); if (ret == ESP_OK && bytes_read > 0) { process_audio_data(buffer, bytes_read); } vTaskDelay(pdMS_TO_TICKS(10)); } ``` --- ## 音频数据处理 ### 音频数据格式 从 USB 声卡读取的数据通常是: - **格式**: PCM - **位深度**: 16-bit (int16_t) - **采样率**: 44100 Hz 或 48000 Hz - **声道**: 1 (单声道) 或 2 (立体声) - **字节序**: Little-endian ### 数据布局示例 立体声 16-bit PCM: ``` buf[0] = 左声道样本低字节 buf[1] = 左声道样本高字节 buf[2] = 右声道样本低字节 buf[3] = 右声道样本高字节 buf[4] = 左声道下一个样本低字节 ... ``` ### 示例:访问样本数据 ```c void process_audio_data(uint8_t *data, size_t len) { // 转换为 16-bit 样本数组 int16_t *samples = (int16_t *)data; int num_samples = len / 2; // 每个样本 2 字节 // 遍历样本 for (int i = 0; i < num_samples; i++) { int16_t sample = samples[i]; // 如果是立体声,左右声道交替 // sample 0, 2, 4... = 左声道 // sample 1, 3, 5... = 右声道 } // 计算音量(振幅平均值) if (num_samples > 0) { int64_t sum = 0; for (int i = 0; i < num_samples; i++) { sum += abs(samples[i]); } int32_t avg_amplitude = sum / num_samples; ESP_LOGI(TAG, "Average amplitude: %"PRId32, avg_amplitude); } } ``` --- ## 设备断开处理 ```c static void uac_device_callback(uac_host_device_handle_t handle, const uac_host_device_event_t event, void *arg) { if (event == UAC_HOST_DRIVER_EVENT_DISCONNECTED) { ESP_LOGI(TAG, "Device disconnected"); // 清理资源 if (handle == s_mic_dev_handle) { s_mic_dev_handle = NULL; } // 关闭设备 uac_host_device_close(handle); // 释放缓冲区(如果需要) if (buffer != NULL) { free(buffer); buffer = NULL; } } } ``` --- ## 控制功能 ### 设置音量 ```c // 设置音量 (0-100) uac_host_device_set_volume(dev_handle, 50); // 获取音量 uint8_t volume; uac_host_device_get_volume(dev_handle, &volume); ``` ### 设置静音 ```c // 静音 uac_host_device_set_mute(dev_handle, true); // 取消静音 uac_host_device_set_mute(dev_handle, false); // 获取静音状态 bool mute; uac_host_device_get_mute(dev_handle, &mute); ``` ### 设置音量(dB) ```c // 设置音量(单位:1/256 dB) // 256 = 1 dB // 32767 = 127.996 dB // -32767 = -127.996 dB uac_host_device_set_volume_db(dev_handle, 256); // 1 dB // 获取音量(dB) int16_t volume_db; uac_host_device_get_volume_db(dev_handle, &volume_db); ``` --- ## 流控制 ### 暂停流 ```c uac_host_device_suspend(dev_handle); ``` ### 恢复流 ```c uac_host_device_resume(dev_handle); ``` ### 停止流 ```c uac_host_device_stop(dev_handle); ``` --- ## 完整示例代码结构 ```c /* * 完整的 USB Host UAC 示例代码结构 */ #include "usb/usb_host.h" #include "usb/uac_host.h" /* 事件队列 */ static QueueHandle_t s_event_queue = NULL; /* 设备句柄 */ static uac_host_device_handle_t s_dev_handle = NULL; /* 事件结构 */ typedef struct { event_group_t event_group; union { struct { uint8_t addr; uint8_t iface_num; uac_host_driver_event_t event; void *arg; } driver_evt; struct { uac_host_device_handle_t handle; uac_host_device_event_t event; void *arg; } device_evt; }; } s_event_queue_t; /* 驱动事件回调 */ static void uac_host_lib_callback(uint8_t addr, uint8_t iface_num, const uac_host_driver_event_t event, void *arg) { // 发送事件到队列 s_event_queue_t evt = { .event_group = UAC_DRIVER_EVENT, .driver_evt.addr = addr, .driver_evt.iface_num = iface_num, .driver_evt.event = event, .driver_evt.arg = arg }; xQueueSend(s_event_queue, &evt, 0); } /* 设备事件回调 */ static void uac_device_callback(uac_host_device_handle_t handle, const uac_host_device_event_t event, void *arg) { // 发送事件到队列 s_event_queue_t evt = { .event_group = UAC_DEVICE_EVENT, .device_evt.handle = handle, .device_evt.event = event, .device_evt.arg = arg }; xQueueSend(s_event_queue, &evt, 0); } /* USB Host 任务 */ static void usb_lib_task(void *arg) { const usb_host_config_t host_config = { .skip_phy_setup = false, .intr_flags = ESP_INTR_FLAG_LOWMED, }; usb_host_install(&host_config); xTaskNotifyGive((TaskHandle_t)arg); while (true) { uint32_t event_flags; usb_host_lib_handle_events(portMAX_DELAY, &event_flags); if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { usb_host_device_free_all(); break; } } usb_host_uninstall(); vTaskDelete(NULL); } /* UAC Host 任务 */ static void uac_lib_task(void *arg) { ulTaskNotifyTake(pdTRUE, portMAX_DELAY); uac_host_driver_config_t uac_config = { .create_background_task = true, .task_priority = 5, .stack_size = 4096, .core_id = 0, .callback = uac_host_lib_callback, .callback_arg = NULL }; uac_host_install(&uac_config); s_event_queue_t evt_queue = {0}; uint8_t *buffer = malloc(32768); while (1) { if (xQueueReceive(s_event_queue, &evt_queue, portMAX_DELAY)) { if (UAC_DRIVER_EVENT == evt_queue.event_group) { // 处理驱动事件(设备连接等) } else if (UAC_DEVICE_EVENT == evt_queue.event_group) { // 处理设备事件(数据就绪、断开等) } } } } void app_main(void) { s_event_queue = xQueueCreate(10, sizeof(s_event_queue_t)); static TaskHandle_t uac_task_handle = NULL; xTaskCreatePinnedToCore(uac_lib_task, "uac_events", 4096, NULL, 2, &uac_task_handle, 0); xTaskCreatePinnedToCore(usb_lib_task, "usb_events", 4096, (void *)uac_task_handle, 5, NULL, 0); } ``` --- ## 硬件连接 ### ESP32-S3 USB Host 接口 | ESP32-S3 引脚 | 功能 | USB 声卡连接 | | ------------- | ------ | ------------ | | GPIO19 | USB_D- | USB_D- (D-) | | GPIO20 | USB_D+ | USB_D+ (D+) | | GND | GND | GND | | 5V | VBUS | 5V 供电 | ### 注意事项 1. **供电**: USB 声卡(如 CM108A)需要 5V 供电 2. **模式选择**: 某些开发板需要跳帽选择 Host/Device 模式 3. **USB OTG**: 可能需要 USB OTG 适配器 4. **电缆质量**: 使用质量好的 USB 电缆 --- ## 故障排除 ### 问题 1: 编译错误 - 找不到头文件 **解决**: 确认 `idf_component.yml` 中添加了组件依赖,运行 `idf.py reconfigure` ### 问题 2: 设备不识别 **解决**: - 检查 USB 电缆连接 - 确认 ESP32 在 Host 模式 - 检查 5V 供电 - 查看串口输出的错误信息 ### 问题 3: 读取不到音频数据 **解决**: - 确认设备已启动流: `uac_host_device_start()` - 检查 `UAC_HOST_DEVICE_EVENT_RX_DONE` 事件 - 确认缓冲区大小合适 ### 问题 4: 传输错误 **解决**: - 降低采样率 - 减小缓冲区大小 - 检查 USB 电缆质量 --- ## API 速查表 ### 初始化/清理 | 函数 | 说明 | | ------------------------- | ------------------ | | `uac_host_install()` | 安装 UAC Host 驱动 | | `uac_host_uninstall()` | 卸载 UAC Host 驱动 | | `uac_host_device_open()` | 打开 UAC 设备 | | `uac_host_device_close()` | 关闭 UAC 设备 | ### 设备信息 | 函数 | 说明 | | --------------------------------- | ------------ | | `uac_host_get_device_info()` | 获取设备信息 | | `uac_host_get_device_alt_param()` | 获取设备参数 | | `uac_host_printf_device_param()` | 打印设备参数 | ### 流控制 | 函数 | 说明 | | --------------------------- | ---------- | | `uac_host_device_start()` | 启动音频流 | | `uac_host_device_suspend()` | 暂停音频流 | | `uac_host_device_resume()` | 恢复音频流 | | `uac_host_device_stop()` | 停止音频流 | ### 数据读写 | 函数 | 说明 | | ------------------------- | ------------ | | `uac_host_device_read()` | 读取音频数据 | | `uac_host_device_write()` | 写入音频数据 | ### 控制功能 | 函数 | 说明 | | --------------------------------- | ---------------- | | `uac_host_device_set_mute()` | 设置静音 | | `uac_host_device_get_mute()` | 获取静音状态 | | `uac_host_device_set_volume()` | 设置音量 (0-100) | | `uac_host_device_get_volume()` | 获取音量 (0-100) | | `uac_host_device_set_volume_db()` | 设置音量 (dB) | | `uac_host_device_get_volume_db()` | 获取音量 (dB) | ### 事件处理 | 函数 | 说明 | | -------------------------- | -------------------------------- | | `uac_host_handle_events()` | 手动处理事件(不使用后台任务时) | --- ## 相关文档 - [ESP-IDF USB Host 文档](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/usb_host.html) - [USB Audio Class 规范](https://www.usb.org/documents) - [组件主页](https://components.espressif.com/components/espressif/usb_host_uac) - [项目示例代码](
qingkai
2026年3月31日 17:36
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码