RK3588 声卡识别正常,喇叭却没有声音:ES8388 + AW83118 音频调试实战


📺 B站 嵌入式孙老师博主个人介绍
📘 博主书籍-京东购买链接Yocto项目实战教程
📘 加博主微信,进技术交流群jerrydev


最近在调试一块基于 RK3588 的嵌入式设备,音频部分采用一颗 ES8388 Codec、两颗 AW83118 功放和四个喇叭。

系统启动后,ES8388 已经通过 I²C 识别,ALSA 声卡注册正常,aplay -l 可以找到播放设备,speaker-test 也在持续输出左右声道信息。但实际测试时,四个喇叭都没有声音。
在这里插入图片描述

这类现象很容易让人误以为是音频文件、音量或者喇叭本身的问题。但排查下来,问题分别出现在三个位置:

  • 功放 GPIO 缺少 pinctrl,软件状态和物理电平不一致;
  • ES8388 绑定了不合适的 Codec 驱动,DACPOWER 被反复覆盖;
  • AW83118 的 MUTE 极性判断错误。

整个过程让我重新梳理了几个问题:

  • speaker-test 正常运行,是否代表音频链路已经正常?
  • I²C 可以识别 ES8388,为什么 Codec 仍然没有输出?
  • Codec 内部静音与功放 MUTE 有什么区别?
  • 四个物理喇叭是不是四声道?
  • ALSA、DAPM、Codec 寄存器和 GPIO 之间是什么关系?

本文以这次调试过程为背景,梳理 RK3588 音频从“声卡注册”到“喇叭出声”的完整路径。文中只保留通用硬件和驱动信息,不涉及具体产品及项目名称。

一、先从硬件链路看起

整条播放链路是什么

这套硬件的音频路径如下:

音频文件
   ↓
ALSA / ASoC
   ↓
RK3588 I2S0
   ↓
ES8388 Codec
   ↓
LOUT2 / ROUT2
   ↓
两颗 AW83118 功放
   ↓
四个物理喇叭

各部分职责并不相同:

模块作用
RK3588 I²S传输数字音频数据
ES8388将数字音频转换成左右声道模拟信号
AW83118放大模拟信号,驱动喇叭
I²C配置 ES8388 内部寄存器
GPIO控制 AW83118 的使能和静音
ASoC/DAPM管理 Codec 内部音频路径和电源

因此,声卡注册成功只代表:

CPU DAI + Codec DAI + Machine Driver

已经组成了一张 ALSA 声卡。

真正让喇叭出声,还要继续经过 Codec DAC、模拟输出、功放使能和功放静音等环节。

四个喇叭不是四声道

两颗 AW83118 接收的是同一组左右声道信号:

ES8388 HPO_R → 功放1右通道 → 右喇叭1
ES8388 HPO_L → 功放1左通道 → 左喇叭1

ES8388 HPO_R → 功放2右通道 → 右喇叭2
ES8388 HPO_L → 功放2左通道 → 左喇叭2

所以硬件上虽然有四个独立喇叭,逻辑上仍然是双声道:

两个左声道喇叭 + 两个右声道喇叭

四个喇叭的型号可以相同,区别主要是安装位置和所连接的左右声道。它们不是四路独立音频,也没有前后左右四声道的顺序关系。

I²C 和 I²S 分别负责什么

ES8388 同时连接了 I²C 和 I²S,但两者的用途完全不同:

I²C:配置 Codec
I²S:传输音频数据

RK3588 通过 I²C 配置 ES8388 内部寄存器,包括:

  • DAC 电源;
  • 时钟和采样率;
  • 输入输出路径;
  • 数字静音;
  • 播放音量;
  • LOUT和ROUT输出开关。

I²S 负责传输实际的 PCM 音频数据:

MCLK:Codec 主时钟
SCLK:位时钟
LRCK:左右声道时钟
SDO :RK3588 发送给 Codec 的播放数据
SDI :Codec 发送给 RK3588 的录音数据

所以,i2cdetect 能看到 ES8388,只能证明 RK3588 可以访问它的寄存器,不能证明 I²S 数据、DAC 输出和外部功放已经正常。

二、声卡正常,声音到底断在哪里

第一步:确认声卡和 I²C 设备

ES8388 挂在 I²C7,7-bit 地址为 0x11

&i2c7 {
	status = "okay";

	es8388: codec@11 {
		status = "okay";
		compatible = "everest,es8388";
		reg = <0x11>;

		#sound-dai-cells = <0>;

		clocks = <&mclkout_i2s0>;
		clock-names = "mclk";

		assigned-clocks = <&mclkout_i2s0>;
		assigned-clock-rates = <12288000>;
	};
};

