发布时间:2024-09-14 12:01
录音: 模拟信号转换为数字信号的过程,完成这个功能的就是 ADC 芯片。
放音: 是将数字信号转化为模拟信号,而完成这个功能的是 DAC 芯片。
处理器想要进行录音与放音, 就需要ADC / DAC两颗芯片即可。但是音频不单单是能出声、能听到就行。我们往往需要听到的声音动听、录进去的语音贴近真实、可以调节音效、对声音能够进行一些处理(需要 DSP 单元)、拥有统一的标准接口,方便开发等等。
将这些针对声音的各种要求全部叠加到 DAC 和 ADC 芯片上,那么就会得到一个专门用于音频的芯片,也就是音频编解码芯片,英文名字就是 Audio CODEC,所以我们在手机或者电脑的介绍中看到“CODEC”这个词语,一般说的都是音频编解码。
既然音频 CODEC 的本质是 ADC 和 DAC,那么采样率和采样位数就是衡量一款音频CODEC 最重要的指标。比如常见音频采样率有 8K、44.1K、48K、192K 甚至 384K 和 768K,采样位数常见的有 8 位、16 位、24 位、32 位。采样率和采样位数越高,那么音频 CODEC 越能真实的还原声音,也就是大家说的 HIFI。因此大家会看到高端的音频播放器都会有很高的采样率和采样位数,同样的价格也会越高。
以IMX6UL使用的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 整体框图如图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fDxbIy0v-1605964559989)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201026083220250.png)]
I2S 是飞利浦公司提出的一种用于数字音频设备之间进行音频数据传输的总线。和 I2C、SPI 这些常见的通信协议一样,I2S 总线用于主控制器和音频 CODEC 芯片之间传输音频数据。因此,要想使用 I2S 协议,主控制器和音频 CODEC 都得支持 I2S 协议,I.MX6ULL 的 SAI 外设就支持 I2S 协议,WM8960 同样也支持 I2S。I2S 接口需要 3 根信号线(如果需要实现收和发,那么就要 4根信号线,收和发分别使用一根信号线):
SCK:串行时钟信号,也叫做位时钟(BCLK),音频数据的每一位数据都对应一个 SCK,立体声都是双声道的,因此 SCK=2×采样率×采样位数。比如采样率为 44.1KHz、16 位的立体声音频,那么SCK= 2×44100×16 =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 倍。
以下为IIS时序图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yf3dCAIf-1605964559994)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201026084120331.png)]
随着技术的发展,在统一的 I2S 接口下,出现了不同的数据格式,根据 DATA 数据相对于 LRCK 和 SCLK 位置的不同,出现了 LeftJustified(左对齐)和 RightJustified(右对齐)两种格式,这两种格式的时序图如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bAEMv281-1605964559995)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201026084601926.png)]
音频 CODEC 支持 I2S 协议,那么主控制器也必须支持 I2S 协议。I.MX6ULL提供了一个叫做 SAI 的外设,全称为 SynchronousAudio Interface,翻译过来就是同步音频接口。
I.MX6ULL 的 SAI 是一个全双工、支持帧同步的串行接口,支持 I2S、AC97、TDM 和音频DSP,SAI 主要特性如下:
①、帧最大为 32 个字。
②、字大小可选择 8bit 或 32bit。
③、每个接收和发送通道拥有 32×32bit 的 FIFO。
④、FIFO 错误以后支持平滑重启。
WM8960 与 I.MX6ULL 之间有两个通信接口:I2C 和 SAI,因此设备树中会涉及到 I2C 和 SAI 两个设备节点。其中 I2C 用于配置 WM8960,SAI 接口用于音频数据传输,依次配置这两个接口。
首先配置一下 I2C 接口,根据原理图知道 WM8960 连接到了 I.MX6ULL 的 I2C2 接口上,因此在设备树中的“i2c2”节点下需要添加 wm8960 信息。
有 2 个必要的属性:
compatible:兼容属性,属性值要设置为“wlf,wm8960”。所以linux 内核里面全局搜索“wlf,wm8960”的话就会找到WM8960的I2C驱动文件,此文件为sound/soc/codecs/wm8960.c。
reg:设置 WM8960 的 I2C 地址,在开发板中 WM8960 的 I2C 地址为0X1A。
还要几个其他的可选属性:
wlf,shared-lrclk:这是一个 bool 类型的属性,如果添加了此属性,WM8960 的 R24 寄存器的 LRCM 位(bit2)就会置 1。当 LRCM 为 1 的时候只有当 ADC 和 DAC 全部关闭以后 ADCLRC和 DACLRC 时钟才会关闭。
wlf,capless:这也是一个 bool 类型的属性,如果添加了此属性,OUT3 引脚将会使能,并且为了响应耳机插入响应事件,HP_L 和 HP_R 这两个引脚都会关闭。
&i2c2 {
clock_frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
codec: wm8960@1a {
compatible = "wlf,wm8960";
reg = <0x1a>;
clocks = <&clks IMX6UL_CLK_SAI2>; /* 为了更好的同步,引入mclk时钟 */
clock-names = "mclk";
wlf,shared-lrclk;
};
... ...
}
和我们前面讲过的 IIC 接口、ECSPI 等接口一样,在 imx6ull.dtsi 文件中会有关于 SAI 相关接口的描述,这部分是 NXP 原厂编写的,不需要做任何修改,SAI2 的设备子节点内容如下所示:
sai2: sai@0202c000 {
compatible = "fsl,imx6ul-sai",
"fsl,imx6sx-sai";
reg = <0x0202c000 0x4000>;
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_SAI2_IPG>,
<&clks IMX6UL_CLK_DUMMY>,
<&clks IMX6UL_CLK_SAI2>,
<&clks 0>, <&clks 0>;
clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3";
dma-names = "rx", "tx";
dmas = <&sdma 37 24 0>, <&sdma 38 24 0>;
status = "disabled";
};
直接搜索 compatible 属性中的两个兼容值,那么就会找到 I.MX6ULL 的 SAI 接口驱动文件,路径为sound/soc/fsl/fsl_sai.c。从第 13 行可以看出,SAI2 默认是关闭的,因此我们需要将其打开,也就是设置 status 属性的值为“okay”,这个工作肯定是在具体板子对应的.dts 文件中完成的,其实就是向 sai2 节点里面追加或者修改一些属性值。打开 imx6ull-14x14-evk-emmc.dts 文件:
&sai2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sai2
&pinctrl_sai2_hp_det_b>;
assigned-clocks = <&clks IMX6UL_CLK_SAI2_SEL>,
<&clks IMX6UL_CLK_SAI2>;
assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
assigned-clock-rates = <0>, <12288000>;
status = "okay";
};
主要是对 sai2 节点做了三个方面的定义:SAI2 接口的 pinctrl、相应的时钟、修改 status 为“okay”。重点是 pinctrl 的设置,因为关系到 SAI2 接口的 IO 设置,从 pinctrl-0 属性可以看出这里一共有两组 IO:pinctrl_sai2 和 pinctrl_sai2_hp_det_b,这两组 IO 内容如下:
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
>;
};
... ...
pinctrl_sai2_hp_det_b: sai2_hp_det_grp {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER4__GPIO5_IO04 0x17059
>;
};
pinctrl_sai2 描述的是 SAI2 接口的 IO 配置。pinctrl_sai2_hp_det_b 描述的是耳机插入检测引脚,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: 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";
};
简单看一下 sound 节点中几个重要的属性:
compatible:非常重要,用于匹配相应的驱动文件,有两个属性值,在整个 linux 内核源码中搜索这两个属性值即可找到对应的驱动文件,这里找到的驱动文件为:sound/soc/fsl/imx-wm8960.c。
mode:最终用户看到的此声卡名字,这里设置为“wm8960-audio”。
cpu-dai:CPU DAI(Digital Audio Interface)句柄,这里是 sai2 这个节点。
audio-codec:音频解码芯片句柄,也就是 WM8960 芯片,这里为“codec”这个节点。
asrc-controller:asrc 控制器,asrc 全称为 Asynchronous Sample Rate Converters,翻译过来就是异步采样频率转化器。
hp-det:耳机插入检测引脚设置,第一个参数为检测引脚,3 表示 JD3 为检测引脚。第二个参数设置检测电平,设置为 0 的时候,hp 检测到高电平表示耳机插入;设置为 1 的时候,hp 检测到高电平表示是喇叭,也就是耳机拔出了。
audio-routing:音频器件一系列的连接设置,每个条目都是一对字符串,第一个字符串是连接的 sink,第二个是连接的 source(源)。
设备树配置完成以后就可以使能内核自带的 WM8960 驱动了,直接通过图形化界面配置即可,打开 linux 内核的图形化配置界面:
make menuconfig
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RjNWrjGP-1605964559998)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201026093404320.png)]
-> 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信息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PGZ9udaD-1605964559999)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201026093851548.png)]
系统最终启动以后会打印出 ALSA 设备列表,现在的音频 CODEC 驱动基本都是 ALSA 架构的, WM8960 驱动也是根据 ALSA 架构编写的。因此在 ALSA 设备列表中就会找到“wm8960-audio”这个声卡
进入系统以后查看一下/dev/snd 目录:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QZolYESI-1605964560001)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201026094248384.png)]
这些文件就是ALSA音频驱动框架对应的设备文件,这些文件的作用如下:
controlC0:用于声卡控制,C0 表示声卡 0。
pcmC0D0c 和 pcmC0D1c:用于录音的 pcm 设备,其中的“COD0”和“C0D1”分别表示声卡 0 中的设备 0 和设备 1,最后面的“c”是 capture 的缩写,表示录音。
pcmC0D0p 和 pcmC0D1p:用于播放的 pcm 设备,其中的“COD0”和“C0D1”分别表示声卡 0 中的设备 0 和设备 1,最后面的“p”是 playback 的缩写,表示放音。
timer:定时器。
音频驱动使能以后还不能直接播放音乐或录音,我们还需要移植 alsa-lib 和 alsa-utils 这两个东西。
1c**:用于录音的 pcm 设备,其中的“COD0”和“C0D1”分别表示声卡 0 中的设备 0 和设备 1,最后面的“c”是 capture 的缩写,表示录音。
pcmC0D0p 和 pcmC0D1p:用于播放的 pcm 设备,其中的“COD0”和“C0D1”分别表示声卡 0 中的设备 0 和设备 1,最后面的“p”是 playback 的缩写,表示放音。
timer:定时器。
音频驱动使能以后还不能直接播放音乐或录音,我们还需要移植 alsa-lib 和 alsa-utils 这两个东西。