# 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`](file:///d:/CodingProjects/Antigravity/AvaotaF1/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`](file:///d:/CodingProjects/Antigravity/AvaotaF1/board.dts) 第 759-797 行: ```dts &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`](file:///d:/CodingProjects/Antigravity/AvaotaF1/src/test_mic.sh) 脚本: **功能**: 1. 检查 ALSA 设备 2. 配置麦克风(打开 MIC Switch,设置 Gain) 3. 执行 5 秒录音测试 4. 播放录音验证 **关键命令**: ```bash # 打开 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 设备验证 在开发板上检查音频设备: ```bash root@(none):/# cat /proc/asound/cards 0 [audiocodec ]: audiocodec - audiocodec audiocodec 1 [sndi2s0 ]: sndi2s0 - sndi2s0 sndi2s0 ``` ✅ **结果**:audiocodec (card 0) 和 sndi2s0 (card 1) 都已正常识别。 ```bash root@(none):/# arecord -l **** List of CAPTURE Hardware Devices **** card 0: audiocodec [audiocodec], device 0: ... ``` ✅ **结果**:audiocodec 录音设备已注册。 --- ### 2. 麦克风配置测试 执行配置命令: ```bash # 打开 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. 录音测试(第一次) ```bash 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 ```bash 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 (最大值) ```bash 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`](file:///d:/CodingProjects/Antigravity/AvaotaF1/src/test_audio.cpp) 第 23 行: ```cpp const char* DEVICE_NAME = "hw:0,0"; // audiocodec 是 card 0 ``` ✅ **兼容性**: - `hw:0,0` 正确指向 audiocodec - 也可以使用 `hw:audiocodec`(更明确) --- ### 2. 集成方案 在主程序 [`main.cpp`](file:///d:/CodingProjects/Antigravity/AvaotaF1/src/main.cpp) 的 `audio_capture_thread()` 中: ```cpp 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 成果 ### 核心成就 1. ✅ **确认硬件类型**:模拟麦克风(非 PDM/DMIC) 2. ✅ **验证 Device Tree**:audiocodec 已完整配置 3. ✅ **创建测试脚本**:[`test_mic.sh`](file:///d:/CodingProjects/Antigravity/AvaotaF1/src/test_mic.sh) 4. ✅ **完成硬件测试**:录音功能正常 5. ✅ **调优增益参数**:MIC Gain = 25 (最佳) 6. ✅ **集成方案设计**:AudioCapture 集成代码 ### 技术文档 - [MIC.md](file:///d:/CodingProjects/Antigravity/NaviGlass/NaviGlassClient/MIC.md) - 官方麦克风使用文档 - [board.dts](file:///d:/CodingProjects/Antigravity/NaviGlass/NaviGlassClient/board.dts) - Device Tree 配置 - [test_mic.sh](file:///d:/CodingProjects/Antigravity/NaviGlass/NaviGlassClient/src/test_mic.sh) - 自动化测试脚本 - [Microphone_Configuration_Complete.md](file:///d:/CodingProjects/Antigravity/NaviGlass/Docs/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) 1. **音频系统集成** - 集成 MIC 配置到主程序 - WebSocket 音频上传测试 - HTTP TTS 下载播放测试 - 完整语音对话闭环验证 2. **或进入摄像头开发**(取决于优先级) - GC2083 摄像头配置 - MPP 库集成 - JPEG 硬件编码 ### 中期目标(Day 6-7) 3. **摄像头系统** - ISP 参数加载 - 视频流采集 - WebSocket 图像上传 4. **IMU 系统** - MPU6050 I2C 配置 - 数据读取和校准 - UDP 上报 5. **系统完整集成** - 多线程协同 - 性能优化 - 稳定性测试 --- ## 经验总结 ### 收获 1. **硬件确认的重要性** - 开发前必须确认实际硬件类型 - 文档(MIC.md)比猜测更可靠 2. **Device Tree 的灵活性** - 如果驱动已配置,无需重复造轮子 - 运行时配置(amixer)比编译时更灵活 3. **逐步调优的必要性** - 增益参数需要实测调优 - 音量和音质需要平衡 ### 启示 - **模拟麦克风 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 引脚配置复杂 **遇到的问题**: 1. Device Tree 配置困难 - PD1/PD2 支持 TWI0,但 PD1 被其他功能占用 - PL2/PL3 不支持 TWI0 功能 2. 设备响应地址但拒绝寄存器访问 - `Write addr (0xD0): ACK` - 地址响应正常 - `Write reg (0x75): NACK` - 寄存器访问失败 3. 可能原因: - 需要特殊的初始化序列 - 寄存器 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`](file:///d:/CodingProjects/Antigravity/AvaotaF1/src/imu/icm42688_spi.cpp) **关键特性**: - GPIO 模拟 SPI(Mode 0: CPOL=0, CPHA=0) - 速度约 500kHz(可调) - 完整的寄存器读写功能 - 6 轴数据读取(加速度、陀螺仪、温度) **SPI 时序实现**: ```cpp 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; } ``` **读取寄存器**: ```cpp 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. 集成方案 **在主程序中**: ```cpp 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 成果(更新) ### 核心成就 1. ✅ **确认硬件类型**:模拟麦克风(非 PDM/DMIC) 2. ✅ **验证 Device Tree**:audiocodec 已完整配置 3. ✅ **创建测试脚本**:[`test_mic.sh`](file:///d:/CodingProjects/Antigravity/AvaotaF1/src/test_mic.sh) 4. ✅ **完成硬件测试**:录音功能正常 5. ✅ **调优增益参数**:MIC Gain = 25 (最佳) 6. ✅ **集成方案设计**:AudioCapture 集成代码 7. ✅ **IMU 配置完成**:ICM-42688 SPI 模式成功 8. ✅ **6 轴数据采集**:加速度、陀螺仪、温度 ### 技术文档 - [MIC.md](file:///d:/CodingProjects/Antigravity/AvaotaF1/MIC.md) - 官方麦克风使用文档 - [board.dts](file:///d:/CodingProjects/Antigravity/AvaotaF1/board.dts) - Device Tree 配置 - [test_mic.sh](file:///d:/CodingProjects/Antigravity/AvaotaF1/src/test_mic.sh) - 麦克风测试脚本 - [icm42688_spi.cpp](file:///d:/CodingProjects/Antigravity/AvaotaF1/src/imu/icm42688_spi.cpp) - IMU SPI 驱动 - [test_imu.cpp](file:///d:/CodingProjects/Antigravity/AvaotaF1/src/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) 1. **音频系统集成** - 集成 MIC 配置到主程序 - WebSocket 音频上传测试 - HTTP TTS 下载播放测试 - 完整语音对话闭环验证 2. **IMU 系统集成** - 集成到主程序多线程 - UDP 数据上报 - 姿态解算(可选) ### 中期目标(Day 6-7) 3. **摄像头系统** - GC2083 配置 - MPP 库集成 - JPEG 硬件编码 - WebSocket 图像上传 4. **系统完整集成** - 多线程协同 - 性能优化 - 稳定性测试 --- ## 经验总结 ### 收获 1. **硬件确认的重要性** - 开发前必须确认实际硬件类型 - 文档(MIC.md)比猜测更可靠 2. **Device Tree 的灵活性** - 如果驱动已配置,无需重复造轮子 - 运行时配置(amixer)比编译时更灵活 3. **逐步调优的必要性** - 增益参数需要实测调优 - 音量和音质需要平衡 4. **接口选型的技巧** - I2C:简单但对时序要求高,易受干扰 - SPI:引脚多但可靠性高,速度快 - 遇到 I2C 问题时,尝试 SPI 是明智选择 5. **GPIO 模拟的实用性** - 绕过复杂的 Device Tree 配置 - 完全用户空间实现,调试方便 - 性能对 IMU 等低速设备完全够用 ### 启示 - **模拟麦克风 vs PDM**:模拟方案配置更简单,但抗干扰能力较弱 - **运行时配置**:MIC Switch 默认关闭,需要程序主动打开 - **增益设置**:过高会放大噪声,过低影响音量,需要找到平衡点 - **SPI vs I2C**:高性能场景优先选择 SPI - **传感器数据理解**:加速度计测量重力+运动,静止时仍有读数 --- **模拟麦克风 + IMU 传感器配置完成!** 🎉