
干显示类开发的人多少都遇到过这种“玄学”问题同一个板卡同一份固件插到显示器A一切正常插到显示器B画面发灰、偏色换到电视C干脆一切分辨率就闪雪花。第一反应多半是查TMDS信号质量、查PCB走线、查电源纹波折腾一圈下来示波器波形干净得很问题却原封不动。其实这种“换个Sink就出状况”的怪现象十有八九出在HDMI链路里那批不起眼的DataIsland Packet上尤其是AVI InfoFrame和通用控制包General Control Packet这两个包。这篇文章就把我在调试HDMI时踩过的坑和总结的细节一次说清楚做驱动、做硬件、搞嵌入式音视频的应该都能用得上。1. DataIsland在HDMI链路里到底是个什么角色1.1 TMDS上那三种“时间片”HDMI的物理层用的是TMDS编码像素数据、音频辅助数据、控制信号都在同一对差分线上跑靠时间片来划分。一帧画面在链路上被分成三种周期Video Data Period视频数据期传真正的像素Data Island Period数据岛期传包结构的辅助数据Control Period控制期传行场同步和CTL控制信号。简单理解就是一条高速公路上大部分时间在跑货柜车视频像素隔一段时间穿插几辆快递车数据包还有交警巡逻车控制信号维持秩序。这里面最容易被忽略的就是Data Island Period。音频、信息帧、内容保护相关的认证信息全都塞在数据岛期里。它不像视频像素那么直观出了问题画面不一定立刻花屏更多是颜色不对、黑屏、偶发噪点这类“软故障”所以排查起来特别费劲。1.2 数据岛包的基本骨架每个DataIsland Packet的长度是32字节4字节HeaderHB0~HB3后面接4个Subpacket每个7字节。Header里的HB0是包类型HB1是版本HB2是数据长度HB3是保留字段。实际抓包时通过HB0就能判断这个包是干嘛的。常见的包类型如下表HB0包类型用途0x00Null Packet填充空包保证链路速率0x01AVI InfoFrame视频属性描述本文主角之一0x02Audio InfoFrame音频格式描述0x03SPD InfoFrame源设备产品描述0x04MPEG Source InfoFrameMPEG源信息0x06Dynamic Range and Mastering InfoFrameHDR静态元数据0x0FGeneral Control Packet链路通用控制本文主角之二0x13Gamut Metadata Packet色域元数据很多固件工程师对AVI InfoFrame和Audio InfoFrame比较熟GC包却常常不当回事甚至有些自研固件压根不实现GC包。这个态度早晚要出事后面的踩坑复盘会细说。2. AVI InfoFrame决定“画面对不对”的第一张牌2.1 字节级看AVI到底哪些字段在影响画面AVI InfoFrame的头部是固定的HB00x01HB10x01HB20x0D表示后面有13个数据字节HB3保留。第一个数据字节PB0是校验和这个校验和覆盖的范围是从HB0到最后一个数据字节要求所有字节求和后模256等于0。接下来值得逐个盯的字段PB1的低4位R0-R3RGB量化范围。0由Sink决定1有限范围16-2352全范围0-255。这个字段出了错画面直接发灰或过曝。PB2的C0-C1颜色编码格式0RGB1YCbCr 4:2:22YCbCr 4:4:4。颜色格式填错画面会出现明显色偏或显示不出来。PB3ITC标志和YCbCr量化范围。ITC表示内容是否来自IT/PC设备影响Sink的画质增强处理。PB4VICVideo Identification Code视频识别码。它告诉Sink当前是哪种分辨率、刷新率比如VIC16就是1080p60VIC97就是2160p24。扩展字段里的CD位色深标识。24bit时CD030bit时CD136bit时CD248bit时CD3。色深字段和实际输出不符时画面色彩会出现断层或色阶。2.2 RGB量化范围发灰、发白的头号原因先说一个非常常见的现象PC主机接电视桌面字体看着灰蒙蒙黑色不黑白色又过曝。这个问题绝大多数情况不是面板问题而是RGB Quantization Range匹配错了。PC显卡默认输出的通常是RGB全范围0-255但电视的HDMI输入普遍按照限制范围16-235来解释信号。如果AVI InfoFrame里R0-R3字段写的是0默认电视就会自己猜而很多电视默认猜成有限范围。全范围数据被当成有限范围处理后0-15的暗部被裁掉235以上的亮部被压缩画面观感就是对比度崩了、灰蒙蒙一片。反过来也常见设备输出的是16-235的有限范围数据AVI里却写成全范围。Sink按照0-255解释16这个本该是纯黑的电平被当成灰阶处理黑色自然就发灰了。所以调试时别偷懒R0-R3一定要根据实际输出数据范围显式配置不要留0让Sink去猜。配合示波器或分析仪确认TX端输出的CSC配置做成一个固定检查项。2.3 VIC与色深匹配错了要么不显示要么颜色断层VIC是AVI里最核心的一个字段Sink靠它和实际的像素时序一起判断当前分辨率。定制分辨率时VIC通常填0此时Sink会完全依靠Timing来识别很多老电视兼容性就比较差。常用的VIC码我列几个VIC分辨率与时序1640x480 4:3 60Hz41280x720 16:9 60Hz161920x1080 16:9 60Hz311920x1080 16:9 50Hz321920x1080 16:9 24Hz973840x2160 16:9 24Hz993840x2160 16:9 30Hz1013840x2160 16:9 60HzVIC和实际时序不匹配时有些Sink会显示错误分辨率的EDID信息有些直接黑屏。我遇到过一台投影仪Source侧像素时钟和行场参数都按1080p60配置VIC却写的4720p60结果投影仪识别成720p画面被裁切。查了半小时寄存器最后发现固件里VIC是从另一个分辨率结构体里取的默认值。色深CD字段也需要注意。如果Source的实际输出是8bitCD就写0如果输出10bitCD写1。写错之后很多显示器显示出来的颜色断层明显尤其是暗部渐变场景一道一道的色带。这不是示波器能看出来的问题必须抓包核对。3. General Control Packet最容易被当成“可有可无”的包3.1 GC包到底是干嘛的GC包General Control Packet实现的是链路级别的通用控制最常见也最重要的是AVMUTE功能。AVMUTE在HDMI里的角色就是让Sink在源端切换参数时先“闭眼闭嘴”切完再“睁眼”避免切换瞬间出现雪花噪点、爆音。抓包时常见的GC包格式是HB00x0FHB10x00HB20x00HB30x00第一个数据字节是校验和第二个数据字节是控制字。控制字里不同位代表不同命令比如Set AvMute、Clear AvMute、Set System Time等。很多工程师觉得这个包无所谓反正视频数据照发不误。实际上在切换分辨率、帧率、色彩空间、HDR开关时如果Source不先发Set AvMute再切换、切换完成后再发Clear AvMuteSink在一瞬间会尝试用旧参数解码新数据表现就是花屏、雪花、撕片。强制关机再开机可能没问题但热切换时必现。HDMI 2.1时代QMSQuick Media Switching这类快速切换功能本质上也是靠GC包配合实现的只会更重要不会更次要。3.2 校验和覆盖范围不一样GC包最容易翻车的地方GC包最大的坑在它的校验和算法和AVI InfoFrame完全不同。AVI是整包校验GC包只对头部前两个字节做校验后面的payload不参与。具体来说GC包的校验和只需要满足 HB0 HB1 checksum 0 (mod 256)。HB0是0x0FHB1是0x00所以checksum固定是0xF1。这就是为什么抓包软件里看到的GC包经常长这样0F 00 00 00 F1 00。后面那个00是控制字如果是不敏感控制字则填0。很多开发第一次写GC包时按AVI那套整包求和取补的做法去算算出来的校验和全是乱的。Sink识别不了AVMUTE命令发不出去切换分辨率时照样花屏雪花。而且这种错误特别隐蔽因为GC包没有周期性重传的硬性要求抓包时不仔细看根本注意不到。3.3 什么时候必须发GC包根据我实际项目的经验这几个时机必须稳准狠地发GC包从1080p切到2160p或者反过来分辨率和像素时钟变化前先Set AvMute。帧率切换比如60Hz切24Hz切换前Set AvMute。色彩空间切换RGB转YCbCr、4:4:4转4:2:0切换前Set AvMute。HDR开关切换SDR/HDR模式切换时先Set AvMute等HDR元数据包也发出来之后再Clear。HDCP重新认证期间建议配合AVMUTE避免画面出现绿色噪点。Clear AvMute的时机也有讲究。切完分辨率后Source需要等Sink的DDC握手完成、HDCP认证结束再Clear。Clear发早了Sink还没准备好画面还是会闪Clear发晚了用户会看到一段无理由的黑屏。这个时机最稳的做法是轮询HDCP状态等它变成已认证再延迟几个vsync周期Clear。4. 校验和与时序边界DataIsland最容易出幺蛾子的两个位置4.1 校验和算法的标准写法与自检AVI InfoFrame这类全包校验的包校验和算法很简单但特别容易在改字段后忘了重算。写固件时建议抽个公共函数别每次手算。uint8_t calc_infoframe_checksum(const uint8_t *packet, uint8_t len) { uint8_t sum 0; for (int i 0; i len; i) { sum packet[i]; } return (uint8_t)(0x100 - sum); // 加上这个值后整包求和 mod 256 0 }这个函数接收的packet从HB0开始len是包总长度调用时把校验和字节本身空出来或者先填0函数返回的字节就是要填进校验和位置的值。改完任何一个字段都要重新调用一次。GC包的校验和单独写uint8_t calc_gcp_checksum(void) { // 只要 0x0F 0x00 checksum mod 256 0 return (uint8_t)(0x100 - (0x0F 0x00)); // 0xF1 }4.2 DataIsland的发送时机不是想发就发第二个容易翻车的点是发送时机。DataIsland只能在消隐期发还要遵循前导符Preamble和Guard Band的规定。如果前导符没做好Sink根本找不到包边界整包丢弃。示波器上看信号挺正常但解析出来的内容全是乱的。AVI InfoFrame还有一个特殊要求不能只发一次最好在每一帧或者每两帧的视频消隐期内周期性重发。为什么要重发因为Sink从省电模式唤醒、热插拔恢复、HDCP重握手之后内部状态可能被重置或者一开始就没收到你开机时发的那包。如果固件只在初始化时发一次就会遇到“开机正常、睡眠唤醒后颜色不对”这种诡异故障。我调过的一个案子就是机器休眠唤醒后电视画面颜色偏淡要重新拔插HDMI线才恢复。抓包发现唤醒后AVI InfoFrame没有重新发送电视的量化范围状态丢失回到了默认的有限范围。固件加上“每次唤醒后重发AVI”后问题解决。另外在HDMI 2.1的FRL模式下DataIsland的结构和TMDS模式有差异老固件直接复用TMDS模式下的组包逻辑是会出问题的。做2.1项目时AVI和GC的打包发送代码一定要按FRL的要求重新核对不能默认一样。5. 一次真实排障复盘发灰和闪屏居然都是DataIsland惹的祸5.1 现象一显示器B画面发灰黑位不黑某ARM主控方案外接HDMI桥接芯片输出到一台4K显示器画面灰蒙蒙黑色不黑。示波器量TMDS差分信号波形干净换HDMI线问题依旧换另一台显示器一切正常。排查链路是这样的先怀疑主控CSC配置确认输出RGB全范围0-255再查桥接芯片寄存器确认色彩格式、色深设置没被初始化代码覆盖接着怀疑EDID里没声明量化范围支持但EDID只是能力声明不直接决定当前包内容。最后借了一台HDMI协议分析仪抓包发现AVI InfoFrame里R0-R3字段是0默认。显示器收到默认值后按有限范围16-235处理全范围的0-255数据被错误映射暗部被裁掉、亮部被压扁整体就是发灰。把R0-R3改成2全范围同时重新计算校验和问题消失。这里有个细节只改寄存器数值不重算校验和显示器照样丢弃AVI包颜色还是不对。很多桥接芯片的寄存器里校验和是独立的一个字节需要软件同步更新。5.2 现象二电视C切分辨率瞬间雪花闪屏另一个案子是同一平台接大屏电视从4K切到1080p时电视屏幕瞬间闪雪花大约0.5秒后才恢复正常。视音频信号本身没有断续HDMI握手日志显示DDC和HDCP都正常。用分析仪在切分辨率瞬间抓包发现Source在切换过程中压根没有发送GC包。TV在等待源端明确通知“我要切参数了”结果什么都没等到只能拿旧参数去解新数据解出来全是噪点。修复方式是在切分辨率前发一次Set AvMute控制字置位等新时序的DDC握手和HDCP认证完成后再发Clear AvMute。加完之后切换过程变成瞬间黑场几乎无感再没出现过雪花。这个案子告诉我们GC包平时不发可能没什么问题但在关键切换时刻不发Sink的行为就不可控了。建议把“切换前Set、切换后Clear”做成HDMI驱动的标准流程而不是等出了问题再加。6. 手头工具与日常习惯把DataIsland调试变“可复现”6.1 工具清单与使用逻辑DataIsland相关的调试最好用的当然是HDMI协议分析仪。常见的牌子有Teledyne LeCroy、Quantum Data、Unigraf。分析仪能直接解码AVI、Audio、GC、SPD等包还能按包类型触发抓取比如设置在AVI错误时停止抓包。屏幕上出现异常时回看触发时刻前后的报文定位很快。没有分析仪的团队最低成本方案是逻辑分析仪或支持HDMI输入的示波器抓TMDS后做8b/10b解码再自己解析Data Island。但这个方法工作量大而且解析容易出错只建议在没有选择时用。DDC层面的EDID读取可以用I2C调试工具单独抓判断Source主控是否读到了正确的EDID以及读EDID的时序是否符合规范。很多分辨率识别问题其实在EDID阶段就已经埋下了。6.2 发版前必跑的抓包场景我在团队里推了一个“HDMI发版四测”的清单每次固件改动涉及HDMI相关模块都要跑一遍测试场景操作重点观察包热插拔播放中拔插HDMI线AVI是否重发GC是否跟着链路事件走分辨率切换在Sink支持的多组分辨率间切换切换前是否有Set AvMute切换后是否有Clear AvMuteHDR开关播放HDR片源并关闭HDR是否发出DRI/HDR元数据包AVI色深字段是否匹配休眠唤醒主机待机后唤醒唤醒后AVI和GC是否正确重发这四个场景做下来基本能把DataIsland相关的问题覆盖七八成。剩下的偶发问题多半是时序边界或对端兼容性需要具体抓包分析。6.3 一个容易被忽视的寄存器陷阱很多HDMI桥接芯片的I2C寄存器里都有“自动生成AVI InfoFrame”的功能。开发者有时候会手动覆盖某个字段但寄存器写入顺序不对或者使能位被后续代码重置导致芯片实际发出的包是旧的、校验和没更新过的。建议调试时先在分析仪上确认实际发出的包内容再回头查代码里寄存器写对了没有不要想当然认为写了就生效。检查寄存器也可以加进量产测试的工装流程里利用I2C脚本批量读取AVI相关寄存器对照黄金样机的寄存器快照提前发现写错配置的板子。做HDMI调试这几年我最大的体会是很多看起来是“玄学”的问题一旦养成“先看DataIsland再看信号质量”的习惯都会变得有迹可循。AVI的量化范围、VIC、色深GC包的AVMUTE时机校验和覆盖范围这几个细节建议直接做进固件的自检清单和发版测试用例里。每次发版前跑一轮抓包回归比出了问题再拿着示波器到处捅要省太多时间。