ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

RK3576 HDMI调试踩坑:I2C SDA/SCL交换导致EDID读取失败

RK3576 HDMI调试踩坑:I2C SDA/SCL交换导致EDID读取失败

1. 一块板子引发的血案:为什么HDMI搭错线能让人调三天

搞嵌入式的人大概都有过这种经历:板子打回来,电源正常,系统能跑,串口有打印,一切看起来都对,但屏幕就是不亮。你开始怀疑屏幕坏了、线材有问题、驱动没编进去,折腾一圈下来发现——原理图上两根线画反了。

这次踩的坑就属于这一类。项目主控用的是RK3576,需要从HDMI接口输出画面到一个外接显示设备。硬件方案上,HDMI连接器到主控之间走了I2C做EDID读取,同时还有HPD(热插拔检测)信号和几路电源控制。问题出在I2C的SDA和SCL上——PCB上这两根线被交换了。

你可能觉得这算什么大事,I2C就两根线,换回来不就行了?但实际调试的时候,你面对的是一个“没有任何反应”的系统。HDMI没有输出,串口log里I2C通信全是超时,你甚至不确定是硬件问题还是软件问题。更麻烦的是,这个板子上的I2C总线还挂了EEPROM和其他外设,你不能简单地把所有设备都判死刑。

这篇文章会把整个排查过程拆开讲清楚:从HDMI链路的基本工作原理,到I2C在其中的角色,再到怎么一步步定位到“线搭错了”这个结论,最后给出硬件改板和软件规避的具体方案。如果你正在用RK3576做HDMI相关设计,或者任何涉及I2C通信的嵌入式项目,这些经验应该能帮你少走几天弯路。

2. HDMI链路里到底藏了多少根线

2.1 不只是TMDS:HDMI的完整信号构成

很多人一提HDMI就觉得是“四对差分线传视频”,这个理解不算错,但远远不够。一个能正常工作的HDMI链路,至少包含以下几类信号:

  • TMDS差分对:四对,其中三对传视频和音频数据,一对传时钟。这是高速信号,速率从几百Mbps到几Gbps不等。
  • DDC通道:本质上就是一路I2C,包含SDA和SCL两根线。它的作用是让源端(我们的板子)读取接收端(显示器)的EDID信息,也就是显示器支持哪些分辨率、刷新率、音频格式等。
  • HPD:热插拔检测,接收端通过拉高这根线告诉源端“我插上了,可以开始通信”。
  • CEC:消费电子控制,用于设备间联动控制,很多项目里直接不用。
  • +5V电源:源端通过HDMI线给接收端的EDID芯片供电,让显示器在未开机时也能被读取到信息。

你看,光是和“通信”相关的就有DDC和HPD两套机制。DDC走的是I2C协议,HPD是GPIO电平。这两个任何一个出问题,HDMI都不会正常输出。

2.2 为什么I2C在HDMI里这么关键

EDID读取是整个HDMI握手的第一步。源端上电后,会先检测HPD是否有效,然后通过DDC通道(I2C)去读接收端EDID ROM里的数据。只有读到了合法的EDID,系统才知道该输出什么分辨率、什么时序。

如果I2C通信失败,会发生什么?分几种情况:

  • 如果驱动写得比较“硬”,读不到EDID就直接不输出,你会看到HDMI接口完全没有信号。
  • 如果驱动有fallback逻辑,可能会用一个默认分辨率强行输出,但兼容性很差,有些显示器能亮,有些不能。
  • 如果I2C时好时坏,可能会出现“偶尔能亮、偶尔不亮”的玄学现象。

这次的问题属于第一种:I2C完全不通,HDMI直接躺平。

2.3 RK3576的HDMI控制器架构

RK3576内部集成了HDMI TX控制器,它负责TMDS信号的编码和发送。但EDID读取这部分,通常不是HDMI控制器自己完成的,而是通过SoC内部的I2C控制器去访问外部连接器上的DDC线路。

这里就涉及到一个硬件设计上的关键点:HDMI连接器的DDC引脚(Pin 15和Pin 16)需要连接到SoC的某一路I2C总线。这一路I2C总线上通常还会挂一个EEPROM,用于存储板级信息或者作为备用EDID。

在我们的设计里,HDMI的DDC连到了I2C总线的一路,同时这路I2C还挂了一个EEPROM。问题就出在这里:PCB上SDA和SCL走线交换了。

3. 排查过程:从“屏幕不亮”到“线搭错了”

3.1 第一轮怀疑:软件配置和驱动

