一、音频接口简介
1 为何需要音频编解码芯片?
处理器要想 "听到" 外界的声音,必须把外界的声音转化为自己能够理解的 "语言",处理器能理解的就是 0 和 1,也就是二进制数据。所以我们需要先把外界的声音转换为处理器能理解的 0 和 1,在信号处理领域,外界的声音是模拟信号,处理器能理解的是数字信号,因此这里就涉及到一个模拟信号转换为数字信号的过程,而完成这个功能的就是 ADC 芯片。
同理,如果处理器要向外界传达自己的 "心声",也就是放音,那么就涉及到将处理器能理解的 0 和 1 转化为外界能理解的连续变化的声音,这个过程就是将数字信号转化为模拟信号,而完成这个功能的是 DAC 芯片。
现在我们知道了,处理器如果既想 "听到" 外界的声音,又想向外界传达自己的 "心声",那么就需要同时用到 DAC 和 ADC 这两款芯片。那是不是买两颗 DAC 和 ADC 芯片就行了呢?答案肯定是可以的,但是音频不单单是能出声、能听到就行。我们往往需要听到的声音动听、录进去的语音贴近真实、可以调节音效、对声音能够进行一些处理 (需要 DSP 单元)、拥有统一的标准接口,方便开发等等。将这些针对声音的各种要求全部叠加到 DAC 和 ADC 芯片上,那么就会得到一个专门用于音频的芯片,也就是音频编解码芯片,英文名字就是 Audio CODEC,所以我们在手机或者电脑的介绍中看到 "CODEC" 这个词语,一般说的都是音频编解码。
既然音频 CODEC 的本质是 ADC 和 DAC,那么采样率和采样位数就是衡量一款音频 CODEC 最重要的指标。比如常见音频采样率有 8K、44.1K、48K、192K 甚至 384K 和 768K,采样位数常见的有 8 位、16 位、24 位、32 位。采样率和采样位数越高,那么音频 CODEC 越能真实的还原原声音,也就是大家说的 HIFI。因此大家会看到高端的音频播放器都会有很高的采样率和采样位数,同样的价格也会越高。当然了,实际的效果还与其他部分有关,采样率和采样位数只是其中重要的指标之一。
接下来看一下专用的音频编解码芯片都有哪些特性。
2、WM8960 音频芯片
WM8960 是一颗由 wolfson (欧胜) 公司出品的音频编解码芯片,是一颗低功耗、高质量的立体声音频 CODEC。集成 D 类喇叭功放,每个通道可以驱动一个 1W 喇叭 (8Ω)。内部集成 3 个立体声输入源,可以灵活配置,拥有一路完整的麦克风接口。WM8960 内部 ADC 和 DAC 都为 24 位,WM8960 主要特性如下所示:
①、DAC 的 SNR (信噪比) 为 98dB,3.3V、48KHz 下 THD (谐波失真) 为 - 84dB。
②、ADC 的 SNR (信噪比) 为 94dB,3.3V、48KHz 下 THD (谐波失真) 为 - 82dB。
③、3D 增强。
④、立体声 D 类功放,可以直接外接喇叭,8Ω 负载下每通道 1W。
⑤、集成耳机接口。
⑥、集成麦克风接口。
⑦、采样率支持 8K、11.025K、12K、16K、22.05K、24K、32K、44.1K 和 48K。
......
WM8960 整体框图如图所示:

