Linux内核中的音频架构

参考:

Linux 音频驱动实验-CSDN博客

Linux-音频应用编程-CSDN博客

分层架构概述

Linux音频架构可以看作一个分层、模块化的软件栈,从底层的硬件到顶层的应用程序,大致分为以下4层:


🏗️ Linux音频架构总览(4层模型)

层级 名称 作用 代表组件
应用层 用户空间应用程序 生成或消费音频数据 aplayGStreamerMPV、浏览器、游戏
API层 音频编程接口 为应用提供统一的音频访问方式 ALSA-libPulseAudioPipeWireJACK
内核层 ALSA核心驱动 管理硬件设备、路由音频数据 sound/core/sound/soc/(ASoC)、具体硬件驱动
硬件层 物理音频设备 数字↔模拟信号转换、声音输出/输入 CPU I2S控制器、Codec芯片、扬声器、麦克风

🔍 各层详解(从底层到顶层)

1️⃣ 硬件层(物理设备)

最底层,就是实际的音频芯片和外设:

  • SoC内部的I2S/PCM控制器:负责将数字音频数据以特定时序发送给Codec。

  • Codec芯片(如WM8960、ES8316、TLV320):负责数模转换(DAC)、模数转换(ADC)、音量调节、混音等。

  • 物理输出/输入:扬声器、耳机接口、麦克风、LINE IN等。

2️⃣ 内核层(ALSA驱动框架)

