目录开篇先搞懂音频系统的完整链路到底是怎么回事完整的音频播放链路一、核心架构ALSA 与 ASoC大白话全解析1. 什么是 ALSA2. 什么是 ASoCASoC 架构的三大核心组件二、音频硬件核心知识点小白必懂1. 核心硬件Codec 芯片2. 核心总线I2S 音频总线3. 核心时钟MCLK 主时钟4. 控制总线I2C/SPI三、保姆级实战ES8388 音频驱动完整适配基于 RK3568前置准备步骤 1确认硬件连接步骤 2修改设备树添加音频配置核心配置讲解步骤 3内核配置开启 ES8388 驱动步骤 4编译烧录验证驱动是否正常工作步骤 5安卓系统音频适配四、小白音频驱动必踩的坑全场景问题排查指南问题 1完全没声音tinyplay 也没声音问题 2有声音但是全是杂音、破音问题 3播放声音速度不对快了或者慢了问题 4播放正常但是录音无声结尾说两句大家好我是黒漂技术佬。上一篇我们搞定了显示驱动点亮了屏幕后台很多兄弟又遇到了新的难题“佬我焊了个 ES8388 音频 Codec 芯片改了设备树结果没声音要么就是全是杂音根本不知道从哪里查问题音频驱动也太复杂了”太懂了音频驱动是嵌入式开发里出了名的 “玄学”涉及到的链路长、模块多从内核 ALSA 架构、Codec 驱动、DMA 传输到安卓音频 HAL 层、AudioFlinger 服务任何一个环节出问题都会导致没声音、杂音新手根本不知道从哪里下手。今天这篇我就用大白话给你讲透 ALSA 音频架构拆解 RK 平台的音频系统手把手带你完成ES8388 Codec 芯片的驱动适配从设备树配置、驱动调试到没声音、杂音、录音无声的全场景问题排查小白也能轻松搞定。开篇先搞懂音频系统的完整链路到底是怎么回事很多新手搞音频驱动上来就瞎改设备树连音频信号的完整链路都没搞懂不踩坑才怪。先给大家用大白话讲清楚从安卓 App 播放音乐到喇叭发出声音完整的链路是什么样的。完整的音频播放链路应用层安卓 App比如音乐播放器调用安卓系统的音频 API把要播放的音频数据传给安卓系统的音频服务安卓框架层AudioFlinger 服务把音频数据混音、重采样转换成硬件支持的格式通过 HAL 层传给内核的音频驱动内核驱动层ALSA 架构的 ASoC 驱动把音频数据通过 DMA 传输给 I2S 控制器I2S 控制器把数字音频信号传输给外部的 Codec 芯片硬件层Codec 芯片把收到的数字音频信号转换成模拟音频信号经过功率放大后输出给喇叭 / 耳机发出声音。录音的链路完全反过来麦克风的模拟信号经过 Codec 转换成数字信号通过 I2S 传给内核再一层层传给上层的录音 App。我们做音频驱动适配核心就是做两件事配置好 I2S 控制器和 Codec 芯片之间的硬件连接I2S、MCLK 时钟、控制总线在设备树里配置好 Codec 芯片的驱动参数让内核的 ASoC 驱动能正常控制 Codec 芯片完成数字和模拟信号的转换。一、核心架构ALSA 与 ASoC大白话全解析1. 什么是 ALSAALSA 的全称是 Advanced Linux Sound Architecture高级 Linux 声音架构是现在 Linux 内核里标准的音频驱动框架替代了老旧的 OSS 架构我们现在所有的 Linux 音频驱动都是基于 ALSA 架构开发的。ALSA 架构给我们提供了两大核心能力给内核驱动开发者提供了一套标准的音频驱动开发 API不用我们自己处理复杂的 DMA、I2S 时序给用户空间提供了一套标准的音频设备接口/dev/snd/还有一套用户空间库 alsa-lib上层应用可以通过这套库控制音频硬件不用关心底层的驱动细节。2. 什么是 ASoCASoC 的全称是 ALSA System on Chip是基于 ALSA 架构专门为嵌入式 SOC 芯片设计的音频驱动子系统我们 RK3568 平台的音频驱动就是基于 ASoC 架构开发的。为什么要有 ASoC因为传统的 ALSA 驱动把 SOC 的音频控制器驱动和 Codec 芯片驱动耦合在一起换个平台、换个 Codec就要重写整个驱动极其不灵活。而 ASoC 架构把驱动拆成了三个独立的部分实现了平台和 Codec 的解耦ASoC 架构的三大核心组件Platform 驱动平台驱动对应 SOC 芯片的音频相关控制器比如 RK3568 的 I2S 控制器、DMA 控制器、时钟控制器。这部分驱动瑞芯微官方已经写好了我们不用改只需要在设备树里使能对应的控制器就行Codec 驱动编解码器驱动对应外部的音频 Codec 芯片比如 ES8388、WM8960、AC108驱动里实现了 Codec 芯片的寄存器配置、音量控制、EQ 调节、ADC/DAC 控制等功能。这部分驱动内核里已经集成了绝大多数常用的 Codec 芯片我们也不用自己写只需要在设备树里配置好就行Machine 驱动板级驱动相当于胶水把 Platform 驱动和 Codec 驱动绑定在一起描述了开发板上的硬件连接情况比如 I2S 用了哪一路MCLK 时钟是多少Codec 的控制总线是 I2C 还是 SPI耳机 / 喇叭的检测引脚等等。这部分是我们适配的核心需要在设备树里配置好对应的硬件连接信息。小白必记RK 平台适配音频驱动99% 的工作就是修改设备树的 Machine 驱动相关配置Platform 驱动和 Codec 驱动官方和内核已经写好了我们不用写一行驱动代码二、音频硬件核心知识点小白必懂1. 核心硬件Codec 芯片Codec 芯片也就是音频编解码器是整个音频系统的核心它的核心功能有两个DAC数模转换把 SOC 通过 I2S 传过来的数字音频信号转换成模拟音频信号放大后输出给喇叭 / 耳机ADC模数转换把麦克风输入的模拟音频信号转换成数字信号通过 I2S 传给 SOC实现录音功能。RK 平台开发板最常用的 Codec 芯片是 ES8388性价比高资料全内核已经集成了完整的驱动我们今天的实战就以 ES8388 为例。2. 核心总线I2S 音频总线I2S 总线是 SOC 和 Codec 芯片之间传输数字音频信号的总线和 I2C、SPI 总线不一样它是专门为音频设计的由 4 根线组成表格信号线全称作用方向SCLKBCLK位时钟位同步时钟一个时钟周期传输 1 位音频数据SOC → CodecLRCKWS帧时钟 / 左右声道时钟用来区分左右声道高电平代表左声道低电平代表右声道也可能反过来一个 LRCK 周期对应一帧音频数据SOC → CodecSDOUT串行数据输出SOC 把数字音频数据通过这个线传给 Codec对应播放功能SOC → CodecSDIN串行数据输入Codec 把录音的数字音频数据通过这个线传给 SOC对应录音功能Codec → SOC3. 核心时钟MCLK 主时钟Codec 芯片要正常工作必须有一个主时钟 MCLK一般由 SOC 的时钟控制器提供常用的频率是 12.288MHz、24.576MHz这个时钟必须和音频采样率对应不然会出现声音播放速度不对、杂音的问题。4. 控制总线I2C/SPI我们要配置 Codec 芯片的寄存器、调节音量、切换输入输出通道必须通过控制总线给 Codec 芯片发送命令绝大多数 Codec 芯片都是用 I2C 总线控制ES8388 就是用 I2C 控制的。三、保姆级实战ES8388 音频驱动完整适配基于 RK3568我们以 RK3568ES8388 为例手把手带你完成完整的音频驱动适配所有步骤一步不落小白跟着走就能出声音。前置准备RK3568 开发板已经焊接好 ES8388 Codec 芯片硬件连接正常配套的安卓 SDK已经搭建好编译环境喇叭 / 耳机、麦克风用来测试播放和录音。步骤 1确认硬件连接首先我们要确认开发板上的硬件连接整理出对应的引脚和总线信息这是设备树配置的基础举个例子表格功能硬件连接I2S 总线RK3568 的 I2S1 控制器和 ES8388 的 I2S 引脚连接控制总线ES8388 挂在 I2C1 总线上I2C 地址 0x10MCLK 时钟由 RK3568 的 CLK_I2S1_OUT 提供频率 12.288MHz喇叭输出ES8388 的 SPK_OUT 引脚接喇叭耳机输出ES8388 的 HP_OUT 引脚接耳机麦克风输入ES8388 的 MIC1 引脚接麦克风步骤 2修改设备树添加音频配置RK3568 的音频配置都在板级.dts 文件里我们只需要添加对应的 I2S 控制器、Codec 芯片、Machine 驱动的节点就行每一行都加了注释你只需要把参数换成你自己的硬件连接就行。打开你的板级.dts 文件添加下面的配置dts// 1. 使能I2C1控制器ES8388挂在I2C1上 i2c1 { status okay; clock-frequency 400000; pinctrl-names default; pinctrl-0 i2c1_xfer; // ES8388 Codec芯片节点 es8388: es838810 { compatible everest,es8388; // 和内核驱动匹配 reg 0x10; // ES8388的I2C地址一般是0x10具体看规格书 status okay; // 主时钟配置12.288MHz #sound-dai-cells 0; clocks cru CLK_I2S1_OUT; clock-names mclk; // 耳机检测引脚没有的话可以不写 hp-det-gpios gpio0 RK_PB6 GPIO_ACTIVE_LOW; pinctrl-names default; pinctrl-0 hp_det_pin; }; }; // 2. 使能I2S1控制器和ES8388通信 i2s1_8ch { status okay; #sound-dai-cells 0; rockchip,clk-trcm 1; pinctrl-names default; pinctrl-0 i2s1m0_sclk i2s1m0_lrck i2s1m0_sdo i2s1m0_sdi; }; // 3. 核心Machine驱动把Platform和Codec绑定在一起 sound { compatible simple-audio-card; // 内核标准的simple-audio-card驱动 status okay; model rk3568-es8388; // 声卡名称随便起 simple-audio-card,format i2s; // 音频格式标准I2S格式 simple-audio-card,bitclock-master dailink_master; // 位时钟主设备SOC是主 simple-audio-card,frame-master dailink_master; // 帧时钟主设备 simple-audio-card,mclk-fs 256; // MCLK和采样率的倍数256倍是标准值 // 绑定SOC的I2S控制器Platform驱动 dailink_master: simple-audio-card,cpu { sound-dai i2s1_8ch; // 对应我们上面使能的I2S1控制器 }; // 绑定ES8388 Codec芯片Codec驱动 simple-audio-card,codec { sound-dai es8388; // 对应我们上面的ES8388节点 }; }; // 耳机检测引脚的pinctrl配置 pinctrl { es8388 { hp_det_pin: hp-det-pin { rockchip,pins 0 RK_PB6 RK_FUNC_GPIO pcfg_pull_up; }; }; }; // 4. 配置音频相关的时钟 cru { status okay; };核心配置讲解compatible everest,es8388这个是核心必须和内核里 ES8388 驱动的 compatible 属性完全一致内核才能匹配到对应的驱动simple-audio-card这是内核里标准的 Machine 驱动专门用来绑定 Platform 和 Codec 驱动我们不用自己写 Machine 驱动只需要在设备树里配置好这个节点就行99% 的场景都够用simple-audio-card,mclk-fs 256MCLK 时钟和采样率的倍数比如 48kHz 采样率MCLK 就是 48000*25612288000Hz12.288MHz这个是标准值绝大多数 Codec 芯片都用 256 倍sound-dai用来绑定 I2S 控制器和 Codec 芯片必须和前面的节点对应不能写错。步骤 3内核配置开启 ES8388 驱动默认情况下RK SDK 的内核已经开启了 ES8388 驱动如果没开启我们只需要修改内核配置开启对应的选项进入内核目录执行菜单配置bash运行cd kernel make ARCHarm64 menuconfig进入以下路径开启对应的驱动plaintextDevice Drivers --- Sound card support --- Advanced Linux Sound Architecture --- ALSA for SoC audio support --- * SoC Audio for Rockchip I2S/TDM/PDM controllers * Everest Semi ES8388 CODEC * ASoC Simple sound card support保存配置重新编译内核和设备树bash运行cd ../ ./build.sh -CKu步骤 4编译烧录验证驱动是否正常工作把编译好的 boot.img 和 update.img 烧录到开发板重启重启完成后进入 ADB shell验证声卡是否注册成功bash运行adb shell su cat /proc/asound/cards能看到我们配置的声卡rk3568-es8388说明声卡已经正常注册驱动加载成功了查看 ALSA 音频设备bash运行ls /dev/snd/能看到controlC0、pcmC0D0p、pcmC0D0c这些设备文件说明 ALSA 设备已经正常生成用 tinyplay 工具测试播放内核里自带了 tinyplay 这个音频播放工具我们可以用它直接测试播放不用依赖安卓系统bash运行# 找一个wav格式的音频文件推到开发板里 adb push test.wav /data/ # 用tinyplay播放 tinyplay /data/test.wav如果喇叭能正常播放声音说明我们的音频驱动完全正常了用 tinycap 工具测试录音bash运行# 录音保存到test_record.wav tinycap /data/test_record.wav # 按CtrlC停止录音然后播放录音 tinyplay /data/test_record.wav能正常播放录音的内容说明录音功能也正常了。步骤 5安卓系统音频适配如果 tinyplay 能正常播放但是安卓系统里的音乐播放器没声音说明安卓的音频 HAL 层没有适配好我们只需要修改两个配置文件就行audio_policy_configuration.xml这个文件是安卓音频策略的配置文件在 SDK 的device/rockchip/rk3568/目录下修改里面的声卡名称和我们设备树里的model rk3568-es8388一致audio_hw.xml修改里面的声卡配置对应我们的 I2S 控制器和 Codec 芯片重新编译安卓系统烧录 update.img安卓系统就能正常播放声音了。四、小白音频驱动必踩的坑全场景问题排查指南音频驱动最常见的问题就是没声音、杂音、播放速度不对、录音无声我把所有问题的排查步骤按优先级给你整理好了按这个顺序查99% 的问题都能解决。问题 1完全没声音tinyplay 也没声音按这个顺序排查先查硬件测量 Codec 芯片的供电有没有 3.3V 电压没供电先查电源电路测量 MCLK 时钟有没有 12.288MHz 的时钟输出没时钟先查设备树里的时钟配置测量 I2C 总线能不能正常读写 Codec 的寄存器用i2cdetect能不能扫到 Codec 的 I2C 地址扫不到就是 I2C 接线错了或者设备树配置错了测量喇叭 / 耳机的引脚有没有电压输出没输出就是 Codec 没正常工作。再查驱动和设备树看内核日志搜索es8388、asoc、i2s相关的日志看有没有报错比如 Codec 初始化失败、I2S 配置失败、时钟申请失败检查设备树里的 I2S 控制器、Codec 节点的 status 是不是 okay有没有被禁用检查 simple-audio-card 节点里的 sound-dai是不是和 I2S、Codec 节点对应有没有写错检查 Codec 的 I2C 地址是不是正确和规格书里的一致。查 Codec 寄存器配置用i2cget工具读取 Codec 芯片的 ID 寄存器确认能正常读写然后看 Codec 的初始化是不是正常有没有正确配置 DAC、ADC、音量、输出通道。问题 2有声音但是全是杂音、破音核心原因和解决方案时钟不对这是最常见的原因MCLK 频率不对或者 mclk-fs 配置错了导致采样率不匹配出现杂音。检查 MCLK 时钟是不是 12.288MHzmclk-fs 是不是 256I2S 的位时钟是不是和采样率对应电源纹波太大Codec 的供电不稳定有很大的纹波导致模拟信号有杂音。给 Codec 的供电引脚加 10uF0.1uF 的滤波电容靠近芯片引脚音量开太大DAC 的音量调到了最大出现削顶失真破音。把音量调小一点在 Codec 驱动里把最大音量限制在合理范围I2S 接线不对LRCK 和 SCLK 接反了或者左右声道搞反了导致数据错乱出现杂音。检查 I2S 的接线确认 SCLK、LRCK、SDOUT 的接线正确接地不好模拟地和数字地没分开有数字干扰导致杂音。把 Codec 的模拟地和数字地单点接地做好屏蔽。问题 3播放声音速度不对快了或者慢了100% 是时钟配置错了MCLK 频率不对比如应该是 12.288MHz实际输出的是 24.576MHz导致播放速度快了一倍mclk-fs 配置错了应该是 256写成了 512导致采样率计算错误I2S 的位时钟配置错了和采样率、声道数、位宽不匹配重新计算位时钟BCLK 采样率 * 声道数 * 位宽比如 48kHz 双声道 16 位BCLK480002161536000Hz1.536MHz。问题 4播放正常但是录音无声硬件问题麦克风没接对或者麦克风的偏置电压没开检查麦克风的接线看 Codec 的规格书有没有开启麦克风的偏置电压Codec 配置问题ADC 没开启或者输入通道选错了比如麦克风接在 MIC1但是 Codec 配置成了 MIC2 输入修改设备树里的配置或者 Codec 驱动里的输入通道选择I2S 接线问题SDIN 引脚接反了SOC 收不到 Codec 的录音数据检查 SDIN 的接线Codec 的 SDO 接 SOC 的 SDI音量问题ADC 的增益调到了最小录音音量为 0调大 ADC 的增益试试。结尾说两句这篇文章我们彻底搞懂了 ALSA/ASoC 音频架构拆解了音频系统的完整链路完成了 ES8388 Codec 芯片的驱动适配还给了你一套万能的音频问题排查指南以后再遇到没声音、杂音的问题再也不用瞎改了。下一篇我们进入摄像头驱动的内容详解 V4L2 子系统手把手带你完成 RK 平台的摄像头驱动适配实现安卓相机原生调用解决摄像头不出图、花屏的问题。我是黒漂技术佬关注我带你零基础入门 RK 安卓驱动开发不踩坑。有任何音频驱动的问题评论区留言我都会一一回复。
第 15 篇 音频驱动适配与调试,ALSA 架构详解
目录开篇先搞懂音频系统的完整链路到底是怎么回事完整的音频播放链路一、核心架构ALSA 与 ASoC大白话全解析1. 什么是 ALSA2. 什么是 ASoCASoC 架构的三大核心组件二、音频硬件核心知识点小白必懂1. 核心硬件Codec 芯片2. 核心总线I2S 音频总线3. 核心时钟MCLK 主时钟4. 控制总线I2C/SPI三、保姆级实战ES8388 音频驱动完整适配基于 RK3568前置准备步骤 1确认硬件连接步骤 2修改设备树添加音频配置核心配置讲解步骤 3内核配置开启 ES8388 驱动步骤 4编译烧录验证驱动是否正常工作步骤 5安卓系统音频适配四、小白音频驱动必踩的坑全场景问题排查指南问题 1完全没声音tinyplay 也没声音问题 2有声音但是全是杂音、破音问题 3播放声音速度不对快了或者慢了问题 4播放正常但是录音无声结尾说两句大家好我是黒漂技术佬。上一篇我们搞定了显示驱动点亮了屏幕后台很多兄弟又遇到了新的难题“佬我焊了个 ES8388 音频 Codec 芯片改了设备树结果没声音要么就是全是杂音根本不知道从哪里查问题音频驱动也太复杂了”太懂了音频驱动是嵌入式开发里出了名的 “玄学”涉及到的链路长、模块多从内核 ALSA 架构、Codec 驱动、DMA 传输到安卓音频 HAL 层、AudioFlinger 服务任何一个环节出问题都会导致没声音、杂音新手根本不知道从哪里下手。今天这篇我就用大白话给你讲透 ALSA 音频架构拆解 RK 平台的音频系统手把手带你完成ES8388 Codec 芯片的驱动适配从设备树配置、驱动调试到没声音、杂音、录音无声的全场景问题排查小白也能轻松搞定。开篇先搞懂音频系统的完整链路到底是怎么回事很多新手搞音频驱动上来就瞎改设备树连音频信号的完整链路都没搞懂不踩坑才怪。先给大家用大白话讲清楚从安卓 App 播放音乐到喇叭发出声音完整的链路是什么样的。完整的音频播放链路应用层安卓 App比如音乐播放器调用安卓系统的音频 API把要播放的音频数据传给安卓系统的音频服务安卓框架层AudioFlinger 服务把音频数据混音、重采样转换成硬件支持的格式通过 HAL 层传给内核的音频驱动内核驱动层ALSA 架构的 ASoC 驱动把音频数据通过 DMA 传输给 I2S 控制器I2S 控制器把数字音频信号传输给外部的 Codec 芯片硬件层Codec 芯片把收到的数字音频信号转换成模拟音频信号经过功率放大后输出给喇叭 / 耳机发出声音。录音的链路完全反过来麦克风的模拟信号经过 Codec 转换成数字信号通过 I2S 传给内核再一层层传给上层的录音 App。我们做音频驱动适配核心就是做两件事配置好 I2S 控制器和 Codec 芯片之间的硬件连接I2S、MCLK 时钟、控制总线在设备树里配置好 Codec 芯片的驱动参数让内核的 ASoC 驱动能正常控制 Codec 芯片完成数字和模拟信号的转换。一、核心架构ALSA 与 ASoC大白话全解析1. 什么是 ALSAALSA 的全称是 Advanced Linux Sound Architecture高级 Linux 声音架构是现在 Linux 内核里标准的音频驱动框架替代了老旧的 OSS 架构我们现在所有的 Linux 音频驱动都是基于 ALSA 架构开发的。ALSA 架构给我们提供了两大核心能力给内核驱动开发者提供了一套标准的音频驱动开发 API不用我们自己处理复杂的 DMA、I2S 时序给用户空间提供了一套标准的音频设备接口/dev/snd/还有一套用户空间库 alsa-lib上层应用可以通过这套库控制音频硬件不用关心底层的驱动细节。2. 什么是 ASoCASoC 的全称是 ALSA System on Chip是基于 ALSA 架构专门为嵌入式 SOC 芯片设计的音频驱动子系统我们 RK3568 平台的音频驱动就是基于 ASoC 架构开发的。为什么要有 ASoC因为传统的 ALSA 驱动把 SOC 的音频控制器驱动和 Codec 芯片驱动耦合在一起换个平台、换个 Codec就要重写整个驱动极其不灵活。而 ASoC 架构把驱动拆成了三个独立的部分实现了平台和 Codec 的解耦ASoC 架构的三大核心组件Platform 驱动平台驱动对应 SOC 芯片的音频相关控制器比如 RK3568 的 I2S 控制器、DMA 控制器、时钟控制器。这部分驱动瑞芯微官方已经写好了我们不用改只需要在设备树里使能对应的控制器就行Codec 驱动编解码器驱动对应外部的音频 Codec 芯片比如 ES8388、WM8960、AC108驱动里实现了 Codec 芯片的寄存器配置、音量控制、EQ 调节、ADC/DAC 控制等功能。这部分驱动内核里已经集成了绝大多数常用的 Codec 芯片我们也不用自己写只需要在设备树里配置好就行Machine 驱动板级驱动相当于胶水把 Platform 驱动和 Codec 驱动绑定在一起描述了开发板上的硬件连接情况比如 I2S 用了哪一路MCLK 时钟是多少Codec 的控制总线是 I2C 还是 SPI耳机 / 喇叭的检测引脚等等。这部分是我们适配的核心需要在设备树里配置好对应的硬件连接信息。小白必记RK 平台适配音频驱动99% 的工作就是修改设备树的 Machine 驱动相关配置Platform 驱动和 Codec 驱动官方和内核已经写好了我们不用写一行驱动代码二、音频硬件核心知识点小白必懂1. 核心硬件Codec 芯片Codec 芯片也就是音频编解码器是整个音频系统的核心它的核心功能有两个DAC数模转换把 SOC 通过 I2S 传过来的数字音频信号转换成模拟音频信号放大后输出给喇叭 / 耳机ADC模数转换把麦克风输入的模拟音频信号转换成数字信号通过 I2S 传给 SOC实现录音功能。RK 平台开发板最常用的 Codec 芯片是 ES8388性价比高资料全内核已经集成了完整的驱动我们今天的实战就以 ES8388 为例。2. 核心总线I2S 音频总线I2S 总线是 SOC 和 Codec 芯片之间传输数字音频信号的总线和 I2C、SPI 总线不一样它是专门为音频设计的由 4 根线组成表格信号线全称作用方向SCLKBCLK位时钟位同步时钟一个时钟周期传输 1 位音频数据SOC → CodecLRCKWS帧时钟 / 左右声道时钟用来区分左右声道高电平代表左声道低电平代表右声道也可能反过来一个 LRCK 周期对应一帧音频数据SOC → CodecSDOUT串行数据输出SOC 把数字音频数据通过这个线传给 Codec对应播放功能SOC → CodecSDIN串行数据输入Codec 把录音的数字音频数据通过这个线传给 SOC对应录音功能Codec → SOC3. 核心时钟MCLK 主时钟Codec 芯片要正常工作必须有一个主时钟 MCLK一般由 SOC 的时钟控制器提供常用的频率是 12.288MHz、24.576MHz这个时钟必须和音频采样率对应不然会出现声音播放速度不对、杂音的问题。4. 控制总线I2C/SPI我们要配置 Codec 芯片的寄存器、调节音量、切换输入输出通道必须通过控制总线给 Codec 芯片发送命令绝大多数 Codec 芯片都是用 I2C 总线控制ES8388 就是用 I2C 控制的。三、保姆级实战ES8388 音频驱动完整适配基于 RK3568我们以 RK3568ES8388 为例手把手带你完成完整的音频驱动适配所有步骤一步不落小白跟着走就能出声音。前置准备RK3568 开发板已经焊接好 ES8388 Codec 芯片硬件连接正常配套的安卓 SDK已经搭建好编译环境喇叭 / 耳机、麦克风用来测试播放和录音。步骤 1确认硬件连接首先我们要确认开发板上的硬件连接整理出对应的引脚和总线信息这是设备树配置的基础举个例子表格功能硬件连接I2S 总线RK3568 的 I2S1 控制器和 ES8388 的 I2S 引脚连接控制总线ES8388 挂在 I2C1 总线上I2C 地址 0x10MCLK 时钟由 RK3568 的 CLK_I2S1_OUT 提供频率 12.288MHz喇叭输出ES8388 的 SPK_OUT 引脚接喇叭耳机输出ES8388 的 HP_OUT 引脚接耳机麦克风输入ES8388 的 MIC1 引脚接麦克风步骤 2修改设备树添加音频配置RK3568 的音频配置都在板级.dts 文件里我们只需要添加对应的 I2S 控制器、Codec 芯片、Machine 驱动的节点就行每一行都加了注释你只需要把参数换成你自己的硬件连接就行。打开你的板级.dts 文件添加下面的配置dts// 1. 使能I2C1控制器ES8388挂在I2C1上 i2c1 { status okay; clock-frequency 400000; pinctrl-names default; pinctrl-0 i2c1_xfer; // ES8388 Codec芯片节点 es8388: es838810 { compatible everest,es8388; // 和内核驱动匹配 reg 0x10; // ES8388的I2C地址一般是0x10具体看规格书 status okay; // 主时钟配置12.288MHz #sound-dai-cells 0; clocks cru CLK_I2S1_OUT; clock-names mclk; // 耳机检测引脚没有的话可以不写 hp-det-gpios gpio0 RK_PB6 GPIO_ACTIVE_LOW; pinctrl-names default; pinctrl-0 hp_det_pin; }; }; // 2. 使能I2S1控制器和ES8388通信 i2s1_8ch { status okay; #sound-dai-cells 0; rockchip,clk-trcm 1; pinctrl-names default; pinctrl-0 i2s1m0_sclk i2s1m0_lrck i2s1m0_sdo i2s1m0_sdi; }; // 3. 核心Machine驱动把Platform和Codec绑定在一起 sound { compatible simple-audio-card; // 内核标准的simple-audio-card驱动 status okay; model rk3568-es8388; // 声卡名称随便起 simple-audio-card,format i2s; // 音频格式标准I2S格式 simple-audio-card,bitclock-master dailink_master; // 位时钟主设备SOC是主 simple-audio-card,frame-master dailink_master; // 帧时钟主设备 simple-audio-card,mclk-fs 256; // MCLK和采样率的倍数256倍是标准值 // 绑定SOC的I2S控制器Platform驱动 dailink_master: simple-audio-card,cpu { sound-dai i2s1_8ch; // 对应我们上面使能的I2S1控制器 }; // 绑定ES8388 Codec芯片Codec驱动 simple-audio-card,codec { sound-dai es8388; // 对应我们上面的ES8388节点 }; }; // 耳机检测引脚的pinctrl配置 pinctrl { es8388 { hp_det_pin: hp-det-pin { rockchip,pins 0 RK_PB6 RK_FUNC_GPIO pcfg_pull_up; }; }; }; // 4. 配置音频相关的时钟 cru { status okay; };核心配置讲解compatible everest,es8388这个是核心必须和内核里 ES8388 驱动的 compatible 属性完全一致内核才能匹配到对应的驱动simple-audio-card这是内核里标准的 Machine 驱动专门用来绑定 Platform 和 Codec 驱动我们不用自己写 Machine 驱动只需要在设备树里配置好这个节点就行99% 的场景都够用simple-audio-card,mclk-fs 256MCLK 时钟和采样率的倍数比如 48kHz 采样率MCLK 就是 48000*25612288000Hz12.288MHz这个是标准值绝大多数 Codec 芯片都用 256 倍sound-dai用来绑定 I2S 控制器和 Codec 芯片必须和前面的节点对应不能写错。步骤 3内核配置开启 ES8388 驱动默认情况下RK SDK 的内核已经开启了 ES8388 驱动如果没开启我们只需要修改内核配置开启对应的选项进入内核目录执行菜单配置bash运行cd kernel make ARCHarm64 menuconfig进入以下路径开启对应的驱动plaintextDevice Drivers --- Sound card support --- Advanced Linux Sound Architecture --- ALSA for SoC audio support --- * SoC Audio for Rockchip I2S/TDM/PDM controllers * Everest Semi ES8388 CODEC * ASoC Simple sound card support保存配置重新编译内核和设备树bash运行cd ../ ./build.sh -CKu步骤 4编译烧录验证驱动是否正常工作把编译好的 boot.img 和 update.img 烧录到开发板重启重启完成后进入 ADB shell验证声卡是否注册成功bash运行adb shell su cat /proc/asound/cards能看到我们配置的声卡rk3568-es8388说明声卡已经正常注册驱动加载成功了查看 ALSA 音频设备bash运行ls /dev/snd/能看到controlC0、pcmC0D0p、pcmC0D0c这些设备文件说明 ALSA 设备已经正常生成用 tinyplay 工具测试播放内核里自带了 tinyplay 这个音频播放工具我们可以用它直接测试播放不用依赖安卓系统bash运行# 找一个wav格式的音频文件推到开发板里 adb push test.wav /data/ # 用tinyplay播放 tinyplay /data/test.wav如果喇叭能正常播放声音说明我们的音频驱动完全正常了用 tinycap 工具测试录音bash运行# 录音保存到test_record.wav tinycap /data/test_record.wav # 按CtrlC停止录音然后播放录音 tinyplay /data/test_record.wav能正常播放录音的内容说明录音功能也正常了。步骤 5安卓系统音频适配如果 tinyplay 能正常播放但是安卓系统里的音乐播放器没声音说明安卓的音频 HAL 层没有适配好我们只需要修改两个配置文件就行audio_policy_configuration.xml这个文件是安卓音频策略的配置文件在 SDK 的device/rockchip/rk3568/目录下修改里面的声卡名称和我们设备树里的model rk3568-es8388一致audio_hw.xml修改里面的声卡配置对应我们的 I2S 控制器和 Codec 芯片重新编译安卓系统烧录 update.img安卓系统就能正常播放声音了。四、小白音频驱动必踩的坑全场景问题排查指南音频驱动最常见的问题就是没声音、杂音、播放速度不对、录音无声我把所有问题的排查步骤按优先级给你整理好了按这个顺序查99% 的问题都能解决。问题 1完全没声音tinyplay 也没声音按这个顺序排查先查硬件测量 Codec 芯片的供电有没有 3.3V 电压没供电先查电源电路测量 MCLK 时钟有没有 12.288MHz 的时钟输出没时钟先查设备树里的时钟配置测量 I2C 总线能不能正常读写 Codec 的寄存器用i2cdetect能不能扫到 Codec 的 I2C 地址扫不到就是 I2C 接线错了或者设备树配置错了测量喇叭 / 耳机的引脚有没有电压输出没输出就是 Codec 没正常工作。再查驱动和设备树看内核日志搜索es8388、asoc、i2s相关的日志看有没有报错比如 Codec 初始化失败、I2S 配置失败、时钟申请失败检查设备树里的 I2S 控制器、Codec 节点的 status 是不是 okay有没有被禁用检查 simple-audio-card 节点里的 sound-dai是不是和 I2S、Codec 节点对应有没有写错检查 Codec 的 I2C 地址是不是正确和规格书里的一致。查 Codec 寄存器配置用i2cget工具读取 Codec 芯片的 ID 寄存器确认能正常读写然后看 Codec 的初始化是不是正常有没有正确配置 DAC、ADC、音量、输出通道。问题 2有声音但是全是杂音、破音核心原因和解决方案时钟不对这是最常见的原因MCLK 频率不对或者 mclk-fs 配置错了导致采样率不匹配出现杂音。检查 MCLK 时钟是不是 12.288MHzmclk-fs 是不是 256I2S 的位时钟是不是和采样率对应电源纹波太大Codec 的供电不稳定有很大的纹波导致模拟信号有杂音。给 Codec 的供电引脚加 10uF0.1uF 的滤波电容靠近芯片引脚音量开太大DAC 的音量调到了最大出现削顶失真破音。把音量调小一点在 Codec 驱动里把最大音量限制在合理范围I2S 接线不对LRCK 和 SCLK 接反了或者左右声道搞反了导致数据错乱出现杂音。检查 I2S 的接线确认 SCLK、LRCK、SDOUT 的接线正确接地不好模拟地和数字地没分开有数字干扰导致杂音。把 Codec 的模拟地和数字地单点接地做好屏蔽。问题 3播放声音速度不对快了或者慢了100% 是时钟配置错了MCLK 频率不对比如应该是 12.288MHz实际输出的是 24.576MHz导致播放速度快了一倍mclk-fs 配置错了应该是 256写成了 512导致采样率计算错误I2S 的位时钟配置错了和采样率、声道数、位宽不匹配重新计算位时钟BCLK 采样率 * 声道数 * 位宽比如 48kHz 双声道 16 位BCLK480002161536000Hz1.536MHz。问题 4播放正常但是录音无声硬件问题麦克风没接对或者麦克风的偏置电压没开检查麦克风的接线看 Codec 的规格书有没有开启麦克风的偏置电压Codec 配置问题ADC 没开启或者输入通道选错了比如麦克风接在 MIC1但是 Codec 配置成了 MIC2 输入修改设备树里的配置或者 Codec 驱动里的输入通道选择I2S 接线问题SDIN 引脚接反了SOC 收不到 Codec 的录音数据检查 SDIN 的接线Codec 的 SDO 接 SOC 的 SDI音量问题ADC 的增益调到了最小录音音量为 0调大 ADC 的增益试试。结尾说两句这篇文章我们彻底搞懂了 ALSA/ASoC 音频架构拆解了音频系统的完整链路完成了 ES8388 Codec 芯片的驱动适配还给了你一套万能的音频问题排查指南以后再遇到没声音、杂音的问题再也不用瞎改了。下一篇我们进入摄像头驱动的内容详解 V4L2 子系统手把手带你完成 RK 平台的摄像头驱动适配实现安卓相机原生调用解决摄像头不出图、花屏的问题。我是黒漂技术佬关注我带你零基础入门 RK 安卓驱动开发不踩坑。有任何音频驱动的问题评论区留言我都会一一回复。