当前位置: 首页 > news >正文

正点原子嵌入式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整体框架如下图所示:

CS42L51框图

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

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

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

I2S时序图

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

真实的I2S时序图

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

I2S左对齐和右对齐数据格式

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框架图如下图所示:

SAI框图

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

硬件原理图分析

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

音频原理图

上图中重点关注有三方面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上。

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

CS42L51节点参考设置

根据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的设备子节点内容如下所示:

STM32MP1 SAI接口子节点

在示例代码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内容如下:

sai2引脚配置

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)(选中)

结果如下图所示:

使能CS42L51驱动

使能sound驱动

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

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

结果如下图所示:

使能sound驱动

使能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 (选中)

结果如下图所示:

使能SAI驱动

修改CS42L51驱动

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

intf_ctl |= (1 << 0);

结果如下图所示:

修改CS42L51驱动代码结果

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

CS42L51信息

系统最终启动以后会打印出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下的软件全部选中,结果如下图所示:

要配置的alsa-utils

保存退出,然后重新编译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帮助信息

结果如下图所示:

amixer帮助信息

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

查看设置项

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

amixer scontrols //查看所有设置项

结果如下图所示:

scontrols命令对应的设置项

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

amixer controls //查看所有设置项

结果如下图所示:
controls命令对应设置项

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

查看设置值

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

amixer scontents //查看设置值

结果如下图所示:

scontrols命令对应设置项

从上图可以看出“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开发板上有一个板载麦克风,如下图所示:

板载麦克风

将上图中的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接口如图下图所示:

PHONE接口

使用一根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里面就是关于声卡的各种设置信息,可以打开此文件查看一下里面的内容,如下图所示:

asound.state文件部分内容

如果要使用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来执行声卡设置工作。

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

声卡开机自动设置

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

alsamixer简介

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

alsaximer配制界面

  • 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 音频驱动

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

conda相关的命令操作

准备切换conda环境 cd C:\ProgramData\Anaconda3\Scripts查看所有环境 conda info --envs选择环境 activate pytorch安装torch pip install D:\installPackage\torch-1.2.0-cp36-cp36m-win_amd64.whl安装torchvision pip install D:\installPackage\torchvision-0.4.0-cp3…...

如何快速使用Vue3在electron项目开发chrome Devtools插件

1、建立Vue项目 为了方便快速建立项目&#xff0c;我已经写好脚手架&#xff0c;直接clone项目&#xff0c;快速开发 点击快速进入源代码 拉取代码 git clone https://github.com/xygengcn/electron-devtool.git安装依赖 yarn运行项目 yarn dev打包项目 yarn build2、安装…...

干洗店服务预约小程序有什么作用

要说干洗店&#xff0c;近些年的需求度非常高&#xff0c;一方面是人们生活品质提升&#xff0c;另一方面则是各种服饰对洗涤要求提升等&#xff0c;很多人的衣服很多也会通过干洗店进行清洁。 而对从业商家来说&#xff0c;市场庞大一方面需要不断进行市场教育、品牌提升&…...

【跟小嘉学 Rust 编程】三十四、Rust的Web开发框架之一: Actix-Web的进阶

系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…...

软件安装(1)——Xshell安装

一、前言 本篇文章主要用于介绍Xshell破解版的安装 二、具体步骤 1. 下载Xshell7 链接&#xff1a;https://pan.baidu.com/s/1sFZz1uPb7yeDl6dlM4xtpg 提取码&#xff1a;a7m8 2. 安装Xshell7 选择文件安装目录后安装即可...

Kafka基本原理、生产问题总结及性能优化实践 | 京东云技术团队

Kafka是最初由Linkedin公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff09;、多副本的&#xff08;replica&#xff09;&#xff0c;基于zookeeper协调的分布式消息系统&#xff0c;它的最大的特性就是可以实时的处理大量数据以满足各种需求场景&a…...

java8利用Stream方法求两个List对象的交集、差集与并集(即:anyMatch和allMatch和noneMatch的区别详解)

1、anyMatch 判断数据列表中是否存在任意一个元素符合设置的predicate条件&#xff0c;如果是就返回true&#xff0c;否则返回false。 接口定义&#xff1a; boolean anyMatch(Predicate<? super T> predicate); 方法描述&#xff1a; 在anyMatch 接口定义中是接收 P…...

Centos7下生成https自签名证书

1、安装openssl yum install openssl2、生成带密码的私有秘钥文件 openssl genrsa -des3 -out server.key 2048使用带密码的私有秘钥文件时需要输入密码&#xff0c;这里直接输入&#xff1a;123456 3、生成不带密码的私有秘钥文件 openssl rsa -in server.key -out serve…...

从中序和后序遍历序列构造二叉树

注意&#xff1a;该解法是基于二叉树中的值不存在重复所写的。 代码如下&#xff0c;可开袋即食 class Solution {private Map<Integer,Integer> map;public TreeNode buildTree(int[] inorder, int[] postorder) {map new HashMap<>();for(int i 0; i < in…...

