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 发布
-
+
首页
ESP32-S3 UAC设备工作原理分析
## 1. 整体架构 这个项目使用 **TinyUSB栈** + **ESP-IDF** 实现USB Audio Class 2.0设备。主要分为三层: ``` ┌─────────────────────────────────────┐ │ 应用层 (usb_uac_main.c) │ │ - 回调函数定义 │ │ - 编解码器/I2S初始化 │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ UAC驱动层 (usb_device_uac.c) │ │ - USB协议处理 │ │ - 任务管理 (spk/mic任务) │ │ - 数据缓冲管理 │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ TinyUSB栈 + 硬件层 │ │ - USB PHY │ │ - USB端点传输 │ └─────────────────────────────────────┘ ``` ## 2. 音频数据流向(USB → ESP32 → 扬声器) 当USB主机发送音频数据时: ### 步骤1: USB接收回调 **文件**: `managed_components/espressif__usb_device_uac/usb_device_uac.c:369-407` - `tud_audio_rx_done_post_read_cb()` 被调用 - 数据从USB FIFO读取到 `s_uac_device->spk_buf` - 通知 `usb_spk_task` 任务 ### 步骤2: 扬声器任务 **文件**: `managed_components/espressif__usb_device_uac/usb_device_uac.c:435-453` - 等待通知 - 调用用户回调 `output_cb()` ### 步骤3: 应用层回调 **文件**: `main/usb_uac_main.c:22-27` ```c uac_device_output_cb(uint8_t *buf, size_t len, void *arg) ``` - 通过 `bsp_extra_i2s_write()` 发送到I2S编解码器 ## 3. 如何读取USB音频数据 如果你想在代码中读取/处理从USB主机接收到的音频数据,有两种方法: ### 方法一:修改 `uac_device_output_cb` 回调(推荐) 在 `main/usb_uac_main.c:22-27` 中: ```c static esp_err_t uac_device_output_cb(uint8_t *buf, size_t len, void *arg) { // 在这里处理音频数据! // buf: 指向音频数据的指针 // len: 数据长度(字节) // 示例:打印前几个样本 int16_t *samples = (int16_t *)buf; int num_samples = len / 2; // 16-bit = 2字节 for (int i = 0; i < num_samples && i < 10; i++) { printf("Sample %d: %d\n", i, samples[i]); } // 继续发送到I2S播放 size_t bytes_written = 0; bsp_extra_i2s_write(buf, len, &bytes_written, 0); return ESP_OK; } ``` ### 方法二:在UAC驱动层读取 如果你需要更底层的访问,可以在 `managed_components/espressif__usb_device_uac/usb_device_uac.c:404-405` 处处理: ```c s_uac_device->spk_data_size = tud_audio_read(s_uac_device->spk_buf, bytes_require); // 这里 s_uac_device->spk_buf 包含了音频数据 xTaskNotifyGive(s_uac_device->spk_task_handle); ``` ## 4. 音频数据格式 根据配置(`sdkconfig.defaults.esp32s3`): - **采样率**: 48kHz - **位深度**: 16-bit (PCM) - **通道数**: 2 (立体声) - **数据格式**: little-endian, int16_t 数据布局示例: ``` buf[0] = 左声道样本低字节 buf[1] = 左声道样本高字节 buf[2] = 右声道样本低字节 buf[3] = 右声道样本高字节 buf[4] = 左声道下一个样本低字节 ... ``` ## 5. 关键数据结构 在 `managed_components/espressif__usb_device_uac/include/usb_device_uac.h:17-37` 中定义的回调类型: ```c typedef esp_err_t (*uac_output_cb_t)(uint8_t *buf, size_t len, void *cb_ctx); typedef esp_err_t (*uac_input_cb_t)(uint8_t *buf, size_t len, size_t *bytes_read, void *cb_ctx); ``` - `output_cb`: **USB主机 → ESP32** 的音频数据(扬声器输出) - `input_cb`: **ESP32 → USB主机** 的音频数据(麦克风输入) ## 6. 初始化流程 **文件**: `main/usb_uac_main.c:49-63` ```c void app_main(void) { // 1. 初始化编解码器 bsp_extra_codec_init(); bsp_extra_codec_set_fs(CONFIG_UAC_SAMPLE_RATE, 16, CONFIG_UAC_SPEAKER_CHANNEL_NUM); // 2. 配置UAC设备回调 uac_device_config_t config = { .output_cb = uac_device_output_cb, .input_cb = uac_device_input_cb, .set_mute_cb = uac_device_set_mute_cb, .set_volume_cb = uac_device_set_volume_cb, .cb_ctx = NULL, }; // 3. 初始化UAC设备 uac_device_init(&config); } ``` ## 7. 任务结构 UAC驱动创建以下任务: 1. **TinyUSB任务** (`tusb_device_task`): 处理USB协议栈 2. **扬声器任务** (`usb_spk_task`): 处理音频输出 3. **麦克风任务** (`usb_mic_task`): 处理音频输入 ## 总结 如果你想读取和处理USB音频数据,**只需修改 `uac_device_output_cb` 函数**,在调用 `bsp_extra_i2s_write()` 之前或之后添加你的处理代码即可。这个回调会在每次从USB接收到音频数据时被调用。
qingkai
2026年3月31日 17:35
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码