正点原子嵌入式linux驱动开发——Linux 音频驱动

音频是最常用到的功能,音频也是linux和安卓的重点应用场合。STM32MP1带有SAI接口,正点原子的STM32MP1开发板通过此接口外接了一个CS42L51音频DAC芯片,本章就来学习一下如何使能CS42L51驱动,并且CS42L51通过芯片来完成音乐播放与录音。

音频接口简介

音频解码芯片

处理器要想“听到”外界的声音必须要把外界的声 音转化为自己能够理解的“语言”,处理器能理解的就是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。因此会看到高端的音频播放器都会有很高的采样率和采样位数,同样的价格也会越高。当然了,实际的效果还与其他部分有关,采样率和采样位数只是其中重要的指标之一。

CS42L51简介

前面已经分析了为何需要音频编解码芯片,那是因为专用的音频编解码芯片提供了很多针对音频的特性。以正点原子STM32MP157开发板所使用的CS42L51这颗芯片为例,来看一下专用的音频编解码芯片都有哪些特性

CS42L51是一 颗由Cirrus公司出品的音频编解码芯片,是一颗低功耗、高质量的立体声音频CODEC。CS42L51的最主要特点在于它只需要1.8V的低电压供电,但是却可以输出立体声,可以为16Ω耳机提供46mW的输出能量。在2.5V的时候可以16Ω耳机提供88mW的输出能量,CS42L51主要特性如下所示:

  1. 24位转换器。
  2. 支持4kHz-96kHz的采样率。
  3. 多位Delta Sigma架构。
  4. 低功耗,立体声播放的时候1.8V电压下功耗为12.93mW。录音与播放一起工作时为20.18mW。
  5. 数字信号处理引擎。
  6. 3:1立体声输入选择器。
  7. 带有溢漫的自动水平监控。
  8. ……

CS42L51整体框架如下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第1张图片

依次来看一下上图中这四部分接口都是什么功能:

  1. 此部分是CS42L51提供的输入接口,作为立体声音频输入源,一共提供了三路,分别为Stereo Input1、Stereo Input2、Stereo Input3/Mic INPUT1&2。麦克风或线路输入就连接到那个接口上,这部分是需要硬件工程师重点关心的,因为音频选择从哪一路进入需要在画PCB的时候就应该定好。
  2. 此部分是CS42L51提供的输出接口,只支持耳机输出接口。如果要实现功放就要外接其他电路
  3. 此部分是数字音频接口,用于和主控器连接,使用的接口为I2S。
  4. 此部分为控制接口,是一个标准的I2C接口,CS42L51要想工作必须对其进行配置,这个I2C接口就是用于配置CS42L51的

I2S总线接口

I2S(Inter-IC Sound)总线有时候也写作IIS。I2S是飞利浦公司提出的一种用于数字音频设备之间进行音频数据传输的总线。和I2C、SPI这些常见的通信协议一样,I2S总线用于主控制器和音频CODEC芯片之间传输音频数据。因此,要想使用I2S协议,主控制器和音频CODEC都得支持I2S协议,STM32MP1的SAI外设就支持I2S协议,CS42L51同样也支持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根数据线,比如CS42L51的SDOUT和SDIN,就是分别用于放音和录音。不管音频数据是多少位的,数据的最高位都是最先传输的。数据的最高位总是出现在一帧开始后(LRCK变化)的第2个SCK脉冲处

另外,有时候为了使音频CODEC芯片与主控制器之间能够更好的同步,会引入另外一个叫做MCLK的信号,也叫做主时钟或系统时钟,一般是采样率的256倍或384倍。

下图就是一帧立体声音频时序图:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第2张图片

下面是教程里面用逻辑分析仪抓取到的一帧真实的音频个时序图:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第3张图片

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

正点原子嵌入式linux驱动开发——Linux 音频驱动_第4张图片

STM32MP1 SAI总线接口

