4、音频编解码器:常用音频Codec芯片介绍、配置流程与驱动开发

音频编解码器,也就是我们常说的Codec芯片,是PIS系统里音频通路的核心。说白了,它就是把数字音频信号转成模拟信号(播放),或者把模拟信号转成数字信号(录音)。我这些年经手过的Codec芯片少说也有十几种,从低端到高端,从单声道到多通道,踩过的坑真不少。今天咱们就聊聊常用的Codec芯片、怎么配置它、以及驱动怎么开发。

4.1 常用音频Codec芯片选型

选Codec芯片,我一般先看三个指标:信噪比(SNR)、采样率支持、以及接口类型。PIS系统里,语音播报和背景音乐是常态,所以对音质要求不算极端,但稳定性必须高。

我个人习惯把常用Codec分成三类:

  • 入门级:比如WM8960、TLV320AIC23B。适合成本敏感的项目,信噪比在90dB左右,支持I2S接口。我在一个公交报站器项目里用过WM8960,驱动简单,但底噪偏大,后来加了RC滤波才压下去。
  • 主流级:比如CS42L52、SGTL5000。信噪比能到100dB以上,支持多路输入输出。我记得有个地铁PIS项目,要求同时播放语音和背景音乐,还要能混音,CS42L52的硬件混音功能帮了大忙。
  • 高端级:比如ADAU1361、TAS5805M。这些芯片自带DSP,可以做EQ、动态范围压缩。嗯,说实话,PIS系统里用到DSP的场景不多,但如果你要做语音增强或者噪声抑制,这类芯片就很有优势。
芯片型号 信噪比 接口 典型应用
WM8960 98dB I2S, I2C 低成本语音播报
CS42L52 103dB I2S, I2C/SPI 多通道混音播放
ADAU1361 110dB I2S, TDM, I2C 带DSP处理的音频系统
我的小建议:选型时别只看数据手册。我建议你拿开发板实际听一下底噪,尤其是功放开启时的pop声。有些芯片数据很漂亮,但实际用起来就是有咔咔声。

4.2 Codec芯片的配置流程

配置Codec,说白了就是通过控制接口(I2C或SPI)往寄存器里写值。你想想看,一个Codec芯片少说几十个寄存器,多的上百个,每个位都控制着不同的功能。我刚开始做的时候,对着数据手册一个一个寄存器配,配完发现没声音,查了半天才发现是某个bit没置1。

配置流程我总结为四步:

  1. 硬件初始化:先给Codec上电,复位引脚拉低再拉高,等待芯片稳定。这一步不能省,我曾经因为复位时序不对,芯片死活不响应I2C。
  2. 接口配置:设置I2S格式(比如I2S左对齐、右对齐)、采样率(48kHz还是16kHz)、位深(16bit还是24bit)。这里要注意,Codec的I2S格式必须和主控的I2S控制器一致,否则出来的声音就是噪音。
  3. 音频通路配置:选择输入源(比如LINE_IN还是MIC)、输出目标(耳机还是喇叭)、音量大小。PIS系统里,我一般把语音通道和音乐通道分开配置,方便独立控制音量。
  4. 附加功能配置:比如EQ、静音、自动增益控制(AGC)。这些功能看需求,不是必须的。

下面是一个典型的WM8960初始化代码片段,我用I2C写寄存器:

// WM8960 初始化配置示例
void wm8960_init(void) {
    // 复位芯片
    wm8960_write_reg(0x0F, 0x0000);  // 软件复位
    
    // 配置时钟和采样率
    wm8960_write_reg(0x00, 0x0080);  // 使能VREF和VMID
    wm8960_write_reg(0x02, 0x0138);  // 设置采样率48kHz
    
    // 配置音频接口
    wm8960_write_reg(0x04, 0x0002);  // I2S格式,16bit
    
    // 配置输出通路
    wm8960_write_reg(0x1A, 0x00F0);  // 左声道输出使能
    wm8960_write_reg(0x1B, 0x00F0);  // 右声道输出使能
    
    // 设置音量
    wm8960_write_reg(0x1C, 0x00AF);  // 左声道音量 0dB
    wm8960_write_reg(0x1D, 0x00AF);  // 右声道音量 0dB
}
注意:写寄存器时一定要确认I2C地址对不对。WM8960的7位地址是0x1A,但有些数据手册写的是0x34(8位地址)。我曾经因为这个搞了一下午,最后发现是地址搞反了。