板子回来第一次上电,串口有正常启动log,系统起来了,但接上显示器没有任何反应。第一反应是软件问题,于是开始查:

  • 设备树里HDMI节点有没有使能?确认了,status = "okay"。
  • I2C控制器有没有使能?确认了,对应的i2c节点也是okay。
  • HDMI驱动有没有加载?用lsmod看了,相关模块都在。
  • 内核log里有没有HDMI相关的报错?有,出现了i2c timeout和edid read failed。

到这里,基本可以确定是EDID读取失败导致HDMI没有输出。但为什么读取失败?可能是I2C通信本身有问题,也可能是EDID芯片没供电,还可能是HPD信号不对。

3.2 第二轮怀疑:HPD和电源

先查HPD。用万用表量了HDMI连接器的HPD引脚,插上显示器后电平有变化,说明显示器那边正常拉高了。再查+5V,也正常。EDID芯片的供电来自HDMI线的+5V,量了一下EEPROM的供电引脚,电压正常。

那问题就缩小到I2C通信本身了。

3.3 第三轮怀疑:I2C总线上其他设备

这路I2C上还挂了一个EEPROM。用i2cdetect扫了一下总线,发现所有地址都扫不到。这就有意思了——如果只是HDMI的EDID读不到,那可能是显示器那边的问题,但现在连板载EEPROM都扫不到,说明整条I2C总线都不工作。

这时候开始怀疑几种可能:

  • I2C控制器配置有问题(时钟频率、引脚复用等)。
  • 上拉电阻没焊或者阻值不对。
  • PCB走线有问题,SDA和SCL交换了。

3.4 用示波器抓波形:真相浮出水面

拿示波器同时抓SDA和SCL两根线。发送读取命令时,可以看到SCL上有正常的时钟脉冲,但SDA上的波形很奇怪——它看起来像是被SCL干扰了,而不是正常的数据跳变。

再仔细看,发现一个关键现象:当主机发送起始条件时,SDA应该在SCL高电平期间拉低。但实际抓到的波形里,是SCL在SDA高电平期间拉低了。这说明什么?说明我们以为的SDA和SCL,在物理上被交换了。

为了确认这个判断,我把探头对调了一下再抓,波形立刻变得正常了。到这一步,问题定位完成:PCB上HDMI DDC的SDA和SCL走线交换了。

4. I2C通信的核心机制与常见硬件陷阱

4.1 I2C的起始、停止和数据有效性

I2C协议看起来简单,但它的时序规则非常严格。核心规则就几条:

  • 起始条件:SCL为高时,SDA从高变低。
  • 停止条件:SCL为高时,SDA从低变高。
  • 数据有效性:SCL为高期间,SDA必须保持稳定;SDA只能在SCL为低时改变。

如果SDA和SCL交换了,会发生什么?主机发出的起始条件会变成“SCL在SDA高时拉低”,从设备根本识别不到起始条件,通信自然无法建立。

4.2 开漏输出与上拉电阻

I2C的SDA和SCL都是开漏(open-drain)结构,这意味着:

  • 设备只能把线拉低,不能主动拉高。
  • 线的高电平靠上拉电阻提供。
  • 总线上任何一个设备拉低,整条线就是低。

这就解释了为什么SDA和SCL交换后,波形会那么奇怪——两个开漏引脚互相干扰,谁也没法正常通信。

上拉电阻的选取也有讲究。阻值太大,上升沿太慢,高速通信时波形会变形;阻值太小,功耗增加,低电平时的灌电流可能超过器件规格。一般3.3V系统用4.7kΩ比较常见,高速模式(400kHz以上)可能会用2.2kΩ甚至更小。

4.3 电平转换的必要性

如果HDMI接收端的DDC电压和SoC的I2C电压不一致,就需要电平转换。比如SoC侧是1.8V,HDMI侧是5V,直接连会烧芯片。常见的方案是用专用的I2C电平转换芯片,或者用MOS管搭一个简单的双向电平转换电路。

这次的设计里,SoC侧和HDMI侧都是3.3V,所以不需要电平转换。但如果你的设计里电压不一致,这一步绝对不能省。

5. 硬件改板与软件规避方案

5.1 硬件改板:飞线还是改PCB

确认SDA和SCL交换后,最直接的解决办法是改板。但改板有几种选择:

  • 飞线:在PCB上把两根线割断,用细导线交叉连接。适合调试阶段,但不适合量产。
  • 改PCB:下一版修正走线。这是最终方案,但需要时间。
  • 软件规避:如果I2C控制器支持引脚交换,可以在软件层面把SDA和SCL的功能对调。