这是Linux音频的核心,由ALSA(高级Linux声音架构) 主导。在内核源码的 sound/ 目录下,它包含几个关键部分:

  • ALSA核心(sound/core/ :提供PCM、混音器、MIDI、定时器等核心API,管理设备节点(/dev/snd/)和 /proc/asound/ 信息。

  • ASoC(ALSA片上系统,sound/soc/:专为嵌入式SoC设计的分层驱动模型,将音频驱动拆解为:

    • Platform:CPU端的I2S/DMA驱动。

    • Codec:音频编解码芯片驱动。

    • Machine:板级适配层,将Platform和Codec绑定,定义音频路由和GPIO控制。

  • 硬件驱动(sound/pci/sound/usb/等):具体声卡的驱动实现。

内核层最终会生成:

  • 设备文件:/dev/snd/controlC0/dev/snd/pcmC0D0p 等,供用户空间访问。

  • 信息接口:/proc/asound/ 下的各种状态文件。

3️⃣ API层(用户空间音频服务)

应用程序一般不直接操作内核设备文件,而是通过更高级的API库或服务:

  • ALSA-lib(libasound :用户空间库,提供标准API。应用程序可以直接调用它打开 /dev/snd/pcmC0D0p 读写音频数据,也能通过 controlC0 调节音量。

  • PulseAudio:高级音频服务,提供混音、音频路由、网络音频传输等。支持多应用同时播放声音,自动采样率转换。广泛应用于桌面Linux。

  • PipeWire:新一代音视频服务,设计更现代、低延迟,正在逐步取代PulseAudio和JACK。

  • JACK:专业音频服务,以极低延迟为目标,常用于音频工作站(DAW)。

对于嵌入式Linux,轻量级场景常直接使用ALSA-lib (如aplayarecord工具),或者使用基于ALSA封装的TinyALSA (Android常用)。复杂多媒体场景则用GStreamer,它内部通过ALSA插件与内核通信。

4️⃣ 应用层

用户最终使用的程序:

  • 命令行工具:aplayarecordamixer

  • 多媒体框架:GStreamer、FFmpeg。

  • 图形应用:浏览器HTML5音频、MPV播放器、游戏。

  • 通信应用:VoIP、对讲机。


🔄 音频数据的流转路径(从播放到出声)

aplay test.wav 为例,数据如何从文件流向扬声器?

  1. 应用层aplay 调用 ALSA-lib 的 snd_pcm_writei() 写入PCM数据。

  2. API层(ALSA-lib) :将数据通过 ioctl 写入 /dev/snd/pcmC0D0p 设备文件。

  3. 内核ALSA核心:将用户空间数据拷贝到内核DMA缓冲区。

  4. ASoC Platform驱动:配置I2S控制器,通过DMA将缓冲区数据发送到I2S总线。

  5. Codec驱动:接收I2S数据,配置Codec内部DAC进行数模转换。

  6. 硬件层:DAC输出模拟信号,经功放放大后驱动扬声器发声。

整个过程中,数据像水流一样从应用层一路"流"到物理扬声器,任何一环出问题(如权限、路由、采样率不匹配)都会导致无声或杂音。


🎛️ 控制通路(音量、路由)

除了音频数据,还有一个控制通路 负责调节音量、选择输入源等。应用程序通过 /dev/snd/controlC0 发送控制命令(如 ioctl(SNDRV_CTL_IOCTL_ELEM_WRITE)),内核驱动再通过I2C/SPI等总线设置Codec芯片的寄存器。


🧩 嵌入式音频的"特殊性"

与标准PC相比,嵌入式Linux音频有几个显著特点:

  • ASoC是核心 :嵌入式几乎都使用ASoC框架,因此理解 sound/soc/ 下的Platform、Codec、Machine驱动分层至关重要。

  • 设备树(Device Tree):嵌入式音频硬件的连接方式(I2S使用哪组引脚、Codec挂在哪条I2C总线等)全部通过设备树描述,驱动需要正确解析设备树节点才能工作。

  • 资源受限:嵌入式设备CPU和内存有限,通常不会运行PulseAudio等重量级服务,而是直接使用ALSA-lib或更轻量的TinyALSA。

  • 调试手段有限 :不能像桌面Linux那样轻易安装 pavucontrol 等GUI工具,更多依赖 aplay/arecord 测试、查看 /proc/asound/ 信息和内核 printk 日志。


🛠️ 排错思维(音频出了问题,查哪层?)

当你的嵌入式板子出现"无声"、"杂音"、"录音失败"时,按这个顺序排查:

排查步骤 命令/方法 对应层级
1. 硬件是否识别? cat /proc/asound/cards 内核层
2. 设备节点是否生成? ls -l /dev/snd/ 内核层
3. 驱动是否加载正确? `dmesg grep -i audiodmesg
4. 音量是否打开? amixer -c 0 scontents API层
5. 音频路由是否正确? amixer -c 0 cget ...(查看控制项) API层
6. 应用程序能否打开设备? aplay -D hw:0,0 test.wav API层/应用层
7. 数据是否写入? 使用 strace 跟踪 aplay 的系统调用 应用层

📚 总结

Linux音频架构是分层设计 的,每一层都相对独立,因此当出现问题时,可以像剥洋葱一样一层层排查。对于嵌入式开发者来说,内核层的ASoC框架是需要重点掌握的,而用户空间则要根据产品需求选择ALSA-lib、GStreamer或更轻量的方案。

Linux音频架构中的内核部分

内核音频部分的核心就是 ALSA(高级Linux声音架构) ,而在嵌入式领域,它几乎完全基于 ASoC(ALSA片上系统) 框架来构建。下面我帮你把内核部分的结构、核心组件和数据流彻底拆解清楚。


🧱 内核音频架构的三大核心组件

在内核源码的 sound/ 目录下,音频驱动被清晰地划分为三层,每一层都有明确的职责:

层级 目录位置(内核源码) 职责 关键文件/概念
1. ALSA 核心层 sound/core/ 提供PCM、控制、MIDI等核心API,管理设备节点和 /proc 接口,不涉及任何具体硬件。 pcm.ccontrol.csound.c
2. ASoC 框架层 sound/soc/ 嵌入式核心。将音频驱动拆分为平台(Platform)、编解码器(Codec)、机器(Machine)三部分,实现驱动复用和板级适配。 soc-core.csoc-pcm.c
3. 硬件驱动层 sound/pci/sound/usb/sound/soc/下的具体芯片目录 具体的硬件驱动实现,如 sound/soc/codecs/wm8960.c 具体芯片驱动文件

🔍 ASoC 框架:嵌入式音频的"三驾马车"

ASoC 将音频驱动拆解为三个独立部分,这是理解嵌入式音频内核的关键:

1. Platform(平台驱动)

  • 位置sound/soc/xxx/(xxx为具体SoC平台,如 samsung/rockchip/imx/

  • 作用:负责CPU端的音频数据传输,主要包括:

    • I2S/PCM控制器配置:设置采样率、位深、帧格式等。

    • DMA(直接内存访问)管理:将音频数据从内存搬运到I2S发送FIFO,或从I2S接收FIFO搬运到内存,减少CPU负担。

  • 关键回调trigger()hw_params()pointer()

2. Codec(编解码器驱动)

  • 位置sound/soc/codecs/

  • 作用:负责音频编解码芯片(如 WM8960、ES8316、TLV320AIC23)的控制:

    • DAC/ADC配置:设置采样率、位深、数字滤波器模式。

    • 音量控制:通过 I2C/SPI 总线操作Codec内部寄存器。

    • 音频路由:配置输入/输出通道的映射(如哪个引脚接麦克风、哪个接扬声器)。

  • 关键回调hw_params()set_fmt()mute()

3. Machine(机器驱动)

  • 位置sound/soc/xxx/(与Platform同目录,但文件名为 xxx_machine.c

  • 作用板级适配层,将具体的Platform和Codec绑定在一起,并描述板级硬件连接:

    • 确定使用哪组I2S端口

    • 确定Codec挂载在哪个I2C总线上

    • 定义音频路由(如:播放音频 → I2S → Codec DAC → 扬声器)。

  • 关键内容snd_soc_dai_link 结构体,描述了Platform、Codec和I2S端口之间的连接。


🔄 内核音频数据的流转路径(PCM播放为例)

当用户空间执行 aplay test.wav 时,数据在内核中的流转路径如下:

复制代码
用户空间 (aplay)
    ↓ (通过 /dev/snd/pcmC0D0p)
ALSA 核心层 (sound/core/pcm_native.c)
    ↓ (调用 soc_pcm_ops)
ASoC 框架层 (sound/soc/soc-pcm.c)
    ↓ (分别调用 Platform 和 Codec 驱动)
Platform 驱动 (配置 I2S 和 DMA)
    ↓ (数据由DMA搬运)
I2S 总线 (硬件传输)
    ↓ (Codec 接收)
Codec 驱动 (配置 DAC 并输出)
    ↓
扬声器 (声音)

关键点 :整个过程中,ALSA核心层只负责管理设备节点、处理用户空间的 ioctl 请求、管理数据缓冲区;而真正的硬件操作由ASoC的Platform和Codec驱动完成。

音频的驱动位置

注意:音频的驱动一般不放在内核的drivers目录下

1. 为什么 sound/ 不从属于 drivers/

最根本的原因:ALSA(先进Linux声音架构)不仅仅是一个驱动程序,它更是一个完整的音频总线框架和API接口。

在Linux内核开发者看来:

  • drivers/ 目录 :存放的是具体的、独立的硬件驱动程序 ,比如一个具体的USB网卡(drivers/net/usb/)或一个具体的I2C触摸屏(drivers/input/touchscreen/)。它们通常只包含单一的驱动文件。

  • sound/ 目录 :不仅包含给具体硬件用的驱动(sound/pci/sound/usb/),更包含了一整套音频框架核心(sound/core/嵌入式专用框架(sound/soc/

将核心框架代码和驱动代码放在一个独立的顶层目录,而不是混杂在 drivers/ 里,可以保持代码的清晰分层,也让开发者更容易维护庞大的音频子系统。


2. 音频驱动到底分布在哪几个地方?

音频驱动并不是只在一个地方,它根据硬件接口和软件架构,分布在以下三个关键位置:

目录位置 包含的内容 你是什么情况下会关注?
sound/pci/ / sound/usb/ 标准"声卡"驱动:用于标准PC(如Intel HDA)或通用USB音频类设备(免驱USB声卡)。 当你编译x86通用内核,或者排查插入开发板的USB声卡问题时。
sound/soc/ 嵌入式音频驱动核心(ASoC) :这是全志、瑞芯微、NXP、晶晨等所有嵌入式SoC音频驱动的根据地 这是你99%的时间需要关注的地方 。你的 wm8960es8316 驱动都在 sound/soc/codecs/ 下,而CPU端的I2S驱动则在 sound/soc/你的芯片厂商名/ 下。
drivers/ (部分) 非主流/遗留音频设备 :例如一些老旧的并口音频设备(drivers/parport/),或通过I2C/SPI总线挂载的音频DAC芯片驱动 (如果你写了一个纯粹挂载在I2C上的DAC驱动,它可能会放在 drivers/iio/drivers/hwmon/ 里,然后通过 snd_soc_register_codec 注册进ALSA框架)。 当你进行极低层的硬件移植,且这个音频设备不属于标准的I2S/PCM接口时。

3. 在嵌入式开发中,你的焦点在哪里?

在你熟悉的A113X2(晶晨)平台下,你可以认为音频驱动完全不在 drivers/ 目录下。它的完整路径是:

  • CPU端的I2S/DMA驱动 :位于 sound/soc/meson/(因为Amlogic的SoC在内核中属于Meson平台)。

  • Codec(编解码器)驱动 :位于 sound/soc/codecs/

  • 板级Machine驱动(绑定上述两者) :也位于 sound/soc/meson/

这才是嵌入式Linux音频驱动的"标准答案" 。如果哪天你打开某个内核源码,发现里面没有 sound/soc/ 目录,那这版内核大概率不支持嵌入式SoC的音频。


4. 为什么 Linux 不把 sound/ 重命名为 drivers/sound/

这是一种 "架构高于驱动" 的设计思想。类似的顶层目录还包括:

  • net/ :网络协议栈(TCP/IP、IPv6等),虽然网卡驱动在 drivers/net/,但协议栈核心不能放在那里。

  • fs/ :文件系统框架(VFS、Ext4、FAT等),具体的块设备驱动则在 drivers/block/

同理,sound/ 目录下存放的是 Linux 整个音频框架的"灵魂"(核心API、PCM中间层、Control接口),而具体的硬件操作对象(如USB Audio、I2S Codec)则在这个框架下作为"驱动"运行。


总结

结论

  1. 标准PC/通用音频设备 (插USB声卡):驱动在 sound/usb/sound/pci/

  2. 嵌入式SoC音频(99%的情况) :驱动全在 sound/soc/ 下,完全不走 drivers/

  3. 极少数非标准设备 :可能会在 drivers/ 下,但最终会回调 sound/ 的API。

所以,当你调试A113X2板子的音频时,永远记住:去 sound/soc/meson/sound/soc/codecs/ 找代码,而不是去 drivers/ 这就像找网络协议栈要去 net/ 而不是 drivers/net/ 一样,是Linux内核的顶层设计逻辑。

/proc/asound/和/dev/snd/目录

这两个目录是 Linux 音频子系统 ALSA (Advanced Linux Sound Architecture) 的两扇不同窗口,/proc/asound/ 是供你查看和调试的信息面板 ,而 /dev/snd/ 是应用程序实际操作的控制手柄

它们就像一套音响系统:/proc/asound/ 是贴在设备背面的技术参数铭牌 ,告诉你它有几种接口、当前状态如何;而 /dev/snd/ 是你手上的遥控器和音频线,用来切换音源、调节音量或传输音频数据。

📂 /proc/asound/:音频系统的"信息中心"

这个目录里的文件都由内核动态生成,存放在内存中,目的是让你和系统能了解当前音频硬件的配置和运行状态。

你可以把它想象成一个实时更新的仪表盘。里面一些关键文件的作用如下:

文件/目录 作用 嵌入式开发中的价值
cards 列出系统识别到的所有声卡,如 0 [MyBoard ]: ... 首先检查这个文件,确认你的Codec有没有被内核驱动成功枚举到。
pcm 列出可用的PCM(脉冲编码调制)设备,这是播放和录音的基础。 用于查看系统暴露了哪些录音/播放接口,供 aplay/arecord 等工具使用。
card0/ 每张声卡(如 card0)的子目录,包含其专属信息。 这是排查特定声卡问题的核心。
card0/pcm0p/ 代表 card0 的播放 (p - playback) 设备子目录。 可以查看 infohw_params(硬件参数)等文件,了解该播放通道的采样率、位深等设置细节。
version 显示当前ALSA框架的版本号。 确认你的ALSA版本,排查版本兼容性问题。

在调试时,你通常会先用 cat /proc/asound/cards 确认声卡存在,然后再去 cardX/ 下查看更具体的状态信息,就像排查网络问题时先 ifconfig 再看路由表一样。

🎛️ /dev/snd/:应用程序的"操作界面"

这个目录下是各种设备文件 ,它们是应用程序(如 aplayGStreamer 或你的自定义程序)与音频硬件进行实际数据交互的接口。

这些文件可以理解为物理设备在系统中的"代理人"。它们都是字符设备文件(以 c 开头),主设备号通常是 116,通过不同的次设备号来区分功能。

设备文件(示例) 含义与用途 谁在使用
controlC0 声卡0的控制设备,用于音量调节、通道选择、混音控制等。 alsamixer, amixer
pcmC0D0p 声卡0,设备0的播放 (playback) PCM设备。应用程序将音频数据写入此设备进行播放。 aplay, GStreamer
pcmC0D0c 声卡0,设备0的录音 (capture) PCM设备。应用程序从此设备读取音频数据。 arecord, GStreamer
timer ALSA的定时器设备,用于提供时间基准。 需要精准时钟同步的专业音频应用。
seq 音序器设备,用于MIDI(乐器数字接口)数据的传输和路由。 MIDI合成器或音序器软件。

命名规则解析pcmC0D0p 中的 C0 指 Card 0(第一张声卡),D0 指 Device 0(该声卡上的第一个PCM设备),最后的 p 代表播放方向,如果是 c 则代表录音方向。

在嵌入式开发中,如果你的应用使用ALSA API(或封装的库如TinyALSA)访问音频,最终操作的就是这些设备文件。

⚙️ 两者如何协同工作:一个播放场景

当你执行 aplay -D hw:0,0 test.wav 播放音频时:

  1. aplay 首先通过 ALSA 库,打开 /dev/snd/pcmC0D0p 这个设备文件。

  2. ALSA 核心驱动会根据当前声卡在 /proc/asound/card0/ 中记录的硬件参数(如采样率、位深),配置 DMA(直接内存访问)和 I2S(集成电路内置音频总线)控制器。

  3. 应用程序将音频数据写入 /dev/snd/pcmC0D0p,数据被送入硬件的音频缓冲区进行播放。

如果应用需要调节音量,它会打开 /dev/snd/controlC0,通过 ioctl 系统调用将音量值传给驱动,驱动最终操作硬件寄存器。

🛠️ 嵌入式调试实战

在嵌入式Linux开发板上,音频调试的流程通常如下:

  1. 检查硬件是否被识别

    复制代码
    cat /proc/asound/cards

    如果没有列出你的声卡,说明驱动或设备树(Device Tree)配置可能存在问题。

  2. 检查设备节点是否生成

    复制代码
    ls -l /dev/snd/

    如果找不到 controlC0pcmC0D0p 这类文件,说明设备节点没有成功创建,可能是 udev 规则或 devtmpfs 的问题,或者驱动加载不完整。

  3. 查看声卡详细信息

    复制代码
    cat /proc/asound/card0/pcm0p/info
    cat /proc/asound/card0/pcm0p/sub0/hw_params  # 查看当前硬件参数

    这些信息能帮你确认驱动是否正确配置了采样率、位深等参数。

⚠️ 权限问题:一个常见陷阱

嵌入式开发中,经常遇到应用程序无法打开音频设备的问题,最常见的原因就是权限不足/dev/snd/ 下的设备文件通常归 root 用户和 audio 组所有。如果你的应用程序不是以 root 运行,并且用户不在 audio 组内,就没有读写权限。

复制代码
# 查看设备文件权限
ls -l /dev/snd/
# crw-rw---- 1 root audio 116, ... /dev/snd/controlC0

解决方法

  • 将你的应用程序用户添加到 audio 组。

  • 或者在嵌入式系统中,简单地用 chmod 666 /dev/snd/* 修改权限(这在开发阶段很常用,但生产环境不推荐)。

为啥有那么多声卡和设备

这个问题问得很好!当你进入 /proc/asound//dev/snd/ 目录,看到一堆 card0card1pcmC0D0ppcmC0D1c 时,确实会感到困惑------明明板子上只有一个耳机孔和一个麦克风,怎么冒出来这么多设备?

其实,"声卡"和"设备"在Linux内核中是抽象概念,它们并不等同于物理上你能看到的音频接口。下面我帮你把"为什么有这么多"的逻辑彻底拆解开。


🎯 核心结论

一个声卡(card)不一定对应一个物理芯片;一个PCM设备(pcmCxDy)不一定对应一个物理接口。

Linux内核中的声卡和PCM设备,是驱动开发者根据硬件能力软件路由需求抽象出来的逻辑对象。数量多,是因为要完整描述硬件的所有能力,并给用户空间提供灵活的控制接口。


🧩 为什么会有多张声卡(card0、card1...)?

每张声卡代表一个独立的音频硬件单元,通常是:

来源 示例 说明
板载Codec芯片 card0 SoC内置或外接的音频编解码器(如WM8960),负责耳机输出和麦克风输入。
HDMI/DP音频 card1 HDMI接口自带音频通道,在Linux中会被识别为一张独立的声卡。
USB声卡 card2 插入一个USB耳机或USB音频适配器,会动态注册为一张新声卡。
蓝牙音频 card3 蓝牙A2DP(蓝牙音频传输模型协定)耳机连接后,也会生成一个声卡设备。
虚拟声卡(环回设备) card4 比如 snd_aloop 驱动创建的环回设备,用于应用间音频数据传输(录系统声音)。

所以:如果你的板子上有"耳机孔 + HDMI + USB音频",你就会看到至少3张声卡。每一张都是独立的硬件路径。


🧩 为什么一张声卡下有多个PCM设备(pcmC0D0p、pcmC0D1c...)?

每个PCM设备代表一个独立的音频数据通道。一张声卡可以同时提供多个通道,因为它们对应不同的硬件功能:

PCM设备 含义 举例
pcmC0D0p Card0 的 第一个PCM设备(D0) ,方向为 播放(p) 耳机输出(立体声)
pcmC0D0c Card0 的 第一个PCM设备(D0) ,方向为 录音(c) 板载麦克风输入
pcmC0D1p Card0 的 第二个PCM设备(D1) ,方向为 播放(p) 扬声器输出(可能不同声道)
pcmC0D2c Card0 的 第三个PCM设备(D2) ,方向为 录音(c) 线路输入(LINE IN)

为什么会有多个D(设备编号)?

因为一个Codec芯片内部可能包含多个独立的DAC/ADC通路。例如:

  • 一个Codec芯片可能有:

    • 一组DAC用于耳机输出(D0p)

    • 一组DAC用于扬声器输出(D1p,可能带不同音效处理)

    • 一组ADC用于麦克风输入(D0c)

    • 一组ADC用于线路输入(D1c)

内核驱动会为每个独立的通路注册一个PCM设备,让用户空间可以分别控制它们。


🧩 还有更细的:PCM设备的子设备(subdevice)

你可能会看到类似 pcmC0D0p/sub0 这样的路径,这是因为一个PCM设备还可以支持多个子流(substream)

子设备 作用
sub0 主数据通道,用于正常的播放/录音。
sub1 辅助通道,可能用于多声道音频的扩展,或低延迟监控路径。

嵌入式场景下,一个PCM设备通常只有一个子设备,所以 /proc/asound/card0/pcm0p/sub0/ 就是你需要关注的地方,里面包含了 hw_paramsinfo 等运行时信息。


🧩 控制设备(controlC0)是干什么的?

除了PCM设备,每个声卡还有一个控制设备 (如 /dev/snd/controlC0):

  • 作用:用于调节音量、选择输入源、切换音频路由、开启/关闭音效等。

  • 与PCM设备的区别 :PCM设备传输音频数据 ,控制设备传输控制命令

操作示例:

复制代码
# 查看声卡0的所有控制项
amixer -c 0 controls

# 调节音量
amixer -c 0 set "Master" 80%

这些控制项由Codec驱动通过 snd_soc_kcontrol 结构体注册,最终通过 controlC0 暴露给用户空间。


📋 一张表总结:数量为什么多?

你看到的"多余"设备 背后的原因
多张声卡(card0, card1...) 不同硬件来源(板载、HDMI、USB、蓝牙、虚拟)。
一张卡多个PCM设备(D0, D1...) 不同的DAC/ADC通路(耳机、扬声器、麦克风、线路输入)。
每个PCM有播放和录音(p 和 c) 同一个硬件通路可能同时支持播放和录音(全双工)。
控制设备(controlCx) 独立于数据通道的音量、路由控制接口。
定时器、音序器(timer, seq) 专业音频应用需要的时间同步和MIDI支持。

🛠️ 嵌入式开发中的实际意义

当你看到 /proc/asound/ 下有很多设备时,不要慌。关键在于:

  • 先确认哪张声卡是你想用的

    复制代码
    cat /proc/asound/cards

    根据驱动加载顺序,板载Codec通常是 card0

  • 确认你想用的PCM设备编号

    复制代码
    cat /proc/asound/card0/pcm0p/info

    查看 card0 的播放设备信息,确认它支持哪些采样率、位深、声道数。

  • 在应用程序中指定正确的设备

    复制代码
    # 使用 card0 的 PCM 播放设备
    aplay -D hw:0,0 test.wav
    
    # 如果板载 Codec 是 card1(比如 HDMI 占用了 card0),就改成 hw:1,0
  • 如果设备树配置不当,可能会生成多余的声卡或PCM设备

    • 检查设备树中 sound 节点的 dai-link 数量,每个 dai-link 通常对应一个PCM设备。

    • 如果某个DAI链路没有实际硬件连接,但被配置了,就会生成一个无法正常工作的PCM设备,需要从设备树中移除或禁用。


💡 总结

Linux内核"生成这么多声卡和设备",本质上是为了完整暴露硬件的所有能力 ,并给用户空间提供精细的控制粒度。对于嵌入式开发者来说,不需要全部理解,只需要:

  1. cat /proc/asound/cards 找到目标声卡编号。

  2. aplay -Larecord -L 查看可用的PCM设备名称。

  3. 在应用中指定正确的设备(如 hw:0,0plughw:0,0)。

  4. 如果设备树配置不匹配,可能会生成多余或缺失的设备,需要回头检查设备树。