SAI接口(串行音频接口)灵活性高、配置多样,可支持多种音频协议。该接口适用许多立体声或单声道应用。支持I2S标准、LSB或MSB对齐、PCM/DSP、TDM等等协议。全称为Synchronous Audio Interface。SAI的特性:

  1. 有两个独立的音频子模块,子模块可以用作接收和发送功能,并且带有自身的FIFO。
  2. 每个音频子模块集成多达8个字,每个字32位的FIFO。
  3. 两个音频子模块间可以是同步或异步模式。
  4. 多个SAI之间看实现同步。
  5. 两个音频子模块的主/从配置相互独立。
  6. 数据大小可配置:8位、10位、16位、20位、24位或32位。
  7. ……

SAI框架图如下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第5张图片

从图中可以看出SAI是由两个各自带有时钟发生器的音频子模块组成。在红色框架里标明了每个子模块都有4个专用引脚控制(SD、SCK、FS、MCLK)。如果将两个子模块声明为同步模块,那么SCK、FS和MCLK就可以共用引脚,不相同的只有数据SD引脚了。

硬件原理图分析

正点原子STM32MP1开发板音频原理图如下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第6张图片

上图中重点关注有三方面SAI、I2C和HT6872,依次来看一下这个三方面:

  1. SAI接口一共用到5根数据线,这5根数据线用于STM32MP1与CS42L51之间的音频数据收发。
  2. CS42L51在使用的时候需要进行配置,配置接口为I2C,连接到STM32MP1的I2C4上。
  3. CS42L51是没有功放接口的,就要提供一个HT6872把HP_R耳机数据放大。

最后音频的耳机和MIC录音功能是有JP13来控制的,如果1-2连接就使用耳机进行录音,2-3连接就使用板载MIC录音。

音频驱动使能

ST官方已经写好了CS42L51驱动,因此直接配置内核使能CS42L51驱动即可,按照如下所示步骤使能CS42L51驱动。

修改设备树

前面分析原理图的时候已经说过了,CS42L51与STM32MP1之间有两个通信接口:I2C和SAI,因此设备树中会涉及到I2C和SAI两个设备节点。其中I2C用于配置CS42L51,SAI接口用于音频数据传输,依次来配置一下这两个接口。

CS42L51 I2C接口设备树

首先配置一下I2C接口,根据原理图知道CS42L51连接到了STM32MP1的I2C4接口上,因此在设备树中的“i2c4”节点下需要添加cs42l51信息。打开Documentation/devicetree/bindings/sound/cs42l51.txt,此文件仅仅用于描述如何在I2C节点下添加cs41l51相关信息,此文档适用于所有的主控,不局限于STM32MP1。

相关属性如下:

  1. Compatible:兼容属性,属性值要设置为“cirrus,cs42l51”。在Linux内核全局搜索“cirrus,cs42l51”的话就会找到cs42l51的I2C驱动,此文件为:sound/soc/codecs/cs42l51.c。
  2. reg:设置cs42l51的地址,在正点原子的STM32MP1开发板中cs42l51的I2C地址为0x4a。
  3. clocks:时钟。
  4. clock-names:比如为“MCLK”。
  5. XX-supply:这些都是电源相关的属性。指定芯片的电压,这里一般给1.8v。所以还需要添加一个 1.8v的电源节点。
  6. reset-gpios:指定芯片的复位引脚,根据原理图可知复位引脚连接在PZ7上。

绑定文档给出的参考节点内容如下所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第7张图片

根据cs42l51.txt这份绑定文档就可以在任意一个主控的I2C节点下添加cs42l51相关信息了,ST官方STM32M157C-DK2开发板使用的也是CS42L51,因此可以直接拿来使用,要注意官方的cs42l51是使用I2C1来配置的,正点原子的开发板是使用I2C4来配置的。打开stm32mp157d-atk.dts文件,先在里面创建I2C4节点,然后将arch/arm/boot/dts/stm32mp15xx-dkx.dtsi文件中ST为官方DK2开发板编写的CS42L51节点复制到I2C4节点下。完成以后的IC4和CS42L51节点内容如下所示:

示例代码 48.3.1.2 添加cs42l51节点后的i2c4节点
1  &i2c4 {
2      pinctrl-names = "default", "sleep";
3      pinctrl-0 = <&i2c4_pins_a>;
4      pinctrl-1 = <&i2c4_pins_sleep_a>;
5      status = "okay";
6      /delete property/dmas;
7      /delete property/dma-names;
8      cs42l51: cs42l51@4a {
9          compatible = "cirrus,cd42l51";
10         reg = <0x4a>;
11         #sound-dai-cells = <0>;
12         VL-supply = <&v3v3>;
13         VD-supply = <&v1v8_audio>;
14         VA-supply = <&v1v8_audio>;
15         VAHP-supply = <&v1v8_audio>;
16         reset-gpios = <&gpioz 7 GPIO_ACTIVE_LOW>;
17         clocks = <&sai2a>;
18         clock-names = "MCLK";
19         status = "okay";
20
21         cs42l51_port: port {
22             #address-cells = <1>;
23             #size-cells = <0>;
24
25             cs42l51_tx_endpoint: endpoint@0 {
26                 reg = <0>;
27                 remote-endpoint = <&sai2a_endpoint>;
28                 frame-master;
29                 bitclock-master;
30             };
31
32             cs42l51_rx_endpoint: endpoint@1 {
33                 reg = <1>;
34                 remote-endpoint = <&sai2b_endpoint>;
35                 frame-master;
36                 bitclock-master;
37             }:
38         };
39     };
40 };

示例代码48.3.1.2里,新追加了一个I2C4节点和添加了i2c4的一个cs42l51子节点。

第3、4行,设置I2C4的引脚,要根据自己的实际硬件情况设计,正点原子STM32MP157开发板上I2C4_SDA和I2C4_SCL分别为PZ5和PZ4。stm32mp15-pinctrl.dtsi文件中I2C4引脚默认配置的就是PZ5和PZ4,因此无需修改。

第8-39行,就是cs42l51子节点。

第8-19行,就是跟cs42l51.txt文档一样,只是将复位引脚改为了PZ7,这里就不多说了。重点是第13-15行,多了个1.8V的电源管理,所以需要添加一个1.8V的节点,稍后添加。

第21-38行,cs42l51_port子节点用来设置CS42L51的数据输入/输出端口信息,第25-30行cs42l51_tx_endpoint端口用来接收CPU上哪个SAI的数据,第27行就是告诉CS42L51从sai2a_endpoint获取音频数据,此接口是用来播放音频的通道。第32-37行cs42l51_rx_endpoint端口用来将获取到的音频数据发送到CPU的哪个SAI接口上,第34行就是告诉CS42L51从外面获取的音频数据传输到sai2b_endpoint,此接口是用来录音的通道

最后我们还需要在根节点里添加一个v1v8_audio的电源管理。如下示例代码所示:

示例代码 48.3.1.3 追加的v1v8_audio节点内容
1 v1v8_audio: regulator-v1v8-audio {
2     compatible = "regulator-fixed";
3     regulator-name = "v1v8_audio";
4     regulator-min-microvolt = <1800000>;
5     regulator-max-microvolt = <1800000>;
6     regulator-always-on;
7     regulator-boot-on;
8 };

至此,关于cs42l51的I2C配置接口设备树就已经添加好了。

STM32MP1 SAI音频接口设备树

接下来就是STM32MP1的SAI音频接口设备树相关内容的修改了,同样的,先查阅一下相应的绑定文档:devicetree/bindings/sound/st,stm32-sai.txt。和前面讲过的IIC接口、SPI等接口一样,在stm32mp151.dtsi文件中会有关于SAI相关接口的描述,这部分是ST原厂编写的,不需要做任何修改,SAI2的设备子节点内容如下所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第8张图片

在示例代码48.3.1.4中sai2节点有两个子节点,分别为sai2a和sai2b,刚好对应两个子模块。直接搜索“st,stm32h7-sai”兼容值,那么就会找STM32MP1的SAI接口驱动文件,路径为sound/soc/stm/stm32_sai.c,此驱动文件不需要去研究,是已经ST写好的SAI驱动。从第9行可以看出, SAI2默认是关闭的,因此需要将其打开,也就是设置status属性的值为“okay”,这个工作肯定是在具体板子对应的.dts文件中完成的,其实就是向sai2节点以及对应子节点里面追加或者修改一些属性值。

前面说了,ST官方的DK2开发板也使用了CS42L51,因此DK2开发板的设备树文件stm32mp15xx-dkx.dtsi里面肯定有具体的SAI2节点信息。可以直接将其复制到stm32mp157d-atk.dts文件里面,内容如下:

示例代码48.3.1.5 拷贝的SAI2节点内容
&sai2 {
	clocks = <&rcc SAI2>, <&rcc PLL3_Q>, <&rcc PLL3_R>;
	clock-names = "pclk", "x8k", "x11k";
	pinctrl-names = "default", "sleep";
	pinctrl-0 = <&sai2a_pins_a>, <&sai2b_pins_b>;
	pinctrl-1 = <&sai2a_sleep_pins_a>, <&sai2b_sleep_pins_b>;
	status = "okay";

	sai2a: audio-controller@4400b004 {
		#clock-cells = <0>;
		dma-names = "tx";
		clocks = <&rcc SAI2_K>;
		clock-names = "sai_ck";
		status = "okay";

		sai2a_port: port {
			sai2a_endpoint: endpoint {
				remote-endpoint = <&cs42l51_tx_endpoint>;
				format = "i2s";
				mclk-fs = <256>;
				dai-tdm-slot-num = <2>;
				dai-tdm-slot-width = <32>;
			};
		};
	};

	sai2b: audio-controller@4400b024 {
		dma-names = "rx";
		st,sync = <&sai2a 2>;
		clocks = <&rcc SAI2_K>, <&sai2a>;
		clock-names = "sai_ck", "MCLK";
		status = "okay";

		sai2b_port: port {
			sai2b_endpoint: endpoint {
				remote-endpoint = <&cs42l51_rx_endpoint>;
				format = "i2s";
				mclk-fs = <256>;
				dai-tdm-slot-num = <2>;
				dai-tdm-slot-width = <32>;
			};
		};
	};
};

示例代码48.3.1.5中的内容,主要是对sai2节点做了四个方面描述:SAI2接口引脚的pinctrl设置、时钟配置、修改status为“okay”以及指定收发接口。重点来看一下pinctrl的设置,因为关系到SAI2接口的IO配置,从pinctrl-0属性可以看出这里一共有两组IO:sai2a_pins_a和 sai2b_pins_b,打开stm32mp15-pinctrl.dtsi文件,这两组IO内容如下:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第9张图片

sai2a_pins_a和sai2b_pins_b就是描述的是SAI2接口的IO配置,这个要根据自己板子的实际硬件情况修改,正点原子的STM32MP1开发板上SAI2所使用的引脚和ST官方的DK2开发板一样,因此这里不需要做任何修改。

对于正点原子的STM32MP1开发板,SAI部分的设备树信息不需要做任何修改,直接使用ST官方写好的即可

STM32MP1 sound节点

最后需要在根节点“/”下创建一个名为“sound”的子节点,此节点用作控制音频数据的收发。可以参考文档:Documentation/devicetree/bindings/sound/audio-graph-card.txt。此文档有很多种配置方法,还要根据你的音频选择对应的配置方法。ST官方已经针对DK2开发板编写了 sound节点,直接拷贝过来即可。在stm32mp15xx-dkx.dtsi文件中找到 sound节点,然后将其拷贝到stm32mp157d-atk.dts根节点下,拷贝完成以后的sound节点内容如下所示:

示例代码48.3.1.7 sound节点内容
sound: sound {
		compatible = "audio-graph-card";
		label = "STM32MP1-DK";
		routing =
			"Playback" , "MCLK",
			"Capture" , "MCLK",
			"MICL" , "Mic Bias";
		dais = <&sai2a_port &sai2b_port>;
		status = "okay";
	};

简单看一下sound节点中几个重要的属性:

  • compatible:非常重要,用于匹配相应的驱动文件,在整个linux内核源码中搜索这“audio-graph-card”属性值即可找到对应的驱动文件,这里找到的驱动文件为sound/soc/generic/audio-graph-card.c。
  • model:最终用户看到的此声卡名字,这里设置为“STM32MP1-DK”。
  • routing:音频器件一系列的连接设置,每个条目都是一对字符串,第一个字符串是连接的sink,第二个是连接的source。
  • dais:用来指定音频有多少个接收和发送,sai2a_port和sai2b_port就是CS42L51发送和接收的接口。

使能/修改内核的CS42L51驱动

