ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RK3568上OpenHarmony音频驱动调试实录:从耳机无声到双通道

RK3568上OpenHarmony音频驱动调试实录:从耳机无声到双通道 从耳机无声到双通道输出我在RK3568板卡上把OpenHarmony 5.0.2的音频驱动调通了给一块RK3568的开发板适配OpenHarmony 5.0.2的音频驱动最尴尬的不是编译报错而是开机之后系统一切正常但耳机插上去就是一片静默。屏幕亮着应用跑着耳机里只有微弱的电流底噪播放器进度条在走声音就是出不来。我用的音频codec是RK809这颗芯片本质上是颗电源管理IC但内部集成了一组完整的音频编解码通路。从耳机无声到左右双声道都正常出声前后折腾了三天半中间踩了I2S时钟、驱动模型、通路配置、耳机检测、上层路由不少坑。这篇文章把整个适配过程做个复盘重点讲清楚OpenHarmony音频子系统的HDF驱动模型、RK809内部通路的配置思路、双通道输出时的数据格式校验以及几个很容易被忽略的细节。如果你正在把OpenHarmony往RK3568、RK3588或者其他带RK809/817的板子上迁移这篇内容值得收藏。1. 这次适配的完整背景板子、芯片和那一串静默1.1 一句话说清楚这次任务我这边的目标是让一块基于RK3568的行业板卡在OpenHarmony 5.0.2系统下支持耳机和喇叭播放音频并且播放时要走正常的双声道立体声。听起来简单实际上要做的部分是内核/HDF层能把codec驱动挂起来I2S控制器能正确收发数据DMA把PCM数据送到正确的位置codec内部模拟通路和耳机放大电路按正确时序开启上层AudioFramework还能识别到这个设备并对耳机插拔做正确响应。这就不是改一个寄存器的事了。OpenHarmony的音频架构和Linux传统的ASoC框架有区别但也有大量相似的地方尤其是底层硬件驱动这块还是要面对I2S、DMA、codec、PA这些实体。稍有不对结果就是“系统正常但耳机没声”。1.2 RK809这颗chip在音频链路上到底干了什么RK809是瑞芯微配套的PMIC自带一路I2S接口的音频codec。它在板卡上的角色可以类比成一个“音频前置处理器”SoC通过I2S总线把数字音频流丢过来RK809负责数模转换把数字信号转成模拟信号再经过内置耳机放大器HP Driver推到耳机座或者经过功放、喇叭输出到外放。这类集成式PMIC codec的优点是很省物料和面积缺点是音频性能和独立codec比会有一点差距但做行业板卡、工控、商业显示这类场景完全够用。调试的时候要认清楚它的datasheet里几个关键部分DAC、ADC、耳机放大、模拟通路选择Input/Output MUX、头戴检测Jack Detect。我们这次遇到的问题几乎都围绕这些模块。1.3 OpenHarmony音频栈与PC音频栈的区别做过PC音频开发的朋友可能用过WASAPI、ASIO这些抽象。在PC上音频栈已经把硬件差异消化得差不多了程序员只需要关心采样率、位深、声道数这些“高层参数”。但在OpenHarmony上从应用层到codec之间的每一层都得自己趟。OpenHarmony的音频服务在上层有AudioPolicyService、AudioServer中间通过HDI层向HDF驱动发起音频设备操作HDF驱动再控制I2S控制器和codec芯片。也就是说问题可能发生在任意一层上层的路由策略没配对、中层的HDI接口没实现好、底层的codec寄存器没配置对最后表现都是耳机没声音。这就决定了调试时不能只看代码还要有一个从上层到下层逐层验证的思路。2. 适配前要梳理清楚的音频数据通路2.1 从AP到耳机孔的完整信号流我先画一条逻辑链路方便后面理解AudioRenderer应用→ Audio Server → Audio HDI → HDF AudioHost → I2S控制器 → RK809 codec → 耳机功放/喇叭。在这条链路里通常最容易出问题的两个点是HDF驱动层有没有正确把PCM流送到I2S控制器以及RK809内部有没有把数字信号正确路由到耳机放大器。前者偏SoC侧后者偏codec侧。I2S总线上走的数据格式一般是标准I2S或右对齐双声道16bit数据在线上按左右声道交错排列。SoC的I2S控制器通过BCLK位时钟、LRCK帧时钟和DIN/DOUT数据线跟codec通信。只要其中一个信号极性、分频关系不对输出就是噪声或者干脆没声音。2.2 HDF驱动节点与配置文件的对应关系在OpenHarmony 5.0.2里音频相关HDF配置文件一般放在vendor/对应厂商/hdf_config/audio目录下。常见的几个文件是audio_config.hcs、audio_codec_config.hcs。这些hcs文件描述了AudioCodec、AudioDai、AudioDma等设备的节点绑定关系。我在适配时优先确认了这几项Codec设备有没有正确挂在I2C总线上地址和实际芯片是否一致AudioDai和I2S控制器的绑定是否正确比如SAI对应的是哪个实例DMA通道和设备是否匹配。这块如果配置错HDF层直接报probe失败后面什么都没戏。2.3 调试工具与命令准备建议提前把以下工具准备好能省一半时间hilog查看OpenHarmony上层音频服务的日志可以过滤AudioService、AudioPolicy等tagdmesg查看内核态和HDF驱动的日志Codec probe、I2S控制器初始化都会在里面留痕/proc/asound/cards和/proc/asound/pcm确认声卡和PCM设备有没有注册成功tinymix查看和修改codec寄存器控件这是最核心的工具i2cget/i2cset在没有tinyalsa工具时直接操作I2C寄存器定位问题更直接逻辑分析仪如果手头有优先测MCLK、BCLK、LRCK三根时钟线。调试环境准备好之后就是最耗时的“剥洋葱”阶段了。3. 耳机无声的定位过程一步步剥洋葱3.1 第一阶段确认codec驱动是否真正probe拿到板子第一件事不是马上放歌而是先开机后看dmesg里有没有RK809 codec驱动的probe记录。我遇到的现象是dmesg里压根没有codec初始化的日志HDF日志里明确报了I2C设备匹配失败。查了一圈原因是hcs配置里codec挂在I2C1上但板子上RK809实际挂在I2C2上地址倒是没变0x1B。这是个很低级的错误但对刚接触这类板卡的人来说很容易漏。改完设备树/hcs里i2c的总线编号重新编译烧录再次看dmesg这次codec的初始化日志终于出来了。这里分享一个经验HDF驱动如果probe失败不要急着去读datasheet先用i2cdetect或者调试串口工具扫描一下I2C总线上实际有哪些设备确认器件地址后再和代码对比。硬件工程师给的原理图和实际板卡走线有时候会对不上必须以实测为准。3.2 第二阶段核对I2S时钟与codec主从关系codec probe成功后用tinyplay放一首wav依然没声音。这时候我把关注点放到I2S时钟上。RK3568的I2S控制器可以配置为master或slaveRK809同样有master/slave模式。一般板卡会设计成SoC做mastercodec做slave也就是MCLK、BCLK、LRCK都由SoC提供。我在代码里确认了I2S控制器配置为master模式同时RK809的寄存器也配置成slave模式主从关系没问题。然后用逻辑分析仪去量RK809侧的MCLK引脚。结果发现MCLK频率完全不对配置的是12.288MHz实际测出来只有几百kHz。后来查I2S驱动的时钟树发现是父时钟源选错了使用了低频的24M晶振直通而没有走到PLL的分频链路。把I2S控制器的时钟源切换到CPLL/GPLL这条高速PLL链路上重新配置分频系数后MCLK终于稳定在12.288MHzBCLK 1.536MHzLRCK 48kHz。时钟正常了但耳机里还是只有噪声。3.3 第三阶段通路配置与信号增益时钟正常却出噪声说明数字侧基本通了问题出现在codec内部模拟通路。我通过tinymix把RK809内部的各个控制节点导出来逐项检查DAC使能、耳机放大使能、通路选择这几个关键项。典型的错误是DAC单元没有真正上电或者DAC输出到耳机放大器之间的MUX没有被选通。RK809内部有一个输出选择寄存器可以决定DAC的信号送去耳机放大还是送去线性输出。我在默认配置里看到耳机放大通路是关闭的只开了LINE_OUT通路。通过tinymix把耳机放大使能并把DAC输出路由切到HP通路后再播放wav耳机里终于出现了正常的声音。但这时候发现只有左声道有声音右耳机几乎没声。3.4 第四阶段耳机检测与上层路由还有一个藏在最后的坑耳机虽然能出声了但如果你在OpenHarmony上打开设置里的声音选项它会认为没有插入耳机。这是因为耳机检测Jack Detect的电路没有正确接到SoC的GPIO或者驱动里没有上报headset状态。我检查了原理图RK809的HP_DET引脚经过一个电阻连接到RK3568的一个GPIO但这个GPIO在hcs里没有配置成中断输入。配置好GPIO的中断触发方式并把检测结果上报给AudioPolicyService后上层才真正识别到耳机设备。这也解释了为什么最底层驱动通了但系统设置里还是显示“外放”。4. 双通道输出的关键配置与验证4.1 声道数从哪来数据格式在DMA/DAI/Codec之间的传递左声道有声音右声道没声音这个问题在不同项目里原因各不相同但排查思路是一致的。我先看DMA侧的配置确认PCM设备打开的时候申请的声道数是2数据格式是16bit交错排列也就是每一帧数据在DMA里按L、R、L、R排列。接着看I2S控制器DAI配置确认发送模式是I2S标准格式。注意标准I2S格式下LRCK为低时传输的是左声道数据为高时传输的是右声道数据。但有些SoC的I2S控制器默认的是左右声道与LRCK的极性和codec预期相反。如果这里配置反了左右声道会整体对调但两个声道都会有声音。现在只有左声道有声更像是数据被“压”到了一个声道。我查了I2S控制器的TCR寄存器发现数据格式被配置成了单声道mono。也就是说虽然应用层和DMA都是双声道16bit但I2S控制器在传输时只采样了LRCK的某一个相位右声道的数据被丢弃了。把TCR寄存器中关于channel的配置改成2声道标准格式后再用左右声道分离的测试音频验证两个耳机都有了声音。4.2 常用采样率下的时钟参数计算参考很多朋友在那几个采样率下反复折腾时间参数这里直接给出一份参考计算表方便对着查。公式是LRCK fs也就是采样率本身BCLK fs × channels × bitsMCLK一般取BCLK的四倍、八倍或按codec建议的fs倍率RK809常见用256 × fs。以双声道16bit为例48kHz采样率LRCK48kHzBCLK48k×2×161.536MHzMCLK48k×25612.288MHz44.1kHz采样率LRCK44.1kHzBCLK44.1k×2×161.4112MHzMCLK44.1k×25611.2896MHz16kHz采样率语音场景LRCK16kHzBCLK16k×2×16512kHzMCLK16k×2564.096MHz。如果调试中发现“能出声音但音调不对、速度忽快忽慢”多半就是BCLK或者LRCK的频率没按这个比例配好。频率偏高声音听起来像“加速”偏低就是“慢速播放”。这种事用耳朵就能判断再用逻辑分析仪确认一下具体数值就行。4.3 左右声道独立验证的实操方法驱动调完了验证是否“真立体声”这一步不能省。我用一个小工具生成左右声道交替取反的正弦波左声道输出1kHz、右声道输出反向的1kHz。播放时如果耳机左右两个单元发出的声音互相抵消会明显感觉中间是空的如果两端有声说明左右声道独立。另一个更直接的方法是生成左声道静音、右声道正弦波的测试文件分别播放确认。实测下来只要DMA、I2S控制器、RK809内部通道三者配置一致左右声道的分离度就没问题。这个环节建议一定做因为它能发现一个隐性bug某些情况下左右声道信号会“串”明明只发了一个声道的信号两个耳机里却都有声响一般是地线处理或者DAC输出共用了模拟通路导致的。5. 调试踩坑记录与实用排查清单5.1 常见问题与解决思路速查表我把这段时间遇到和同行交流过的常见问题整理了一张表查起来很方便现象可能原因排查方向dmesg无codec probe记录I2C总线号或器件地址配置错误i2cdetect扫描实际总线设备tinyplay播放无声音I2S主从关系、时钟未挂在PLL逻辑分析仪测MCLK/BCLK/LRCK只有噪声codec内部DAC或模拟通路未使能tinymix查看DAC/HP通路使能状态只有单声道I2S控制器被配置为mono模式检查TCR/RCR寄存器channel配置左右声道颠倒I2S极性配置与codec预期不一致交换LRCK极性或互换左右slot槽位上层识别不到耳机Jack Detect GPIO未正确上报配置GPIO中断并让AudioPolicy识别能识别到设备但无声音DAI与DMA解复用配置不匹配检查SAI/DMA绑定关系音量调高之后破音codec模拟增益过高下调DAC模拟增益先保证数字域不削波这个表不是万能但覆盖了OpenHarmony适配RK809时大多数“耳机无声”类问题的方向。5.2 三条值得记住的排查习惯第一按层排查不跳级。从底层往上验证先确认I2C能读写codec再确认I2S时钟正常再确认DMA数据有输出最后才检查上层路由。每一层验证通过后再往上走。第二善用dmesg和hilog的过滤关键词。HDF的日志通常带audio、i2s、codec这些tag提前把过滤命令记好能少翻很多无效信息。尤其在OpenHarmony 5.0.2这种大版本上日志有时候会刷得飞快没有过滤基本没法看。第三修改完寄存器后注意清缓存和重启音频服务。有些寄存器是写后立即生效的有些需要等音频通路进入idle状态后再生效。我在调DAC增益时遇到过改完没生效的情况后来发现是音频服务还占着声卡设备把对应的audio_server进程重启之后才读到新值。5.3 关于工具链和固件版本的一个提醒最后再提一个容易忽略的点。OpenHarmony 5.0.2本身的内核态驱动框架和较老版本有差异适配时尽量使用厂商SDK中针对5.0.x分支提供的驱动包不要直接拿旧工程的hcs配置替换。我前两次编译过了但运行异常都和配置中某些节点名、服务名字段和当前内核框架不匹配有关。另外如果你用的是Linux内核比如5.10去跑OpenHarmony用户态调试时也可以参考Linux内核ALSA的驱动写法两边在codec寄存器配置和I2S时序设计上是完全共通的。我在怀疑RK809某个寄存器含义时直接把Linux主线里rockchip的codec驱动源码翻出来比对比自己看datasheet快得多。这次调试给我最深的感受是OpenHarmony的音频链路并不复杂但每一层都有自己的“脾气”。只要把数据流从硬件到上层完整梳理清楚遇到的问题都有迹可循。希望这篇记录能让你少走几个弯路。
返回列表