☰
HDMI接口原理全解析:TMDS编码、差分信号与音视频传输机制
2026/10/6 4:30:46 网站建设 项目流程

HDMI 这个接口,大多数人每天都在用,但真正把它内部的数据流讲清楚的人并不多。很多人以为 HDMI 就是一根"高清线",插上就有画面有声音,可一旦遇到花屏、闪屏、外接显示器没信号、颜色发灰这类问题,就完全不知道从哪下手。我自己在调试 FPGA 视频通路和嵌入式显示方案的时候,被 HDMI 的时序和色彩格式反复折磨过很多次,后来才慢慢把它的底层逻辑理顺:视频走的是三对差分信号,音频是 PCM 级无压缩数据打包在数据包里靠协议采样还原,而色彩空间上它同时支持 RGB 和 YUV 两种格式。这篇内容就是把这些年踩过的坑和理清的原理一次性讲透,适合做嵌入式视频、FPGA 图像通路、显示器调试以及想搞懂 HDMI 到底怎么工作的朋友参考。不管你是刚接触 TMDS 的新手,还是已经能跑通通路但说不清细节的老手,应该都能从里面找到点有用的东西。

1. 从一根线到三对差分:HDMI 物理层的真实面目

1.1 为什么 HDMI 要用差分信号而不是单端信号

先问一个最基础但很多人答不上来的问题:HDMI 为什么不用一根线传一个信号,非要搞成"三对差分"?这背后其实是高速信号传输的必然选择。HDMI 1.4 的 TMDS 时钟最高跑到 340MHz,每个时钟周期传 10bit,单通道速率就是 3.4Gbps,三通道加起来超过 10Gbps。这个速率下如果用单端信号,参考地一旦有噪声,接收端根本分不清是 0 还是 1。

差分信号的核心优势在于共模抑制。一对差分线传的是幅度相等、极性相反的两个信号,接收端只看两者的差值。外界干扰同时耦合到两根线上,做差之后就被抵消掉了。这就好比两个人抬一根扁担,路上颠簸对两个人是同时作用的,扁担两端的相对高度差基本不变。HDMI 的 TMDS 就是靠这个机制,在几 Gbps 的速率下还能保持眼图张开。

具体到接口定义,标准 HDMI Type-A 有 19 个引脚,其中真正传高速数据的是四对差分线:

差分对引脚作用
TMDS Data07/9传输蓝色分量及控制信号
TMDS Data14/6传输绿色分量及控制信号
TMDS Data21/3传输红色分量及控制信号
TMDS Clock10/12传输像素时钟

注意这里有个容易搞混的点:Data0/1/2 并不是严格对应 R/G/B,而是在不同编码阶段承载不同内容。在视频数据周期里,Data2 承载 R、Data1 承载 G、Data0 承载 B;但在控制周期里,它们传的是 HSYNC、VSYNC、CTL0~3 这些控制位。这个"分时复用"的设计是理解 HDMI 的关键,后面讲编码的时候会再展开。

1.2 三对数据线加一对时钟线的工作节奏

HDMI 的传输节奏由 TMDS Clock 决定。每个时钟周期,三对数据线各传一个 10bit 的 TMDS 字符,合起来就是 30bit 的并行数据。接收端用时钟去采样这三路数据,恢复出原始的像素信息。

这里要强调一个实操中经常被忽略的细节:TMDS Clock 的频率等于像素时钟,而不是比特率。比如 1080p60 的像素时钟是 148.5MHz,那么 TMDS Clock 就是 148.5MHz,而每对数据线的比特率是 148.5MHz × 10 = 1.485Gbps。很多人第一次算的时候会把时钟和比特率搞混,导致配置 PLL 的时候参数算错,画面直接出不来。

我在用 FPGA 做 HDMI 输出的时候,第一步永远是先把像素时钟算准。以 720p60 为例,CEA-861 标准规定的像素时钟是 74.25MHz,那么:

  • TMDS Clock = 74.25MHz
  • 单通道比特率 = 742.5Mbps
  • 三通道总带宽 = 2.2275Gbps

这个带宽要能覆盖视频加音频加控制信息的全部开销。如果算出来带宽不够,就会出现音频断续或者画面撕裂。所以选 FPGA 或者视频处理芯片的时候,一定要先确认它的 TMDS 发送器能跑到多高的速率,别等到板子打回来才发现跑不动。

1.3 差分对的布线为什么这么讲究

