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处理的音频系统 |
4.2 Codec芯片的配置流程
配置Codec,说白了就是通过控制接口(I2C或SPI)往寄存器里写值。你想想看,一个Codec芯片少说几十个寄存器,多的上百个,每个位都控制着不同的功能。我刚开始做的时候,对着数据手册一个一个寄存器配,配完发现没声音,查了半天才发现是某个bit没置1。
配置流程我总结为四步:
- 硬件初始化:先给Codec上电,复位引脚拉低再拉高,等待芯片稳定。这一步不能省,我曾经因为复位时序不对,芯片死活不响应I2C。
- 接口配置:设置I2S格式(比如I2S左对齐、右对齐)、采样率(48kHz还是16kHz)、位深(16bit还是24bit)。这里要注意,Codec的I2S格式必须和主控的I2S控制器一致,否则出来的声音就是噪音。
- 音频通路配置:选择输入源(比如LINE_IN还是MIC)、输出目标(耳机还是喇叭)、音量大小。PIS系统里,我一般把语音通道和音乐通道分开配置,方便独立控制音量。
- 附加功能配置:比如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
}
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驱动写完了,怎么知道它工作正常?我一般按这个顺序排查:
- I2C通信:先确认能读写寄存器。用i2cget/i2cset工具读一下芯片ID寄存器,如果能读到预期值,说明通信没问题。
- I2S数据:用示波器看BCLK、LRCLK、DIN/DOUT有没有波形。没波形说明主控没发数据,有波形但声音不对,可能是格式不匹配。
- 模拟输出:用万用表量一下Codec的输出引脚,看有没有直流偏置。如果有直流,说明输出通路配置有问题,可能会烧喇叭。
优化方面,PIS系统里最头疼的是pop声。说白了,就是Codec上电或下电时,电容充放电产生的咔嗒声。我常用的办法是:先静音,再上电;先下电,再取消静音。驱动里用digital_mute回调来控制这个时序。
好了,关于音频Codec芯片的介绍、配置流程和驱动开发,今天就聊到这儿。下一章咱们会讲音频数据的处理,比如混音、音量控制这些实战内容。到时候再细聊。