752 lines
19 KiB
Markdown
752 lines
19 KiB
Markdown
# Avaota F1 开发日志 - Day 3:音频系统开发
|
||
|
||
**版本**:v1.0
|
||
**日期**:2024-11-24
|
||
**主机环境**:Ubuntu 24.04 LTS
|
||
**目标平台**:Avaota F1 (全志 V821 / 32-bit RISC-V)
|
||
|
||
---
|
||
|
||
## 第一部分:阶段 3 - 音频系统实现
|
||
|
||
### 1. 音频采集模块(✅ 完成)
|
||
|
||
#### 1.1 代码实现
|
||
|
||
实现了完整的音频采集类 [`audio_capture.cpp`](file:///d:/CodingProjects/Antigravity/NaviGlass/NaviGlassClient/src/audio/audio_capture.cpp):
|
||
|
||
**核心功能**:
|
||
- 基于 **ALSA (Advanced Linux Sound Architecture)**
|
||
- 支持 PDM 麦克风采集
|
||
- 音频格式:16kHz, 16-bit, Mono (单声道)
|
||
|
||
**关键实现**:
|
||
|
||
1. **设备初始化** (`init()`):
|
||
```cpp
|
||
snd_pcm_open(&m_pcm_handle, device, SND_PCM_STREAM_CAPTURE, 0);
|
||
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
|
||
snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0);
|
||
snd_pcm_hw_params_set_channels(handle, params, 1); // Mono
|
||
```
|
||
|
||
2. **PCM 数据读取** (`read()`):
|
||
```cpp
|
||
snd_pcm_sframes_t ret = snd_pcm_readi(handle, buffer, frames);
|
||
```
|
||
|
||
3. **错误处理与自动恢复**:
|
||
- **Overrun (缓冲区溢出)**:自动调用 `snd_pcm_prepare()` 恢复
|
||
- **Suspend (设备挂起)**:调用 `snd_pcm_resume()` 唤醒设备
|
||
- **通用错误**:使用 `snd_pcm_recover()` 尝试恢复
|
||
|
||
#### 1.2 缓冲区优化
|
||
|
||
为了平衡延迟和稳定性,配置了合理的缓冲区参数:
|
||
- **Period Size**: 160 frames (10ms @ 16kHz)
|
||
- **Buffer Size**: 1600 frames (100ms @ 16kHz)
|
||
|
||
这样的配置提供了:
|
||
- **低延迟**:10ms 周期,适合实时语音交互
|
||
- **稳定性**:100ms 缓冲,防止频繁的 underrun/overrun
|
||
|
||
---
|
||
|
||
### 2. 音频播放模块(✅ 完成)
|
||
|
||
#### 2.1 代码实现
|
||
|
||
创建了音频播放类 [`audio_player.h`](file:///d:/CodingProjects/Antigravity/NaviGlass/NaviGlassClient/src/audio/audio_player.h) 和 [`audio_player.cpp`](file:///d:/CodingProjects/Antigravity/NaviGlass/NaviGlassClient/src/audio/audio_player.cpp):
|
||
|
||
**核心功能**:
|
||
- 支持 I2S 扬声器 (MAX98357A)
|
||
- 音频格式:16kHz, 16-bit, Mono
|
||
- 与音频采集模块使用相同的参数配置
|
||
|
||
**关键实现**:
|
||
|
||
1. **播放设备初始化** (`init()`):
|
||
```cpp
|
||
snd_pcm_open(&m_pcm_handle, device, SND_PCM_STREAM_PLAYBACK, 0);
|
||
// 配置相同的音频参数 (16kHz, S16_LE, Mono)
|
||
```
|
||
|
||
2. **PCM 数据写入** (`write()`):
|
||
```cpp
|
||
snd_pcm_sframes_t ret = snd_pcm_writei(handle, buffer, frames);
|
||
```
|
||
|
||
3. **Underrun 处理**:
|
||
- 检测到 `-EPIPE` 错误时自动恢复
|
||
- 重新准备设备并重试写入
|
||
- 避免播放卡顿和杂音
|
||
|
||
---
|
||
|
||
### 3. 测试程序(✅ 完成)
|
||
|
||
#### 3.1 功能实现
|
||
|
||
创建了独立的音频测试程序 [`test_audio.cpp`](file:///d:/CodingProjects/Antigravity/NaviGlass/NaviGlassClient/src/test_audio.cpp),包含三个测试模式:
|
||
|
||
**测试 1:音频采集 (录音)**
|
||
- 录音时长:5 秒
|
||
- 输出文件:`test_recording.pcm` (RAW PCM 格式)
|
||
- 用途:验证麦克风和 ALSA 采集功能
|
||
|
||
```bash
|
||
/tmp/test_audio capture
|
||
```
|
||
|
||
**测试 2:音频播放**
|
||
- 播放录制的 PCM 文件
|
||
- 用途:验证扬声器和 ALSA 播放功能
|
||
|
||
```bash
|
||
/tmp/test_audio playback
|
||
```
|
||
|
||
**测试 3:回环测试**
|
||
- 同时进行录音和播放
|
||
- 用途:验证全双工音频能力,检测延迟
|
||
- 现象:应听到自己的声音(有轻微延迟)
|
||
|
||
```bash
|
||
/tmp/test_audio loopback
|
||
```
|
||
|
||
#### 3.2 使用说明
|
||
|
||
```bash
|
||
# 运行所有测试(交互式)
|
||
./test_audio
|
||
|
||
# 运行单个测试
|
||
./test_audio capture # 或 1
|
||
./test_audio playback # 或 2
|
||
./test_audio loopback # 或 3
|
||
```
|
||
|
||
---
|
||
|
||
### 4. 编译配置(✅ 完成)
|
||
|
||
#### 4.1 编译脚本
|
||
|
||
创建了专用的编译脚本 [`build_phase3.sh`](file:///d:/CodingProjects/Antigravity/NaviGlass/NaviGlassClient/src/build_phase3.sh):
|
||
|
||
**关键配置**:
|
||
```bash
|
||
# 32位 RISC-V 工具链
|
||
CXX="${TOOLCHAIN_DIR}/bin/riscv32-unknown-linux-g++"
|
||
|
||
# ALSA 库链接
|
||
LDFLAGS+=" -lasound"
|
||
|
||
# 编译模块
|
||
- utils/logger.cpp
|
||
- audio/audio_capture.cpp
|
||
- audio/audio_player.cpp
|
||
- test_audio.cpp
|
||
```
|
||
|
||
**编译步骤**:
|
||
```bash
|
||
cd ~/path/to/AvaotaF1/src
|
||
chmod +x build_phase3.sh
|
||
./build_phase3.sh
|
||
```
|
||
|
||
**输出**:
|
||
- 可执行文件:`test_audio`
|
||
- 架构:ELF 32-bit RISC-V
|
||
- 已 strip,体积优化
|
||
|
||
---
|
||
|
||
## 第二部分:技术要点总结
|
||
|
||
### 1. ALSA 库关键概念
|
||
|
||
#### 1.1 PCM 设备
|
||
- **Capture (采集)**:从麦克风读取数据
|
||
- **Playback (播放)**:向扬声器写入数据
|
||
- **设备名**:`hw:0,0` (硬件卡 0, 设备 0) 或 `default` (默认设备)
|
||
|
||
#### 1.2 音频参数
|
||
| 参数 | 值 | 说明 |
|
||
|------|-----|------|
|
||
| Format | `SND_PCM_FORMAT_S16_LE` | 16-bit signed little-endian |
|
||
| Sample Rate | 16000 Hz | 16 kHz (语音级别) |
|
||
| Channels | 1 | 单声道 (Mono) |
|
||
| Access | `SND_PCM_ACCESS_RW_INTERLEAVED` | 交织模式 |
|
||
|
||
#### 1.3 错误码处理
|
||
| 错误码 | 含义 | 解决方案 |
|
||
|--------|------|----------|
|
||
| `-EPIPE` | Overrun (采集) / Underrun (播放) | `snd_pcm_prepare()` |
|
||
| `-ESTRPIPE` | 设备挂起 | `snd_pcm_resume()` |
|
||
| 其他 | 通用错误 | `snd_pcm_recover()` |
|
||
|
||
---
|
||
|
||
### 2. 架构设计对比
|
||
|
||
#### ESP32S3 vs AvaotaF1
|
||
|
||
| 组件 | ESP32S3 (原) | AvaotaF1 (新) |
|
||
|------|--------------|---------------|
|
||
| **麦克风接口** | PDM (ESP_I2S 库) | PDM (ALSA) |
|
||
| **扬声器接口** | I2S STD (ESP_I2S 库) | I2S (ALSA + MAX98357A) |
|
||
| **API 风格** | Arduino 同步阻塞 | Linux ALSA 标准 |
|
||
| **缓冲管理** | ESP_I2S 自动 | 手动配置 Period/Buffer |
|
||
| **错误恢复** | 硬件自动 | 软件手动处理 |
|
||
|
||
**关键差异**:
|
||
- ESP32S3 的 I2S 库高度封装,使用简单
|
||
- ALSA 需要手动配置参数,但更灵活强大
|
||
- AvaotaF1 需要显式处理 Overrun/Underrun
|
||
|
||
---
|
||
|
||
### 3. 与网络模块集成
|
||
|
||
音频系统已按照 [`main.cpp`](file:///d:/CodingProjects/Antigravity/NaviGlass/NaviGlassClient/src/main.cpp) 的设计预留接口:
|
||
|
||
#### 3.1 音频采集上行 (WebSocket)
|
||
|
||
```cpp
|
||
// 主程序中的音频采集线程
|
||
void audio_capture_thread() {
|
||
AudioCapture mic("hw:0,0", 16000, 1);
|
||
mic.init();
|
||
|
||
WSClient ws_aud(SERVER_HOST, SERVER_PORT, "/ws_audio");
|
||
ws_aud.connect();
|
||
|
||
while (running) {
|
||
int16_t buffer[320]; // 20ms @ 16kHz
|
||
mic.read(buffer, 320);
|
||
ws_aud.send_binary((uint8_t*)buffer, 640); // 640 bytes
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3.2 音频播放下行 (HTTP)
|
||
|
||
```cpp
|
||
// 主程序中的音频播放线程
|
||
void audio_player_thread() {
|
||
AudioPlayer speaker("hw:0,0", 16000, 1);
|
||
speaker.init();
|
||
|
||
HTTPClient http;
|
||
http.connect("http://192.168.110.188:8081/stream.wav");
|
||
|
||
http.stream_download([&](const uint8_t* data, size_t size) {
|
||
speaker.write((const int16_t*)data, size / 2);
|
||
return true;
|
||
});
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 第三部分:待完成事项
|
||
|
||
### 当前阶段完成度:约 80%
|
||
|
||
**✅ 已完成**:
|
||
1. 音频采集类实现 - **100%**
|
||
2. 音频播放类实现 - **100%**
|
||
3. 测试程序 - **100%**
|
||
4. 编译脚本 - **100%**
|
||
|
||
**⚠️ 待验证**(需要开发板环境):
|
||
1. **Device Tree 配置** - **0%**
|
||
- 启用 PDM 麦克风驱动
|
||
- 启用 I2S 扬声器驱动
|
||
- 验证 `/proc/asound/cards` 设备存在
|
||
|
||
2. **ALSA 设备测试** - **0%**
|
||
- `arecord -l` 列出录音设备
|
||
- `aplay -l` 列出播放设备
|
||
- 原始 ALSA 工具测试
|
||
|
||
3. **音频模块集成测试** - **0%**
|
||
- 运行 `test_audio` 程序
|
||
- 验证录音质量
|
||
- 验证播放音质
|
||
|
||
4. **端到端验证** - **0%**
|
||
- 与服务器联调
|
||
- WebSocket 音频上传测试
|
||
- HTTP TTS 播放测试
|
||
|
||
---
|
||
|
||
## 第四部分:部署与测试指南
|
||
|
||
### 1. 编译(PC 端)
|
||
|
||
```bash
|
||
cd ~/path/to/AvaotaF1/src
|
||
./build_phase3.sh
|
||
|
||
# 验证输出
|
||
file test_audio
|
||
# 预期:ELF 32-bit LSB executable, UCB RISC-V, statically linked
|
||
```
|
||
|
||
### 2. 部署(SD 卡方式)
|
||
|
||
```bash
|
||
# PC 端:复制到 SD 卡
|
||
cp test_audio /path/to/sd_card/
|
||
|
||
# 板端:挂载并运行
|
||
mount /dev/mmcblk0p1 /mnt/extsd
|
||
cp /mnt/extsd/test_audio /tmp/
|
||
chmod +x /tmp/test_audio
|
||
```
|
||
|
||
### 3. 前置检查(板端)
|
||
|
||
```bash
|
||
# 检查声卡设备
|
||
cat /proc/asound/cards
|
||
# 应该看到:0 [xxx]: ...
|
||
|
||
# 列出录音设备
|
||
arecord -l
|
||
# 应该看到:card 0: ..., device 0: ...
|
||
|
||
# 列出播放设备
|
||
aplay -l
|
||
# 应该看到:card 0: ..., device 0: ...
|
||
```
|
||
|
||
> [!WARNING]
|
||
> 如果以上命令返回 "No soundcards found",说明 Device Tree 音频驱动未正确配置,需要先修改 DTS 并重新编译烧录内核。
|
||
|
||
### 4. 原始 ALSA 测试(板端)
|
||
|
||
```bash
|
||
# 测试录音(5 秒)
|
||
arecord -D hw:0,0 -f S16_LE -r 16000 -c 1 -d 5 test.wav
|
||
|
||
# 测试播放
|
||
aplay -D hw:0,0 test.wav
|
||
```
|
||
|
||
### 5. 音频模块测试(板端)
|
||
|
||
```bash
|
||
# 运行所有测试
|
||
/tmp/test_audio
|
||
|
||
# 或单独测试
|
||
/tmp/test_audio capture # 录音 5 秒
|
||
/tmp/test_audio playback # 播放录音
|
||
/tmp/test_audio loopback # 回环测试
|
||
```
|
||
|
||
---
|
||
|
||
## 第五部分:可能遇到的问题
|
||
|
||
### 问题 1:ALSA 设备不存在
|
||
|
||
**现象**:
|
||
```
|
||
[AudioCapture] Cannot open device 'hw:0,0': No such device
|
||
```
|
||
|
||
**原因**:
|
||
- Device Tree 未启用音频驱动
|
||
- 内核音频模块未编译
|
||
- 硬件连接问题
|
||
|
||
**解决**:
|
||
1. 检查 `/sys/class/sound/` 目录
|
||
2. 修改 Device Tree 启用 `dmic` 和 `i2s0` 节点
|
||
3. 重新编译内核:`make kernel_menuconfig` → 启用 ALSA 驱动
|
||
4. 烧录新固件并重启
|
||
|
||
---
|
||
|
||
### 问题 2:录音有声音但有杂音
|
||
|
||
**可能原因**:
|
||
- 采样率不匹配
|
||
- 缓冲区配置不当
|
||
- 硬件增益过高
|
||
|
||
**解决**:
|
||
```bash
|
||
# 使用 alsamixer 调整音量
|
||
alsamixer
|
||
# 按 F4 选择 Capture,调整麦克风增益
|
||
|
||
# 或使用 amixer
|
||
amixer set 'Capture' 50%
|
||
```
|
||
|
||
---
|
||
|
||
### 问题 3:播放音质差或卡顿
|
||
|
||
**可能原因**:
|
||
- Underrun (缓冲区欠载)
|
||
- CPU 占用过高
|
||
- 缓冲区过小
|
||
|
||
**解决**:
|
||
- 增大 Buffer Size:修改 `audio_player.cpp` 中的 `buffer_size` 参数
|
||
- 调整线程优先级:
|
||
```cpp
|
||
pthread_setschedprio(pthread_self(), 90); // 实时优先级
|
||
```
|
||
|
||
---
|
||
|
||
### 问题 4:回环测试有严重延迟
|
||
|
||
**原因**:
|
||
- 缓冲区过大
|
||
- Period 设置不当
|
||
|
||
**解决**:
|
||
- 减小 Period Size:从 160 改为 80 (5ms)
|
||
- 减小 Buffer Size:从 1600 改为 800 (50ms)
|
||
|
||
---
|
||
|
||
## 第六部分:下一步计划
|
||
|
||
### 阶段 4:摄像头系统(Day 4-5)
|
||
|
||
**优先级**:高(最大技术风险点)
|
||
|
||
**任务清单**:
|
||
1. 确认 GC2083 摄像头配置
|
||
2. 加载 ISP 参数文件 (`isp_ini_gc2083`)
|
||
3. 验证 `sample_virvi` 示例程序
|
||
4. 实现 Camera 类 (调用 MPP 库)
|
||
5. 实现 JPEG 硬件编码
|
||
6. 集成到 WebSocket 视频流
|
||
|
||
**为何现在进入摄像头阶段**:
|
||
- 音频系统代码已完成,等待板端验证
|
||
- 摄像头涉及 MPP 库和 ISP 配置,技术难度最高
|
||
- 预留更多时间处理可能的画质问题(绿屏/过暗)
|
||
|
||
---
|
||
|
||
## 🏆 Day 3 成果
|
||
|
||
**核心成就**:
|
||
1. ✅ 完成音频采集类实现 (ALSA + PDM)
|
||
2. ✅ 完成音频播放类实现 (ALSA + I2S)
|
||
3. ✅ 创建完整的测试程序 (3 种测试模式)
|
||
4. ✅ 配置编译脚本和工具链
|
||
5. ✅ 文档化所有技术要点和部署流程
|
||
|
||
**技术栈确认**:
|
||
- **音频框架**: ALSA (Advanced Linux Sound Architecture)
|
||
- **采集设备**: PDM 麦克风 (hw:0,0)
|
||
- **播放设备**: I2S 扬声器 MAX98357A (hw:0,0)
|
||
- **音频格式**: 16kHz, 16-bit, Mono, PCM
|
||
|
||
**代码质量**:
|
||
- 完整的错误处理机制
|
||
- 自动恢复功能 (Overrun/Underrun/Suspend)
|
||
- 详细的日志输出
|
||
- 优化的缓冲区配置
|
||
|
||
**音频系统软件开发完成**:
|
||
1. ✅ 音频采集和播放类已实现
|
||
2. ✅ 测试程序已创建
|
||
3. ⚠️ 等待板端 Device Tree 配置才能验证
|
||
|
||
---
|
||
|
||
## 第七部分:I2S 音频硬件配置(✅ 完成)
|
||
|
||
### 1. 硬件方案确认
|
||
|
||
**芯片平台**:全志 V821 (sun300iw1p1)
|
||
**音频输出方案**:I2S0 + MAX98357A 数字功放
|
||
**关键引脚**:
|
||
- **PD12**: I2S0_BCLK (位时钟)
|
||
- **PD13**: I2S0_LRCK (左右声道时钟)
|
||
- **PD15**: I2S0_DOUT0 (音频数据输出)
|
||
|
||
---
|
||
|
||
### 2. Device Tree 配置
|
||
|
||
#### 2.1 问题分析
|
||
|
||
**初始错误**:
|
||
```
|
||
sunxi:pin-42000000.pinctrl:[ERR]: unsupported function i2s0 on pin PD12
|
||
sunxi-snd-plat-i2s: probe of 42032000.i2s0_plat failed with error -22
|
||
```
|
||
|
||
**根本原因**:
|
||
1. Pinctrl 驱动中 PD12/13/15 的 I2S 功能定义为 `i2s0_bclk`、`i2s0_lrck`、`i2s0_dout0`
|
||
2. 不支持通用的 `"i2s0"` 函数名
|
||
3. Pinctrl 框架不支持为多个引脚指定不同的 function
|
||
|
||
#### 2.2 最终解决方案
|
||
|
||
创建**三个独立的 pinctrl 节点**,每个引脚一个:
|
||
|
||
```dts
|
||
/* I2S0 individual pin definitions for MAX98357A */
|
||
i2s0_bclk_pin: i2s0_bclk@0 {
|
||
pins = "PD12";
|
||
function = "i2s0_bclk";
|
||
drive-strength = <20>;
|
||
bias-disable;
|
||
};
|
||
|
||
i2s0_lrck_pin: i2s0_lrck@0 {
|
||
pins = "PD13";
|
||
function = "i2s0_lrck";
|
||
drive-strength = <20>;
|
||
bias-disable;
|
||
};
|
||
|
||
i2s0_dout0_pin: i2s0_dout0@0 {
|
||
pins = "PD15";
|
||
function = "i2s0_dout0";
|
||
drive-strength = <20>;
|
||
bias-disable;
|
||
};
|
||
|
||
i2s0_pins_b: i2s0_pins@1 {
|
||
pins = "PD12", "PD13", "PD15";
|
||
function = "gpio_in";
|
||
};
|
||
```
|
||
|
||
#### 2.3 I2S 平台设备配置
|
||
|
||
修改 `&i2s0_plat` 节点引用三个独立的引脚配置:
|
||
|
||
```dts
|
||
&i2s0_plat {
|
||
pinctrl-used;
|
||
pinctrl-names = "default","sleep";
|
||
pinctrl-0 = <&i2s0_bclk_pin &i2s0_lrck_pin &i2s0_dout0_pin>;
|
||
pinctrl-1 = <&i2s0_pins_b>;
|
||
status = "okay";
|
||
};
|
||
```
|
||
|
||
#### 2.4 MAX98357A Codec 节点
|
||
|
||
在根节点添加 MAX98357A 驱动:
|
||
|
||
```dts
|
||
/ {
|
||
max98357a: max98357a {
|
||
#sound-dai-cells = <0>;
|
||
compatible = "maxim,max98357a";
|
||
status = "okay";
|
||
};
|
||
};
|
||
```
|
||
|
||
并在 `&i2s0_mach` 中引用:
|
||
|
||
```dts
|
||
&i2s0_mach {
|
||
status = "okay";
|
||
i2s0_codec: soundcard-mach,codec {
|
||
sound-dai = <&max98357a>;
|
||
};
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### 3. Pinctrl 驱动调试
|
||
|
||
#### 3.1 驱动源码分析
|
||
|
||
**文件位置**:`bsp/drivers/pinctrl/pinctrl-sun300iw1.c`
|
||
|
||
**关键发现**:
|
||
- PD12/13/15 在非 FPGA 配置块中有完整的 I2S 功能定义
|
||
- Function 0x6 对应 I2S 功能
|
||
- 必须使用准确的函数名:`i2s0_bclk`、`i2s0_lrck`、`i2s0_dout0`
|
||
|
||
**驱动修改**(已在前期完成):
|
||
- 注释掉简化版本的 PD12/13 定义(没有 I2S 功能)
|
||
- 确保使用完整版本的引脚定义
|
||
|
||
---
|
||
|
||
### 4. 编译与烧录
|
||
|
||
#### 4.1 SDK 配置
|
||
|
||
添加 alsa-utils 工具:
|
||
|
||
```bash
|
||
cd ~/ProgramFiles/AvaotaF1/avaota_sdk/tina-v821-release
|
||
make menuconfig
|
||
# 选择: Sound ---> alsa-utils
|
||
```
|
||
|
||
#### 4.2 编译步骤
|
||
|
||
```bash
|
||
source build/envsetup.sh
|
||
lunch avaota_f1-tina
|
||
ulimit -n 4096
|
||
make -j4
|
||
pack
|
||
```
|
||
|
||
#### 4.3 烧录
|
||
|
||
使用 PhoenixSuit 烧录生成的固件:
|
||
```
|
||
out/v821_linux_avaota_f1_uart0_nor.img
|
||
```
|
||
|
||
---
|
||
|
||
### 5. 硬件验证(✅ 成功)
|
||
|
||
#### 5.1 系统状态检查
|
||
|
||
```bash
|
||
root@(none):/# cat /proc/asound/cards
|
||
0 [audiocodec ]: audiocodec - audiocodec
|
||
1 [sndi2s0 ]: sndi2s0 - sndi2s0 # ✅ I2S 设备已识别
|
||
|
||
root@(none):/# cat /proc/asound/pcm
|
||
00-00: ... playback 1 : capture 1
|
||
01-00: sunxi-snd-plat-i2s-HiFi HiFi-0 : playback 1 # ✅ I2S 播放设备
|
||
|
||
root@(none):/# ls /dev/snd/
|
||
controlC0 controlC1 pcmC0D0c pcmC0D0p pcmC1D0p timer # ✅ 设备节点存在
|
||
```
|
||
|
||
#### 5.2 Pinctrl 配置验证
|
||
|
||
```bash
|
||
root@(none):/# cat /sys/kernel/debug/pinctrl/42000000.pinctrl/pinconf-pins | grep -A 3 "pin 108\|pin 109\|pin 111"
|
||
pin 108 (PD12): output drive strength (20 mA) # ✅ 配置已生效
|
||
pin 109 (PD13): output drive strength (20 mA) # ✅
|
||
pin 111 (PD15): output drive strength (20 mA) # ✅
|
||
```
|
||
|
||
#### 5.3 音频测试(🎉 成功)
|
||
|
||
**硬件连接**:
|
||
| MAX98357A | Avaota F1 |
|
||
|-----------|-----------|
|
||
| BCLK | PD12 |
|
||
| LRC | PD13 |
|
||
| DIN | PD15 |
|
||
| VIN | 3.3V |
|
||
| GND | GND |
|
||
| SD | 3.3V |
|
||
|
||
**测试命令**:
|
||
```bash
|
||
# 播放随机白噪音测试
|
||
dd if=/dev/urandom bs=192000 count=1 | aplay -D hw:1,0 -f S16_LE -r 48000 -c 2
|
||
```
|
||
|
||
**测试结果**:
|
||
```
|
||
Playing raw data 'stdin' : Signed 16 bit Little Endian, Rate 48000 Hz, Stereo
|
||
✅ 扬声器输出刺耳的白噪音 - 证明硬件通路完全正常!
|
||
```
|
||
|
||
---
|
||
|
||
### 6. 成功标志
|
||
|
||
✅ **I2S 设备已识别**:`sndi2s0` (card 1)
|
||
✅ **设备节点已创建**:`/dev/snd/pcmC1D0p`
|
||
✅ **Pinctrl 配置生效**:Drive strength 20mA
|
||
✅ **I2S 驱动加载**:`sunxi-snd-plat-i2s` mapping ok
|
||
✅ **MAX98357A 工作**:音频数据成功解码并放大
|
||
✅ **扬声器输出正常**:能听到测试音(白噪音)
|
||
|
||
**关键成就**:
|
||
- 从零开始分析并修复 Device Tree 配置
|
||
- 调试 pinctrl 驱动,找到正确的函数定义
|
||
- 创建三个独立节点,绕过 pinctrl 框架限制
|
||
- 验证完整的音频输出硬件通路
|
||
|
||
---
|
||
|
||
### 7. 经验总结
|
||
|
||
#### 7.1 Device Tree 配置原则
|
||
|
||
1. **精确匹配函数名**:必须使用驱动源码中定义的准确名称
|
||
2. **独立节点策略**:每个引脚不同功能时,必须创建独立节点
|
||
3. **Pinctrl 属性**:`pinctrl-0` 可以引用多个节点(phandle 列表)
|
||
4. **Drive Strength**:I2S 信号需要较高的驱动强度(20mA)
|
||
|
||
#### 7.2 调试方法
|
||
|
||
1. **查看内核日志**:`dmesg | grep -i "i2s\|pinctrl"`
|
||
2. **检查 pinmux 状态**:`/sys/kernel/debug/pinctrl/*/pinmux-pins`
|
||
3. **检查 pinconf 状态**:`/sys/kernel/debug/pinctrl/*/pinconf-pins`
|
||
4. **验证设备节点**:`cat /proc/asound/cards`, `ls /dev/snd/`
|
||
|
||
#### 7.3 常见陷阱
|
||
|
||
❌ **错误做法**:
|
||
```dts
|
||
pins = "PD12", "PD13", "PD15";
|
||
function = "i2s0"; // ❌ 驱动不支持
|
||
```
|
||
|
||
❌ **错误做法**:
|
||
```dts
|
||
pins = "PD12", "PD13", "PD15";
|
||
function = "i2s0_bclk", "i2s0_lrck", "i2s0_dout0"; // ❌ 框架不支持多值
|
||
```
|
||
|
||
✅ **正确做法**:
|
||
```dts
|
||
/* 每个引脚独立定义 */
|
||
i2s0_bclk_pin: i2s0_bclk@0 {
|
||
pins = "PD12";
|
||
function = "i2s0_bclk"; // ✅ 一个引脚一个功能
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 🏆 Day 3 最终成果
|
||
|
||
### 软件开发
|
||
1. ✅ AudioCapture 类实现(ALSA 录音)
|
||
2. ✅ AudioPlayer 类实现(ALSA 播放)
|
||
3. ✅ test_audio 测试程序(3 种测试模式)
|
||
4. ✅ 编译脚本和工具链配置
|
||
|
||
### 硬件配置
|
||
5. ✅ I2S0 引脚 Device Tree 配置(PD12/13/15)
|
||
6. ✅ MAX98357A Codec 驱动集成
|
||
7. ✅ Pinctrl 驱动调试和修复
|
||
8. ✅ 音频输出硬件验证成功
|
||
|
||
### 下一步行动
|
||
1. 在阶段四中实现音频采集(PDM 麦克风配置)
|
||
2. 集成音频模块到主程序网络流
|
||
3. 开始摄像头系统开发(阶段 4)
|