差分信号虽然抗干扰强,但对 PCB 布线有硬性要求。我见过太多因为布线不当导致 HDMI 出不了图的案例,这里把几个关键点列出来:

  • 等长匹配:一对差分线内两根线的长度差要控制在 5mil 以内,否则两根线上的信号到达时间不一致,差值信号就会失真。三对数据线之间也要尽量等长,一般要求控制在 50mil 以内。
  • 阻抗控制:HDMI 差分线的特征阻抗要求 100Ω,这需要根据叠层和线宽线距精确计算。阻抗不匹配会导致反射,眼图闭合。
  • 远离干扰源:差分对要远离电源、晶振、开关电源这些噪声源,至少保持 3 倍线宽的间距。
  • 参考地完整:差分线下方要有完整的地平面,不能有跨分割的情况,否则回流路径被打断,共模噪声会急剧上升。

提示:如果你画的板子 HDMI 时好时坏,先别怀疑芯片,拿示波器量一下差分对的眼图,十有八九是布线或者阻抗的问题。

2. TMDS 编码:8bit 到 10bit 到底发生了什么

2.1 为什么非要编码,直接传不行吗

原始像素数据是 8bit 的,为什么 TMDS 要把它编码成 10bit 再传?多出来的 2bit 不是浪费带宽吗?这个问题想通了,TMDS 就理解一半了。

直接传 8bit 数据有两个致命问题。第一是直流平衡:如果连续传很多个 0 或者很多个 1,信号的平均电平就会偏离中心,接收端的耦合电容会积累电荷,导致判决阈值漂移。第二是跳变不足:如果数据长时间不变,接收端就没法从数据里恢复时钟,因为 TMDS 是源同步的,时钟是单独传的,但数据本身也需要足够的跳变来维持信号完整性。

TMDS 编码用 8bit 变 10bit 解决了这两个问题。编码后的 10bit 字符有两个特性:一是直流平衡,任意时刻 1 和 0 的数量差不超过 1;二是跳变丰富,保证接收端有足够的边沿。多出来的 2bit 就是用来做这个"平衡和跳变"的代价,换来的是几 Gbps 下稳定的传输。

2.2 编码算法的两个阶段

TMDS 编码分两个阶段,我尽量用大白话讲清楚。

第一阶段是最小化跳变。对输入的 8bit 数据,根据其中 1 的个数决定是否做异或或者异或非运算。如果 1 的个数大于 4,就用 XNOR;否则用 XOR。这样做的目的是让相邻 bit 之间的跳变尽量少,降低功耗和 EMI。这一步会输出一个 9bit 的中间结果,其中第 9bit 是标志位,告诉接收端用的是 XOR 还是 XNOR。

第二阶段是直流平衡。根据前面统计的 1 的个数和当前的"运行不一致度"(running disparity),决定是否对 9bit 结果取反,最终输出 10bit。运行不一致度是一个累积值,记录到目前为止传出去的 1 比 0 多多少或者少多少。编码器会尽量让这个值在 0 附近摆动,保证长期来看直流是平衡的。

用伪代码表示大概是这样:

def tmds_encode(data8, disparity_in): # 第一阶段:最小化跳变 ones = bin(data8).count('1') if ones > 4 or (ones == 4 and data8 & 1 == 0): q_m = data8 ^ (data8 >> 1) # 简化示意 use_xnor = True else: q_m = data8 ^ (data8 >> 1) use_xnor = False # 第二阶段:直流平衡 # 根据 disparity_in 和 q_m 中 1 的个数决定是否取反 # 最终输出 10bit return encoded_10bit, disparity_out

实际工程中不会自己写这个编码器,FPGA 厂商都会提供 TMDS 编码 IP,或者用 SelectIO 里的硬核。但理解这个流程,在调试的时候能帮你判断问题出在哪一环。

2.3 控制周期和数据周期的切换

前面提到 Data0/1/2 是分时复用的,这里展开讲。TMDS 的传输分两种周期:

  • 控制周期:传 HSYNC、VSYNC、CTL0~CTL3 这些控制信号。控制周期用固定的 10bit 编码,比如 CTL0=0、CTL1=0 对应 10'b1101010100 这样的固定码字。
  • 数据周期:传实际的像素数据,用上面讲的 8bit 到 10bit 编码。

视频的有效像素区域传数据周期,消隐区域传控制周期。接收端通过检测特定的码字序列来判断当前是控制周期还是数据周期,从而正确解析。这个机制保证了视频时序的同步,也是为什么 HDMI 能自动识别分辨率和刷新率的原因。

注意:控制周期的固定码字是 HDMI 协议规定的,不能随便改。如果你自己写发送逻辑,控制周期的编码必须严格按标准来,否则接收端识别不了,直接黑屏。

