
拿到RK3568板子我习惯先把音频通路调通再谈其他外设。原因很简单音频链路横跨硬件、设备树、内核框架、用户态工具任何一个环节不匹配表现都是同一个“没声音”排查起来却是一环套一环。很多朋友上来就aplay test.wav结果发现播放命令不报错、日志正常喇叭就是不出声问题往往不在ALSA用户态而在更底层的I2S配置。这次我用一块RK3568核心板加ES8316 Codec做例子把 ALSA 驱动框架、I2S 总线的引脚与时序、设备树配置、以及从播放到调试的完整流程都过一遍。内容不需要你有多深的驱动开发底子但至少要清楚 Linux 设备树的基本写法知道dmesg怎么抓日志能看懂原理图上的芯片引脚名。把这套流程跑通之后换其他 RK 平台、其他 Codec思路基本是通用的。1. 动手之前先搞懂RK3568的音频链路1.1 硬件层I2S总线上到底有几根线很多人一看到“I2S”就以为只有一根数据线实际上一组标准 I2S 接口至少包含 4 根线位时钟 BCLK、帧时钟 LRCK、数据线 SDO/SDI以及主时钟 MCLK。其中 BCLK 决定每一位数据的传输节奏频率等于采样率 × 位宽 × 声道数LRCK 决定左右声道边界频率等于采样率MCLK 是 Codec 内部 Sigma-Delta 调制器工作所需的参考时钟通常按采样率的 256 倍或 512 倍给出。举个例子播放 16bit 双声道、48kHz 采样率的 WAV 时LRCK 就是标准的 48kHzBCLK 为 48kHz × 16 × 2 1.536MHzMCLK 若按 256fs 计算则是 12.288MHz。很多 Codec 规格书里会明确写 MCLK 范围比如 ES8316 支持 256fs、384fs、512fs 等你在设备树里配置的mclk-fs必须落在芯片支持的范围之内否则即使数据线全通Codec 也不能正确解码。除了这几根主线还有 I2C 控制线。ES8316 这类 Codec 的寄存器都要通过 I2C 读写比如音量、EQ、左右声道开关、ADC/DAC 路径选择全部由 I2C 控制。数据走 I2S控制走 I2C两者配合才能工作。实际调试中I2C 好不好使直接决定 Codec 驱动能不能探测成功I2S 通不通则决定波形能不能实时传过去两个层面要分开排查。1.2 软件层ALSA里的ASoC三件套ALSA 提供用户态 API但驱动侧真正干活的是 ASoC 框架。ASoC 把音频驱动拆成三部分Machine Driver、Platform Driver、Codec Driver。Platform Driver 对应CPU侧的 I2S 控制器负责把内存里的音频数据搬到 I2S 总线上Codec Driver 对应音频编解码芯片负责把 I2S 数字信号转成模拟信号Machine Driver 则把 Platform 和 Codec 绑定到一起告诉内核这两者可以通过哪条链路连接。打个不严谨的比方Platform 是厨师Codec 是灶台Machine Driver 是菜单。菜单上写了“宫保鸡丁”厨师才知道要给哪个灶台传菜。如果菜单没写对厨师和灶台都在但菜就是端不出来。设备树里的simple-audio-card就是最简单的一份“菜单”它能把 CPU DAI 和 Codec DAI 用 phandle 绑起来不需要额外写 Machine Driver。理解这个结构特别重要。很多人只盯着 codec 节点配置结果 CPU DAI 的 pinctrl 没配或者 DAI Link 里 codec phandle 指向错了整个声卡都不会注册。后面我会结合设备树代码具体讲绑定关系。1.3 为什么主从模式这么关键I2S 总线没有像 I2C 那样的应答机制数据传输全靠主设备持续产生时钟。CPU DAI 和 Codec 必须明确谁做主、谁做从主设备负责产生 BCLK 和 LRCK从设备被动接收。如果两边都配成主时钟线就会打架波形乱成一团如果两边都配成从总线上一片安静谁都不干活。在 RK3568 播放场景里通常让 CPU 侧 I2S 做主Codec 做从这样只要 SoC 的 CRU 时钟树配置正确BCLK/LRCK 就会按采样率自动产生。少数情况会让 Codec 做主例如 Codec 内部有时钟源且 SoC 需要跟随外部时钟这种场景多半出现在通话同步需求里。我们做基础播放按“CPU主、Codec从”配置即可后面的设备树示例都基于这个前提。2. 配置前的准备内核与用户态工具2.1 内核配置要勾选的项驱动不是凭空运行的内核编译时得把对应的驱动代码编进去。RK3568 平台一般用 4.19 或 5.10 内核Rockchip 官方 BSP 里已经带了很多音频驱动你只要确保下面几类配置打开SND 和 SND_SOC 主框架这是 ALSA/ASoC 的根基。Rockchip I2S/TDM 控制器驱动内核配置项名称在 4.19 和 5.10 里略有差异可能是CONFIG_SND_SOC_ROCKCHIP_I2S也可能是CONFIG_SND_SOC_ROCKCHIP_I2S_TDM。具体的 Codec 驱动比如CONFIG_SND_SOC_ES8316也可以在内核配置里搜索ES8316关键词来确认。简单声卡驱动CONFIG_SND_SIMPLE_CARD如果走 device tree 的simple-audio-card节点就必须打开。如果用的是 Rockchip SDK 里的 defconfig这些选项默认多半已经打开。但很多定制板卡会裁剪内核配置所以第一步还是确认好。在 build 目录下执行grep -i SND_SOC .config看看有没有被注释缺了就通过make menuconfig补上或者直接在 defconfig 里加配置后重新编译。我踩过一次坑Codec 驱动其实已经编进去了但CONFIG_SND_SIMPLE_CARD没开导致设备树里的simple-audio-card节点没有对应的驱动匹配声卡始终不注册。查了半天设备树最后发现是内核选项的问题。所以内核配置这一关不能跳。2.2 用户态调试工具准备驱动注册成功只是第一步拿来播放还得靠用户态工具。Linux 根文件系统一般用alsa-utils里面包含aplay、arecord、amixer等命令。Buildroot 里勾选alsa-utils就能编译进去Debian/Ubuntu 根文件系统则用apt install alsa-utils安装。Android BSP 或部分精简 Linux 系统里没有 ALSA 工具而是用 tinyalsa对应命令是tinyplay、tinycap、tinymix。tinyalsa 更轻量Rockchip SDK 里一般默认带。调试时最好确认板子上的工具类型因为aplay的参数和tinyplay不完全一样混用容易出问题。准备测试文件也有讲究。建议先用工具生成一段标准的 WAV比如 48kHz、16bit、双声道、1kHz 正弦波声道里分别放左右不同的频率方便判断声道是否反了。最简单的生成方式可以用 Python 的wave模块或者用 Audacity 导出 WAV。不要在系统刚调通时就用 MP3 测试因为 MP3 解码还得靠上层播放器问题定位范围会变大。3. 设备树里的I2S配置详解手把手配置实例3.1 先确认引脚复用pinctrlRK3568 的引脚多半是多功能复用的一颗物理引脚既能当 GPIO也能当 UART、I2C、I2S 专用信号。设备树里必须通过 pinctrl 把相关引脚切到 I2S 功能否则 I2S 控制器往引脚上送信号引脚本身还是 GPIO 模式波形根本出不去。RK3568 的 I2S1 通常有 m0 和 m1 两组复用选项对应不同的物理引脚。修改设备树时先查原理图确定你的 I2S1 数据线、时钟线接到 RK3568 的哪个 Bank 哪个 Pin再选择对应 mux。下面代码以 I2S1 的 m0 组为例i2s1_8ch { status okay; pinctrl-names default; pinctrl-0 i2s1m0_lrck i2s1m0_bclk i2s1m0_sclk i2s1m0_sdo i2s1m0_sdi; };注意i2s1m0_sclk其实就是 MCLKRockchip 平台上有些引脚名写作sclk有些写作mclk本质是同一个信号。如果原理图上 MCLK 没有单独引出而是由某个 PWM 或者固定时钟产生那就不走这个 pinctrl需要单独处理 Codec 的 MCLK 来源。我之前帮人调过一块板子MCLK 是由外部有源晶振直接供给 Codec 的设备树里 I2S 的 pinctrl 就不需要配sclk引脚反而要避免 SoC 侧重复输出时钟。引脚复用配置错误的表现很直接dmesg没有报错声卡也注册成功但示波器看 I2S 引脚全是高电平或完全没波形。所以确认 pinctrl 是调 I2S 的第一步。3.2 CPU侧I2S节点怎么配CPU 侧的 I2S 节点除了 pinctrl还要注意时钟路由参数。RK3568 的 I2S 控制器支持发送和接收两条时钟路径在 TDM/多通道模式下可以独立选择从哪根数据线收发。普通双声道播放不需要太复杂但最好把rockchip,clk-trcm设置成同源时钟避免接收和发送的时钟不同步。i2s1_8ch { status okay; pinctrl-names default; pinctrl-0 i2s1m0_lrck i2s1m0_bclk i2s1m0_sclk i2s1m0_sdo i2s1m0_sdi; rockchip,clk-trcm cru TRCM_I2S1_TX; };这里面的TRCM_I2S1_TX表示发送和接收都跟随发送时钟域。对播放场景来说主要关心 TX 侧时钟配置成同源之后BCLK 和 LRCK 会在发送数据时稳定输出。如果这块配错可能出现播放偶尔有声音、偶尔没声音或者录音采样率漂移的怪现象。另外在 I2S1 的clocks和clock-names属性里一般还要引用 I2S 控制器自身的时钟例如clocks cru CLK_I2S1, cru CLK_I2S1_8CH_TX_OUT;具体以 SDK 内核源码里的 dtsi 为准不必手动每个都填只需要保证status okay后控制器具备正确的时钟源即可。这部分在 Rockchip SDK 的 rk3568.dtsi 里已经写好了我们板级 dts 只需要打开对应节点并配置 pinctrl。3.3 Codec节点与I2C地址Codec 节点挂在某条 I2C 总线上RK3568 有多个 I2C 控制器ES8316 具体挂在哪一条同样要看原理图。下面示例是挂在 I2C2 上地址为 0x11i2c2 { status okay; es8316: es831611 { compatible everest,es8316; reg 0x11; #sound-dai-cells 0; clocks cru MCLK_I2S1; clock-names mclk; }; };compatible要和驱动里的 of_match_table 匹配reg是芯片的 I2C 从机地址#sound-dai-cells必须设置为 0这样 machine driver 引用 codec 节点时不需要额外指定 DAI index。clocks指向 I2S1 的 MCLKclock-names是 mclk驱动通过这个名字去找时钟。这里最常见的坑是 I2C 地址写错。ES8316 的 AD0/SDA 引脚电平不同地址可能是 0x10 或 0x11一定要看原理图上 AD0 的接法。地址不对时设备树看起来没问题但dmesg里的 Codec 驱动会一直报ES8316 has no codec之类提示或者i2c transfer error甚至直接不出现任何关于 codec 的日志。用i2cdetect -y 2扫一下总线能看到 0x11 或 0x10 有设备才能继续往 I2S 方向查。3.4 Machine驱动与声卡绑定有了 CPU DAI 和 Codec DAI最后得用 machine 驱动把它们绑成一张声卡。最快的方式是simple-audio-card不需要写 C 代码在设备树里用 phandle 指过去就行sound { compatible simple-audio-card; simple-audio-card,name rk3568-es8316; simple-audio-card,format i2s; simple-audio-card,mclk-fs 256; simple-audio-card,cpu { sound-dai i2s1_8ch; }; simple-audio-card,codec { sound-dai es8316; clocks cru MCLK_I2S1; clock-names mclk; }; };simple-audio-card,format i2s是指标准 I2S 格式。如果是左对齐、右对齐或者 DSP 格式需要对应修改。mclk-fs是 MCLK 与采样率的倍数关系256 代表 48kHz 采样率下 MCLK 为 12.288MHz。如果你的 Codec 需要 512fs或者实际时钟树上没那么高的 MCLK就按硬件手册改。simple-audio-card,cpu和simple-audio-card,codec里分别放sound-dai属性指向前面 I2S 节点和 Codec 节点。内核在注册声卡时会根据这两个 phandle 建立一条 dai_link再调用对应的 DAI 驱动。正常注册后/proc/asound/cards里就能看到名为rk3568-es8316的声卡。如果不想用 simple-audio-card也可以写一个 machine driver注册snd_soc_card结构体手动填充 dai_link。但那样代码量明显增加除非要处理耳机检测、多路音频路由、HDMI 音频切换等复杂场景否则先用 simple-audio-card 把通路跑通是最务实的方案。4. 编译、烧录、上电验证一套走下来4.1 确认驱动加载成功设备树改完编译内核和 dtb烧录到板子后先别急着放歌。第一步看 dmesg确认 ALSA、I2S、Codec 三个层面的驱动都正常探测dmesg | grep -Ei asoc|es8316|rockchip-i2s|rk3568.*i2s如果看到类似下面的日志说明平台侧和 Codec 侧都注册成功了es8316 2-0011: ASoC: no DMI platform driver? rockchip-i2s fe410000.i2s: I2S: sclk is 0 asoc-simple-card sound: rockchip-i2s - es8316 mapping ok其中fe410000.i2s是 RK3568 I2S1 的寄存器基地址具体基地址可能因芯片版本不同但日志里能看到mapping ok基本就说明 DAI Link 绑定成功。接着用 ALSA 工具查看声卡列表aplay -l输出示例**** List of PLAYBACK Hardware Devices **** card 0: rk3568es8316 [rk3568-es8316], device 0: rockchip-i2s1-hifi i2s1-hifi-0 [rockchip-i2s1-hifi i2s1-hifi-0] Subdevices: 1/1 Subdevice #0: subdevice #0看到 card 0 出现就说明声卡设备已经创建。如果这里没有输出大概率是设备树绑定问题回到第 3 章检查 phandle 和 compatible。4.2 播放第一首WAV播放命令很简单aplay -D hw:0,0 test.wavhw:0,0表示声卡 0、PCM 设备 0。如果你的板子上有多张声卡比如 HDMI 也在aplay -l 会列出多个 card需要根据名字选择正确的设备。用hw设备访问时采样率、位宽、声道数必须和 WAV 一致ALSA 不会自动做格式转换。如果不确定可以先试试plughw:0,0它会自动协商并转换格式aplay -D plughw:0,0 test.wavAndroid 系统里没有 aplay用 tinyplaytinyplay /sdcard/test.wavtinyplay 会读取 WAV 头并自动设置 PCM 参数功能上类似 aplay但对格式的支持没那么全。如果播放没有声音但命令正常返回进入下一步检查 Codec 通路和音量。4.3 音量与通路控制即使 I2S 数据流正常、codec 检测到信号如果 codec 内部的 DAC 电源没打开、耳机放大器处于 mute 状态、SPK 通路没选对模拟输出依然听不到声音。ES8316 这类 Codec 的控制项很多先列出全部混音器控制项amixer -c 0 scontrols或者 tinyalsa 用tinymix重点找这些关键字DAC Volume、Playback Path、SPK、HP、Mute。不同驱动导出的名字不一样但一般可以看到类似DAC1 Playback Volume Playback Path SPK Mute HP Mute先把音量调大再选通路最后解除静音amixer -c 0 set DAC1 Playback Volume 120 amixer -c 0 set Playback Path SPK amixer -c 0 set SPK Mute 0tinymix 的写法类似但要用控制项的完整名称和 value index。调通之后如果是一块带功放的板子还要确认功放使能引脚有没有被软件拉高。很多开发板的功放 IC 有一个 EN 或 SD 引脚硬件上可能接在某个 GPIO 上需要在设备树里配置成默认输出高电平或者用用户态 GPIO 操作拉高。这部分不归 ALSA 管容易漏掉我就见过几个人调了半天 codec最后发现是功放电源压根没开。5. 实际调试中踩过的坑5.1 播放没声音先拆开“时钟/引脚/通路”三个层面排查遇到播放没声音不要漫无目的地改设备树。我习惯按顺序检查看dmesg有没有关于声卡注册失败的报错比如ASoC: failed to instantiate card、ES8316: i2c error这类关键词。看/proc/asound/cards和aplay -l是否已经出现声卡。用示波器或逻辑分析仪抓 I2S 引脚确认 MCLK、BCLK、LRCK、SDO 有没有波形。播放时 SDO 上能看到类似阶梯状的脉冲串如果全是固定的方波或者电平纹丝不动说明数据没送到引脚问题在 CPU DAI、pinctrl 或者 DMA。如果 I2S 波形正常把排查重点放在 Codec 通路用 amixer 打开所有可能影响模拟输出的音量、通路、静音控制项。最后用万用表或示波器看 Codec 模拟输出引脚比如耳机座左右声道引脚或喇叭功放输入端确认有没有音频信号。没有信号再回头查功放供电和 Boot 配置。这套排查顺序很笨但确实有效。我从第一次调 I2S 到现在只要严格按照这个顺序来基本都能定位到问题层面不至于在用户态和内核态之间反复横跳。5.2 有I2S信号但没有模拟输出这个现象特别典型逻辑分析仪看 BCLK、LRCK、SDO 都有波形但喇叭没声音。第一反应应该是 Check codec 通路而不是怀疑时序。有一次我调 ES8316I2S 数据波形非常漂亮播放 WAV 也不报错但耳机里一点声音没有。最后用 tinymix 逐个控制项翻发现DAC1 Playback Volume是 0而且HP Mute默认很大概率是处于静音状态。所以我在前面特别强调音量与通路控制。嵌入式 Linux 不像 PC 有 Desktop 音频栈自动调节音量Codec 驱动探测后很多控制项保持默认值有些是静音有些音量是 0。用户态播放之前必须先用 amixer/tinymix 设置好不能假设“出厂就是最大声”。如果通路都打开了还没声音再检查 Codec 是不是处于 PDN 或 reset 状态。ES8316 这类芯片有复位引脚硬件复位之后默认进入低功耗模式必须通过 I2C 写寄存器唤醒。如果驱动没有正确初始化这部分I2S 数据进来但它懒得理你。5.3 只有一边响或者左右声道反了左右声道反了通常是硬件上 LRCK 极性反了。标准 I2S 协议中LRCK 为低电平时传输左声道数据高电平时传输右声道数据但不同 Codec 可能有细微差异。如果你确认硬件连接没问题可以在设备树里调整 DAI format 相关的属性或者检查 simple-audio-card 里的simple-audio-card,frame-inversion等时隙配置。只有一边响现象更复杂。可能是一根数据线虚焊也可能是 I2S 发送端/接收端只配置了单声道还可能是 TDM 模式下 slot 对不齐。调试时可以先播放一段左右声道频率不同的 WAV比如左声道 1kHz、右声道 500Hz然后根据听到的声音判断哪边缺失、哪边反了。再拿原理图确认 LRCK 和 SDO 是否真正连到了 Codec 对应引脚很多板子为了布线方便把 SDO 和 SDI 交叉或者错位这时设备树里需要配置rockchip,i2s-tx-route/rockchip,i2s-rx-route来指定实际使用的数据线对。我见过一块定制板I2S1 的 SDO 物理上接到了 ES8316 的 SDI数字输入原理图命名却叫 SDO等到量原理图网络标号才发现命名不一致导致人们一直被误导。遇到只有一边响先不要怀疑软件拿万用表从 CPU 引脚量到 Codec 引脚通了再说。5.4 爆音、杂音和“沙沙”声爆音和杂音最让人头大因为有时候不是配置错误而是链路中的某一处噪声被放大。先说最常见的MCLK 与采样率不匹配。如果设备树里的mclk-fs设置和 Codec 实际需要的倍率不一致Codec 内部的采样时钟会抖动在输出端听到的就是持续的“沙沙”声。解决方法是查 Codec 数据手册确认它接受的 MCLK 范围然后用逻辑分析仪抓 MCLK 实际频率。电源纹波也会导致杂音尤其是模拟电源和数字电源没有分开滤波时。I2S 数据线走线太长、没有包地或者 Codec 的模拟输出走线靠近高频开关电源都很容易引入噪声。排查时先用示波器看 Codec 的 AVDD 引脚纹波大就先处理硬件软件再怎么调音量都无济于事。还有一类爆音来自 DMA 缓冲配置不合理。ALSA 在底层用 DMA 搬运 PCM 数据如果 buffer 太小、period 太短CPU 来不及填充数据播放时就会周期性出现“咔嗒”声。可以试试调整hw_params里的 buffer size/period size或者用aplay --buffer-size参数做测试但不建议一上来就动这块先排除时钟和硬件问题再说。5.5 录音不通但播放正常录音不通时很多人会第一时间怀疑 Codec 的 ADC 坏了其实播放正常只能说明 I2S 发送方向、DAC 通路、Codec 的 PLL 供电是好的。录音涉及 I2S 接收方向、DMA 接收通道、Codec ADC 通路三个环节都可能出问题。先确认 I2S 引脚上有没有来自 MIC 或 LINE IN 的模拟信号再检查的 Codec ADC 通路选择tinymix里一般会有类似ADC PGA Gain、Mic Select、ADC Path的控制项。然后看 I2S 的 SDI 引脚能不能收到 Codec 发出的数据用逻辑分析仪在录音时观察 SDI 波形如果 Codec 已经输出数据SDI 上没有问题就锁定在 SDI 引脚复用或 Codec 数据输出配置。最后用arecord -D hw:0,0 -f S16_LE -r 48000 -c 2 test.wav录音再回放确认用户态参数是否和 Codec 驱动能力匹配。录音问题比播放更隐蔽排查时一定要把“模拟输入到 Codec ADC”“Codec 到 SoC SDI”“SDI 到 DMA 内存”这三段分开看不能只靠一个 arecord 结果猜。6. 从播放到更复杂场景ALSA还能怎么玩6.1 多声卡与路由RK3568 这种 SoC 不可能只跑一个 ES8316往往还有 HDMI Audio、蓝牙音频甚至多路 I2S。多声卡存在时/proc/asound/cards会有多个 card 节点用户态程序可以通过default设备或.asoundrc配置来选择默认声卡。嵌入式 Linux 下很多人直接用aplay -D hw:0,0找不到想要的声卡这时可以用名字指定设备比如aplay -D hw:rk3568es8316 test.wav。如果要在多个声卡之间做混音或路由可以把 PulseAudio 或 PipeWire 跑起来但这对资源有限的嵌入式板子不一定友好。轻量一点的方案是直接改asound.conf或~/.asoundrc把默认设备指定到具体声卡pcm.!default { type hw card 0 device 0 } ctl.!default { type hw card 0 }这个文件看起来不起眼但在没有桌面音频框架的板子上很管用。前段时间我在 RK3568 板子上接了一个 USB 麦克风和一个 I2S Codec系统默认声卡顺序经常变化就是靠固定card 0/1名称并用.asoundrc锁定的。6.2 直接调用PCM API与ALSA用户态库如果整个产品只是要固定播放一段开机音或按键音没必要跑一个那么重的播放器。可以直接写一小段 C 代码调用 ALSA 用户态库打开 PCM 设备写入 WAV 数据#include stdio.h #include alsa/asoundlib.h int main(int argc, char *argv[]) { snd_pcm_t *handle; snd_pcm_hw_params_t *params; int rc; unsigned int rate 48000; int dir; rc snd_pcm_open(handle, hw:0,0, SND_PCM_STREAM_PLAYBACK, 0); if (rc 0) return 1; snd_pcm_hw_params_alloca(params); snd_pcm_hw_params_any(handle, params); snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); snd_pcm_hw_params_set_channels(handle, params, 2); snd_pcm_hw_params_set_rate_near(handle, params, rate, dir); snd_pcm_hw_params(handle, params); /* 这里读取 WAV 数据并 snd_pcm_writei 循环写入 */ snd_pcm_close(handle); return 0; }这段代码去掉了错误处理和 WAV 解析但核心流程已经说明白了snd_pcm_open打开设备snd_pcm_hw_params*配置硬件参数snd_pcm_writei写入 PCM 数据。编译时加-lasound。实际项目里这种直接调 ALSA API 的方式很常见比拉起 tinyplay 这类外部进程更可控启动也更快速。6.3 系统启动自动播放提示音很多 RK3568 产品会在系统起来后播放一段开机音这个需求完全可以通过 init 脚本完成。在设备启动时执行aplay -D default /usr/share/sounds/boot.wav如果担心播放阻塞系统启动可以后台执行。更精细的做法是把这段提示音做成一个 systemd service限定在声卡 ready 之后启动比如[Unit] DescriptionBoot sound Aftersystemd-modules-load.service [Service] Typeoneshot ExecStart/usr/bin/aplay -D default /usr/share/sounds/boot.wav [Install] WantedBymulti-user.target但要注意ALSA 设备如果还没来得及初始化服务启动时hw:0,0可能不存在。我一般会在脚本里先循环检查/proc/asound/cards确认声卡节点出现后再执行 aplay否则第一次启动总是没有声音。我刚开始调 RK3568 音频的时候也走了很多弯路后来把所有 I2S 信号用逻辑分析仪完整抓了一遍之后问题才一目了然。所以如果你的板子也有音频问题不要急着改代码先确认硬件连接、再确认时钟波形、最后查通路和音量一步步来ALSA 这套东西其实很有规律。希望这份记录能帮你少折腾几天。