我们当时选择了飞线先验证,确认HDMI能正常输出后,下一版PCB修正了走线。

5.2 软件规避的可行性分析

有些SoC的I2C控制器支持通过寄存器配置来交换SDA和SCL的引脚功能。RK3576的I2C控制器是否支持这个功能,需要查TRM(技术参考手册)。如果支持,可以在设备树或者驱动里配置,不用改硬件。

但即使支持,也不建议在量产项目里这么做。原因很简单:软件规避会增加维护成本,而且一旦有人改了配置或者换了芯片型号,问题会再次出现。硬件问题最好在硬件层面解决。

5.3 验证方法:怎么确认HDMI链路完全正常

改完之后,怎么确认HDMI链路完全正常?我一般会做这几步:

  1. 用i2cdetect扫描总线,确认EEPROM和HDMI DDC地址都能扫到。
  2. 用i2cget读取EDID的前几个字节,确认数据合法(EDID头部固定是00 FF FF FF FF FF FF 00)。
  3. 接上显示器,确认系统能识别到显示器的分辨率和刷新率。
  4. 实际输出画面,确认图像稳定、无闪烁、无偏色。

这四步都过了,才能说HDMI链路没问题。

6. 常见问题速查与避坑经验

6.1 I2C通信失败排查清单

现象可能原因排查方法
所有地址都扫不到SDA/SCL交换、上拉缺失、控制器未使能示波器抓波形、量上拉电压
部分地址能扫到地址冲突、某个设备拉死总线逐个断开设备排查
通信时好时坏上拉阻值不合适、走线过长、干扰调整上拉、缩短走线、加屏蔽
读到的数据全FF设备未供电、设备未就绪量供电、加延时重试

6.2 实操心得:几个容易忽略的细节

  • 上拉电阻的位置:尽量靠近主控端,不要放在总线末端。
  • 走线长度:I2C虽然速率不高,但走线太长也会导致上升沿变缓,400kHz以上尤其明显。
  • 地线回路:HDMI连接器的地要和板子的地良好连接,否则DDC通信会受干扰。
  • HPD时序:有些显示器HPD拉高比较慢,驱动里要加足够的延时再去读EDID。

6.3 关于RK3576的HDMI设计建议

如果你正在用RK3576做HDMI相关设计,这几点值得注意:

  • 仔细核对原理图里HDMI连接器的DDC引脚和SoC I2C引脚的对应关系,SDA对SDA,SCL对SCL。
  • PCB布局时,DDC走线尽量短,远离高速差分线。
  • 如果这路I2C还挂了其他设备,确保地址不冲突。
  • 调试阶段先用i2cdetect确认总线能扫到设备,再调HDMI输出。

7. 从这次踩坑里提炼出的通用方法论

嵌入式调试最怕的就是“不知道问题在哪一层”。软件工程师习惯先查驱动和配置,硬件工程师习惯先量电平和波形。但实际项目里,问题往往跨越软硬件边界。

这次的经验告诉我,遇到“完全没反应”的问题,应该按这个顺序排查:

  1. 电源和时钟:最基本的,先确认供电正常、时钟正常。
  2. 物理层:量关键信号的电平,确认没有短路、断路、交换。
  3. 协议层:用示波器或逻辑分析仪抓波形,确认时序符合协议规范。
  4. 驱动层:确认驱动加载、设备树配置、寄存器配置正确。
  5. 应用层:确认上层软件逻辑没有问题。

这个顺序的核心逻辑是:从底层往上层查,因为底层问题会表现为上层各种奇怪的现象。如果你反过来查,很容易被表象迷惑,浪费大量时间。

另外,I2C虽然简单,但它的开漏结构和严格时序规则决定了它对硬件设计很敏感。SDA和SCL交换、上拉缺失、电平不匹配,这些问题在原理图阶段很容易被忽略,但到了调试阶段就是灾难。所以画原理图和PCB的时候,对I2C总线多看一眼,可能就能省下几天的调试时间。

最后说一个实际体会:调试的时候,示波器比万用表有用得多。万用表只能告诉你“现在是高还是低”,示波器能告诉你“什么时候高、什么时候低、上升沿多快、有没有干扰”。对于I2C这种时序敏感的协议,示波器基本是必备工具。如果手头没有示波器,一个便宜的逻辑分析仪也能凑合用,至少能看到时序关系对不对。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进