20 KiB
Avaota F1 开发日志 - Day 4:模拟麦克风配置
版本:v1.0
日期:2025-11-26
主机环境:Windows + Ubuntu 24.04 LTS
目标平台:Avaota F1 (全志 V821 / 32-bit RISC-V)
项目背景
根据 Day 3 的计划,原定配置 PDM 数字麦克风(DMIC)以实现音频采集功能。但在实际开发中发现,开发板使用的是板载模拟麦克风,通过芯片内部 Audio Codec 进行音频采集,而非独立的 PDM/DMIC 设备。
第一部分:硬件类型确认
1. 文档确认
查阅 MIC.md 文档后确认:
硬件配置:
- 麦克风类型:模拟麦克风(板载)
- 接口:通过芯片内部 Audio Codec
- ALSA 设备名称:
hw:audiocodec或hw:0,0 - 驱动:
audiocodec(已内置于内核)
关键区别:
| 项目 | 原计划 (PDM/DMIC) | 实际硬件 (模拟麦克风) |
|---|---|---|
| 接口 | PDM 数字信号 | 模拟信号 → ADC |
| Device Tree | 需配置 &dmic 节点 |
使用 &codec 节点 |
| 引脚配置 | 需 pinctrl 设置 | 无需额外引脚配置 |
| ALSA 设备 | hw:snddmic |
hw:audiocodec |
| 运行时配置 | 即插即用 | 需打开 MIC Switch |
结论:配置难度大幅降低,无需修改 Device Tree。
第二部分:Device Tree 验证
1. 检查现有配置
查看 board.dts 第 759-797 行:
&codec {
tx-hub-en;
rx-sync-en;
dac-vol = <63>; /* DAC 音量 */
dacl-vol = <160>; /* DAC L 音量 */
adc-vol = <160>; /* ✅ ADC 音量(麦克风采集)*/
lineout-gain = <31>; /* LINEOUT 增益 */
mic-gain = <31>; /* ✅ 麦克风增益(最大值)*/
adcdelaytime = <0>; /* ADC 延迟时间 */
pa-pin-max = <1>;
pa-pin-0 = <&pio PC 15 GPIO_ACTIVE_HIGH>;
pa-pin-level-0 = <1>;
pa-pin-msleep-0 = <0>;
status = "okay"; /* ✅ 已启用 */
};
&codec_plat {
status = "okay";
};
&codec_mach {
status = "okay";
soundcard-mach,cpu {
sound-dai = <&codec_plat>;
};
soundcard-mach,codec {
sound-dai = <&codec>;
};
};
验证结果:
- ✅
&codec节点已启用(status = "okay") - ✅
mic-gain已设置为 31(最大值) - ✅
adc-vol已配置为 160 - ✅ 无需修改 Device Tree
第三部分:测试脚本开发
1. 创建自动化测试脚本
为了简化测试流程,创建了 test_mic.sh 脚本:
功能:
- 检查 ALSA 设备
- 配置麦克风(打开 MIC Switch,设置 Gain)
- 执行 5 秒录音测试
- 播放录音验证
关键命令:
# 打开 MIC 开关(必须)
amixer -Dhw:audiocodec cset name="MIC Switch" 1
# 设置 MIC 增益
amixer -Dhw:audiocodec cset name="MIC Gain" 15
# 录音测试
arecord -D hw:audiocodec -f S16_LE -t wav -r 16000 -c 1 -d 5 /tmp/test_mic.wav
# 播放验证(使用 I2S 扬声器)
aplay -D hw:1,0 /tmp/test_mic.wav
第四部分:开发板测试
1. ALSA 设备验证
在开发板上检查音频设备:
root@(none):/# cat /proc/asound/cards
0 [audiocodec ]: audiocodec - audiocodec
audiocodec
1 [sndi2s0 ]: sndi2s0 - sndi2s0
sndi2s0
✅ 结果:audiocodec (card 0) 和 sndi2s0 (card 1) 都已正常识别。
root@(none):/# arecord -l
**** List of CAPTURE Hardware Devices ****
card 0: audiocodec [audiocodec], device 0: ...
✅ 结果:audiocodec 录音设备已注册。
2. 麦克风配置测试
执行配置命令:
# 打开 MIC 开关
root@(none):/# amixer -Dhw:audiocodec cset name="MIC Switch" 1
numid=16,iface=MIXER,name='MIC Switch'
; type=BOOLEAN,access=rw------,values=1
: values=on
# 设置初始增益为 15
root@(none):/# amixer -Dhw:audiocodec cset name="MIC Gain" 15
numid=15,iface=MIXER,name='MIC Gain'
; type=INTEGER,access=rw---R--,values=1,min=0,max=31,step=0
: values=15
✅ 结果:MIC Switch 已打开,增益设置成功。
3. 录音测试(第一次)
root@(none):/# arecord -D hw:audiocodec -f S16_LE -t wav -r 16000 -c 1 -d 5 /tmp/test.wav
Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 16000 Hz, Mono
root@(none):/# ls -lh /tmp/test.wav
-rw-r--r-- 1 root root 160.0K Nov 26 15:32 test.wav
root@(none):/# aplay -D hw:1,0 /tmp/test.wav
Playing WAVE '/tmp/test.wav' : Signed 16 bit Little Endian, Rate 16000 Hz, Mono
✅ 结果:
- 录音成功,文件大小正常(160KB ≈ 5 秒 @ 16kHz)
- 播放时有声音,听得清
- 但声音很小
4. 增益调优
问题:MIC Gain 15 时音量较小
方案:逐步增加增益值
测试 1:MIC Gain = 25
root@(none):/# amixer -Dhw:audiocodec cset name="MIC Gain" 25
root@(none):/# arecord -D hw:audiocodec -f S16_LE -t wav -r 16000 -c 1 -d 5 /tmp/test2.wav
root@(none):/# aplay -D hw:1,0 /tmp/test2.wav
✅ 结果:音量适中,清晰度良好
测试 2:MIC Gain = 31 (最大值)
root@(none):/# amixer -Dhw:audiocodec cset name="MIC Gain" 31
root@(none):/# arecord -D hw:audiocodec -f S16_LE -t wav -r 16000 -c 1 -d 5 /tmp/test3.wav
root@(none):/# aplay -D hw:1,0 /tmp/test3.wav
✅ 结果:音量最大,但有轻微底噪
最佳配置:MIC Gain = 25
- 音量适中
- 音质清晰
- 无明显杂音
第五部分:AudioCapture 集成
1. 验证设备名称
检查 test_audio.cpp 第 23 行:
const char* DEVICE_NAME = "hw:0,0"; // audiocodec 是 card 0
✅ 兼容性:
hw:0,0正确指向 audiocodec- 也可以使用
hw:audiocodec(更明确)
2. 集成方案
在主程序 main.cpp 的 audio_capture_thread() 中:
void audio_capture_thread() {
LOG_INFO("Starting audio capture thread...");
// 1. 配置麦克风(打开 MIC 开关和增益)
system("amixer -Dhw:audiocodec cset name='MIC Switch' 1");
system("amixer -Dhw:audiocodec cset name='MIC Gain' 25");
// 2. 初始化音频采集
AudioCapture mic("hw:audiocodec", 16000, 1);
if (!mic.init()) {
LOG_ERROR("Failed to initialize microphone");
return;
}
// 3. 连接 WebSocket 服务器
WSClient ws_aud(SERVER_HOST, SERVER_PORT, "/ws_audio");
if (!ws_aud.connect()) {
LOG_ERROR("Failed to connect to audio WebSocket");
return;
}
// 4. 音频采集循环
int16_t buffer[320]; // 20ms @ 16kHz
while (g_running) {
snd_pcm_sframes_t frames = mic.read(buffer, 320);
if (frames > 0) {
// 发送音频数据到服务器
ws_aud.send_binary((uint8_t*)buffer, frames * 2);
}
}
LOG_INFO("Audio capture thread stopped");
}
关键点:
- 使用
system()调用amixer配置(简单直接) - 或者可以使用 ALSA Mixer API(更优雅)
第六部分:技术总结
1. 关键发现
硬件差异:
- 原计划:PDM 数字麦克风(DMIC)
- 实际硬件:模拟麦克风(Audio Codec ADC)
配置方式:
- Device Tree:✅ 已完整配置,无需修改
- 运行时:需通过
amixer打开 MIC Switch
2. 配置要点
| 配置项 | 说明 | 推荐值 |
|---|---|---|
| MIC Switch | 麦克风开关 (必须打开) | 1 (on) |
| MIC Gain | 麦克风增益 (0-31) | 25 |
| ADC Volume | ADC 音量 (0-255) | 160 (默认) |
3. 音频参数
- 采样率:16000 Hz (16kHz)
- 位深度:16-bit signed PCM (S16_LE)
- 声道数:1 (Mono)
- ALSA 设备:
hw:audiocodec或hw:0,0
第七部分:问题与解决
问题 1:音量太小
现象:MIC Gain 15 时录音音量偏小
原因:初始增益设置过低
解决:增加 MIC Gain 到 25
问题 2:增益过高有杂音
现象:MIC Gain 31 时有轻微底噪
原因:增益过高放大了底噪
解决:降低到 25,平衡音量和音质
🏆 Day 4 成果
核心成就
- ✅ 确认硬件类型:模拟麦克风(非 PDM/DMIC)
- ✅ 验证 Device Tree:audiocodec 已完整配置
- ✅ 创建测试脚本:
test_mic.sh - ✅ 完成硬件测试:录音功能正常
- ✅ 调优增益参数:MIC Gain = 25 (最佳)
- ✅ 集成方案设计:AudioCapture 集成代码
技术文档
- MIC.md - 官方麦克风使用文档
- board.dts - Device Tree 配置
- test_mic.sh - 自动化测试脚本
- Microphone_Configuration_Complete.md - 完整配置报告
测试结果
| 测试项 | 状态 | 备注 |
|---|---|---|
| ALSA 设备识别 | ✅ 通过 | audiocodec (card 0) |
| MIC Switch 配置 | ✅ 通过 | amixer 控制正常 |
| 录音功能 | ✅ 通过 | 5 秒录音成功 |
| 音质测试 | ✅ 通过 | 清晰、无明显杂音 |
| 增益调优 | ✅ 完成 | MIC Gain = 25 最佳 |
配置完成度
音频系统:
- ✅ 音频播放(I2S + MAX98357A)- Day 3 完成
- ✅ 音频采集(模拟麦克风 + audiocodec)- Day 4 完成
- ⏳ 端到端集成(WebSocket + HTTP)- 待完成
整体进度:约 60% (+10%)
下一步计划
短期目标(Day 5)
-
音频系统集成
- 集成 MIC 配置到主程序
- WebSocket 音频上传测试
- HTTP TTS 下载播放测试
- 完整语音对话闭环验证
-
或进入摄像头开发(取决于优先级)
- GC2083 摄像头配置
- MPP 库集成
- JPEG 硬件编码
中期目标(Day 6-7)
-
摄像头系统
- ISP 参数加载
- 视频流采集
- WebSocket 图像上传
-
IMU 系统
- MPU6050 I2C 配置
- 数据读取和校准
- UDP 上报
-
系统完整集成
- 多线程协同
- 性能优化
- 稳定性测试
经验总结
收获
-
硬件确认的重要性
- 开发前必须确认实际硬件类型
- 文档(MIC.md)比猜测更可靠
-
Device Tree 的灵活性
- 如果驱动已配置,无需重复造轮子
- 运行时配置(amixer)比编译时更灵活
-
逐步调优的必要性
- 增益参数需要实测调优
- 音量和音质需要平衡
启示
- 模拟麦克风 vs PDM:模拟方案配置更简单,但抗干扰能力较弱
- 运行时配置:MIC Switch 默认关闭,需要程序主动打开
- 增益设置:过高会放大噪声,过低影响音量,需要找到平衡点
第八部分:IMU 传感器配置
1. 硬件选型
传感器型号:ICM-42688-P
- 6 轴 IMU(3 轴加速度计 + 3 轴陀螺仪)
- 支持 I2C 和 SPI 双接口
- 高精度、低功耗
- TDK InvenSense 出品
2. 接口选择:I2C vs SPI
I2C 尝试(失败)
初始方案:
- 使用 GPIO 模拟 I2C(PD3/PD2)
- 原因:硬件 I2C 引脚配置复杂
遇到的问题:
- Device Tree 配置困难
- PD1/PD2 支持 TWI0,但 PD1 被其他功能占用
- PL2/PL3 不支持 TWI0 功能
- 设备响应地址但拒绝寄存器访问
Write addr (0xD0): ACK- 地址响应正常Write reg (0x75): NACK- 寄存器访问失败
- 可能原因:
- 需要特殊的初始化序列
- 寄存器 Bank 切换问题
- I2C 时序要求严格
SPI 方案(成功)✅
优势:
- 协议简单,无 ACK/NACK 握手
- 时序可靠,主设备完全控制
- 高速传输(可达 MHz 级)
- ICM-42688 官方推荐接口
实现方式:GPIO 模拟 SPI
3. 硬件连接(SPI 模式)
ICM-42688 模块 → Avaota F1
-------------- ---------
VCC → 3.3V
GND → GND
SCL/SCLK → PD3 (GPIO 99)
SDA/MOSI → PD2 (GPIO 98)
AD0/MISO → PD4 (GPIO 100)
CS → PD5 (GPIO 101)
INT1/INT2 → 悬空(未使用)
4. 软件实现
驱动文件:icm42688_spi.cpp
关键特性:
- GPIO 模拟 SPI(Mode 0: CPOL=0, CPHA=0)
- 速度约 500kHz(可调)
- 完整的寄存器读写功能
- 6 轴数据读取(加速度、陀螺仪、温度)
SPI 时序实现:
static uint8_t spi_transfer_byte(uint8_t tx_byte) {
uint8_t rx_byte = 0;
for (int i = 7; i >= 0; i--) {
// 设置 MOSI
if (tx_byte & (1 << i)) mosi_high();
else mosi_low();
sclk_high(); // 上升沿:从设备采样
if (miso_read()) rx_byte |= (1 << i);
sclk_low(); // 下降沿:从设备准备下一位
}
return rx_byte;
}
读取寄存器:
uint8_t ICM42688::read_register(uint8_t reg) {
cs_select();
spi_transfer_byte(reg | 0x80); // 读操作:地址最高位为 1
uint8_t value = spi_transfer_byte(0x00); // Dummy 字节
cs_deselect();
return value;
}
5. 测试结果
成功识别:
[INFO] ICM42688 detected, WHO_AM_I = 0x47
[INFO] ICM42688 initialized successfully
数据采集(静止状态):
[Sample 1]
Accel: X= -2.52 Y= -9.40 Z= 1.37 m/s²
Gyro: X= -0.06 Y= 0.00 Z= -0.18 °/s
Temp: 19.33 °C
数据分析:
- ✅ 加速度计:总加速度 ≈ 9.8 m/s²(重力加速度)
- ✅ 陀螺仪:接近 0°/s(静止状态)
- ✅ 温度:19.3°C(室温)
- ✅ 数据稳定:10 次采样波动极小
6. 配置参数
| 参数 | 配置值 | 说明 |
|---|---|---|
| 加速度计量程 | ±16g | AFS_16G |
| 陀螺仪量程 | ±2000°/s | GFS_2000DPS |
| 输出数据率 | 1kHz | ODR_1KHZ |
| SPI 时钟 | ~500kHz | GPIO 模拟 |
7. 技术要点
为什么加速度计静止也有数值?
传感器测量的是加速度,包括:
- 运动加速度(移动时产生)
- 重力加速度(始终存在,约 9.8 m/s²)
静止状态下:
- 运动加速度 = 0
- 重力加速度 ≠ 0(分解在 X/Y/Z 三轴)
- 总加速度 = √(X² + Y² + Z²) ≈ 9.8 m/s²
陀螺仪数据理解:
- ✅ 静止时:0°/s±噪声
- ✅ 旋转时:几十到几百°/s
8. 集成方案
在主程序中:
void imu_thread() {
LOG_INFO("Starting IMU thread...");
ICM42688 imu("/dev/spidev0.0", 0); // SPI 模式
if (!imu.init()) {
LOG_ERROR("Failed to initialize IMU");
return;
}
// UDP 发送器
UDPSender udp(SERVER_HOST, UDP_PORT);
while (g_running) {
if (imu.read_sensor()) {
// 构造 IMU 数据包
ImuData data = {
.ax = imu.get_accel_x(),
.ay = imu.get_accel_y(),
.az = imu.get_accel_z(),
.gx = imu.get_gyro_x(),
.gy = imu.get_gyro_y(),
.gz = imu.get_gyro_z(),
.temp = imu.get_temperature()
};
// UDP 发送
udp.send((uint8_t*)&data, sizeof(data));
}
usleep(10000); // 100Hz
}
}
🏆 Day 4 成果(更新)
核心成就
- ✅ 确认硬件类型:模拟麦克风(非 PDM/DMIC)
- ✅ 验证 Device Tree:audiocodec 已完整配置
- ✅ 创建测试脚本:
test_mic.sh - ✅ 完成硬件测试:录音功能正常
- ✅ 调优增益参数:MIC Gain = 25 (最佳)
- ✅ 集成方案设计:AudioCapture 集成代码
- ✅ IMU 配置完成:ICM-42688 SPI 模式成功
- ✅ 6 轴数据采集:加速度、陀螺仪、温度
技术文档
- MIC.md - 官方麦克风使用文档
- board.dts - Device Tree 配置
- test_mic.sh - 麦克风测试脚本
- icm42688_spi.cpp - IMU SPI 驱动
- test_imu.cpp - IMU 测试程序
测试结果
| 测试项 | 状态 | 备注 |
|---|---|---|
| ALSA 设备识别 | ✅ 通过 | audiocodec (card 0) |
| MIC Switch 配置 | ✅ 通过 | amixer 控制正常 |
| 录音功能 | ✅ 通过 | 5 秒录音成功 |
| 音质测试 | ✅ 通过 | 清晰、无明显杂音 |
| 增益调优 | ✅ 完成 | MIC Gain = 25 最佳 |
| IMU 识别 | ✅ 通过 | WHO_AM_I = 0x47 |
| IMU 数据读取 | ✅ 通过 | 6 轴数据正常 |
| SPI 通信 | ✅ 通过 | GPIO 模拟稳定 |
配置完成度
传感器系统:
- ✅ 音频播放(I2S + MAX98357A)- Day 3 完成
- ✅ 音频采集(模拟麦克风 + audiocodec)- Day 4 完成
- ✅ IMU 传感器(ICM-42688 SPI)- Day 4 完成
- ⏳ 摄像头(GC2083 MIPI)- 待完成
- ⏳ 端到端集成(WebSocket + HTTP + UDP)- 待完成
整体进度:约 70% (+10%)
下一步计划
短期目标(Day 5)
-
音频系统集成
- 集成 MIC 配置到主程序
- WebSocket 音频上传测试
- HTTP TTS 下载播放测试
- 完整语音对话闭环验证
-
IMU 系统集成
- 集成到主程序多线程
- UDP 数据上报
- 姿态解算(可选)
中期目标(Day 6-7)
-
摄像头系统
- GC2083 配置
- MPP 库集成
- JPEG 硬件编码
- WebSocket 图像上传
-
系统完整集成
- 多线程协同
- 性能优化
- 稳定性测试
经验总结
收获
-
硬件确认的重要性
- 开发前必须确认实际硬件类型
- 文档(MIC.md)比猜测更可靠
-
Device Tree 的灵活性
- 如果驱动已配置,无需重复造轮子
- 运行时配置(amixer)比编译时更灵活
-
逐步调优的必要性
- 增益参数需要实测调优
- 音量和音质需要平衡
-
接口选型的技巧
- I2C:简单但对时序要求高,易受干扰
- SPI:引脚多但可靠性高,速度快
- 遇到 I2C 问题时,尝试 SPI 是明智选择
-
GPIO 模拟的实用性
- 绕过复杂的 Device Tree 配置
- 完全用户空间实现,调试方便
- 性能对 IMU 等低速设备完全够用
启示
- 模拟麦克风 vs PDM:模拟方案配置更简单,但抗干扰能力较弱
- 运行时配置:MIC Switch 默认关闭,需要程序主动打开
- 增益设置:过高会放大噪声,过低影响音量,需要找到平衡点
- SPI vs I2C:高性能场景优先选择 SPI
- 传感器数据理解:加速度计测量重力+运动,静止时仍有读数
模拟麦克风 + IMU 传感器配置完成! 🎉