赞
踩
音频是常用到的功能,I.MX6ULL ALPHA开发板通过I.MX6ULL自带的SAI接口外接了一个 WM8960音频 DAC芯片,本文将介绍如何驱动 WM8960,并通过 WM8960芯片来完成音乐播放与录音
在信号处理领域,外界的声音是模拟信号,处理器能理解的是数字信号。因此处理器要听到外界的声音就涉及到模拟信号到数字信号的转换;同样处理器要向外界放出声音就涉及到数字信号到模拟信号的转换。模数和数模转换需要用到ADC/DAC芯片。
但是音频不是单单的数模或模数转换就可以了,还需要能够调节音效、对声音进行一些处理(需要 DSP 单元)、拥有统一的标准接口,方便开发等。将这些针对声音的各种要求全部叠加到 DAC 和 ADC 芯片上,就会得到一个专门用于音频的芯片,即音频编解码芯片,英文名字就是 Audio CODEC,CODEC 的本质是 ADC/DAC,因此采样率和采样位数就是衡量一款音频 CODEC 最重要的指标。
WM8960 是一颗由 wolfson(欧胜)公司出品的低功耗、高质量音频编解码芯片。集成 D 类喇叭功放,每个通道可以驱动一个 1W 喇叭(8Ω )。内部集成 3 个立体声输入源,可以灵活配置,拥有一路完整的麦克风接口。 WM8960 内部 ADC 和 DAC 都为24 位。
WM8960 整体框图如下示:
– ①输入接口,立体声音频输入源,共提供了三路,为 LINPUTx/RINPUTx
– ②输出接口,输出给耳机或喇叭,SPK_LP/SPK_LN 用于连接左声道的喇叭;SPK_RP/SPK_RN 用于连接右声道的喇叭;HP_L/HP_R,用于连接耳机
– ③数字音频接口,用于和主控制器连接,共有 5 根线,支持 I2S 格式
– ④控制接口,是一个标准的 I2C 接口, 用于配置 WM8960
I2S (Inter-IC Sound) 总线是飞利浦公司提出的一种用于数字音频设备之间进行音频数据传输的总线。和 I2C、 SPI 这些常见的通信协议一样, I2S 总线用于主控制器和音频 CODEC 芯片之间传输音频数据。I2S 接口需要 3 根信号线(如果需要实现收和发,那么就要 4 根信号线,收和发分别使用一根信号线):
比如采样率为 44.1KHz、 16 位的立体声音频,那么 SCK=2× 44100× 16=1411200Hz=1.4112MHz
WS 的频率等于采样率,比如采样率为 44.1KHz 的音频, WS=44.1KHz
比如 WM8960 的 ADCDAT 和 DACDAT,就是分别用于录音和放音
不管音频数据是多少位的,数据的最高位都是最先传输的。数据的最高位总是出现在一帧开始后(LRCK变化)的第 2 个 SCK 脉冲处。另外,有时为了使音频 CODEC 芯片与主控制器之间能够更好的同步,会引入另外一个叫做 MCLK 的信号,也叫做主时钟或系统时钟,一般是采样率的 256 倍或 384 倍
随着技术的发展,在统一的 I2S 接口下,出现了不同的数据格式,根据 DATA 数据相对于 LRCK 和 SCLK 位置的不同,出现了 Left Justified (左对齐) 和 Right Justified (右对齐) 两种格式,时序图如下示:
I.MX6ULL 提供一个叫做 SAI 的外设,全称为 Synchronous Audio Interface,即同步音频接口。SAI 是一个全双工、支持帧同步的串行接口,支持 I2S、AC97、TDM 和音频 DSP,主要特性如下:
– 帧最大为 32 个字
– 字大小可选择 8bit 或 32bit
– 每个接收和发送通道拥有 32× 32bit 的 FIFO
– FIFO 错误以后支持平滑重启
I.MX6ULL 的 SAI 框图如下示,其中 SAI_TX/RX 开头的就是 SAI 外设提供给外部连接音频 CODEC 的信号线
正点原子 ALPHA 开发板音频原理图如下示:
上图中重点关注 SAI 和 I2C 这两个接口:
– SAI 接口共用到 6 根数据线,用于 I.MX6ULL 与 WM8960 之间的音频数据收发
– I2C接口连接到了 I.MX6ULL 的 I2C2 上,用于配置 WM8960
NXP 官方已经写好了 WM8960 驱动,因此直接配置内核使能 WM8960 驱动即可,按照如下所示步骤使能 WM8960 驱动
/* 可参考绑定手册Documentation/devicetree/bindings/sound/wm8960.txt添加 */
codec: wm8960@1a {
//全局搜索该属性就可找到WM8960的驱动文件sound/soc/coders/wm8960.c
compatible = "wlf,wm8960";
reg = <0x1a>; //设置WM8960的I2C地址
clocks = <&clks IMX6UL_CLK_SAI2>; //指定时钟源为SAI2
clock-names = "mclk"; //指定时钟名字为mclk
wlf,shared-lrclk;
};
在 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";
};
打开具体的设备树文件,向 sai2 节点里追加或修改相应属性值
&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 接口的 IO 设置: pinctrl_sai2 描述的是 SAI2 接口的 IO 配置;pinctrl_sai2_hp_det_b 描述的是耳机插入检测引脚
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
>;
};
sound { //用于匹配驱动文件,搜索以下属性值就可以找到对应的 //驱动文件:sound/soc/fsl/imx-wm8960.c compatible = "fsl,imx6ul-evk-wm8960", "fsl,imx-audio-wm8960"; model = "wm8960-audio"; //最终用户看到的此声卡名字 cpu-dai = <&sai2>; //DAI(Digital Audio Interface)句柄,即sai2这个节点 audio-codec = <&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 */ //耳机插入检测引脚设置,第一个参数为检测引脚, 3表示JD3为检测引脚 //第二个参数设置检测电平,设为0时,hp检测到高电平表示耳机插入; //设为1时,hp检测到高电平表示是喇叭,也就是耳机拔出了 hp-det = <3 0>; /* hp-det-gpios = <&gpio5 4 0>; mic-det-gpios = <&gpio5 4 0>; */ //音频器件一系列的连接设置,每个条目都是一对字符串 //第一个字符串是连接的 sink,第二个是连接的 source(源) 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"; };
设备树配置完成以后就可以使能内核自带的 WM8960 驱动了,直接通过图形化界面配置即可,输入make menuconfig
命令打开 linux 内核的图形化配置界面
-> Device Drivers
-> Sound card support (SOUND [=y])
-> Advanced Linux Sound Architecture (SND [=y])
-> <> OSS Mixer API //不选择
-> <> OSS PCM (digital audio) API //不选择
-> 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 设备列表,现在的音频 CODEC 驱动基本都是 ALSA 架构的,因此在 ALSA 设备列表中就会找到 “wm8960-audio” 这个声卡。进入系统以后查看一下/dev/snd 目录,有如下所示文件:
这些文件就是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
在Ubuntu和开发板中创建一个路径和名字完全一样的目录:/usr/share/arm-alsa,方便交叉编译时的绝对路径引用
mkdir /usr/share/arm-alsa
在 /home/andyxi/linux/tool 目录下创建“alsa-lib”目录,用来保存 alsa-lib 的编译结果
mkdir alsa-lib
将下载好的 alsa-lib 源码拷贝到 /home/andyxi/linux/tool 目录下解压,并进行配置编译安装
//解压 alsa-lib
tar -vxjf alsa-lib-1.2.2.tar.bz2
//进入 alsa-lib 源码目录
cd alsa-lib-1.2.2/
//配置:--with-configdir用于设置 alsa-lib 编译出来的配置文件存放位置
./configure --host=arm-linux-gnueabihf
--prefix=/home/andyxi/linux/tool/alsa-lib
--with-configdir=/usr/share/arm-alsa
//编译
make
//安装
sudo make install
可能会出现提示 libatopology.la 编译失败,是因为 sudo 会切换到 root 用户下,但此时 root 用户下的环境变量中没有交叉编译器路径,因此会提示找不到“arm-linux-gnueabihf-gcc”
//解决方法就是先切换到 root 用户,重新执行一下/etc/profile文件
sudo -s //切换到 root 用户,但使用当前用户本身的环境
source /etc/profile //执行/etc/profile
make install //安装,此时已经工作在 root 下,因此不需要加“sudo”
su andyxi //编译完成以后回原来的用户
编译完成以后前面创建的“alsa-lib”目录就会保存相应的编译结果,将lib目录下的所有文件拷贝到开发板根文件系的 /usr/lib 目录下;并将 /usr/share/arm-alsa 目录下的所有文件拷贝到开发板的/usr/share/arm-alsa 目录下
cd alsa-lib
sudo cp lib/* /home/zuozhongkai/linux/nfs/rootfs/lib/ -af
cd /usr/share/arm-alsa
sudo cp * /home/andyxi/linux/nfs/rootfs/usr/share/arm-alsa/ -raf
在 /home/andyxi/linux/tool 目录下创建“alsa-utils”目录,用来保存 alsa-utils 的编译结果
mkdir alsa-utils
将下载好的 alsa-utils 源码拷贝到 /home/andyxi/linux/tool 目录下解压,并进行配置编译安装(为了避免配置不成功,需切换到 root 用户)
tar -vxjf alsa-utils-1.2.2.tar.bz2 //解压
cd alsa-utils-1.2.2/ //进入 alsa-utils 源码目录
sudo -s //切换到 root 用户
source /etc/profile //执行/etc/profile
//配置
/configure --host=arm-linux-gnueabihf
--prefix=/home/andyxi/linux/tool/alsa-utils
--with-alsa-inc-prefix=/home/andyxi/linux/tool/alsa-lib/include/
--with-alsa-prefix=/home/andyxi/linux/tool/alsa-lib/lib/
--disable-alsamixer --disable-xmlto
make //编译
make install //安装,此时已经工作在 root 下,因此不需要加“sudo”
su andyxi //编译完成以后回原来的用户
编译完成后就会在前面创建的“alsa-utils”目录下生成 bin、 sbin 和 share 三个文件夹,将其分别拷贝至开发板根目录下的/bin、 /sbin 和/usr/share/alsa 目录下
cd alsa-utils
sudo cp bin/* /home/andyxi/linux/nfs/rootfs/bin/ -rfa
sudo cp sbin/* /home/andyxi/linux/nfs/rootfs/sbin/ -rfa
sudo cp share/* /home/andyxi/linux/nfs/rootfs/usr/share/ -rfa
在开发板根文件系统中的/etc/profile 文件中加入如下内容,来指定 alsa 的配置文件
export ALSA_CONFIG_PATH=/usr/share/arm-alsa/alsa.conf
amixer 使用方法
amixer --help
命令即可查看 amixer的帮助信息上图可以看出,amixer 命令分为前缀“s”和“c”各一组,这两组的基本功能都是一样的,只不过“s”开头的是 simple(简单)组,一般使用简化版
查看设置项:使用amixer scontrols
和amixer controls
命令查看对应的设置项
查看设置值:不同的设置项的设置值类型不同,例如使用amixer scontents
命令查看scontents对应的设置值
上图可以看出,“Headphone”项目就是设置耳机音量的,范围 0-127,当前音量为 0。有些设置项是 bool 类型,只有 on 和 off 两种状态
amixer sset 设置项目 设置值
amixer cset 设置项目 设置值
amixer sget 设置项目
amixer cget 设置项目
amixer sset Headphone 100,100
amixer sset Speaker 120,120
amixer sset 'Right Output Mixer PCM' on
amixer sset 'Left Output Mixer PCM' on
aplay test.wav //播放歌曲
ALPHA 开发板上有一个麦克风,可以用来完成录音测试
### mic_in_config.sh 脚本内容 --------------------------------- #!/bin/sh #设置捕获的音量 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 AC(80% volume, 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 //运行
arecord -f cd -d 10 record.wav
//-f 设置录音质量
//cd 表示录音质量为 cd 级别
//-d 指定录音时间,单位是s
//音频名字为 record.wav
//录制完成后可使用aplay播放录制的record.wav音频
//打开 linux 内核里面的 wm8960.c 这个文件,找到 wm8960_reg_defaults 数组
//此数组保存着 wm8960 的默认配置值,为<寄存器地址,值>的形式
//R23寄存器地址为0X17,将该寄存器的值改为0x01C4
static const struct reg_default wm8960_reg_defaults[] = {
{ 0x0, 0x00a7 },
......
{ 0x16, 0x00c3 },
/*{ 0x17, 0x01c0 },*/
{ 0x17, 0x01c4 },
{ 0x18, 0x0000 },
......
{ 0x37, 0x00e9 },
};
//修改完成后重新编译内核,使用新的内核启动开发板,重新测试MIC录音
ALPHA 开发板的 LINE IN 接了双声道,所以不需要共用左声道数据,不需要更改R23寄存器的默认值。 LINE IN 即线路输入
#!/bin/sh #设置捕获的音量 amixer cset name='Capture Volume' 100,100 #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 #录音前应该设置耳机或者扬声器的音量为 0(下面并没有设置)防止干扰 #耳机/喇叭(扬声器)设置播放音量,直流/交流 #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 AC(80% volume, 5 is the MaxVolume) amixer sset 'Speaker DC' 4 #音频输入,左声道管理 #Turn off Left Input Mixer Boost amixer sset 'Left Input Mixer Boost' on #关闭其他通道输入 amixer sset 'Left Boost Mixer LINPUT1' off amixer sset 'Left Input Boost Mixer LINPUT1' 0 #关闭麦克风左声道输入 amixer sset 'Left Boost Mixer LINPUT2' on amixer sset 'Left Input Boost Mixer LINPUT2' 127 #Line_in 右声道输入关闭 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 RINPUT1' 0 amixer sset 'Right Boost Mixer RINPUT2' off amixer sset 'Right Input Boost Mixer RINPUT2' 0 #要想设置成音频输入, 请打开 RINPUT3,看原理图可知 #其他的声道通过上面的配置可关闭,这样是为了避免干扰,需要的时候就打开 #RINPUT3 打开(关键点) amixer sset 'Right Boost Mixer RINPUT3' on amixer sset 'Right Input Boost Mixer RINPUT3' 127
给予 line_in_config.sh 可执行权限并运行,命令如下:
chmod 777 line_in_config.sh //给予可执行权限
./line_in_config.sh //运行
arecord -f cd -d 10 record.wav
//-f 设置录音质量
//cd 表示录音质量为 cd 级别
//-d 指定录音时间,单位是s
//音频名字为 record.wav
//录制完成后可使用aplay播放录制的record.wav音频
//LINE IN接了左右双声道,因此录制出来的音频是立体声的
开发板重启后声卡的所有设置都会消失,必须重新设置声卡。下面介绍如何保存声卡的设置
alsactl 默认将声卡配置文件保存在 /var/lib/alsa 目录下,因此在开发板根文件系统下创建 /var/lib/alsa 目录
mkdir /var/lib/alsa -p
使用 amixer 设置声卡后,输入如下命令保存声卡设置
alsactl -f var/lib/alsa/asound.state store //保存声卡设置
//-f 指定声卡配置文件
//store 表示保存
保存成功后就会生成/var/lib/alsa/asound.state 文件,里面是关于声卡的各种设置信息
如果要使用 asound.state 中的配置信息来配置声卡,执行如下命令:
alsactl -f var/lib/alsa/asound.state restore
打开/etc/init.d/rcS 文件,在最后面追加如下内容
if [ -f "/var/lib/alsa/asound.state" ]; then
echo "ALSA: Restoring mixer setting......"
/sbin/alsactl -f var/lib/alsa/asound.state restore &
fi
设置完成后重启开发板,就会自动设置声卡,会输入如下图示内容
最后使用 aplay 播放音乐测试声卡开机自动配置是否正确
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。