3. 音频为什么能塞进视频数据流里

3.1 PCM 音频的本质:采样点加时间戳

HDMI 的音频是 PCM 级的,也就是无压缩的原始采样数据。这一点很多人有误解,以为 HDMI 传的音频是压缩过的,其实不是。PCM 就是把模拟声音波形按固定时间间隔采样,每个采样点用固定位数量化,直接传这些采样值。

举个例子,48kHz 采样率、16bit 位深的立体声,每秒产生 48000 × 2 × 16 = 1.536Mbit 的数据。这些数据本身没有压缩,就是原始采样点。HDMI 要做的是把这些采样点打包成数据包,插入到视频数据的消隐期里传输。

为什么能插进去?因为视频的消隐期本来就有大量空闲带宽。以 1080p60 为例,有效像素只占每行的一部分,剩下的消隐区足够塞下音频包。HDMI 协议规定了音频包的结构,包括包头、采样数据、校验信息等。

3.2 音频采样率靠协议规定,不靠时钟线

这是 HDMI 音频最容易被误解的地方。HDMI 没有单独的音频时钟线,音频采样率是靠协议"约定"的。发送端在音频包的信息帧里声明当前用的采样率(比如 48kHz),接收端根据这个声明去还原音频时钟。

具体来说,接收端会用视频的像素时钟作为参考,通过一个分数分频器(比如 N/M 分频)生成音频主时钟,再分频得到采样时钟。这个 N 和 M 的值就是协议里规定的,对应不同的采样率。比如 48kHz 对应的 N 和 M 是特定值,44.1kHz 又是另一组值。

这个设计的好处是不用额外传时钟线,坏处是一旦 N/M 配置错了,音频就会变调或者断续。我在调试的时候遇到过一次音频播放速度不对,查了半天发现是接收端芯片的音频 N/M 参数没配对,改过来就正常了。

采样率典型 N/M 配置说明
32kHz4096/6272常用于广播
44.1kHz6272/8918CD 标准
48kHz6144/6144影视标准
96kHz12288/6144高解析音频
192kHz24576/6144极高解析

提示:如果你做的设备音频和视频不同步,先检查音频 N/M 参数,再看音频包插入的位置对不对。这两个地方出问题的概率最高。

3.3 音频包在数据流里的位置

音频包不是随便插的,它有固定的插入位置。HDMI 协议规定音频包要在数据岛的特定位置传输,数据岛位于视频消隐期。发送端要在每个视频帧的消隐期里安排音频包的传输,保证音频数据的连续性。

这里有个实操经验:音频包的插入频率和视频帧率有关。如果视频帧率是 60Hz,那么每秒有 60 次插入机会。每次插入能传的音频采样数有限,所以高采样率多声道的音频需要更密集的插入或者更大的包。设计的时候要算清楚带宽,别让音频包挤占了控制信息的空间。

4. RGB 和 YUV:两种色彩格式的取舍

4.1 RGB 和 YUV 的本质区别

HDMI 支持 RGB 和 YUV 两种色彩格式,这不是随便定的,背后有深刻的历史和工程原因。

RGB 是最直观的格式,每个像素用红绿蓝三个分量表示,三个分量地位平等。显示器最终显示的就是 RGB,所以 RGB 格式的信号到显示器基本不用转换,延迟最低。

YUV 则是把亮度(Y)和色度(U、V)分开。这个设计的依据是人眼对亮度敏感、对色度不敏感。既然对色度不敏感,就可以少传色度信息,从而节省带宽。YUV420 就是每四个像素共享一组色度,色度数据量只有亮度的四分之一。

格式分量带宽典型场景
RGBR/G/B 各 8bit高显示器、PC
YUV444Y/U/V 各 8bit与 RGB 相同专业视频
YUV422Y 全采样,U/V 减半中视频采集
YUV420Y 全采样,U/V 四分之一低视频压缩、播放

4.2 HDMI 里 RGB 和 YUV 怎么选

HDMI 传输的时候,RGB 和 YUV 都可以用,具体用哪个取决于源端和显示端的协商。一般来说:

  • PC 接显示器:默认用 RGB,因为显示器原生就是 RGB,转换最少。
  • 播放器接电视:常用 YUV,因为视频内容本身多是 YUV 编码的,直接传 YUV 省一次转换。
  • 专业视频设备:根据工作流选,后期制作多用 RGB,广播多用 YUV。