依次来看一下图中这四部分接口都是什么功能:
①、此部分是 WM8960 提供的输入接口,作为立体声音频输入源,一共提供了三路,分别为 LINPUT1/RINPUT1、LINPUT2/RINPUT2、LINPUT3/RINPUT3。麦克风或线路输入就连接到此接口上,这部分是需要硬件工程师重点关心的,因为音频选择从哪一路进入需要在画 PCB 的时候就应该定好。
②、此部分是 WM8960 的输出接口,比如输出给耳机或喇叭,SPK_LP/SPK_LN 用于连接左声道的喇叭,支持 1W 的 8Ω 喇叭。SPK_RP/SPK_RN 用于连接右声道的喇叭,同样支持 1W 的 8Ω 喇叭,最后就是 HP_L/HP_R,用于连接耳机。
③、此部分是数字音频接口,用于和主控制器连接,有 5 根线,用于主控制器和 WM8960 之间进行数据 "沟通"。主控制器向 WM8960 的 DAC 发送的数据,WM8960 的 ADC 向主控制器传递的数据都是通过此音频接口来完成的。这个接口非常重要,是我们驱动开发人员重点关注的,此接口支持 I2S 格式。此接口 5 根线的作用如下:
- ADCDAT:ADC 数据输出引脚,采集到的音频数据转换为数字信号以后通过此引脚传输给主控制器。
- ADCLRC:ADC 数据对齐时钟,也就是帧时钟 (LRCK),用于切换左右声道数据,此信号的频率就是采样率。此引脚可以配置为 GPIO 功能,配置为 GPIO 以后 ADC 就会使用 DACLRC 引脚作为帧时钟。
- DACDAT:DAC 数据输入引脚,主控制器通过此引脚将数字信号输入给 WM8960 的 DAC。
- DACLRC:DAC 数据对齐时钟,功能和 ADCLRC 一样,都是帧时钟 (LRCK),用于切换左右声道数据,此信号的频率等于采样率。
- BCLK:位时钟,用于同步。
- MCLK:主时钟,WM8960 工作的时候还需要一路主时钟,此时钟由 I.MX6ULL 提供,MCLK 频率等于采样率的 256 或 384 倍,因此大家在 WM8960 的数据手册里面常看到 MCLK=256fs 或 MCLK=384fs。
④、此部分为控制接口,是一个标准的 I2C 接口,WM8960 要想工作必须对其进行配置,这个 I2C 接口就是用于配置 WM8960 的。
3、I2S 总线接口
I2S (Inter-IC Sound) 总线有时候也写作 IIS,I2S 是飞利浦公司提出的一种用于数字音频设备之间进行音频数据传输的总线。和 I2C、SPI 这些常见的通信协议一样,I2S 总线用于主控制器和音频 CODEC 芯片之间传输音频数据。因此,要想使用 I2S 协议,主控制器和音频 CODEC 都得支持 I2S 协议,I.MX6ULL 的 SAI 外设就支持 I2S 协议,WM8960 同样也支持 I2S,所以本章实验就是使用 I2S 协议来完成的。I2S 接口需要 3 根信号线 (如果需要实现收和发,那么就要 4 根信号线,收和发分别使用一根信号线):
- SCK:串行时钟信号,也叫做位时钟 (BCLK),音频数据的每一位数据都对应一个 SCK,立体声都是双声道的,因此 SCK=2× 采样率 × 采样位数。比如采样率为 44.1KHz、16 位的立体声音频,那么 SCK=2×44100×16=1411200Hz=1.4112MHz。
- WS:字段 (声道) 选择信号,也叫做 LRCK,也叫做帧时钟,用于切换左右声道数据,WS 为 "1" 表示正在传输左声道的数据,WS 为 "0" 表示正在传输右声道的数据。WS 的频率等于采样率,比如采样率为 44.1KHz 的音频,WS=44.1KHz。
- SD:串行数据信号,也就是我们实际的音频数据,如果要同时实现放音和录音,那么就需要 2 根数据线,比如 WM8960 的 ADCDAT 和 DACDAT,就是分别用于录音和放音。不管音频数据是多少位的,数据的最高位都是最先传输的。数据的最高位总是出现在一帧开始后 (LRCK 变化) 的第 2 个 SCK 脉冲处。
另外,有时候为了使音频 CODEC 芯片与主控制器之间能够更好的同步,会引入另外一个叫做 MCLK 的信号,也叫做主时钟或系统时钟,一般是采样率的 256 倍或 384 倍。
下图就是一帧立体声音频时序图:

下图是采用逻辑分析仪抓取到的一帧真实的音频时序图:

上图中通道 0 是 LRCK 时钟,通道 1 为 BCLK,通道 2 是 DACDATA,通道 3 是 MCLK。随着技术的发展,在统一的 I2S 接口下,出现了不同的数据格式,根据 DATA 数据相对于 LRCK 和 SCLK 位置的不同,出现了 Left Justified (左对齐) 和 Right Justified (右对齐) 两种格式,这两种格式的时序图如下图所示:

4、I.MX6ULL SAI 简介
音频 CODEC 支持 I2S 协议,那么主控制器也必须支持 I2S 协议,I.MX6ULL 也提供了一个叫做 SAI 的外设,全称为 Synchronous Audio Interface,翻译过来就是同步音频接口。
I.MX6ULL 的 SAI 是一个全双工、支持帧同步的串行接口,支持 I2S、AC97、TDM 和音频 DSP,SAI 主要特性如下:
①、帧最大为 32 个字。
②、字大小可选择 8bit 或 32bit。
③、每个接收和发送通道拥有 32×32bit 的 FIFO。
④、FIFO 错误以后支持平滑重启。
I.MX6ULL 的 SAI 框图如图所示:

图中右侧"SAI_TX"和"SAI_RX"开头的就是 SAI 外设提供给外部连接音频 CODEC 的信号线。
二、音频驱动使能
1、修改根设备树(sound节点)
在根设备树下补充 WM8960 sound 节点:
sound {
compatible = "fsl,imx6ul-evk-wm8960",
"fsl,imx-audio-wm8960";
model = "wm8960-audio";
cpu-dai = <&sai2>;
audio-codec = <&codec>;
asrc-controller = <&asrc>;
codec-master;
gpr = <&gpr 4 0x100000 0x100000>;
/*
* hp-det = <hp-det-pin hp-det-polarity>;
* hp-det-pin: JD1 JD2 or JD3
* hp-det-polarity = 0: hp detect high for headphone
* hp-det-polarity = 1: hp detect high for speaker
*/
hp-det = <3 0>;
/* hp-det-gpios = <&gpio5 4 0>;
mic-det-gpios = <&gpio5 4 0>; */
audio-routing =
"Headphone Jack", "HP_L",
"Headphone Jack", "HP_R",
"Ext Spk", "SPK_LP",
"Ext Spk", "SPK_LN",
"Ext Spk", "SPK_RP",
"Ext Spk", "SPK_RN",
"LINPUT2", "Mic Jack",
"LINPUT3", "Mic Jack",
"RINPUT1", "Main MIC",
"RINPUT2", "Main MIC",
"Mic Jack", "MICB",
"Main MIC", "MICB",
"CPU-Playback", "ASRC-Playback",
"Playback", "CPU-Playback",
"ASRC-Capture", "CPU-Capture",
"CPU-Capture", "Capture";
};
2、修改I2C设备树(codec节点)
&i2c1 {
......
codec1: wm8960@1a {
compatible = "wlf,wm8960";
reg = <0x1a>;
clocks = <&clks IMX6UL_CLK_SAI2>;
clock-names = "mclk";
wlf,shared-lrclk;
};
......
};
3、修改SAI设备树
使能 sai2 节点并配置其引脚与时钟。
&sai2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sai2>;
assigned-clocks = <&clks IMX6UL_CLK_SAI2_SEL>,
<&clks IMX6UL_CLK_SAI2>;
assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
assigned-clock-rates = <0>, <11289600>;
status = "okay";
};
4、修改IOMUX****设备树
SAI2****引脚复用(iomuxc)
在 &iomuxc 下补充 pinctrl_sai2 定义:
&iomuxc {
......
imx6ul-evk {
......
pinctrl_sai2: sai2grp {
fsl,pins = <
MX6UL_PAD_JTAG_TDI__SAI2_TX_BCLK 0x17088
MX6UL_PAD_JTAG_TDO__SAI2_TX_SYNC 0x17088
MX6UL_PAD_JTAG_TRST_B__SAI2_TX_DATA 0x11088
MX6UL_PAD_JTAG_TCK__SAI2_RX_DATA 0x11088
MX6UL_PAD_JTAG_TMS__SAI2_MCLK 0x17088
>;
};
......
};
};
5、使能内核WM8960驱动
设备树配置完成以后就可以使能内核自带的 WM8960 驱动了,直接通过图形化界面配置即可,输入如下命令打开 linux 内核的图形化配置界面:
make menuconfig
5.1**、取消ALSA模拟****OSS API**
首先取消 ALSA 模拟 OSS,进入如下路径:
Device Drivers
└── Sound card support (SOUND [=y])
└── Advanced Linux Sound Architecture (SND [=y])
├─ < > OSS Mixer API // 取消勾选,不要选y/m
└─ < > OSS PCM (digital audio) API // 取消勾选
结果如图所示:

5.2、使能I.MX6ULL的WM8960驱动
接下来使能 WM8960 驱动,进入如下路径:
-> Device Drivers
-> Sound card support (SOUND [=y])
-> Advanced Linux Sound Architecture (SND [=y])
-> ALSA for SoC audio support (SND_SOC [=y])
-> SoC Audio for Freescale CPUs
-> <*> Asynchronous Sample Rate Converter (ASRC) module support
-> <*> SoC Audio support for i.MX boards with wm8960
结果如图所示:

驱动使能以后重新编译 linux 内核,编译完成以后使用新的 zImage 和.dtb 文件启动,如果设备树和驱动都使能的话系统启动过程中就会如图所示的 log 信息:

三、alsa-lib和alsa-utils****移植
首先下载 alsa-lib 和 alsa-utils 源码:
wget -c https://www.alsa-project.org/files/pub/lib/alsa-lib-1.2.2.tar.bz2 https://www.alsa-project.org/files/pub/utils/alsa-utils-1.2.2.tar.bz2
1、alsa-lib****移植
注意 alsa-lib 编译过程中会生成一些配置文件,而这些配置信息的路径都是绝对路径,因此为了保证 ubuntu 和开发板根文件系统中的路径一致!我们需要在 ubuntu 和开发板中各创建一个路径和名字完全一样的目录,这里我们都创建一个 /usr/share/arm-alsa 目录,ubuntu 中创建命令如下:
cd /usr/share //进入 ubuntu 的/usr/share 目录
sudo mkdir arm-alsa //创建 arm-alsa 目录
最后在开发板根文件系统中也创建一个 /usr/share/arm-alsa 目录,命令如下:
mkdir /usr/share/arm-alsa -p //开发板根文件系统创建 arm-alsa 目录
这样 ubuntu 和开发板根文件系统都有一个 "/usr/share/arm-alsa" 目录,我们交叉编译的时候就不怕存在引用绝对路径了,因为 ubuntu 和开发板中的配置文件路径都是一模一样的。
由于 alsa-utils 要用到 alsa-lib 库,因此要先编译 alsa-lib 库。alsa-lib 就是 ALSA 相关库文件,应用程序通过调用 ALSA 库来对 ALSA 框架下的声卡进行操作。先创建一个名为 "alsa-lib" 的目录用来保存 alsa-lib 的编译结果,然后将 alsa-lib-1.2.2.tar.bz2 拷贝到 ubuntu 中并解压,命令如下:
tar -vxjf alsa-lib-1.2.2.tar.bz2 //解压 alsa-lib
解压完成以后就会得到一个名为 "alsa-lib-1.2.2" 的文件夹,这个就是 alsa-lib 的源码。进入 alsa-lib-1.2.2 目录,然后配置并编译,命令如下:
cd alsa-lib-1.2.2/ //进入 alsa-lib 源码目录
./configure --host=arm-linux-gnueabihf --prefix=$PWD/build --with-configdir=/usr/share/arm-alsa //配置
注意,"--with-configdir" 用于设置 alsa-lib 编译出来的配置文件存放位置,这里设置为前面创建的 "/usr/share/arm-alsa" 目录。
配置完成以后就可以编译了,命令如下:
make //编译
sudo make install //安装
将build/lib 目录下的所有文件拷贝到开发板根文件系统的/usr/lib 目录下,将/usr/share/arm-alsa 目录下的所有文件拷贝到开发板的/usr/share/arm-alsa 目录下。
2、alsa-utils****移植
alsa-utils 是 ALSA 的一些小工具集合,我们可以通过这些小工具测试我们的声卡。将 alsa-utils-1.2.2.tar.bz2 复制到 ubuntu 中并解压,命令如下:
tar -vxjf alsa-utils-1.2.2.tar.bz2 //解压
解压成功以后会得到一个名为 "alsa-utils-1.2.2" 的文件夹,此文件夹就是 alsa-utils 源码。重新创建一个名为 "alsa-utils" 的目录用于存放 alsa-utils-1.2.2 的编译结果。按照如下命令编译 alsa-utils:
cd alsa-utils-1.2.2/ //进入
./configure --host=arm-linux-gnueabihf --prefix=$PWD/build --with-alsa-inc-prefix=path/alsa-lib/include/ --with-alsa-prefix=path/alsa-lib/lib/ --disable-alsamixer --disable-xmlto
make //编译
sudo make install
编译完成以后就会在前面创建的"alsa-utils"目录下生成 bin、sbin 和 share 三个文件夹,将这三个目录中的所有文件分别拷贝到开发板根目录下的/ bin、/sbin 和/usr/share/alsa 目录下。
打开开发板根文件系统中的/etc/profile 文件,在里面加入如下所示内容:
export ALSA_CONFIG_PATH=/usr/share/arm-alsa/alsa.conf
ALSA_CONFIG_PATH 用于指定 alsa 的配置文件,这个配置文件是 alsa-lib 编译出来的。
四、声卡设置与测试
1、amixer 使用方法
1.1、查看帮助信息
amixer --help //查看 amixer 帮助信息
结果如图所示:

从图可以看出,amixer 软件命令分为两组,scontrols、scontents、sset 和 sget 为一组。controls、contents、cset 和 cget 为另一组。这两组的基本功能都是一样的,只不过 "s" 开头的是 simple (简单) 组,这一组命令是简化版,本教程最终使用 "s" 开头的命令设置声卡,因为少输入很多字符。
1.2、查看设置项
我们要先看一下都有哪些设置项,先来看一下 scontrols 对应的设置项,输入如下命令:
amixer scontrols //查看所有设置项
结果如图所示:

再来看一下 controls 对应的设置项,输入如下命令:
amixer controls //查看所有设置项
结果如图所示:

由于篇幅原因只列出了一部分设置项,整体设置项目还是比较多的,很多设置项目我们都不知道是啥意思,毕竟不是从事音频专业的。这里我们只关注一些最常用的设置即可,比如设置耳机和喇叭音量、设置左右声道音量、设置输入音量等等。
1.3**、查看设置值**
不同的设置项对应的设置值类型不同,先查看一下 scontents 对应的设置值,输入如下命令:
amixer scontents //查看设置值
结果如图所示:

从图可以看出"Headphone"项目就是设置耳机音量的,音量范围为 0-127,当前音量为 0。有些设置项是 bool 类型,只有 on 和 off 两种状态。关于 controls 对应的设置值大家自行输入"amixer controls"命令查看即可。
4、设置声卡
知道了设置项和设置值,那么设置声卡就很简单了,直接使用下面命令即可:
amixer sset 设置项目 设置值
或:
amixer cset 设置项目 设置值
5、获取声卡设置值
如果要读取当前声卡某项设置值的话使用如下命令:
amixer sget 设置项目
或:
amixer cget 设置项目
2、音乐播放测试
2.1、使用 amixer 设置声卡
第一次使用声卡之前一定要先使用 amixer 设置声卡,打开耳机和喇叭,并且设置喇叭和耳机音量,输入如下命令:
amixer sset Headphone 100,100
amixer sset Speaker 120,120
amixer sset 'Right Output Mixer PCM' on
amixer sset 'Left Output Mixer PCM' on
2.2、使用 aplay 播放 WAV 格式音乐
声卡设置好以后就可以使用 aplay 软件播放 wav 格式的音乐测试一下,aplay 也是 alsa-utils 提供的。可以在开发板根文件系统下创建一个名为 "music" 的目录来存放音频文件,然后找一首 wav 格式的音乐放到开发板根文件系统中,然后输入如下命令播放:
aplay test.wav //播放歌曲
如果一切设置正常的话就会开始播放音乐,因为 ALPHA 开发板支持喇叭和耳机自动切换,因此如果不插耳机的话默认从喇叭播放音乐。插上耳机以后喇叭就会停止播放音乐,改为耳机播放音乐。
3、WM8960****录音测试
3.1、使用amixer设置声卡
同样的,第一次使用声卡录音之前要先使用 amixer 设置一下声卡,这里为了方便,我们在开发板根文件系统的 /music 目录下创建一个名为 "mic_in_config.sh" 的 shell 脚本,然后在里面输入声卡的设置命令。mic_in_config.sh 脚本内容如下所示:
#!/bin/sh
#正点原子@ALIENTEK
#设置捕获的音量
amixer cset name='Capture Volume' 90,90
#PCM
amixer sset 'PCM Playback' on
amixer sset 'Playback' 256
amixer sset 'Right Output Mixer PCM' on
amixer sset 'Left Output Mixer PCM' on
#ADC PCM
amixer sset 'ADC PCM' 200
#耳机/喇叭(扬声器)设置播放音量,直流/交流
#Turn on Headphone
amixer sset 'Headphone Playback ZC' on
#Set the volume of your headphones(98% volume,127 is the MaxVolume)
amixer sset Headphone 125,125
#Turn on the speaker
amixer sset 'Speaker Playback ZC' on
#Set the volume of your Speaker(98% volume,127 is the MaxVolume)
amixer sset Speaker 125,125
#Set the volume of your Speaker AC(80% volume,100 is the MaxVolume)
amixer sset 'Speaker AC' 4
#Set the volume of your Speaker DC,5 is the MaxVolume
amixer sset 'Speaker DC' 4
#音频输入,左声道管理
#Turn on Left Input Mixer Boost
amixer sset 'Left Input Mixer Boost' off
amixer sset 'Left Boost Mixer LINPUT1' off
amixer sset 'Left Input Boost Mixer LINPUT1' 0
amixer sset 'Left Boost Mixer LINPUT2' off
amixer sset 'Left Input Boost Mixer LINPUT2' 0
#Turn off Left Boost Mixer LINPUT3
amixer sset 'Left Boost Mixer LINPUT3' off
amixer sset 'Left Input Boost Mixer LINPUT3' 0
#音频输入,右声道管理,全部关闭
#Turn on Right Input Mixer Boost
amixer sset 'Right Input Mixer Boost' on
amixer sset 'Right Boost Mixer RINPUT1' off
amixer sset 'Right Input Boost Mixer RINPUT2' 0
amixer sset 'Right Boost Mixer RINPUT2' on
amixer sset 'Right Input Boost Mixer RINPUT2' 127
amixer sset 'Right Boost Mixer RINPUT3' off
amixer sset 'Right Input Boost Mixer RINPUT3' 0
给予 mic_in_config.sh 可执行权限并运行,命令如下:
chmod 777 mic_in_config.sh //给予可执行权限
./mic_in_config.sh //运行
3.2、使用 arecord 录制音频
使用 arecord 来录制一段 10 秒中的音频,arecord 也是 alsa-utils 编译出来的,输入如下命令:
arecord -f cd -d 10 record.wav
-f 是设置录音质量,"-f cd" 表示录音质量为 cd 级别。-d 是指定录音时间,单位是 s,这条指令就是录制一段 cd 级别 10s 的 wav 音频,音频名字为 record.wav。录制的时候大家就可以对着开发板上的 MIC 说话,直到录制完成。
录制完成以后使用 aplay 播放刚刚录制的 record.wav 音频。
觉得有帮助的话,打赏一下呗。。