设备树配置完成以后就可以使能内核自带的CS42L51驱动了,直接通过图形化界面配置即
可,输入“make menuconfig”即可打开图形化界面。ST官方已经默认使能了CS42L51驱动。

使能CS42L51驱动

配置路径如下:

-> Device Drivers
-> Sound card support
-> Advanced Linux Sound Architecture
-> ALSA for SoC audio support
-> CODEC drivers
-> <*> Cirrus Logic CS42L51 CODEC (I2C)(选中)

结果如下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第10张图片

使能sound驱动

接下来使能sound,配置路径如下:

-> Device Drivers
-> Sound card support
-> Advanced Linux Sound Architecture
-> ALSA for SoC audio support
-> -> <*> ASoC Audio Graph sound card support(选中)

结果如下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第11张图片

使能SAI驱动

最后使能SAI接口:

-> Device Drivers
-> Sound card support (SOUND [=y])
-> Advanced Linux Sound Architecture (SND [=y])
-> ALSA for SoC audio support (SND_SOC [=y])
-> STMicroelectronics STM32 SOC audio support
-> <*> STM32 SAI interface (Serial Audio Interface) support (选中)
-> <*> STM32 I2S interface (SPI/I2S block) support (选中)
-> <*> STM32 S/PDIF receiver (SPDIFRX) support (选中)
-> <*> SoC Audio support for STM32 DFSDM (选中)

结果如下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第12张图片

修改CS42L51驱动

CS42L51默认驱动的录音为单通道,所以要修改驱动来实现双通道录音。打开sound/soc/codecs/cs42l51.c文件,找到cs42l51_hw_params函数,将如下代码添加到第477行:

intf_ctl |= (1 << 0);

结果如下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第13张图片

上图中添加的内容是将CS42L51的0X04寄存器的bit0置1。驱动使能以后重新编译linux内核,编译完成以后使用新的uImage和.dtb文件启动,如果设备树和驱动都使能的话系统启动过程中就会如下图所示的log信息:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第14张图片

系统最终启动以后会打印出ALSA设备列表,现在的音频CODEC驱动基本都是ALSA架构的,本章的CS42L51驱动也是根据ALSA架构编写的。因此在ALSA设备列表中就会找到“STM32MP1-DK”这个声卡,如下图所示:

CS42L51声卡

进入系统以后查看一下/dev/snd目录,看看有没有如下图所示文件:

alsa驱动设备文件

上图中的这些文件就是ALSA音频驱动框架对应的设备文件,这些文件的作用如下:

  • controlC0:用于声卡控制,C0表示声卡0。
  • pcmC0D0p:用于播放的pcm设备,最后面的“p”是playback的缩写,表示放音。
  • pcmC0D1c:用于录音的pcm设备,最后面的“c”是capture的缩写,表示录音。
  • timer:定时器。

音频驱动使能以后还不能直接播放音乐或录音,还需要移植alsa-lib和alsa-utils这两个东西

alsa-lib和alsa-utils移植

alsa-lib这个库在buildroot已经默认编译进去可以不用管,只需要使能alsa-utils就行了,还是在buildroot的源码目录下,通过“make menuconfig”进入图形化界面,按照如下路径进入alsa-utils:

-> Target packages
-> Audio and video applications
-> [*] alsa-utils (选中 )
-> [*] alsactl(选中)
......
-> [*] speaker-test(选中)

把alsa-utils下的软件全部选中,结果如下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第15张图片

保存退出,然后重新编译buildroot。编译完成后,把新的rootfs替换到原先的根文件系统中:

cd output/images/ //进入到 output/images目录
sudo tar -axvf rootfs.tar -C /home/zuozhongkai/linux/nfs/rootfs //解压到 nfsroot目录

上述命令将buildroot中output/images/rootfs.tar这个压缩包解压到/home/zuozhongkai/linux/nfs/rootfs这个目录中,这个目录就是正点原子教程中当前nfsroot目录,根据自己的实际情况解压到对应的目录文件中。

声卡设置与测试

amixer使用方法

查看帮助信息

声卡相关选型默认都是关闭的,比如耳机和喇叭的左右声道输出等。因此在使用之前一定要先设置好声卡,alsa-utils自带了amixer这个声卡设置工具。输入如下命令即可查看amixer的帮助信息:

amixer --help //查看amixer帮助信息

结果如下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第16张图片

从上图可以看出,amixer软件命令分为两组,scontrols、scontents、sset和sget为一组。controls、contents、cset和cget为另一组。这两组的基本功能都是一样的,只不过“s”开头的是simple组,这一组命令是简化版,本教程最终使用“s”开头的命令设置声卡,因为少输入很多字符

查看设置项

要先看一下都有哪些设置项,先来看一下scontrols对应的设置项,输入如下命令:

amixer scontrols //查看所有设置项

结果如下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第17张图片

再来看一下controls对应的设置项,输入如下命令:

amixer controls //查看所有设置项

结果如下图所示:
正点原子嵌入式linux驱动开发——Linux 音频驱动_第18张图片

这里只关注一些最常用的设置即可,比如设置耳机和喇叭音量、设置左右声道音量、设置输入音量等等

查看设置值

不同的设置项对应的设置值类型不同,先查看一下scontents对应的设置值,输入如下命令:

amixer scontents //查看设置值

结果如下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第19张图片

从上图可以看出“PCM”项目就是设置耳机音量的,音量范围为0-127,当前音量为103。有些设置项是bool类型,只有on和off两种状态。关于controls对应的设置值自行输入“amixer contents”命令查看即可。

设置声卡

知道了设置项和设置值,那么设置声卡就很简单了,直接使用下面命令即可:

amixer sset 设置项目 设置值

或:

amixer cset 设置项目 设置值

获取声卡设置值

如果要读取当前声卡某项设置值的话使用如下命令:

amixer sget 设置项目

或:

amixer cget 设置项目

音乐播放测试

使用amixer设置声卡

第一次使用声卡之前一定要先使用amixer设置声卡,打开耳机或者喇叭,并且设置喇叭或者耳机音量,还有就是开启左右声道,输入如下命令:

amixer cset name='PCM Playback Switch' 'on','on'
amixer cset name='PCM Playback Volume' '63','63'
amixer cset name='Analog Playback Volume' '204','204'
amixer cset name='PCM channel mixer' 'L R'

使用aplay播放WAV格式音乐

声卡设置好以后就可以使用aplay软件播放wav格式的音乐测试一下,aplay也是alsa-utils提供的。buildroot编译alsa-utils的时候已经在/usr/share/sounds/alsa/路径下存放了一些音乐了,可以直接拿来测试,测试命令如下:

aplay /usr/share/sounds/alsa/Front_Right.wav //播放歌曲

如果一切设置正常的话就会开始播放音乐。

注意:由于CS42L51只有耳机播放的输出接口,没有喇叭接口。正点原子STM32MP1开发板上的喇叭直接接到了耳机右声道上,所以耳机与喇叭不能自动切换,播放音乐的时候会同时有声音。

MIC录音测试

正点原子的STM32MP1开发板上有一个板载麦克风,如下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第20张图片

将上图中的JP13跳接到上方,也就是使用板载MIC录音。如果跳接到下方就是使用耳机自带的话筒。

使用amixer设置声卡

同样的,第一次使用声卡录音之前要先使用amixer设置一下声卡,设置声卡的命令如下所示:

amixer cset name='PGA-ADC Mux Left' '3'
amixer cset name='PGA-ADC Mux Right' '3'
amixer cset name='Mic Boost Volume' '1','1'

使用arecord录制音频

使用arecord来录制一段10秒中的音频,arecord也是alsa-utils编译出来的,输入如下命令:

arecord -f S16_LE -d 10 -D hw:0,1 record.wav

-f是用来设置录音的数据以S16_LE格式进行采样。-d是指定录音时间,单位为s,-D是指定硬件声卡,0是卡数,1是在设备数量。这条指令就是录制一段以S16_LE格式10s的wav音频,音频名字为record.wav。

录制完成以后使用aplay播放刚刚录制的音频,看看录制是否成功,命令如下:

aplay record.wav

PHONE录音测试

最后进行一下phone录音测试,也就是使用耳机上的MIC录音,前提是耳机支持录音。正点原子的STM32MP1开发板上phone接口如图下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第21张图片