这里有个坑:如果源端输出 YUV,显示端却按 RGB 解析,颜色就会完全错乱,通常表现为偏绿或者偏紫。我在调试的时候就遇到过,源端配置成了 YUV422,接收端默认 RGB,画面整个发绿,改成一致就正常了。

4.3 色彩空间转换的注意事项

RGB 和 YUV 之间的转换不是简单的线性变换,涉及到色彩空间标准(BT.601、BT.709、BT.2020)和量化范围(Limited Range 16-235、Full Range 0-255)。这些参数不匹配,画面就会发灰或者过曝。

  • BT.601:标清时代的标准,现在主要用于 480i/576i。
  • BT.709:高清标准,1080p 及以下用这个。
  • BT.2020:4K/8K 时代的标准,色域更广。

量化范围也很关键。Limited Range 把 16 当黑、235 当白,Full Range 把 0 当黑、255 当白。如果源端用 Limited Range,显示端按 Full Range 解析,黑色就会变成深灰,白色变成浅灰,整个画面发灰。反之则会过曝,暗部细节全丢。

注意:调试 HDMI 颜色问题时,先确认色彩空间标准和量化范围是否匹配,这两个参数不匹配是最常见的颜色异常原因。

5. 时序流程:从像素到差分信号的完整链路

5.1 视频时序的基本参数

HDMI 的视频时序遵循 CEA-861 标准,核心参数包括:

  • HActive:一行有效像素数,比如 1920。
  • HBlank:行消隐像素数,比如 280。
  • HTotal:一行总像素数,HActive + HBlank = 2200。
  • VActive:一帧有效行数,比如 1080。
  • VBlank:帧消隐行数,比如 45。
  • VTotal:一帧总行数,VActive + VBlank = 1125。

像素时钟 = HTotal × VTotal × 帧率。以 1080p60 为例:2200 × 1125 × 60 = 148.5MHz,正好对上前面说的像素时钟。

这些参数不是随便定的,CEA-861 对每种分辨率都有明确规定。自己设计时序的时候,HBlank 和 VBlank 不能太小,否则音频包和控制信息没地方放;也不能太大,否则浪费带宽。

5.2 从像素到 TMDS 字符的转换流程

一个像素从进入 HDMI 发送器到变成差分信号,要经过这些步骤:

  1. 像素数据准备:从帧缓冲或者图像源取出像素数据,可能是 RGB 也可能是 YUV。
  2. 色彩空间转换:如果需要,把 RGB 转成 YUV 或者反过来。
  3. TMDS 编码:每个 8bit 分量编码成 10bit TMDS 字符。
  4. 并串转换:10bit 并行数据转成串行比特流。
  5. 差分驱动:串行比特流变成差分信号输出。

这个流程里,TMDS 编码和并串转换是硬件自动完成的,一般不用管。但色彩空间转换和时序生成需要自己配置,这两块也是最容易出问题的地方。

5.3 消隐期的控制信息和音频包插入

消隐期不是空闲的,要传控制信息和音频包。控制信息包括 HSYNC、VSYNC、CTL0~3,音频包按协议插入。发送端要在正确的时间点切换控制周期和数据周期,接收端才能正确解析。

具体来说,每行的消隐期开始后,先传控制周期,然后传数据岛(包含音频包和其他辅助信息),最后回到控制周期,等待下一行有效像素。这个节奏必须严格按时序来,早一点晚一点都可能导致接收端解析错误。

我在 FPGA 上实现的时候,用一个状态机来管理这个流程:HActive 期间传数据周期,HBlank 期间传控制周期和数据岛。状态机的状态切换由像素计数器和行计数器驱动,确保每个周期都在正确的时间点。

6. 调试 HDMI 时最容易踩的几个坑

6.1 画面出不来:先查时钟再查编码

HDMI 不出图是最常见的问题,排查顺序很重要。我的经验是:

  1. 先量 TMDS Clock:用示波器看时钟有没有输出,频率对不对。时钟没有,后面全白搭。
  2. 再量差分对:看三对数据线有没有信号,眼图张不张开。如果眼图闭合,多半是布线或者阻抗问题。
  3. 查 HPD 信号:HPD(Hot Plug Detect)是接收端告诉发送端"我准备好了"的信号。HPD 不对,发送端不会输出。
  4. 查 DDC 通信:EDID 读取失败也会导致不出图,因为发送端不知道接收端支持什么分辨率。

这个顺序能帮你快速定位问题在哪一层,别一上来就怀疑芯片坏了。

6.2 画面闪烁或者花屏:时序和带宽问题