启动后先检查:

dmesg | grep -i es8388
cat /proc/asound/cards
aplay -l

I²C 扫描结果中,0x11 显示为 UU

i2cdetect -y -r 7
10: -- UU -- -- -- -- -- -- -- -- -- -- -- -- -- --

这里的 UU 不是错误,而是表示该地址已经被内核驱动占用。

继续运行:

speaker-test -D hw:0,0 -c 2 -t sine -f 1000

如果能够持续看到:

0 - Front Left
1 - Front Right

说明 PCM 设备已经成功打开,音频数据正在提交。

但这仍然不能证明:

  • ES8388 的 DAC 已经上电;
  • LOUT2/ROUT2 已经打开;
  • AW83118 已经使能;
  • MUTE 已经解除;
  • 喇叭端已经有输出波形。

这也是这次调试中第一个容易产生的误判。

第二步:GPIO 显示高电平,实测却还是低电平

AW83118 主要由两个 GPIO 控制:

信号RK3588 GPIO作用
SPK_ENGPIO4_A3连接两颗 AW83118 的 SDZ
SPK_MUTEGPIO4_B1连接两颗 AW83118 的 MUTE

软件中先打开功放:

amixer -c 0 cset name='spk switch' 1

检查 GPIO:

cat /sys/kernel/debug/gpio | grep gpio-131

debugfs 显示:

out hi

但使用万用表测量 AW83118 的 SDZ 前级,实际仍然是低电平。

软件显示已经拉高,物理引脚却没有变化,这类矛盾通常应该优先检查 PinMux。

最初设备树只声明了:

spk-con-gpio = <&gpio4 RK_PA3 GPIO_ACTIVE_HIGH>;
mute-con-gpio = <&gpio4 RK_PB1 GPIO_ACTIVE_HIGH>;

这只能说明驱动要使用哪个 GPIO,并不能保证对应引脚已经切换到 GPIO 功能。

补上 pinctrl:

&pinctrl {
	speaker {
		spk_en_gpio: spk-en-gpio {
			rockchip,pins =
				<4 RK_PA3 RK_FUNC_GPIO &pcfg_pull_none>;
		};

		spk_mute_gpio: spk-mute-gpio {
			rockchip,pins =
				<4 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};
};

然后将它们加入声卡节点:

&es8388_sound {
	pinctrl-names = "default";
	pinctrl-0 = <&hp_det &spk_en_gpio &spk_mute_gpio>;
};

这里需要注意:如果原来的 pinctrl-0 中已经有 hp_det,修改时要一起保留。重新赋值会覆盖原有内容,并不是追加。

修复后再次执行:

amixer -c 0 cset name='spk switch' 1

此时 debugfs 和实测电平终于一致:

SPK_EN = 3.3V

这个问题也说明了一点:

GPIO 编号正确,不等于物理引脚一定工作在 GPIO 模式。

第三步:功放已经使能,Codec 仍然没有模拟输出

pinctrl 修复以后,AW83118 的 SDZ 已经正常拉高,但喇叭仍然没有声音。

继续分层测量:

MCLK/BCLK/LRCK:有波形
ES8388 HPO_L/R :没有有效音频波形
功放供电       :正常
SPK_EN         :高电平

问题基本可以缩小到 ES8388 的 DAC 和模拟输出路径。

ES8388 的 DACPOWER 寄存器地址为 0x04,关键位如下:

Bit功能
bit7左 DAC 断电控制,0 表示工作
bit6右 DAC 断电控制,0 表示工作
bit5LOUT1 使能
bit4ROUT1 使能
bit3LOUT2 使能
bit2ROUT2 使能

读取寄存器:

i2cdump -y -f 7 0x11 | grep "^00:"

空闲状态下:

0x04 = 0xC0

这是正常的,表示左右 DAC 均处于断电状态。

但运行 speaker-test 后,寄存器仍然保持 0xC0,说明 DAPM 并没有正确打开 DAC 和模拟输出。

为了验证判断,我临时绕过驱动,直接写入 DACPOWER:

i2cset -y -f 7 0x11 0x04 0x3c

写入后喇叭立即有声音,说明:

I²S 数据正常
ES8388 模拟链路正常
AW83118 功放正常
喇叭硬件正常

问题就在 Codec 驱动对 DACPOWER 的管理上。

这里的直接写寄存器只适合定位问题。i2cset -f 会绕过 Codec 驱动和 Regmap,后续 DAPM 状态变化仍然可能覆盖寄存器,不能作为产品中的最终方案。

第四步:ES8388 绑定了不合适的驱动

进一步检查发现,物理芯片虽然是 ES8388,但系统实际绑定的是 es8323.c

这个驱动中的 set_bias_level()suspend() 会将 DACPOWER 整个字节重新写成 0xC0。DAPM 即使尝试打开 DAC,也可能再次被其他电源管理路径覆盖。

一开始可以修改 es8323.c,避免整字节覆盖寄存器。但继续检查内核代码后发现,ES8388 更合适的驱动是:

sound/soc/codecs/es8328-i2c.c

它支持 ES8328/ES8388 系列,并通过 update_bits() 按位维护寄存器,不会让不同 DAPM 控制路径反复覆盖整个 DACPOWER

最终内核配置改为:

# CONFIG_SND_SOC_ES8323 is not set
CONFIG_SND_SOC_ES8328_I2C=y

重新启动后检查:

dmesg | grep -i es8388

应确认设备已经由 es8328 驱动绑定。

再次对比 DACPOWER

# 空闲状态
i2cdump -y -f 7 0x11 | grep "^00:"

# 播放状态
speaker-test -D hw:0,0 -c 2 -t sine -f 1000 \
	>/tmp/speaker-test.log 2>&1 &

sleep 2
i2cdump -y -f 7 0x11 | grep "^00:"

结果变为:

空闲状态:0x04 = 0xC0
播放状态:0x04 = 0x0C

0x0C 对应:

左 DAC   :工作
右 DAC   :工作
LOUT1    :关闭
ROUT1    :关闭
LOUT2    :打开
ROUT2    :打开

这与实际硬件完全对应:功放接在 LOUT2/ROUT2,而 LOUT1/ROUT1 没有使用。

此时 Codec 的电源状态终于能够跟随播放状态正确变化。

第五步:MUTE 极性判断反了

Codec 已经产生模拟输出,但功放是否真正驱动喇叭,还要看 AW83118 的 MUTE。

AW83118 的实际控制关系为:

MUTE 电平输出状态
高电平输出进入 Hi-Z,静音
低电平输出使能,解除静音

因此设备树应配置为:

mute-con-gpio = <&gpio4 RK_PB1 GPIO_ACTIVE_HIGH>;

对应的 ALSA 控制命令为:

# 静音
amixer -c 0 cset name='spk mute switch' 1

# 解除静音
amixer -c 0 cset name='spk mute switch' 0

检查 GPIO:

cat /sys/kernel/debug/gpio | grep gpio-137

正常播放时应为:

SPK_MUTE = out lo

这里必须分清“控件值”和“物理电平”。

当配置为 GPIO_ACTIVE_HIGH 时:

kcontrol = 1 → GPIO 高电平 → 静音
kcontrol = 0 → GPIO 低电平 → 解除静音

最初仅根据资料理解极性时,我将它配置成了 GPIO_ACTIVE_LOW。后面通过万用表、GPIO 状态和实际声音交叉验证,才最终确认:

MUTE 高电平静音,低电平出声

对于这种控制引脚,最终应以芯片资料、电路连接和实测结果共同确认,不能只看变量名判断。

三、最终驱动配置和测试方法

设备树中的音频关系

声卡节点负责把 RK3588 I²S、ES8388 和板级功放控制连接起来:

es8388_sound: es8388-sound {
	status = "okay";
	compatible = "rockchip,multicodecs-card";

	rockchip,card-name = "rockchip-es8388";
	rockchip,format = "i2s";
	rockchip,mclk-fs = <256>;

	rockchip,cpu = <&i2s0_8ch>;
	rockchip,codec = <&es8388>;

	spk-con-gpio =
		<&gpio4 RK_PA3 GPIO_ACTIVE_HIGH>;
	mute-con-gpio =
		<&gpio4 RK_PB1 GPIO_ACTIVE_HIGH>;

	rockchip,audio-routing =
		"Speaker", "LOUT2",
		"Speaker", "ROUT2",
		"Speaker", "Speaker Power";

	pinctrl-names = "default";
	pinctrl-0 =
		<&hp_det &spk_en_gpio &spk_mute_gpio>;
};

audio-routing 中每两个字符串表示一条连接关系:

LOUT2         → Speaker
ROUT2         → Speaker
Speaker Power → Speaker

重复出现的 Speaker 是同一个 DAPM 端点,不代表设备树中定义了多个喇叭。

其基本格式是:

"接收端", "信号来源"

顺序不能随意颠倒。

为什么有多个音量和开关

调试时会看到几个名字相似的 ALSA 控件,但它们处于不同层级:

控件作用
Speaker控制板级 Speaker DAPM 端点
PCM控制 ES8388 DAC 数字音量
Output 2控制 LOUT2/ROUT2 模拟输出音量
spk switch控制 AW83118 的 SDZ
spk mute switch控制 AW83118 的 MUTE

它们并不冲突。

ES8388 内部静音发生在 Codec 内部,AW83118 的 MUTE 则直接控制功放输出是否进入高阻状态:

PCM 数据
   ↓
ES8388 数字静音与音量
   ↓
ES8388 DAC和LOUT2/ROUT2
   ↓
AW83118 MUTE
   ↓
喇叭

功放 MUTE 更适合在上电、关机和音频路径切换时使用,可以避免将 Codec 的上电瞬态直接放大到喇叭。

最简出声测试

当前驱动将 SPK_ENSPK_MUTE 暴露为两个手动 kcontrol。

打开功放:

amixer -c 0 cset name='spk switch' 1

解除静音:

amixer -c 0 cset name='spk mute switch' 0

设置实际使用的 OUT2 音量:

amixer -c 0 sset 'Output 2' 80%

播放左右声道测试音:

speaker-test -D hw:0,0 -c 2 -t wav

当前硬件没有连接 LOUT1/ROUT1,因此修改 Output 1 不会明显改变喇叭音量。真正有效的是:

amixer -c 0 sset 'Output 2' 80%

一组完整的检查命令

# 1. 检查声卡
cat /proc/asound/cards
aplay -l

# 2. 检查 Codec 驱动
dmesg | grep -i es8388

# 3. 检查功放控制
amixer -c 0 cget name='spk switch'
amixer -c 0 cget name='spk mute switch'

# 4. 打开功放并解除静音
amixer -c 0 cset name='spk switch' 1
amixer -c 0 cset name='spk mute switch' 0

# 5. 设置输出音量
amixer -c 0 sset 'Output 2' 80%

# 6. 播放测试音
speaker-test -D hw:0,0 -c 2 -t sine -f 1000 \
	>/tmp/speaker-test.log 2>&1 &

sleep 2

# 7. 检查 DACPOWER
i2cget -y -f 7 0x11 0x04

# 8. 检查 GPIO
cat /sys/kernel/debug/gpio |
	grep -E "gpio-131|gpio-137"

播放时关键状态应为:

DACPOWER = 0x0C
SPK_EN   = HIGH
SPK_MUTE = LOW

修改后别忘记保存 ALSA 状态

spk switchspk mute switch 会受到 alsactl restore 影响。

如果系统启动时执行了 ALSA 状态恢复,上一次保存的值可能覆盖驱动初始化状态。确认配置正确后,可以保存:

amixer -c 0 cset name='spk switch' 1
amixer -c 0 cset name='spk mute switch' 0
amixer -c 0 sset 'Output 2' 80%

alsactl store

否则可能出现本次调试正常,但重启后功放又被关闭或者重新进入静音的情况。

软件状态正常后,继续逐级测量

如果上述状态全部正确但仍然没有声音,最有效的方法不是继续猜设备树,而是使用示波器逐级检查:

RK3588 MCLK/BCLK/LRCK/SDO
           ↓
ES8388 HPO_L/HPO_R
           ↓
AW83118 输入端
           ↓
AW83118 BTL 输出端
           ↓
喇叭连接器

从哪一级开始没有波形,问题基本就在哪一级。

另外,当前硬件的 FAULTZ 没有回接到 RK3588,所以软件无法读取 AW83118 的过流、过温等故障状态。遇到功放保护问题时,还需要直接测量 FAULTZ 或检查功放供电与输出。

最后

这次调试中,最容易产生的误判是:

声卡已经注册,speaker-test 没有报错,所以音频驱动应该已经正常。

实际上,speaker-test 正常只说明 PCM 数据成功提交。真正让喇叭出声,还必须同时满足:

I²S 时钟和数据正常
+ ES8388 绑定正确驱动
+ DACPOWER 跟随 DAPM 变化
+ LOUT2/ROUT2 路径正确
+ 功放引脚完成 GPIO 复用
+ SPK_EN 拉高
+ SPK_MUTE 拉低
+ 功放供电和喇叭接线正常

这次最终解决的三个关键问题也很典型:

软件显示 GPIO 正常,实测没变化
→ 检查 pinctrl 和 PinMux

播放时 DACPOWER 始终为 0xC0
→ 检查 Codec 驱动和 DAPM 寄存器管理

Codec 已有输出,功放仍然无声
→ 检查 SDZ、MUTE 极性和实际电平

对 RK3588 这类嵌入式平台来说,音频调试不能只停留在 dmesgaplay。真正有效的方法,是把下面几层状态对应起来:

ALSA 控件
DAPM 路径
Codec 寄存器
GPIO 电平
硬件实际波形

当这几层状态全部一致时,PCM 数据才算真正走完了从 RK3588 到喇叭的最后一段路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值