音频解码

处理器接收音频需要将声音通过ADC采集,将模拟信号转换成为数字信号,相反播放声音就需要将数字信号转换成为模拟信号使用DAC芯片。音频编解码芯片,英文名字就是 Audio CODEC 。

既然音频 CODEC 的本质是 ADC 和 DAC,那么采样率和采样位数就是衡量一款音频
CODEC 最重要的指标。比如常见音频采样率有 8K、 44.1K、 48K、 192K 甚至 384K 和 768K,采样位数常见的有 8 位、 16 位、 24 位、 32 位。采样率和采样位数越高,那么音频 CODEC 越能真实的还原声音,也就是大家说的 HIFI。

模拟量转成数字量过程,一般可以分为三个过程,分别为采样、量化、编码。

①、此部分是 WM8960 提供的输入接口,作为立体声音频输入源,一共提供了三路,分别为 LINPUT1/RINPUT1、 LINPUT2/RINPUT2、 LINPUT3/RINPUT3。麦克风或线路输入就连接到此接口上。
②、此部分是 WM8960 的输出接口,比如输出给耳机或喇叭, SPK_LP/SPK_LN 用于连接左声道的喇叭,支持 1W 的 8Ω喇叭。

③、此部分是数字音频接口,用于和主控制器连接,有 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 倍。

④、此部分为控制接口,是一个标准的 I2C 接口, WM8960 要想工作必须对其进行配置,这个 I2C 接口就是用于配置 WM8960 的。

I2S总线

I2S 是飞利浦公司提出的一种用于数字音频设备之间进行音频数据传输的总线。I2S 总线用于主控制器和音频 CODEC 芯片之间传输音频数据。因此,要想使用 I2S 协议, 主控制器和音频 CODEC 都得支持 I2S 协议 。

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 倍。

​ 一帧立体声时序图

SAI接口,全称为 Synchronous Audio Interface (同步音频接口)

SAI的主要特性是:

①、帧最大为32字

②、字最大可选择8bit和32bit

③、每个接收和发送通道拥有32x32bit的FIFO

④、FIFO错误以后支持平滑重启 。 FIFO是什么意思_飞鸟211的博客)

使用音频芯片为wm8960芯片,在imx6ull上的硬件框图为

音频驱动使能

nxp的官方写好了wm8960驱动,因此配置内核使能就行。

首先是添加设备树的节点,查看设备树关于此芯片的驱动手册,位置在Documentation/devicetree/bindings/sound/wm8960.txt

可以看到需要两个必备特性,compatitble和reg,还有两个可选特性,wlf,shared-lrclk和wlf,capless。

由于硬件线路将配置接口接在了i2c2上,所以在i2c2上添加设备树的节点。

1
2
3
4
5
6
7
codec: wm8960@1a {
compatible = "wlf,wm8960";
reg = <0x1a>;
clocks = <&clks IMX6UL_CLK_SAI2>;//时钟源SAI2
clock-names = "mclk"; //时钟名字,为了同步一般提供mclk的时钟
wlf,shared-lrclk;
};

之后是SAI的的节点代码,sound是在根节点下的一个代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
sound {
compatible = "fsl,imx6ul-evk-wm8960",
"fsl,imx-audio-wm8960"; //用于匹配驱动时使用
model = "wm8960-audio"; //声卡的名字,相当于设备名字
cpu-dai = <&sai2>; //CPU DAI(Digtial Audio Interface)句柄
audio-codec = <&codec>; //音频解码芯片句柄
asrc-controller = <&asrc>; //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";
};

如果查看使用SAI引脚的设置,查看sai2的节点,首先看imx6ull.dtsi的设备节点,之后对自己的设备树文件进行追加修改

1
2
3
4
5
6
7
8
9
10
1 &sai2 {
2 pinctrl-names = "default";
3 pinctrl-0 = <&pinctrl_sai2
4 &pinctrl_sai2_hp_det_b>;
5 assigned-clocks = <&clks IMX6UL_CLK_SAI2_SEL>,
6 <&clks IMX6UL_CLK_SAI2>;
7 assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
8 assigned-clock-rates = <0>, <12288000>;
9 status = "okay";
10 };

从 pinctrl-0 属性可以看出这里一共有两组 IO: pinctrl_sai2 和 pinctrl_sai2_hp_det_b,看这两个节点中引脚是否正确。