画面闪烁或者花屏,通常是时序参数不对或者带宽不够。检查这几个地方:

  • 像素时钟是否准确:时钟偏差会导致时序错乱。
  • HBlank/VBlank 是否足够:太小会导致数据挤在一起。
  • TMDS 速率是否超限:超过发送器能力会导致数据丢失。
  • 差分对是否等长:不等长会导致采样错误。

我遇到过一次花屏,查了半天发现是 HBlank 设小了,音频包没地方放,挤占了视频数据。把 HBlank 加大就正常了。

6.3 颜色不对:色彩空间和量化范围

颜色不对分几种情况:

  • 整体偏色:多半是 RGB/YUV 格式不匹配。
  • 发灰或者过曝:量化范围不匹配。
  • 颜色偏差:色彩空间标准不匹配。

排查的时候,先确认源端和显示端的格式声明是否一致,再看量化范围和色彩空间标准。这三个参数任意一个不匹配,颜色都会出问题。

6.4 音频异常:N/M 参数和包插入

音频异常表现为无声、断续、变调。排查顺序:

  1. 查 N/M 参数:采样率对应的 N/M 值是否正确。
  2. 查音频包插入位置:是否在数据岛的指定位置。
  3. 查音频格式声明:信息帧里的采样率、位深、声道数是否正确。
  4. 查带宽:音频包是否挤占了视频数据。

音频问题比视频问题更难查,因为涉及的因素更多。我的建议是先用标准测试设备验证,确认是源端问题还是接收端问题,再针对性排查。

7. 一些实战中的经验补充

7.1 用 Python 辅助验证色彩数据

调试色彩问题的时候,我经常用 Python 读图片的 RGB 值来对照。比如用 PIL 库读一张纯色图片,看每个像素的 RGB 值,再和 HDMI 输出的实际颜色对比,能快速判断是色彩空间转换错了还是量化范围错了。

from PIL import Image img = Image.open('test.png') pixel = img.getpixel((100, 100)) print(f"RGB: {pixel}") # 如果 HDMI 输出偏绿,对比源图和实际输出的 RGB 差异

这个方法虽然简单,但在定位颜色问题时特别有效。源图的 RGB 值是已知的,HDMI 输出的颜色如果和源图对不上,就能确定是转换环节出了问题。

7.2 FPGA 方案里的 VDMA 和 HDMI 配合

用 MicroBlaze 或者 Zynq 做 HDMI 输出的时候,VDMA(Video Direct Memory Access)是常用的数据搬运模块。VDMA 从 DDR 里读帧数据,通过 AXI Stream 送给 HDMI 发送 IP。这个链路里最容易出问题的是:

  • VDMA 配置:帧缓存地址、 stride、帧数要配对。
  • AXI Stream 位宽:要和 HDMI IP 的输入位宽匹配。
  • 时钟域:VDMA 和 HDMI IP 可能在不同时钟域,要加异步 FIFO。

我在 Zynq 上跑 1080p 输出的时候,VDMA 的 stride 设错了,画面出现斜条纹。改成正确的行字节数就正常了。这个参数很容易被忽略,但错了画面一定不对。

7.3 多路 HDMI 输入输出的芯片选型

做多路 HDMI 切换的时候,芯片选型要考虑:

  • 输入路数:4 路输入 1 路输出的芯片,要确认是否支持无缝切换。
  • 分辨率支持:最高支持到 4K 还是 1080p。
  • HDCP 支持:如果涉及受保护内容,要确认 HDCP 版本。
  • 控制接口:I2C 还是 SPI,和主控是否匹配。

选型的时候别只看参数表,一定要拿开发板实测。有些芯片参数写得好,实际跑起来发热严重或者切换有黑屏,这些只有实测才知道。

7.4 笔记本外接 HDMI 没画面的常见原因

笔记本外接显示器没画面,不一定是线的问题。常见原因包括:

  • 输出模式没切换:有些笔记本需要手动切换显示模式。
  • 分辨率不匹配:笔记本输出的分辨率显示器不支持。
  • 线材质量:劣质线材跑不了高带宽。
  • 接口松动:HDMI 接口容易松动,重新插拔试试。

排查的时候,先用另一根线或者另一台显示器交叉验证,能快速定位是笔记本、线材还是显示器的问题。

HDMI 这套协议看起来复杂,但拆开来看就是物理层、编码层、协议层三块。物理层保证信号能传过去,编码层保证数据能正确恢复,协议层保证视频音频控制信息能协调工作。把这三层理顺了,再遇到问题就知道从哪查起。我自己从最开始的一头雾水到现在能独立调试通路,靠的就是反复踩坑加对照标准文档。希望这些经验能帮你少走点弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询