# LE Audio 能力检查与 LC3 编解码验证说明
<!--zk+ 本文说明标准 LE Audio 的实际实现边界、测试指令和后续接入条件。-->
本文针对 ATS3089P 开发板,以及“手机或 Auracast 耳机能够发现并接收音频”的标准 LE Audio 目标。工程增加了控制器能力查询、普通 BLE 扫描和 GATT 连接检查,并集成真正的 LC3 CPU 编解码算法用于离线验证。
**当前版本还不能向手机或 Auracast 耳机发送标准 LE Audio 音频。** 仓库中的 Actions 蓝牙主机协议栈缺少标准 BAP、PACS、ASCS、BIG/BIS 和音频 ISO 数据通路。标准广播、单播启动命令会明确返回不支持,避免把普通 BLE 广播、厂商对讲协议或 LC3 文件编码成功误认为标准音频连接成功。
## 1. 本次可以验证哪些功能
| 功能 | 当前结果 | 验证方式 |
| --- | --- | --- |
| 查询控制器版本、支持命令和 LE 特性位 | 已增加查询入口,实际能力以板上日志为准 | `app leaudio probe` |
| 查询最近一次探测结果 | 已增加 | `app leaudio status` |
| 手机搜索开发板普通 BLE 广播 | 使用工程现有广播和名称 | `app leaudio ble on`,手机使用 BLE 调试工具扫描 |
| 手机与开发板普通 GATT 连接 | 使用工程现有 BLE 服务 | BLE 调试工具连接,查看 `ble_connections` |
| 标准 LE Audio 广播到 Auracast 耳机 | 当前 SDK 缺少实现,明确返回 `-ENOTSUP` | `app leaudio broadcast start` |
| 标准 LE Audio 单播音频连接 | 当前 SDK 缺少实现,明确返回 `-ENOTSUP` | `app leaudio unicast start` |
| LC3 压缩编码和解码 | 使用 Google liblc3 在 CPU 上进行离线验证 | 见第 5 节 |
普通 BLE 连接成功只证明扫描、广播和 GATT 通路正常;LC3 编解码成功只证明音频算法可用。完整 LE Audio 还需要标准音频服务、参数协商、ISO 传输、时序和对端互通。
### 1.1 本次验证固件
在工程 `` 目录中,本次最终打包使用以下文件名:
```text
application/bt_watch/outdir/ats3089p_dev_watch/_firmware/ats3089p_dev_watch_261014_lc3_dual_files_v2.fw
application/bt_watch/outdir/ats3089p_dev_watch/_firmware/ats3089p_dev_watch_261014_lc3_dual_files_v2_debug.fw
```
第一项是普通固件,第二项是调试固件。本次 `v2` 增加 LC3 自检同时保存压缩文件和解码 PCM 的功能,**不表示标准 LE Audio 广播/单播已经实现或已经在板上互通**。刷入后,`app lc3 status` 显示 `version=261014-v2`;LE Audio 能力探测模块继续在 `app leaudio status` 中显示 `version=261014-v1`。
本说明记录软件实现、软件回归和实板操作方法;控制器实际特性、实板编码速度及手机/耳机互通状态,需要按第 7 节在板上验证。
## 2. 标准广播与单播的区别
### 2.1 标准广播音频:广播源 → 一个或多个接收器
标准广播音频通过扩展广播让接收器发现广播源,通过周期广播提供 BASE 等音频参数,通过 BIG/BIS 传送 LC3 音频。耳机的“加入广播”是同步周期广播和 BIG/BIS;广播源与每个耳机之间不必建立普通的一对一 GATT 音频连接。
Auracast 接收器或支持广播辅助的手机需要实际支持这些功能。手机 BLE 调试工具能看到一个设备名称,并不代表手机系统或耳机已经识别到一个 Auracast 音频广播。
```text
PCM 音源 → LC3 编码 → ISO 音频数据 → BIG/BIS → Auracast 接收器
↑
扩展广播:广播标识和发现信息
周期广播:BASE、编码参数、音频分组等
```
### 2.2 标准单播音频:手机 ↔ 开发板
单播通常先建立 BLE ACL/GATT 连接,通过 PACS 公布编码能力、通过 ASCS 管理音频端点,再按 BAP 流程配置 LC3 和 QoS,建立 CIG/CIS 音频流。需要明确开发板是单播客户端还是服务器、接收还是发送音频,以及手机支持的角色。
```text
BLE ACL/GATT 连接
↓
PACS 能力发现 → ASCS 端点配置 → LC3/QoS 协商
↓
CIG/CIS 建立 → ISO 数据通路 → LC3 解码/编码 → 扬声器/麦克风
```
经典蓝牙 A2DP 播放音乐、HFP 通话与 LE Audio 使用不同协议。现有经典蓝牙功能正常,不能据此判断标准 LE Audio 已经支持。
## 3. 串口指令
以下命令在原来的普通调试串口终端中输入,不需要 USB CDC。等待开发板启动完成、蓝牙初始化完成后再执行。
### 3.1 查询真实控制器能力
```text
app leaudio probe
app leaudio status
```
`probe` 把只读 HCI 查询提交到独立线程,读取:
- 本地控制器版本信息。
- 64 字节支持命令位图。
- 8 字节 LE 支持特性位图。
`status` 不会重新发起查询,显示最近一次结果及当前普通 BLE 状态。刚提交 `probe` 时可能看到 `probe_busy=1`;探测完成后查看 `valid=1 error=0`。蓝牙尚未初始化时会返回暂不可用,请在蓝牙初始化日志出现后重试。
示例字段如下,具体的 `0/1` 和版本值由实际控制器返回,不应照抄为当前板子的能力:
```text
[leaudio] version=261014-v1 probe_busy=0 valid=1 error=0 ...
[leaudio] hci_version=... revision=... manufacturer=... subversion=...
[leaudio] controller_le_features=... (byte0 first)
[leaudio] ext_adv=... per_adv=... cis_central=... cis_peripheral=... bis_tx=... bis_rx=... iso_host=...
[leaudio] supported_commands[0..7]=...
...
[leaudio] ble_adv_enabled=... ble_connections=... name=... transport=GATT ...
```
特性位从 `0` 开始编号,按控制器返回的 LE feature 位图解码:
| 位号 | 状态字段 | 含义 | 对标准音频的作用 |
| --- | --- | --- | --- |
| 12 | `ext_adv` | LE 扩展广播 | 标准广播音频发现所需的基础能力 |
| 13 | `per_adv` | LE 周期广播 | 广播音频参数与同步所需的基础能力 |
| 28 | `cis_central` | CIS Central;SDK 旧名称为 CIS Master | 控制器作为 CIS 中心设备的能力 |
| 29 | `cis_peripheral` | CIS Peripheral;SDK 旧名称为 CIS Slave | 控制器作为 CIS 外围设备的能力 |
| 30 | `bis_tx` | Isochronous Broadcaster | BIG/BIS 广播发送能力 |
| 31 | `bis_rx` | Synchronized Receiver | BIG/BIS 广播同步接收能力 |
| 32 | `iso_host` | Isochronous Channels Host Support | 主机配置的 ISO 支持声明 |
**位 32 不能单独用于否定控制器硬件的 CIS/BIS 能力。** 当前主机没有启用完整 ISO 音频实现时,它可以为 `0`。同时,位 12、13 为 `1` 也不代表位 30、31 必然为 `1`,不能用“支持扩展广播”代替“支持标准广播音频”。
`supported_commands` 用于向芯片厂家核对 HCI 命令实现范围。仅在头文件中找到一个命令编号,也不能证明固件会执行该命令。
第一行中的 `host_bap=0`、`host_big_bis=0`、`host_unicast=0`、`standard_broadcast=0`、`standard_unicast=0` 是当前主机实现状态,不是控制器特性位。`lc3_cpu=1` 表示本固件已启用独立 LC3 CPU 模块,也不代表标准无线音频已经可用。
### 3.2 普通 BLE 搜索及连接检查
```text
app leaudio ble on
app leaudio ble status
```
手机打开 BLE 调试工具,例如 nRF Connect,扫描开发板。广播名称沿用当前工程设置,命令日志会输出实际 `name`。选中设备建立 GATT 连接,再执行状态命令,观察 `ble_connections` 是否增加。
这项检查沿用已有 BLE 广播及服务,没有伪造 PACS、ASCS 或 Auracast UUID。已有 BLE 录音控制、Find my 等服务的指令格式仍按各自说明使用。
### 3.3 标准广播及单播启动检查
```text
app leaudio broadcast start
app leaudio unicast start
```
当前版本分别提示缺少标准广播或单播主机实现,返回 `-ENOTSUP`。命令不会发送一个主机无法处理后续事件的 Create BIG/CIG 请求,也不会打印“广播成功”“音频连接成功”。
在取得完整 SDK 并补齐标准数据通路前,重复发送命令、修改蓝牙设备名称或仅开启一个 Kconfig 开关不能解决此项限制。
## 4. 当前 SDK 的实际检查结果
工程真正使用的是 `framework/bluetooth/bt_stack/include/acts_bluetooth/` 下的 Actions 接口。`zephyr/include/bluetooth/` 中另一套较新的接口定义不等于当前工程已经具备对应实现,不能混用两套 ISO 结构体和函数。
检查结果如下:
- Actions `iso.h` 只有旧的 CIS 声明,没有标准 BIG/BIS 建立接口。
- 当前 `libbt_stack.a` 未提供 `bt_iso`、BIG、BAP、PACS 或 ASCS 的完整实现。
- `hci_core.c` 中有 CIS 事件处理入口,缺少本次所需的 BIG 建立、终止和同步事件处理流程。
- HCI 头文件中虽然有 Create BIG 等编号及结构定义,但没有因此自动具备广播音频能力。
- SDK 的 `bt_manager_ips.c` 使用厂商对讲协议,可借助扩展/周期广播传送编码数据;它不是标准 BAP/BIS/Auracast。
- `NAV_TYPE` 与 `audioble.dsp` 在 SDK 对讲例程中与“LC3”字段一起使用,但源码未开放其标准帧格式。本次 LC3 验证直接使用 liblc3,不用 NAV 成功来代替标准 LC3 验证。
目前生产工程也没有开启完整 `CONFIG_BT_AUDIO`。仅将此配置或 ISO 相关配置改成 `y`,不会补齐缺失的协议代码,不能直接得到可与标准手机或耳机互通的功能。
## 5. LC3 算法与离线验证
### 5.1 原理
LC3 将每段 PCM 音频编码成具有约定时长和长度的帧;接收方使用相同采样率、帧时长和每帧字节数解码。它本身不负责设备发现、蓝牙配对、音频端点协商、BIS/CIS 建立或文件容器。
本次使用 Google 开源的 `liblc3 1.1.3`,锁定提交 `efe84d3e0ece9e54591098148da3d10f5503b84e`,在主 CPU 上编解码。标准 LC3 算法验证与标准 LE Audio 无线传输分别记录结果。算法成功后,可以继续接入完整 LE Audio SDK 的音频数据通路。
初始验证参数如下:
| 参数 | 值 |
| --- | --- |
| PCM 输入 | 16 kHz、单声道、16 bit、有符号小端 PCM |
| 帧时长 | 10 ms |
| 每帧采样点 | 160 |
| 每帧 PCM 字节 | 320 |
| 每帧 LC3 负载 | 40 字节 |
| LC3 码率 | 32 kbps,即 `40 × 8 ÷ 0.01` |
| PCM 原始码率 | 256 kbps |
| 音频负载压缩比 | 8:1;文件头与帧封装不计入此比值 |
LC3 属于有损压缩,解码后的 PCM 不要求与原 PCM 逐字节相同。验证应检查帧长度、编码/解码返回码、输出采样数、有效音频,以及编码耗时能否满足未来实时传输要求。
### 5.2 操作与文件格式
以下命令均在开发板原有串口终端中执行。文件路径是板端路径,例如 `/NAND:/rec.pcm`,不是电脑上的 `E:\rec.pcm`。文件转换时退出 U 盘模式,让板端重新拥有文件系统。文件任务会拒绝 U 盘占用,以及已经创建文件但尚未启动、正在录音或暂停的录音状态。先执行 `audio record stop`,等 PCM/Opus 文件关闭后再运行文件转换。
LC3 文件任务进行期间,录音文件创建和 U 盘启动都会返回忙;任务包括带输出路径的双文件自检。纯内存 `app lc3 test` 不访问文件系统,可以在录音文件已经预约时执行,但不会解除该录音的文件预约。
任务从提交到清理持有部分唤醒锁,防止深睡中断验证;屏幕仍可按原有策略熄屏。
**内存自检,不需要准备文件:**
```text
app lc3 test
app lc3 status
```
自检默认处理 1 秒的 1 kHz 正弦音频,可指定 `1..60` 秒。它实际调用 LC3 编码器、解码器,验证返回码,并比较去掉延时后的稳态波形误差,后台完成后打印结果。默认不保存文件,也不会自动从扬声器播放。
<!--zk+ v2 自检双文件:保存真实 LC3 压缩数据和对应的解码 PCM,兼容只指定 PCM 的旧命令。-->
**自检并同时保存 LC3 和还原 PCM,随后试听或导出:**
命令格式:
```text
app lc3 test [秒数] [输出.pcm] [输出.lc3]
```
旧的单输出路径命令继续可用:
```text
app lc3 test 1 /NAND:/lc3_test.pcm
app lc3 status
```
它会自动在同目录生成这两个文件:
| 文件 | 内容 | 1 秒自检的文件大小 |
| --- | --- | --- |
| `/NAND:/lc3_test.lc3` | 1 kHz 测试音经过 LC3 编码后的压缩文件 | 4260 字节 |
| `/NAND:/lc3_test.pcm` | 上述编码帧经过 LC3 解码得到的 16 kHz 单声道 PCM | 32000 字节 |
成功后还会打印两个实际保存路径及完整文件大小,例如:
```text
lc3: saved LC3=/NAND:/lc3_test.lc3 bytes=4260; PCM=/NAND:/lc3_test.pcm bytes=32000
```
需要分别指定两个文件名时执行:
```text
app lc3 test 1 /NAND:/TEST2.PCM /NAND:/PACK2.LC3
app lc3 status
```
`TEST2.PCM` 和 `PACK2.LC3` 不要求同名;省略 LC3 路径时,程序将 PCM 文件的 `.pcm` 扩展名替换为 `.lc3`。不指定任何路径的 `app lc3 test` / `app lc3 test 1` 仍只在内存自检,不生成文件。
等待日志显示 `done result=0`、状态 `busy=0` 后播放解码 PCM:
```text
app lc3 play /NAND:/lc3_test.pcm
```
需要拷贝到电脑时,等待任务成功结束,再执行:
```text
app udisk on
```
电脑打开开发板 U 盘,将 `lc3_test.lc3` 和 `lc3_test.pcm` 两个文件复制出来。退出导出时先在电脑安全弹出 U 盘,再发送 `app udisk off`。文件正在生成时不允许进入 U 盘模式。
自检音源是 1 kHz 测试音;LC3 文件保存真正的编码帧,PCM 文件保存这些帧的解码结果,便于对照压缩大小和试听。此操作验证算法输出、文件存储和现有 PCM 播放通路;播放成功仍不代表手机或耳机的 LE Audio 无线传输成功。
<!--zk+- v2 双文件自检和导出流程说明结束。-->
**对实际录音文件进行压缩编码和解码:**
```text
app lc3 encode /NAND:/rec.pcm /NAND:/rec.lc3
app lc3 status
```
等编码完成,再执行:
```text
app lc3 decode /NAND:/rec.lc3 /NAND:/rec_lc3.pcm
app lc3 status
```
等解码完成,再试听:
```text
app lc3 play /NAND:/rec_lc3.pcm
```
如需中途停止后台任务:
```text
app lc3 stop
app lc3 status
```
`app lc3 play` 沿用现有 PCM 播放器,固定按 16 kHz 播放;也可以使用原命令 `app pcm_play <文件路径> 16`。
独立 `encode` 和 `decode` 命令继续有效,可以单独编码真实录音,或解码自检生成的 `.lc3` 文件。解码自检文件时使用新的 PCM 输出名,例如 `app lc3 decode /NAND:/PACK2.LC3 /NAND:/OUT2.PCM`。
命令接受绝对 `.pcm` / `.lc3` 路径,输入文件和输出文件须使用不同文件。**双文件自检中的任意一个输出文件已经存在时都返回 `EEXIST`,两个文件都不会覆盖,请使用新的输出文件名。** 独立转换也拒绝覆盖已有输出。取消或发生错误时,清理本次新建的两个自检输出文件,或本次独立转换输出,以最终 `result` 为准;请等待任务停止完成后再启动下一项操作。`command accepted` 只表示任务已提交,不能代替最终 `done result=0`。
原始 PCM 没有采样率和声道元数据,算法不能仅靠文件扩展名自动判断它是否为 16 kHz 单声道。可以使用原来的单 MIC 录音方式准备输入:
```text
audio record create sr=16 ch0_gain=200 dump_len=0 rec_file
audio record start
audio record stop
```
`start` 后录制一段语音,再发送 `stop`。使用日志中实际生成的 PCM 文件名,例如 `rec1.pcm`;已有文件名自动递增机制继续有效。双 MIC 立体声录音需要先转换成 16 kHz 单声道才能作为本次验证输入。
**状态和耗时的解释:**
`app lc3 status` 输出字段如下;其中数值由最近任务决定:
```text
lc3: version=261014-v2 busy=... mode=... result=... frames=... pcm_bytes=... payload_bytes=... elapsed_ms=... enc_avg_us=... enc_peak_us=... dec_avg_us=... dec_peak_us=... peak=... error_ppm=... snr_decidb=...
```
| 字段 | 含义 |
| --- | --- |
| `busy` | `1`:任务正在处理;`0`:任务已结束或未启动 |
| `mode` | `0`:空闲;`1`:自检;`2`:编码;`3`:解码 |
| `result` | 最终 `0` 表示成功;负值表示错误、任务处理中或取消 |
| `frames` | 处理的 LC3 帧数,包含算法延时所需的冲刷帧 |
| `pcm` / `pcm_bytes` | 有效 PCM 字节数 |
| `payload` / `payload_bytes` | LC3 音频负载字节数,不包含文件头和每帧长度字段;内部状态名为 `lc3_bytes` |
| `elapsed_ms` | 整个后台任务耗时,包含文件操作和线程让出时间 |
| `enc_avg_us` / `enc_peak_us` | 每帧编码平均和最大算法耗时 |
| `dec_avg_us` / `dec_peak_us` | 每帧解码平均和最大算法耗时 |
| `peak` / `decoded_peak` | 解码 PCM 的最大幅度绝对值 |
| `error_ppm` | 自检稳态段的误差能量与原信号能量之比,乘以 `1000000` |
| `snr_decidb` | 自检稳态段 SNR,单位 `0.1 dB`;例如 `207` 表示 `20.7 dB` |
1 秒自检会处理 101 帧,得到 32000 字节的有效 PCM 和 4040 字节 LC3 负载。多出的一帧用于冲刷编码/解码延时;因此短文件的 `PCM总字节 ÷ LC3总负载字节` 会略小于 8:1,正常音频帧仍是 320 字节 PCM → 40 字节 LC3。
自检比较排除起始两帧后的 1 kHz 稳态波形;误差能量须小于信号能量的 10%,对应 SNR 高于约 10 dB,才算通过。这是一项确定的回归检查,不代替语音、音乐试听或 Bluetooth 全部一致性测试。文件转换不附带原始参考波形,`error_ppm` / `snr_decidb` 的音质意义只用于自检。
算法耗时通过 CPU 周期计数计算,**不包含文件 I/O 和线程让出时间**。线程每帧主动让出 CPU,用于离线测试。即使算法耗时小于 10 ms,也还需要实板验证最坏调度延迟、缓存、收发和音频连续性,才能用于实时 LE Audio。
**离线 `.lc3` 文件封装:**
本次采用 Google 编码/解码工具的 `lc3bin` 文件格式。它是离线工具容器,不是 Bluetooth 规定的无线音频容器。文件中每个 40 字节音频负载是 LC3 编码帧,18 字节文件头和 2 字节长度字段不得直接当作 ISO 音频负载发送。
```text
18 字节文件头
2 字节小端帧长度(40) + 40 字节 LC3 帧
2 字节小端帧长度(40) + 40 字节 LC3 帧
……
```
| 文件头偏移 | 字节数 | 内容 |
| --- | --- | --- |
| 0 | 2 | `0xCC1C`,小端 |
| 2 | 2 | 文件头长度 `18` |
| 4 | 2 | 采样率除以 `100 Hz`,固定 `160` |
| 6 | 2 | 码率除以 `100 bps`,固定 `320` |
| 8 | 2 | 声道数,固定 `1` |
| 10 | 2 | 帧时长除以 `10 µs`,固定 `1000` |
| 12 | 2 | 本版本固定 `0` |
| 14 | 4 | 原始有效 PCM 采样点数,小端 |
编码会对最后不足一帧的 PCM 补零,并输出延时冲刷所需的帧。解码根据文件头中的有效采样点数去掉初始算法延时和补零尾部,恢复与输入相同的有效 PCM 长度。解码器检查文件头、帧长及总文件长度,拒绝不支持的参数和截断文件;本次文件入口只处理上述固定参数。
例如,恰好 1 秒、32000 字节的单声道 PCM 编码成 101 帧时,完整 `.lc3` 文件大小是 `18 + 101 × (2 + 40) = 4260` 字节,状态中的 `payload` 是 `4040` 字节。比较压缩效果时应区分音频负载与完整文件大小。
### 5.3 CPU 与 DSP 选择
本次 CPU 实现具有公开算法源码、明确编码参数和可检查帧格式,便于独立验证。SDK 中的 DSP 对讲候选资源在 `application/app_demo/bt_intercom_demo/boards/ats3089p_dev_watch/sdfs_k/audioble.dsp`;当前 bt_watch 板级资源原来没有该文件。
要转向 DSP 标准 LC3 实现,需要厂家确认该镜像支持的 LC3 版本、采样率、帧时长、码率、输入/输出封装、延时、内存和标准互通情况。不能直接把 NAV 文件改扩展名为 `.lc3` 来完成这项确认。
## 6. 源码位置与入口
| 文件/位置 | 职责 |
| --- | --- |
| `src/main/leaudio_validation.c` | 独立线程发起只读 HCI 查询;解码 LE 特性位;打印控制器和主机能力边界;普通 BLE 广播检查 |
| `src/main/leaudio_validation.h` | 能力结构体和检查接口声明 |
| `src/main/system_app_shell.c` | `app leaudio`、LC3 相关串口命令入口 |
| `src/main/CMakeLists.txt` | 检查模块和 LC3 算法的编译接入 |
| `src/main/lc3_validation.c`、`src/main/lc3_validation.h` | LC3 自检双文件保存、独立文件编码/解码、异步任务、状态及文件封装入口 |
| `zephyr/drivers/audio/shell/audio_driver_shell.c` | 文件录音与 LC3 的预约检查:`rec_fs_init()` 访问 FAT 前预约,`rec_finish()` 关闭 PCM/Opus 后释放 |
| `src/card_reader/card_reader_main.c` | U 盘入口占用 USB 后再次检查 LC3 文件任务,避免切盘和任务排队交叠 |
| `src/main/lc3_cpu/include/lc3.h` | liblc3 编码器、解码器和参数查询 API |
| `src/main/lc3_cpu/src/` | Google liblc3 算法源码 |
| `src/main/lc3_cpu/LICENSE` | liblc3 上游许可证 |
| `framework/bluetooth/bt_stack/include/acts_bluetooth/hci.h` | 当前实际使用的 HCI opcode、特性位及返回结构定义 |
| `framework/bluetooth/bt_stack/src/bt_stack/hci_core.c` | 当前 Actions 主机 HCI 命令和事件处理实现 |
| `framework/bluetooth/bt_stack/include/acts_bluetooth/iso.h` | 当前 Actions 旧 ISO/CIS 接口声明;不代表已有标准广播实现 |
| `framework/media/codec_config.c` | SDK NAV/DSP 编解码镜像选择,仅供后续 DSP 路径研究 |
| `application/app_demo/bt_intercom_demo/src/test_codec.c` | SDK 对讲 NAV 文件编码/解码例程,仅供对照 |
| `Kconfig`、`prj.conf` | `CONFIG_APP_LEAUDIO_VALIDATION` 控制能力查询,`CONFIG_APP_LC3_VALIDATION` 控制 LC3 编解码;当前板型启用 |
| `tests/leaudio/run_tests.py`、`tests/leaudio/test_validation.c` | 查询逻辑、特性位、HCI 响应、异常清理及普通 BLE 接口回归测试 |
| `tests/lc3/run_tests.py`、`tests/lc3/test_lc3.c` | 真正的 LC3 算法及生产文件转换代码回归测试,并提取生产录音创建/关闭函数检查双向预约 |
| `tests/lc3/run_shell_tests.py` | 提取生产 `shell_lc3()`,验证命令参数、兼容路径和锁配对 |
`app leaudio probe` 的调用流程:
```text
system_app_shell.c
→ leaudio_probe_start()
→ 置 probe_busy、唤醒独立线程
→ 检查蓝牙初始化状态
→ HCI Read Local Version / Read Supported Commands / LE Read Local Features
→ leaudio_decode_features()
→ 保存最近结果,打印能力
→ app leaudio status 可再次查看
```
`app lc3 test` 的双文件入口为 `lc3_validation_selftest_files()`;只指定 PCM 路径时,兼容入口 `lc3_validation_selftest()` 自动推导同名 LC3 路径。工作线程生成测试音,调用真实 LC3 编码器,将每帧压缩数据写入 LC3 文件,再把同一帧送给解码器,将裁剪延时后的 PCM 写入另一个文件。两个输出全部关闭并通过校验后才报告成功;任一步失败则清理本次创建的两个输出。
新增或修改的重要 C 代码按约定使用 `//zk+ 中文说明` 和 `//zk+- 中文说明`;配置、CMake 和 Python 使用 `#zk+`、`#zk+-`。第三方算法文件保留原版权及许可证;工程接入和测试入口使用中文说明。
### 6.1 录音、LC3 文件任务与 U 盘的互斥流程
`lc3_validation_record_acquire()` / `lc3_validation_record_release()` 在 `lc3_job_lock` 下管理独立的 `record_reserved`。该标志放在 LC3 任务结构之外,因此重新提交纯内存自检不会清掉已有录音预约。
录音驱动在 `rec_fs_init()` 内、任何 FAT 查询或文件创建之前申请预约;如果 LC3 文件任务已提交或正在执行,录音返回忙,不打开录音文件。录音预约从 `create` 起保持,覆盖尚未 `start` 和 `pause` 的阶段。直到 `rec_finish()` 排空并关闭 PCM/Opus 文件才释放,初始化失败路径也释放预约。
LC3 文件任务在同一把锁下检查录音预约,因此录音与转换同时提交时只能一方进入。对 U 盘,任务先公布文件忙状态再检查 USB/MSC 所有权;卡读器先申请 USB 所有权,再复查 LC3 文件忙状态。该检查也覆盖任务已经排队但尚未打开文件的窗口。
这些检查只处理同时使用存储的冲突,不改变原录音采样参数、文件命名、单/双 MIC 默认行为或经典蓝牙流程。纯内存自检不占用文件系统预约。
## 7. 分阶段实板验证
### 阶段一:刷入本次固件,确认原有功能
确认屏幕显示、原有经典蓝牙播放/通话、录音以及离线日志功能仍可正常使用。新验证模块通过命令启动,能力探测不重置控制器,不改动已有经典蓝牙音频路由。
### 阶段二:读取控制器能力
运行 `app leaudio probe`,保存完整日志。确认 `valid=1 error=0`,记录版本、制造商、`controller_le_features` 和全部 `supported_commands`。若 BIS 或所需 CIS 特性为 `0`,优先与厂家确认控制器固件支持情况;若为 `1`,仍需补齐主机实现。
### 阶段三:验证普通 BLE 通路
运行 `app leaudio ble on`,手机扫描并建立 GATT 连接。保存设备名称、连接前后的状态日志。此阶段验收项是普通 BLE 搜索和连接,不记录为 Auracast 或 LE Audio 音频成功。
### 阶段四:验证 LC3 压缩算法
先运行纯内存 LC3 自检,再运行带文件路径的双文件自检,等 `busy=0 result=0` 后进入 U 盘,导出 LC3/PCM 两个文件。再进行实际录音的独立编码和解码。比较音频长度和文件大小,检查返回码及耗时,试听还原 PCM。取得日志和文件后可供研发交叉验证。
### 阶段五:完整标准音频 SDK 接入后验证广播
验收必须包含扩展广播中的标准广播标识、周期广播的 BASE、控制器 BIG 建立成功事件、ISO 负载持续发送,以及标准接收器同步并实际出声。仅显示“广播已启动”或手机搜到名称不能通过验收。
### 阶段六:完整标准音频 SDK 接入后验证单播
验收必须包含手机发现 PACS/ASCS、按 BAP 配置端点、建立所需 CIG/CIS、持续 ISO 收发、LC3 编解码和扬声器出声。继续检查停止、断连、再次连接、参数重配和异常恢复。
## 8. 需要向芯片厂家确认的资料
为实现标准手机或 Auracast 耳机互通,建议一次性提供本次 `probe` 完整日志,并取得以下资料:
1. ATS3089P 对应控制器固件和完整 LE Audio SDK 版本、支持的标准角色与 profile 范围。
2. 标准广播源/广播接收器 BIG/BIS API、事件回调、HCI ISO 数据路径及示例工程。
3. 标准单播客户端/服务器 PACS、ASCS、BAP、CIG/CIS API 和手机互通示例。
4. 扩展广播、周期广播、Broadcast Audio Announcement、BASE、BIGInfo 的生成与解析方法。
5. LC3 CPU/DSP 标准编码接口及格式:采样率、7.5/10 ms 帧时长、字节数、码率、声道排列、延时与内存需求。
6. Controller 支持命令位图与运行时特性位说明,支持的最大 BIS/CIS 数、SDU、PHY、重传和缓存限制。
7. 与现有 A2DP/HFP、普通 BLE 服务共存的限制、调度方法及复用资源说明。
8. 已完成互通的手机、系统版本、耳机型号和固件版本,以及重复启停、断连恢复的验收记录。
取得这些实现和资料后,才能把当前明确拒绝的标准启动入口替换为真正的 BAP/ISO 状态机,并据实验证“可发现、可加入、可出声”。
## 9. 常见现象
| 现象 | 判断和处理 |
| --- | --- |
| `app leaudio` 提示未知参数 | 确认刷入了本次固件,并确认验证模块已启用;旧固件没有该命令 |
| 探测返回 `EAGAIN` 或 `valid=0` | 检查蓝牙初始化是否完成;保留 `error` 及 HCI 日志,再运行 `probe` |
| `bis_tx=0` | 控制器本次返回未声明广播 ISO 发送支持,需要厂家核对固件及型号 |
| `bis_tx=1`,启动仍返回 `ENOTSUP` | 控制器能力与主机协议栈实现不同;当前主机仍缺 BIG/BIS/BAP |
| 手机 BLE 工具能搜到,系统的广播音频列表搜不到 | 当前开启的是普通 BLE 广播,尚未发送标准 Auracast 音频广播信息 |
| GATT 连接成功,耳机没有声音 | 普通 GATT 连接没有建立 CIS/BIS 音频传输 |
| LC3 文件生成了,耳机不能直接播放 | 文件算法验证不负责蓝牙传输;离线容器也不能直接作为 ISO 负载发送 |
| 解码 PCM 声音速度或声调错误 | 按 16 kHz、16 bit、单声道播放;不要按 44.1/48 kHz或双声道解释 |
| 输入是双 MIC 录音 | 本次 LC3 文件入口只接受单声道,先转换为 16 kHz 单声道 PCM |
| 双文件自检返回 `EEXIST` | PCM 或 LC3 路径已有文件,换一组新名字;不要只更换其中一个仍已存在的名字 |
## 10. 回归检查方法
在工程 `` 目录执行:
```powershell
python .\application\bt_watch\tests\leaudio\run_tests.py
python .\application\bt_watch\tests\lc3\run_tests.py
python .\application\bt_watch\tests\lc3\run_shell_tests.py
```
LE Audio 查询测试将验证模块实编为 ARM 指令,通过软件模拟 RTOS/HCI 返回执行,检查特性位、状态、错误响应、缓冲释放和优先级恢复;还会提取生产 `bt_manager_is_ready()` 函数,验证初始化标志、蓝牙引擎就绪及控制器错误的组合。它不连接实板,不代表控制器支持 BIS/CIS,也不代表已与手机或 Auracast 耳机互通。
LC3 测试编译并执行真正的 Google 算法和本次生产转换代码,只替换时钟、线程及文件系统接口;同时提取录音驱动生产 `rec_fs_init()` / `rec_finish()` 函数参与测试。测试覆盖自检、固定帧长度、编码/解码、非整帧尾部、原采样点数恢复、参数校验、已存在的输出文件、U 盘/录音互斥、唤醒锁、文件异常和取消清理。当前 **133 项检查通过**,1 kHz 稳定段恢复音频 SNR 约 **20.7 dB**,997 个输入采样点恢复为 1994 字节 PCM。
双文件回归确认:1 秒自检生成 4260 字节 LC3 和 32000 字节 PCM;独立 `decode` 命令解码该 LC3 文件后,与自检保存的 PCM 逐字节一致。任意输出已存在时,包括零字节文件和大小写路径,预检查拒绝并且不创建另一侧;两个输出的分段写入、同步/关闭错误、压缩文件打开/截断失败、处理中停止和关闭文件期间停止均检查成对清理。路径校验还拒绝重复斜杠、路径分量末尾的空格或点,防止不同字符串被 FAT 解释为同一个文件。
录音预约回归还检查:双方忙状态下拒绝新的文件任务、`create` 尚未 `start` 时即使录音状态为 STOPPED 仍保留预约、选路径/打开/截断失败后释放、关闭与重复停止安全、纯内存自检不清除录音预约,以及任务排队后取消不创建输出文件。这些结果验证算法和文件流程,实板编码速度与无线互通需另行验证。
串口命令测试直接提取生产 `shell_lc3()` 编译为 ARM 指令执行,当前 **292 项检查通过**。检查包括 `test` 的秒数和参数顺序、默认内存自检、旧的单 PCM 路径到双文件兼容入口、显式两个输出路径、独立 `encode` / `decode`、播放/停止/状态路由、无效参数,以及 USB 切换锁的成对申请和释放。此测试不连接实板。
## 11. 官方资料
- [Bluetooth SIG:LE Audio 技术介绍](https://www.bluetooth.com/learn-about-bluetooth/recent-enhancements/le-audio/)
- [Bluetooth SIG:Auracast 广播音频](https://www.bluetooth.com/auracast/)
- [Bluetooth SIG:Basic Audio Profile 规范入口](https://www.bluetooth.com/specifications/specs/basic-audio-profile/)
- [Bluetooth SIG:规范检索](https://www.bluetooth.com/specifications/specs/),可检索 BAP、PACS、ASCS、LC3 和 Bluetooth Core。
- [Google:liblc3 算法源码和说明](https://github.com/google/liblc3)
Bluetooth 技术标准、控制器能力、主机协议实现和具体产品支持范围需要分别核对;本工程文档中的能力结论来自当前源码和库检查,控制器硬件能力以本次实板查询结果为准。
<!--zk+- 后续接入完整标准 SDK 时更新实际支持范围和实板验收记录。-->