Apache ActiveMQ (版本 < 5.18.3) (CNVD-2023-69477)RCE修复方案/缓解方案

一、漏洞描述 Apache ActiveMQ 是美国阿帕奇&#xff08;Apache&#xff09;基金会的一套开源的消息中间件&#xff0c;它支持 Java 消息服务、集群、Spring Framework 等。 二、漏洞成因 ActiveMQ 默认开放了 61616 端口用于接收 OpenWire 协议消息&#xff0c;由于针对异常…...

61. 旋转链表、Leetcode的Python实现

博客主页&#xff1a;&#x1f3c6;李歘歘的博客 &#x1f3c6; &#x1f33a;每天不定期分享一些包括但不限于计算机基础、算法、后端开发相关的知识点&#xff0c;以及职场小菜鸡的生活。&#x1f33a; &#x1f497;点关注不迷路&#xff0c;总有一些&#x1f4d6;知识点&am…...

基于tpshop开发多商户源码支持手机端+商家+门店 +分销+淘宝数据导入+APP+可视化编辑

tpshop多商户源码,tpshop商城源码,tpshop b2b2c源码-支持手机端商家门店 分销淘宝数据导入APP可视化编辑 tpshop商城源码算是 thinkphp框架里做的比较早 比较好的源码了&#xff0c;写法简明 友好面向程序猿。 这是一款前几年的版本 虽然后台看着好了些&#xff0c;丝毫不影响…...

ElasticSearch深度解析入门篇:高效搜索解决方案的介绍与实战案例讲解,带你避坑

ElasticSearch深度解析入门篇&#xff1a;高效搜索解决方案的介绍与实战案例讲解&#xff0c;带你避坑 1.Elasticsearch 产生背景 大规模数据如何检索 如&#xff1a;当系统数据量上了 10 亿、100 亿条的时候&#xff0c;我们在做系统架构的时候通常会从以下角度去考虑问题&a…...

HTML简单实现v-if与v-for与v-model

Vue启动&#xff01;&#xff01; 首先VIewModel将View和Model连接一起&#xff0c;Model的数据改变View的数据也变 使用Visual Studio Code 启动Vue需要vue.js插件和导入CDN(包) vue.js插件&#xff1a;CTRL shift x 在搜索栏搜 索vue.js安装即可 CDN&#xff1a; http…...

【学习笔记】[PA2021] Fiolki 2

Part 1 前置知识&#xff1a;LGV引理 摘抄自oi-wiki&#xff1a; L G V LGV LGV引理可以用来处理有向无环图上不相交路径计数等问题。 基本定义&#xff1a; w ( P ) w(P) w(P)表示 P P P这条路径上所有边的 边权之积 。&#xff08;路径计数时&#xff0c;可以将边权都设为…...

计算1到100的和

一、不好的写法 public static void main(String[] args) {int sum 0;int n 100;for (int i 1; i < n; i) {sum i;}System.out.println("sum" sum);}1.定义两个整型变量&#xff1b; 2.执行100次加法运算&#xff1b; 3.打印结果到控制台&#xff1b; 二、好…...

C++下OpenMP耗时统计

在C中&#xff0c;如果你使用OpenMP进行并行计算&#xff0c;你可以使用omp_get_wtime()函数来测量代码段的执行时间。这个函数返回一个double类型的值&#xff0c;表示从某一固定点到当前时间的秒数。因此&#xff0c;你可以在代码的开始和结束点分别调用这个函数&#xff0c;…...

PTA 函数题(C语言)-- 阶乘计算升级版

题目title&#xff1a; 阶乘计算升级版 题目作者&#xff1a; 陈越 浙江大学 本题要求实现一个打印非负整数阶乘的函数。 函数接口定义&#xff1a; void Print_Factorial ( const int N ); 其中N是用户传入的参数&#xff0c;其值不超过1000。如果N是非负整数&#…...

内网穿透入门

内网穿透 内网穿透&#xff08;英文&#xff1a;Port Forwarding&#xff09;是一种网络技术&#xff0c;用于将公共互联网&#xff08;外网&#xff09;的请求转发到私有局域网&#xff08;内网&#xff09;中的特定设备或服务。在许多情况下&#xff0c;设备或服务位于一个局…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)

UniApp 集成腾讯云 IM 富媒体消息全攻略&#xff08;地理位置/文件&#xff09; 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型&#xff0c;核心实现方式&#xff1a; 标准消息类型&#xff1a;直接使用 SDK 内置类型&#xff08;文件、图片等&#xff09;自…...

macOS 终端智能代理检测

&#x1f9e0; 终端智能代理检测&#xff1a;自动判断是否需要设置代理访问 GitHub 在开发中&#xff0c;使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新&#xff0c;例如&#xff1a; fatal: unable to access https://github.com/ohmyzsh/oh…...