Files
Docs/DevLogs/Day4.md
2025-12-31 16:18:28 +08:00

20 KiB
Raw Blame History

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:audiocodechw: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 脚本:

功能

  1. 检查 ALSA 设备
  2. 配置麦克风(打开 MIC Switch设置 Gain
  3. 执行 5 秒录音测试
  4. 播放录音验证

关键命令

# 打开 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 时音量较小
方案:逐步增加增益值

测试 1MIC 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

结果:音量适中,清晰度良好

测试 2MIC 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.cppaudio_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:audiocodechw:0,0

第七部分:问题与解决

问题 1音量太小

现象MIC Gain 15 时录音音量偏小
原因:初始增益设置过低
解决:增加 MIC Gain 到 25

问题 2增益过高有杂音

现象MIC Gain 31 时有轻微底噪
原因:增益过高放大了底噪
解决:降低到 25平衡音量和音质


🏆 Day 4 成果

核心成就

  1. 确认硬件类型:模拟麦克风(非 PDM/DMIC
  2. 验证 Device Treeaudiocodec 已完整配置
  3. 创建测试脚本test_mic.sh
  4. 完成硬件测试:录音功能正常
  5. 调优增益参数MIC Gain = 25 (最佳)
  6. 集成方案设计AudioCapture 集成代码

技术文档

测试结果

测试项 状态 备注
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

  1. 摄像头系统

    • ISP 参数加载
    • 视频流采集
    • WebSocket 图像上传
  2. IMU 系统

    • MPU6050 I2C 配置
    • 数据读取和校准
    • UDP 上报
  3. 系统完整集成

    • 多线程协同
    • 性能优化
    • 稳定性测试

经验总结

收获

  1. 硬件确认的重要性

    • 开发前必须确认实际硬件类型
    • 文档MIC.md比猜测更可靠
  2. Device Tree 的灵活性

    • 如果驱动已配置,无需重复造轮子
    • 运行时配置amixer比编译时更灵活
  3. 逐步调优的必要性

    • 增益参数需要实测调优
    • 音量和音质需要平衡

启示

  • 模拟麦克风 vs PDM:模拟方案配置更简单,但抗干扰能力较弱
  • 运行时配置MIC Switch 默认关闭,需要程序主动打开
  • 增益设置:过高会放大噪声,过低影响音量,需要找到平衡点

第八部分IMU 传感器配置

1. 硬件选型

传感器型号ICM-42688-P

  • 6 轴 IMU3 轴加速度计 + 3 轴陀螺仪)
  • 支持 I2C 和 SPI 双接口
  • 高精度、低功耗
  • TDK InvenSense 出品

2. 接口选择I2C vs SPI

I2C 尝试(失败)

初始方案

  • 使用 GPIO 模拟 I2CPD3/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

关键特性

  • GPIO 模拟 SPIMode 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 成果(更新)

核心成就

  1. 确认硬件类型:模拟麦克风(非 PDM/DMIC
  2. 验证 Device Treeaudiocodec 已完整配置
  3. 创建测试脚本test_mic.sh
  4. 完成硬件测试:录音功能正常
  5. 调优增益参数MIC Gain = 25 (最佳)
  6. 集成方案设计AudioCapture 集成代码
  7. IMU 配置完成ICM-42688 SPI 模式成功
  8. 6 轴数据采集:加速度、陀螺仪、温度

技术文档

测试结果

测试项 状态 备注
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

  1. 摄像头系统

    • GC2083 配置
    • MPP 库集成
    • JPEG 硬件编码
    • WebSocket 图像上传
  2. 系统完整集成

    • 多线程协同
    • 性能优化
    • 稳定性测试

经验总结

收获

  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 传感器配置完成! 🎉