4.3 驱动开发实战

驱动开发,说白了就是让主控芯片(比如ARM、DSP)能控制Codec干活。Linux下一般用ALSA框架,裸机开发就直接操作I2C和I2S外设。

我以Linux ALSA驱动为例,讲讲Codec驱动的核心部分。ALSA驱动里,Codec驱动主要实现三个回调:

  • init:芯片初始化,就是上面说的写寄存器。
  • hw_params:设置音频参数,比如采样率、位深。
  • digital_mute:静音控制,防止播放开始或停止时的pop声。

嗯,这里要注意,hw_params回调里一定要检查Codec是否支持你设置的参数。我遇到过有人把采样率设成96kHz,但Codec只支持48kHz,结果播放出来的声音像快进一样。

下面是一个简化的ALSA Codec驱动结构:

static struct snd_soc_dai_ops wm8960_dai_ops = {
    .hw_params = wm8960_hw_params,
    .digital_mute = wm8960_mute,
};

static struct snd_soc_codec_driver wm8960_codec_drv = {
    .probe = wm8960_probe,
    .remove = wm8960_remove,
    .controls = wm8960_controls,
    .num_controls = ARRAY_SIZE(wm8960_controls),
};

static int wm8960_hw_params(struct snd_pcm_substream *substream,
                            struct snd_pcm_hw_params *params,
                            struct snd_soc_dai *dai) {
    // 根据采样率设置Codec内部时钟分频
    int rate = params_rate(params);
    switch (rate) {
        case 48000:
            wm8960_write_reg(WM8960_CLOCKING1, 0x0138);
            break;
        case 44100:
            wm8960_write_reg(WM8960_CLOCKING1, 0x012C);
            break;
        default:
            return -EINVAL;  // 不支持的采样率
    }
    return 0;
}

避坑指南:我曾经在调试驱动时,发现播放声音有周期性杂音。查了两天,最后发现是I2S的位时钟极性配置反了。Codec和主控的I2S时钟极性必须一致,否则数据采样会错位。这个坑,我建议你在调试时先用示波器抓一下I2S的BCLK和LRCLK波形,确认时序对不对。

4.4 调试与优化

Codec驱动写完了,怎么知道它工作正常?我一般按这个顺序排查:

  1. I2C通信:先确认能读写寄存器。用i2cget/i2cset工具读一下芯片ID寄存器,如果能读到预期值,说明通信没问题。
  2. I2S数据:用示波器看BCLK、LRCLK、DIN/DOUT有没有波形。没波形说明主控没发数据,有波形但声音不对,可能是格式不匹配。
  3. 模拟输出:用万用表量一下Codec的输出引脚,看有没有直流偏置。如果有直流,说明输出通路配置有问题,可能会烧喇叭。

优化方面,PIS系统里最头疼的是pop声。说白了,就是Codec上电或下电时,电容充放电产生的咔嗒声。我常用的办法是:先静音,再上电;先下电,再取消静音。驱动里用digital_mute回调来控制这个时序。

我的经验:如果pop声还是压不下去,试试在输出通路里加一个软启动电容,或者用Codec自带的ramp up功能。WM8960有个寄存器可以控制输出缓启动,效果不错。

好了,关于音频Codec芯片的介绍、配置流程和驱动开发,今天就聊到这儿。下一章咱们会讲音频数据的处理,比如混音、音量控制这些实战内容。到时候再细聊。