使用一根3.5mm的接口耳机,耳机要有麦。录音的方法跟MIC录音一样即可。

开机自动配置声卡

在使用的时候应该应该会发现开发板重启以后声卡的所有设置都会消失,必须重新设置声卡。也就是说对声卡的设置不能保存,本小节就来学习一下如何保存声卡的设置。

使用alsactl保存声卡设置

声卡设置的保存通过alsactl工具来完成,此工具也是alsa-utils编译出来的。alsactl默认将声卡配置文件保存在/var/lib/alsa目录下。

首先使用amixer设置声卡,然后输入如下命令保存声卡设置:

alsactl -f /var/lib/alsa/asound.state store //保存声卡设置

-f指定声卡配置文件,store表示保存。关于alsactl的详细使用方法,输入“alsactl -h”即可。保存成功以后就会生成/var/lib/alsa/asound.state这个文件,asound.state里面就是关于声卡的各种设置信息,可以打开此文件查看一下里面的内容,如下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第22张图片

如果要使用asound.state中的配置信息来配置声卡,执行如下命令即可:

alsactl -f /var/lib/alsa/asound.state restore
最后面的参数改为restore即可,也就是恢复的意思。

在之前的根文件系统buildroot测试中,曾经创建了/etc/init.d/Sautorun自启动文件,如果没有就自行创建,或者使用/etc/init.d/reS文件,添加如下内容:

示例代码 4 .6.1 /etc/init.d/S autorun 追加内容
1 if [ f "/var/lib/alsa/asound.state" ]; then
2     echo "ALSA: Restoring mixer setting......"
3     /usr/sbin/alsactl -f /var/lib/alsa/asound.state restore &
4 fi

第1行判断/var/lib/alsa/asound.state这个文件是否存在,存在的话就执行下面的。首先输出一行提示符:“ALSA: Restoring mixer setting…”,表示设置声卡,最后调用/usr/sbin/alsactl来执行声卡设置工作。

设置完成以后重启开发板,开发板开机就会自动设置声卡,输出如下图所示内容:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第23张图片

直接使用aplay播放音乐测试声卡开机自动配置是否正确。

alsamixer简介

alsamixer是基于图形化的,直接输入“alsamixer”命令即可打开声卡配置界面,如下图所示:

正点原子嵌入式linux驱动开发——Linux 音频驱动_第24张图片

  • F1键:查看帮助信息。
  • F2键:查看系统信息。
  • F3键:播放设置。
  • F4键:录音设置。
  • F6键:选择声卡,多声卡情况下。
  • Item:设置项全名。

上图最下面一行就是具体的设置项,比如“Bass”、 “PCM”等等,通过键盘上左右键选择设置项。按下上下键来调整大小,比如设置耳机音量大小等。有些项目会显示“MM”,表示静音,按下“M”键修改为“OO”状态打开“M”键用于修改打开或关闭某些项目。

关于alsamixer的介绍就到这里,用起来还是很简单的。

总结

正点原子的STM32MP157开发板,对于音频的驱动要使用CS42L51芯片来完成,这个驱动linux内核自己已经有了,我们只需要在设备树完成配置就可以了。

CS42L51芯片,挂载在I2C4总线下,同时还需要3.3V和1.8V的供电,设备树中需要tx和rx,以及电源的内容;1.8V的电源也要在根节点下重新添加。

之后还需要在设备树中添加SAI2的节点信息,这里可以直接拷贝stm32mp151.dtsi文件中的相关内容,而sai2在pinctrl的复用中,是已经有过设置的,不需要我们自行去编写。

最后在根节点下,添加一个sound节点调用刚才写好的sai2节点和音频连接的设置。这里可以直接在stm32mp15xx-dkx.dtsi中找到sound节点拷贝过来。

之后可以修改为双通道录音,在sound/soc/codecs/cs42l51.c中的477行添加intf_ctl|=(1<<0);就可以了。

之后的音频测试需要再rootfs中,使能alsa-utils的所有配置。然后通过amixer去设置声卡并通过aplay来测试是否可用。

你可能感兴趣的:(linux学习,linux,驱动开发,音视频,学习,笔记,stm32)