- 嵌入式
- 驱动开发
- 通信
- 物联网
【免费下载链接】tinyusb
An open source cross-platform USB stack for embedded system
导读
audio_host 示例是 TinyUSB 主机栈(Host Stack)中针对 USB Audio 类(UAC 1.0 / UAC 2.0)设备的高层驱动 TUH_AUDIO 的完整参考实现。它演示了如何用一套类似 WASAPI/ALSA 的帧级 FIFO API,从 USB 麦克风采集 S16_LE 音频并回放到扬声器,而应用层完全不接触 USB 接口、alternate setting 或端点地址——只按流索引(stream index)选择{格式, 采样率, 声道数}配置元组。读完本文,你将掌握 TUH_AUDIO 的枚举/挂载流程、流配置与异步启停机制、基于 FIFO 的读写模型、音量/静音控制 API,以及示例中三阶段(仅麦克风 / 仅扬声器 / 回声)切换的完整实现。
示例目标与核心特性
示例的运行效果是:将 UAC 1.0 或 UAC 2.0 的 USB 音频设备(麦克风、耳机、USB 声卡)接入主机端口后,板卡自动完成枚举、配置、启流,并在三个 5 秒阶段间循环:
- 仅麦克风(mic-only):采集运行,数据直接丢弃;
- 仅扬声器(spk-only):播放 1 kHz 正弦测试音;
- 回声(echo):采集数据回环到播放端,形成实时回声。
从 README 归纳的核心特性包括:
- 枚举并挂载 USB Audio Class 1.0 与 2.0 设备;
- 发现设备的逻辑流(采集/播放)及其支持的配置(仅离散元组 discrete tuples);
- 报告每条流的静音/音量能力与缓存的音量范围;
- 配置并启动 S16_LE 采集流(44.1 kHz 优先、48 kHz 兜底;立体声优先、单声道可接受);
- 将采集音频回放到同采样率的 S16_LE 播放流(优先同声道数,否则做单/双声道转换);
- 帧级 FIFO API:主循环在采集 FIFO 半满时读取、在播放 FIFO 半空时填充,传输回调不参与 FIFO 服务;
- 用
tuh_audio_start()/tuh_audio_stop()在三阶段间切换,异步结果由tuh_audio_event_cb()打印,失败的流在 100 ms 后自动重启。
支持的设备与驱动边界
设备兼容范围
示例支持以下 UAC 设备:
- UAC1 设备:Type I Format 描述符必须列出离散采样频率(
bSamFreqType > 0); - UAC2 设备:使用直接连接的 Clock Source;
- 典型设备形态:USB 麦克风、USB 耳机(单声道麦克风 + 扬声器)、USB 音频接口。
回环功能要求设备存在与采集采样率匹配的 S16_LE 播放流;没有播放流的设备退化为仅采集。采样率与声道偏好由 src/audio_app.c 中的宏控制:
#define AUDIO_MAX_CHANNELS 2 #define SAMPLE_RATES {44100, 48000} #define FEATURE_UNIT_VOLUME_DB (-20 * 256)默认即 44.1 kHz 立体声。非 PCM 格式会被驱动直接拒绝。
已知限制与取舍(驱动层)
从 README 的 "Limitations and trade-offs" 一节,需要特别留意以下行为边界:
- 反馈端点(feedback):显式反馈端点支持 10.14 与 16.16 两种反馈值格式;隐式反馈 IN 端点被当作普通音频数据端点处理,不用于节拍播放节奏。
- UAC1 非离散频率:
bSamFreqType == 0的 Type I Format 描述符不受支持,驱动要求离散采样频率列表。 - UAC2 时钟拓扑:仅支持直接 Clock Source;Clock Selector、Clock Multiplier、采样率转换器(SRC)、Clock Validity 与 Valid Alternate Settings 控件均不处理。
- 频率范围展开上限:UAC2 采样频率 RANGE 响应最多展开为
CFG_TUH_AUDIO_MAX_SAM_FREQ个离散配置;只读 Clock Source 只暴露当前频率。 - 音量控件发现:master 静音与音量控制在挂载回调前完成探测。仅逻辑声道有音量的 Feature Unit 也被支持:范围从第一个受控声道读取;当不存在可写的 master 控件时,流音量 SET 会写入所有逻辑声道。类型化 API 假设所有逻辑声道共用同一音量范围,需要每声道独立范围的应用应改用底层
tuh_audio_control_xfer()。UAC2 音量发现支持常见的单子区间 RANGE 响应。 MaxPacketsOnly端点属性不受支持:OUT 传输不按wMaxPacketSize补齐填充,IN 传输中的填充也不会从上报的音频数据中剔除。
构建与烧录
CMake 方式(推荐)
cd examples/host/audio_host mkdir -p build && cd build cmake -DBOARD=<your_board> -G Ninja .. cmake --build .<your_board>替换为目标板卡名(如raspberry_pi_pico、stm32f407disco等,参考 hw/bsp 下的 board 定义)。
Make 方式
cd examples/host/audio_host make BOARD=<your_board> all烧录
# CMake 方式:先列出板卡对应的 flash 目标,再选择其一 ninja -t targets ninja audio_host-jlink # 例如支持 J-Link 的板卡 # Make 方式 make BOARD=<your_board> flash使用流程与运行行为
- 构建并烧录示例到目标板;
- 将 USB 音频设备(UAC 1.0 或 2.0)接入 USB 主机端口;
- 打开串口终端观察输出;
- 示例将自动执行:
- 挂载时打印每条流的静音/音量能力、缓存的音量范围及支持的配置;
- 按首选采样率(44.1 kHz 优先、48 kHz 兜底;立体声优先、单声道可接受)查找 S16_LE 采集配置并完成配置;
- 在同采样率的 S16_LE 播放配置上回放采集音频(优先同声道数直通,否则转换);
- 读取并解除麦克风/扬声器 Feature Unit 的静音,把流音量设置为约 -6 dB;仅声道级 Feature Unit 逐逻辑声道更新;
- 由
audio_app_task()在 FIFO 半满/半空水位线处服务两条 FIFO;无采集回环时播放 1 kHz 正弦测试音; - 通过
tuh_audio_start()/tuh_audio_stop()循环三个阶段(各 5 秒),异步结果由tuh_audio_event_cb()打印,失败流 100 ms 后自动重启。
串口输出示例
README 给出的典型输出如下:
TinyUSB Host USB Audio Example Connect a USB Audio Device (UAC 1.0 or 2.0) to test Audio device mounted: idx=0 addr=1 capture stream 1, configurations: 2 master mute supported volume range: min=-23040 max=1536 res=256 (1/256 dB) [0] format=1 rate=44100 channels=2 [1] format=1 rate=48000 channels=2 playback stream 0, configurations: 2 master mute supported volume range: min=-23040 max=1536 res=256 (1/256 dB) [0] format=1 rate=44100 channels=2 [1] format=1 rate=48000 channels=2 Configuring 44100 S16_LE capture (2 channels) Microphone configured Microphone master mute: off Microphone master volume: 0 (1/256 dB) Microphone volume set: -1536 (1/256 dB) Configuring 44100 S16_LE playback (2 channels) Speaker configured Speaker master mute: off Speaker master volume: 0 (1/256 dB) Speaker volume set: -1536 (1/256 dB)注意:volume range: min=-23040 max=1536 res=256中的音量单位是1/256 dB,因此-1536对应 -6 dB,与FEATURE_UNIT_VOLUME_DB (-20 * 256)之外的实际设置目标一致(示例设置为 -6 dB 级别,源码中FEATURE_UNIT_VOLUME_DB定义于 audio_app.c,会先被夹取到缓存音量范围内)。
配置项详解
在 src/tusb_config.h 中可修改以下宏。以示例默认值为基准(部分默认值定义于 audio_host.h):
| 宏 | 示例默认值 | 说明 |
|---|---|---|
CFG_TUH_AUDIO_MAX | 1 | 支持的最大音频设备数量,对应驱动内部_audioh_itf[CFG_TUH_AUDIO_MAX]实例数组大小 |
CFG_TUH_AUDIO_PROTOCOLS | TUH_AUDIO_PROTOCOL_UAC1 \| TUH_AUDIO_PROTOCOL_UAC2 | 编译进驱动的 UAC 协议位掩码,示例同时启用 UAC1 与 UAC2 |
CFG_TUH_AUDIO_MAX_SAM_FREQ | 5 | 每个 alternate setting(UAC1)或 UAC2 Clock Source 保留的离散频率最大数量 |
CFG_TUH_AUDIO_MAX_AS | 4 | 每条逻辑流支持的、非零带宽的 Audio Streaming alternate setting 最大数量 |
CFG_TUH_AUDIO_EPIN_BUFSIZE | 256 | 驱动单次提交的采集(IN)等时传输最大字节数;需要更大每轮询间隔包长的配置会被拒绝。256 可覆盖 2 声道 48 kHz S16_LE(192 B)及常见端点填充(208 B) |
CFG_TUH_AUDIO_EPOUT_BUFSIZE | 256 | 驱动单次提交的播放(OUT)等时传输最大字节数 |
CFG_TUH_AUDIO_STREAM_BUFSIZE | 1024 | 每条流 FIFO 深度(字节),即 4 个 256 B 包;采集 FIFO 满时覆盖最旧帧 |
示例配置中还启用了CFG_TUH_ENABLED 1、CFG_TUH_AUDIO 1,并关闭了 HUB/CDC/HID/MSC/VENDOR 等其他主机驱动;CFG_TUH_DEVICE_MAX与 HUB 数量联动(3 * CFG_TUH_HUB + 1)。CFG_TUH_AUDIO_EPIN_BUFSIZE/CFG_TUH_AUDIO_EPOUT_BUFSIZE在示例中均设为 256,详见 tusb_config.h。
驱动原理:TUH_AUDIO 的挂载、配置与数据通路
挂载期:拓扑发现是异步的
从 audio_host.c 顶部架构注释可知,一个audioh_interface_t代表一个 Audio Control(AC)接口,且每个方向最多拥有一个逻辑流。挂载链路为:
USB 枚举 audioh_open() +-- 校验并保留 AC 描述符区间 +-- 对每个连续 AS 接口执行 audioh_parse_as() | +-- 解析协议相关的 AS 与 Format 描述符 | +-- 关联数据端点与可选反馈端点 | `-- 保存 UAC1 频率列表或 UAC2 Clock Source 引用 +-- audioh_link_feature_units() `-- tuh_audio_descriptor_cb() audioh_set_config() +-- UAC2: audioh_mount_clock_next() 读取 RANGE/CUR -> 重建公共配置 `-- audioh_mount_feature_unit_next() `-- 音量 RANGE 完成 -> tuh_audio_mount_cb()关键点:设备只有在这些异步探测全部完成后才被报告为 mounted。UAC1 的采样率来自 Format Type 描述符,UAC2 则需先向 Clock Source 发起 RANGE 控制请求查询。这也解释了示例为何把挂载后的初始化逻辑放在tuh_audio_mount_cb()中延迟 100 ms 执行的tuh_audio_mount_async()(见 audio_app.c)——此时音量范围等缓存已就绪。
配置与启停:本地配置、异步激活
驱动把应用可见的配置看成扁平的{format, sample_rate, channels}元组列表;内部通过audioh_stream_resolve_config()将公共元组解析回 AS alternate setting 与 rate source 索引。tuh_audio_configure()(audio_host.c)完成:
- 校验设备已挂载、流存在、配置可解析;
- 关闭上一配置的端点(要求无传输在途);
- 初始化帧大小、FIFO 与播放调度器状态;采集 FIFO 深度会向下取整为帧大小的整数倍,保证覆盖(overwrite)模式帧安全;
- 打开数据端点(播放流还会打开显式反馈端点)。
tuh_audio_start()的协议差异体现在激活顺序上(audio_host.c 及audioh_stream_start_active()):
- UAC1:先
SET_INTERFACE(非零 alt),再对端点执行采样率SET_CUR(因为 UAC1 的频率控制目标就是端点); - UAC2:先对可写 Clock Source 执行采样率
CUR,再SET_INTERFACE(非零 alt)。
tuh_audio_start()/tuh_audio_stop()返回true只表示首个控制请求已提交,整条链路的完成通过tuh_audio_event_cb()的TUH_AUDIO_EVENT_START_COMPLETE/TUH_AUDIO_EVENT_STOP_COMPLETE上报;返回false时不会产生事件。tuh_audio_stop()通过SET_INTERFACE(alt 0)停流并停止本地传输再提交。
数据通路:一条在途传输 + 独立 FIFO 服务
流启动后,每次端点完成回调都准备并提交后继传输(audioh_xfer_cb()链路):
- 采集(IN):把完整音频帧拷入带覆盖语义的 FIFO,回调
tuh_audio_capture_cb(),再提交下一个 IN 传输; - 播放(OUT):回调
tuh_audio_playback_cb(),计算下一个分数包大小(Q16.16 帧率调度,target_frames_q16跨反馈更新积分),从 FIFO 读完整包或发送静音,提交下一个 OUT 传输; - 显式反馈:校验并暂存 Q10.14 或 Q16.16 反馈值,提交下一个反馈传输。
驱动每流保持一条等时传输在途,完成后重新提交,因此传输节奏跟随端点的bInterval。tuh_audio_read()/tuh_audio_write()只访问流 FIFO,无需在传输回调中执行;示例正是让主循环中的audio_app_task()在 FIFO 半满/半空水位线处独立读写,传输回调仅统计诊断计数(mic_cb_count/spk_cb_count,每秒经 led_blinking_task 打印清零)。播放写满一包后若 FIFO 没有完整轮询间隔数据,驱动会发送静音且不消耗不完整的部分数据。等时传输要求主机持续轮询tuh_task()(见 main.c 主循环),采集 FIFO 能吸收调度间隙并在满时覆盖最旧帧。
帧级 API 与格式
tuh_audio_stream_config_t(定义于 audio_host.h)即一个离散配置元组。示例中的选择逻辑(audio_app.c)按SAMPLE_RATES顺序、声道数从AUDIO_MAX_CHANNELS向下搜索 S16_LE 采集配置;播放配置优先取与采集流相同的声道数(直连回声),否则取相反声道数做转换。采集与播放并发运行时,同一 AC 实例内两条流必须使用相同采样率。
示例内置的mono_to_stereo()/stereo_to_mono()就地转换(audio_app.c)展示了如何在不额外分配内存的情况下处理声道不匹配。正弦测试音由 spk_fill_sine 用 64 项查找表(峰值 4096,即约 -18 dBFS)按实际播放声道数逐帧填充,相位步进按(SINE_TONE_HZ << 32) / sample_rate计算。
回调与错误恢复模型
TUH_AUDIO 提供以下应用回调(均为 weak 弱实现,见 audio_host.h):
| 回调 | 触发时机 | 示例中的用途 |
|---|---|---|
tuh_audio_descriptor_cb() | 枚举期 AC 描述符校验通过后、挂载前 | 需要原始实体控制的应用须在回调返回前拷贝实体 ID/描述符字段,挂载后用tuh_audio_control_xfer()使用 |
tuh_audio_mount_cb() | 全部探测完成后 | 延迟 100 ms 后执行tuh_audio_mount_async()完成配置与启流 |
tuh_audio_umount_cb() | 设备卸载 | 清空延迟队列并复位流索引与使能标志 |
tuh_audio_capture_cb() | IN 传输完成 | 仅统计计数(FIFO 服务在主循环) |
tuh_audio_playback_cb() | OUT 传输完成、下一包准备前 | 仅统计计数 |
tuh_audio_event_cb() | 异步 start/stop 完成及不可恢复传输失败 | 打印结果、计数失败、100 ms 后自动重启失败流 |
错误恢复路径:TUH_AUDIO_EVENT_XFER_FAILED表示 HCD 无法提交传输或传输以失败结束,不是单个丢失等时包的提示;驱动在上报失败前已停止该流。示例的tuh_audio_event_cb()(audio_app.c)把失败流的使能标志复位,并通过自研的 4 槽位延迟调用队列app_defer_ms_async()在 100 ms 后调用audio_app_restart_stream()重新tuh_audio_start()(驱动保留流配置,因此 start 即恢复)。整个队列无动态内存分配,且挂载/卸载/阶段切换时会清理过期回调,避免重启与阶段停止冲突。
控制请求 API 补充
除上述帧级 API 外,audio_host.h 还提供:
- 底层原始实体控制:
tuh_audio_control_xfer()/tuh_audio_control_xfer_sync()(按实体 ID 发起类特定请求); - 类型化静音/音量:
tuh_audio_mute_get/set()、tuh_audio_volume_get/set()及其_sync同步变体; - 音量语义:
TUH_AUDIO_VOLUME_SILENCE(INT16_MIN)表示静音;SET 接受静音值或缓存范围内的值,有限值按从范围最小值起的分辨率步进取整;TUH_AUDIO_CHANNEL_MASTER(0)选 master 声道; - 同步控制请求会阻塞至传输完成,仅在流停止时使用,否则会打乱流的等时传输并产生可闻的音频伪影。
小结
audio_host 是 TinyUSB 主机端音频能力的最小完整闭环:从 UAC1/UAC2 描述符解析、Clock Source/Feature Unit 异步探测,到{format, rate, channels}元组级配置、等时传输调度与帧级 FIFO 读写,再到静音/音量控制与失败自动恢复。若需将此示例移植到自己的应用,只需替换 audio_app.c 中的配置选择与数据消费逻辑(例如改为写入 WAV 文件、经网络转发或做音频处理),并依据目标设备在 tusb_config.h 中调整CFG_TUH_AUDIO_*系列参数即可。
- 嵌入式
- 驱动开发
- 通信
- 物联网
【免费下载链接】tinyusb
An open source cross-platform USB stack for embedded system
相关推荐
summarize 项目 OpenAI 模型接入指南:模型 ID、Fast 服务层级、Thinking 与配置详解
summarize 项目 OpenAI 模型接入指南:模型 ID、Fast 服务层级、Thinking 与配置详解 本文是 summarize 项目中 Open
嵌入式驱动开发通信物联网TinyUSB USB音频设备延迟优化:UAC2同步机制实现
TinyUSB USB音频设备延迟优化:UAC2同步机制实现 1. USB音频开发痛点与解决方案 嵌入式开发者在实现USB音频设备时常面临三大挑战: 音频卡顿
嵌入式驱动开发通信物联网esp-iot-solution USB Device UAC 组件详解:基于 TinyUSB 的 USB 音频设备驱动实践
esp iot solution USB Device UAC 组件详解:基于 TinyUSB 的 USB 音频设备驱动实践 usb_device_uac 是
物联网嵌入式驱动开发硬件开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考