最近在整理FPGA图像处理的入门案例,把之前的HDMI视频输入与环路输出实验重新撸了一遍。这个实验是FPGA视频处理链路里非常典型的一个起点:接收一路HDMI信号,在FPGA内部完成解析后,一路送显示,一路原样转发输出(环路输出/Loop Out)。很多初学者做FPGA图像处理时,第一步就是卡在“怎么把HDMI信号接进来”这个问题上,这个实验恰好把这条链路完整打通了。
这个实验适合两种人:一是刚学完FPGA基础语法、想往图像处理方向转的开发者,二是工作中需要做视频采集、视频拼接或者多屏同步输出的工程师。整条链路涉及HDMI接口协议、TMDS编解码、I2C读取EDID、时钟管理、视频时序生成等多个知识点,把这些吃透之后,再去接触MIPI、LVDS甚至光口视频传输都会轻松很多。
我用的是黑金开发板,主控是Xilinx Artix-7系列,板上集成了HDMI输入输出接口。整个调试过程花了大概两天时间,踩了几个比较典型的坑,下面把这些细节完整记录下来,方便大家复现。
1. 实验整体思路与方案选型
1.1 环路输出到底在解决什么问题
先理解一下“环路输出”这个概念。在实际工程场景里,我们经常遇到这种情况:一台视频源设备(比如摄像头、电脑主机)输出的HDMI信号,既要送给后端处理器做分析,又要保持原信号继续传给下一级设备。比如安防监控里,前端摄像头的画面既要在本地NVR存储,又要送到控制中心的大屏实时显示;直播间里,主机画面既要上采集卡做推流,又要本地显示器预览。
环路输出解决的正是这个痛点。它不经过复杂的图像处理,直接把输入信号经过简单的缓冲和重新驱动后输出,保证下游设备拿到的信号和原始输入基本一致。这样做的好处是延迟极低(几乎为零),而且不改变原始视频内容。
在这个实验里,FPGA充当的角色就是一个视频信号的“中转站”:HDMI输入接口进来一路信号,FPGA完成解析和同步,然后分两路处理——一路直接给到本地的HDMI输出接口(环路输出),另一路解出来的视频数据可以继续往后面的图像处理模块里送。这个架构也是很多视频处理板卡的通用设计。
1.2 为什么用FPGA而不是专用芯片
可能有人会问,现在市面上有专门的HDMI分线器、视频处理芯片,为什么还要用FPGA自己做?分线器确实能把一路HDMI变成两路,但它没法在分线的同时对视频数据做任何处理。而FPGA的价值在于“分线的同时还能做点别的事”。
举个例子,同一个HDMI输入,你可以一环路输出到显示器,另一路经过FPGA做边缘检测、颜色空间转换、缩放或叠加OSD后再输出。这种灵活度是固定功能的芯片不具备的。实际上很多专业视频处理设备内部的架构,就是FPGA加外围接口芯片的组合。FPGA负责所有可编程的逻辑处理,外围芯片负责把TMDS差分信号转成FPGA能直接处理的并行数据。
另外一个原因是学习的价值。用FPGA做HDMI输入,需要你去理解视频时序、EDID协议、时钟恢复这些底层机制。用过专用芯片的人很多,但真正能在FPGA里把一条完整视频链路搭起来的并不多。这些底层知识在以后做MIPI、LVDS、DisplayPort接口时会直接复用,属于性价比很高的一次投入。
1.3 硬件平台的资源分配与接口布局
我用的这块黑金开发板,HDMI相关的外设布局大致是这样的:板上有两个HDMI接口,一个用作输入(RX),一个用作输出(TX),均通过板载的HDMI连接器引出。输入接口通过硬件电路连接到FPGA的差分IO引脚,同时板上集成了用于EDID读取的I2C总线和用于热插拔检测(HPD)的引脚。
具体的引脚分配,AX7010这块板子的HDMI输入使用了大约16对差分信号(4对TMDS数据通道,1对TMDS时钟通道,外加I2C和HPD引脚)。FPGA内部逻辑不需要额外的PHY芯片,用普通的HP(High Performance)BANK引脚接HDMI的差分信号即可。这里需要注意,Artix-7的HP BANK支持LVDS、TMDS这类高速差分标准,如果接到HR BANK上,最高速率会受限,设计时有条件要优先选择和HDMI连接器相连的HP BANK。
时钟方面,开发板提供一个系统时钟(通常为50MHz或100MHz),HDMI输入信号的像素时钟需要通过MMCM/PLL来恢复和生成。在这套方案里,我直接用输入HDMI的TMDS时钟作为参考,经过MMCM生成像素时钟和5倍像素时钟,提供给接收端逻辑和输出端逻辑使用。
2. 核心原理与关键技术点拆解
2.1 HDMI接口的“物理层”:TMDS信号与4个通道
HDMI接口在物理层使用TMDS(Transition Minimized Differential Signaling,最小化传输差分信号)技术。一条完整的HDMI链路包含4对差分信号线:3对数据通道(分别传输R、G、B颜色分量)和1对时钟通道。数据通道里除了颜色数据,还混有行同步、场同步、以及辅助数据(音频、控制信息等)。
TMDS编码本质上是一个8b/10b编码过程。输入的8位像素数据先经过XOR或XNOR逻辑,使得输出的10位数据中的跳变次数最小化,这样在接收端就能更容易地从信号边沿恢复出时钟。10位数据中,前8位是编码后的数据,第9位是编码方式的指示位(表示用了XOR还是XNOR),第10位是DC平衡位,用来保持信号的直流分量稳定。
像素时钟在这里决定了传输速率。以1080p@60Hz为例,像素时钟是148.5MHz,那么每对数据通道的实际传输速率就是148.5MHz乘以10,也就是1.485Gbps。3对数据通道加起来,总数据率接近4.5Gbps。这种速率对PCB布线、信号完整性都有要求,不过对我们FPGA内部的逻辑实现来说,关键是确保时钟约束正确,否则时序收敛会出问题。
2.2 EDID:让视频源“知道”你要什么
HDMI输入调试过程中最容易被忽略、也最容易出问题的就是EDID(Extended Display Identification Data,扩展显示标识数据)。简单来说,EDID是显示设备(或接收设备)向视频源设备“自我介绍”的一份数据清单,里面包含了设备支持的分辨率、刷新率、颜色深度、音频格式等信息。
视频源(比如电脑显卡)在通过HDMI输出信号之前,会通过I2C总线读取连接在HDMI接口上的EDID数据。如果读不到有效的EDID,或者EDID里没有声明对应的分辨率,显卡就不会输出信号,甚至直接不亮。很多人在调试HDMI输入时遇到“画面黑屏”或“无视频输入”,排查到最后发现是EDID没有正确配置。
在这个实验里,EDID是通过I2C总线读取的。FPGA作为I2C主机,从板上放置的EEPROM(存储EDID数据)或者直接用逻辑模拟一个EDID的响应来回复视频源。由于FPGA内部有足够的逻辑资源,我直接在代码里用ROM存储了一份EDID数据,并实现了I2C从机逻辑来响应主机的读取请求。这种方法比在板上外挂EEPROM更灵活,想改分辨率参数直接改ROM里的数据或者使用IIC协议从外部读取即可。
这里整理一下EDID中几个关键字段的含义:
| EDID字段 | 位置 | 作用 |
|---|---|---|
| 厂商代码 | 0x08-0x09 | 标识设备制造商,不影响功能 |
| EDID版本 | 0x12 | 1.3或1.4,影响解析方式 |
| 支持的分辨率列表 | 0x36-0x47(详细时序描述符) | 声明支持的分辨率、刷新率、像素时钟 |
| 显示范围限制 | 0x22-0x25(DTD段) | 最大像素时钟、最大行/场频率 |
我在实验中用的EDID声明了1920x1080@60Hz和1280x720@60Hz两种分辨率。把视频源设置成其中任意一种,都能正确出图。如果后续你想支持4K分辨率,需要同时更新EDID中的DTD字段和FPGA内部的时钟配置,二者必须匹配。
2.3 HDMI接收通道的核心模块划分
FPGA内部实现HDMI输入接收,核心要解决两件事:一是把串行的TMDS数据恢复成并行像素数据,二是恢复出与数据对齐的像素时钟。
我采用的方案是直接用Xilinx的SelectIO硬件原语来做数据接收。Artix-7器件中,IDELAYE2和ISERDESE2这两个原语是接收高速串行数据的关键。ISERDESE2工作在DDR模式,一个时钟周期内从每个数据通道采到2位数据。3个数据通道加1个时钟通道,配合一个5倍频的时钟,最终恢复出24位RGB数据(每通道8位)和像素时钟。
在实际工程里,比特级的对齐是这种方案的难点。HDMI发送端对每个数据通道做了字符对齐,接收端需要通过扫描不同的延迟tap值(taps),找到每个线路上数据有效的窗口。常见做法是检查接收到的数据是否包含有效的视频时序信息(比如VBLANK/HSYNC标志),用训练序列的方式逐tap扫描。这个环节比较依赖经验,我在第一次调试时因为没有正确配置IDELAYE2的延迟值,导致恢复出的画面有明显的雪花噪点,后来通过扫描不同tap值才找到稳定的数据窗口。
2.4 环路输出的设计要点与延迟控制
环路输出这部分,比输入接收要简单一些,它本质上是把恢复出的并行RGB数据重新做一次TMDS编码并串行化输出。Xilinx器件里的OSERDESE2和OBUFDS就是干这个活的。OSERDESE2把并行数据转为高速串行数据,OBUFDS把单端信号转为差分信号输出。
环路输出的关键指标是延迟。由于视频数据本身经过了输入端的解串和输出端的串化,会引入一定的延迟(通常为单位像素时钟周期到几个像素时钟周期),但这个延迟远小于帧缓冲(Frame Buffer)方式引入的延迟。要真正实现零延迟环路输出,需要在逻辑上做到同一像素时钟域内直接转发数据,不经过异步FIFO或帧缓存。
我用了一个简单的设计来保证环路输出的稳定性:输入接收模块恢复出的像素时钟作为整个环路输出模块的主时钟,RGB数据和同步信号直接以寄存器打拍的方式(约2-3级流水线)送入发送端。这样整个环路输出的延迟控制在几个像素时钟内,显示器上看不出明显的延迟。如果环路输出只是做信号中继用途,这种级别的延迟完全可以满足需求。
3. 实操过程:从建工程到上板验证
3.1 云课堂实验环境准备与工程创建
我这边用的实验环境是Vivado 2019.1,开发板是黑金AX7010(Artix-7 XC7A35T)。虽然不同版本Vivado在界面细节上略有差别,但核心操作流程一致。如果你用的是更新的Vivado版本,记得留意IP核版本和引脚约束格式的兼容性。
拿到例程后,第一步是检查硬件连接。HDMI输入接口接一台视频源(我用了电脑主机,把分辨率设置在1920x1080@60Hz),HDMI输出接口通过一根HDMI线接到显示器。这里要特别注意:实验板的HDMI接口一般是标准HDMI-A型,直接接标准HDMI线即可,不需要转接头。另外要确认板上HDMI接口供电是否充足,我之前遇到过因为USB供电不足导致HDMI链路不稳定的情况,换独立电源后问题解决。
在Vivado里新建工程后,需要导入源码文件。黑金这个例程的源码结构大致如下:顶层文件(top.v)、HDMI接收模块(hdmi_rx)、HDMI输出模块(hdmi_tx)、I2C控制器模块(i2c_master)、EDID存储模块(edid_rom),以及时钟管理模块(clk_wiz)。这种模块化划分很清晰,建议保持原有层级,方便后续做修改和调试。
3.2 关键代码模块的逐段解析
先看顶层模块的信号连接。顶层把HDMI输入引脚、输出引脚、I2C引脚和时钟引脚都例化出来,连接关系是这样的:HDMI输入的4对差分信号(3对数据+1对时钟)进入hdmi_rx模块,恢复出像素时钟(pclk)和24位RGB数据(rgb_data)。这部分数据同时送给hdmi_tx模块和后续的图像处理模块。
hdmi_rx模块的内部实现使用了IDELAYE2和ISERDESE2,核心代码如下(只保留关键逻辑):
// 数据通道解串,以通道0为例 IDELAYE2 #( .IDELAY_VALUE(0) // 可调的延迟值,通过扫描确定最佳位置 ) idelay_ch0 ( .IDATAIN(hdmi_data_p[0]), // 差分转单端后的数据 .DATAOUT(data_in_delayed), // 其他端口省略 ); ISERDESE2 #( .DATA_WIDTH(10), // 10位,对应TMDS 8b/10b .DATA_RATE("DDR"), .INTERFACE_TYPE("NETWORKING") ) iserdes_ch0 ( .D(data_in_delayed), .CLK(pclk_x5), // 5倍像素时钟 .CLKB(~pclk_x5), .CLKDIV(pclk), // 像素时钟 .O({serdes_data[9:0]}), // 其他端口省略 );ISERDESE2在DDR模式下,一个时钟周期内从数据线上采样两次,再用CLKDIV把串行数据拼接成10位并行数据。这10位数据经过8b/10b解码后,得到8位像素数据。注意3个数据通道的解串逻辑完全一样,区别只是连接到不同的引脚。
hdmi_tx模块的发送端逻辑与接收端对应,使用OSERDESE2完成并行转串行:
OSERDESE2 #( .DATA_WIDTH(10), .DATA_RATE("DDR"), .SERDES_MODE("MASTER") ) oserdes_ch0 ( .D1(rgb_data[7]), .D2(rgb_data[6]), // D3-D10 依次填写10位TMDS编码数据 .OCE(1'b1), .CLK(pclk_x5), .CLKDIV(pclk), .OQ(tmds_data_serial), // 其他端口省略 );输出端需要先把8位RGB数据做一次TMDS编码(编码逻辑可以用查找表或者逻辑门实现),把编码后的10位数据并行送入OSERDESE2。编码逻辑的详细实现,在黑金例程里是一个独立的模块(tmds_encoder),它的输入是8位像素和2位控制信号,输出是10位编码数据。
3.3 约束文件的写法与常见坑点
FPGA工程的约束文件是决定能不能跑起来、跑得稳不稳的关键。对于HDMI这种高速接口,约束的主要工作是指定引脚位置(LOC)和电平标准(IOSTANDARD)。
引脚约束举例,后面这几行就明确了HDMI数据通道0的正端引脚位置和电平标准:
set_property PACKAGE_PIN AE4 [get_ports {hdmi_data_p[0]}] set_property IOSTANDARD TMDS_33 [get_ports {hdmi_data_p[0]}] set_property PACKAGE_PIN AF4 [get_ports {hdmi_data_n[0]}] set_property IOSTANDARD TMDS_33 [get_ports {hdmi_data_n[0]}]这里有个要注意的地方:如果直接使用LVDS_25或LVDS_33电平标准,在连接HDMI接收芯片或外部HDMI连接器时可能因为共模电压不匹配而出现问题。黑金例程中的HDMI接口设计使用的是TMDS_33电平,因为HDMI的差分共模电压是3.3V,TMDS_33这个标准专门为HDMI接口定义。
时钟约束方面,需要在XDC中声明输入时钟和生成的时钟:
create_clock -name hdmi_clk -period 13.468 [get_ports hdmi_clk_p]这个period值对应的正是1080p@60Hz的像素时钟148.5MHz(1秒除以148.5MHz约等于6.734纳秒,但输入的是差分时钟,约束时需要注意换算)。如果你要支持720p@60Hz(74.25MHz),period要改成13.468纳秒;1080p@30Hz,period要改成13.468纳秒的两倍(约26.936纳秒)。如果写错了,Vivado会报时钟约束冲突,而且生成的时序报告也不可信。
3.4 上板验证的步骤和预期现象
把比特流下载到板子上之后,验证分几步走。先看显示器有没有画面。正常情况是,显示器上应该能直接看到视频源输出的画面(环路输出生效),同时这个画面后续还可以送到图像处理模块里做实时处理(比如边沿检测、灰度转换),处理后的结果通过一个开关切换,可以选看原始画面还是处理后的画面。
我把这个实验的验证步骤总结成了一张表,方便你对照检查:
| 验证项 | 操作 | 预期结果 |
|---|---|---|
| EDID读取 | 视频源连接HDMI输入,查看电脑显示设置 | 能识别出显示器型号和最大分辨率 |
| 视频输入解析 | 设置电脑分辨率为1920x1080@60Hz | 显示器输出稳定画面,无闪烁、无色偏 |
| 环路输出 | 观察HDMI输出口的画面 | 与输入画面基本一致,延迟极低 |
| 图像处理通路 | 切换模式到边沿检测 | 画面实时显示检测到的边缘信息 |
如果以上每一项都通过了,说明整条HDMI链路是通的。我在第一次跑通的时候,显示器上出现画面那一刻还是很有成就感的——从电信号到像素再到画面,整条链路在自己搭的逻辑里走通了。
4. 调试过程中的问题记录与解决方案
4.1 画面无输出?先从这几个点查起
“无视频输入”这个现象在HDMI输入实验中出现的频率最高,也是最容易劝退初学者的。我在调试中也遇到过这个问题,排查下来主要有三个原因。
第一是EDID没有正确响应。当显卡检测不到显示设备的EDID时,它会认为HDMI线缆没有连接,所以不输出信号。排查方法是:用I2C逻辑分析仪抓一下FPGA对EDID读取的响应时序,看看ACK位是不是正确拉低,数据位是不是按序返回。如果没有逻辑分析仪,可以在FPGA里加一个计数器,当I2C总线上有访问请求时翻转一个LED,这样可以确认I2C总线是有通信的。
第二是I2C上拉电阻的值不对。HDMI的I2C总线需要上拉电阻连接到3.3V,通常上拉到4.7kΩ或者10kΩ。如果板上没有焊接上拉电阻,或者电阻值太大导致上升沿太慢,显卡在超时时间内读不到数据。我之前在调试时就遇到这个问题,I2C总线上升沿时间超过了1微秒,导致偶尔能读到EDID数据、偶尔读不到,画面时有时无。最后在上拉电阻上并联了一个1kΩ的电阻,问题才彻底解决。
第三是热插拔检测(HPD)信号没有正确拉高。HPD是HDMI接口的一个引脚,视频源通过监测这个引脚的电平来判断接收端是否“在线”。如果FPGA没有把HPD引脚拉高,显卡会认为HDMI线没有接好,进而关断输出。确认方法是:用万用表量一下HPD引脚电压,正常情况应该是高电平(3.3V或5V)。
4.2 画面闪烁、雪花噪点的根源:位对齐问题
如果EDID正常、有画面但画面上有大量雪花噪点或者彩色条纹,问题基本出在数据通道的位对齐上。
在用ISERDESE2做解串时,由于PCB布线长度不同、芯片工艺偏差等因素,每对差分线上的数据到达FPGA引脚的时间会有细微差别(走线长度差异导致skew),必须有独立的延迟调整机制来补偿这种差异。IDELAYE2就是干这个的,它可以把输入数据延迟0到31个tap(每个tap约78皮秒,具体值取决于Vivado的器件型号和电压温度)。
我的调试方法是:写一个小程序,让FPGA自动扫描每个通道的延迟值,对每个延迟值采样一段数据,检查是不是有效的TMDS控制字符(比如视频消隐期的控制信号),找到采样错误率最低的窗口,锁定一个中间值作为最终配置。这个扫描过程在纯逻辑里实现也不复杂,本质上就是一个状态机加一个计数器。黑金的例程里没有自动扫描逻辑,是手动设置了一个固定的延迟值。在实际调试中,我建议至少做一个简单的tap扫描逻辑,这会让你对信号完整性有更直观的认识。
这里分享一下我实际扫描到的数据:在室温(25℃左右)下,3个数据通道的最佳延迟值分别稳定在tap 11、tap 14、tap 10附近。这个值会随温度和电压漂移,但漂移范围不大,设置一个中间值可以保证在不同环境温度下都能正常工作。如果做完扫描后把值固定在某个中间位置,长时间运行应该没有问题。
4.3 图像偏色或颜色分量对调的排查
有时候画面能出,但颜色明显不对,比如红色和蓝色对调、画面偏绿、人脸变成“阿凡达”。这类问题大多是通道映射或数据位序的问题。
在使用IDELAYE2和ISERDESE2时,高位和低位的对应关系很容易搞反。TMDS编码的逻辑是:发送端把8位像素数据编码成10位,其中前8位是有效数据,第9位是编码方式,第10位是DC平衡位。如果解串时把第10位当成了第8位,或者把第3位和第4位弄反了,颜色就完全乱了。
排查方法是:用一条纯色测试信号(比如视频源输出一个纯红色画面),观察FPGA恢复出的数据。正常情况应该看到R通道有数据,G、B通道接近0。如果G通道有数据而R通道没有,说明通道映射错了。如果3个通道在纯色下都有数据,而且数值不在预期的范围内,说明可能是位序反转了。这类问题找到规律后改一下数据映射关系就能解决,不需要动硬件。
4.4 环路输出的常见问题:显示器黑屏与HDCP保护
环路输出还有一个常见问题:显示器显示“HDCP错误”或者直接黑屏。这是HDCP(High-bandwidth Digital Content Protection,高带宽数字内容保护)协议在工作。如果视频源开启了HDCP加密,而你的HDMI输出链路没有被授权解密,显示端就无法正常显示画面。
这个实验里的方案没有实现HDCP解密,所以遇到HDCP加密内容时,显示器会提示不支持。这不属于FPGA代码的问题,而是版权保护机制的限制。实际工程中如果需要支持HDCP,通常的做法是使用专用的HDCP处理芯片,或者在FPGA中集成HDCP密钥,但这需要获得许可和密钥授权。对于学习用途来说,关闭视频源的HDCP输出(比如在显卡驱动设置里关闭HDCP)或使用非加密的视频源(很多测试图卡、开发板的HDMI输出默认不加密)就可以了。
5. 这个实验还能怎么玩:扩展方向建议
5.1 从“能出图”到“图像处理”:加一个实时灰度化模块
把HDMI输入链路跑通之后,最自然的扩展就是在环路输出的基础上,把另外一路视频数据送给一个图像处理模块。最简单的例子是灰度化:把RGB数据按亮度公式转换,Y = 0.299R + 0.587G + 0.114B。
在FPGA里做这个运算,需要注意的是浮点转换为整数运算。通常的做法是把系数乘以256(或1024)变成整数,然后做移位运算,避免消耗DSP资源:
// 灰度转换,使用整数近似 // Y = (77*R + 150*G + 29*B) >> 8 wire [15:0] gray_sum = 77 * rgb_r + 150 * rgb_g + 29 * rgb_b; wire [7:0] gray_data = gray_sum[15:8];这里用的系数77、150、29,对应的就是0.299、0.587、0.113乘以256四舍五入的结果。在DDR3内存里缓存几帧图像,可以做帧差、运动检测;加一个FIFO可以做行缓存,配合卷积运算可以实现Sobel边沿检测。这些算法跑在实时视频流上,效果非常直观。
5.2 往上走:接DDR3实现帧缓存与视频拼接
HDMI输入输出链路只是视频处理的地基。往上走一个台阶,就会遇到“视频数据要写进DDR3再读出来”的场景。比如做视频拼接、OSD叠加、旋转缩放,都免不了要帧缓存。
FPGA控制DDR3实现帧缓存,核心是解决好“写数据”和“读数据”两个通道的带宽问题。1080p@60Hz的数据率大约是3Gbps(24位色深),DDR3的带宽远高于这个值,所以关键在于设计好读写仲裁逻辑,确保两路数据互不干扰。黑金这块板子上的DDR3带宽足够跑两路1080p,做画中画拼接没有问题。
有个实际教训想分享一下:我在第一次把HDMI输入和DDR3读写做在一起时,出现了“写入的数据读出来画面撕裂”的现象。排查了半天,发现问题出在写地址和读地址没有做跨时钟域同步处理,DDR3控制器内部出现了FIFO溢出。后来使用了Xilinx的Video Frame Buffer IP核(VDMA)来解决地址管理问题,画面撕裂才消失。所以建议初学者先用VDMA方案,不要自己写DDR3读写控制器,等理解清楚了再尝试自定义实现。
5.3 横向对比:HDMI接收方案的三种实现路径
做HDMI视频输入,工程上其实有三条路可以走,各有优劣,这里做一个对比:
| 方案 | 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 方案一 | 直接使用FPGA引脚接收TMDS信号 | 成本最低,不需要额外芯片 | 时序约束复杂,调试难度高 | 学习、低成本原型验证 |
| 方案二 | 使用HDMI接收芯片(如ADV7611) | 稳定可靠,支持HDCP | 增加BOM成本,灵活性降低 | 产品化设计 |
| 方案三 | 使用FPGA内置的GTX高速收发器 | 支持最高带宽,可处理4K | 只有高端的FPGA系列才支持 | 高性能视频处理平台 |
我在这次实验中用的是方案一,直接拿引脚做接收。这个方案在熟悉之后,对理解HDMI协议底层机制帮助极大,但也必须承认,它的调试成本和门槛是最高的。如果你是在做产品开发,时间紧任务重,建议直接用方案二,省下的调试时间比一颗芯片的成本值钱得多。
5.4 给新手的几个建议和学习路径
如果你准备跟着这个实验做一遍,有几点建议提前说。第一,先把I2C协议和EDID这两个知识点吃透,它们是HDMI输入最容易踩坑的地方,也是最容易“卡住几天没进度”的地方。第二,准备好一台逻辑分析仪,至少要有16通道以上,采样率要能到200MHz以上,调试I2C和HDMI控制信号会非常有用。第三,准备一个能手动设置分辨率和刷新率的视频源,最好还能关闭HDCP,这会省掉很多不必要的麻烦。
学习路径方面,我的建议是:先做HDMI输出实验(往显示器输出彩条信号),再做HDMI输入实验(接收外部信号),最后再做环路输出实验。输出实验能让你先熟悉TMDS编码和引脚约束,输入实验再把接收链路补上,环路输出等于把两者拼起来。一步一个脚印,走起来会顺很多。
6. 实验总结与个人心得
整个HDMI视频输入与环路输出实验做下来,我最大的感受是:FPGA视频处理这个方向,入门的关键在于把“接口协议”和“数据流控制”两件事想透。HDMI接口本身不复杂,但涉及的知识面非常广,从差分信号、8b/10b编码,到I2C协议、EDID数据,再到时序约束、时钟管理,任何一环出问题,都会导致最终画面出不来。
这个实验也很好地体现了FPGA“灵活”的价值。一个固定功能的HDMI分线器只能做分线,而FPGA方案在分线的同时可以做实时图像处理、多路叠加、格式转换。这也是为什么专业视频处理设备里,FPGA一直是核心芯片的原因——它把“连接”和“计算”融合在了一个可编程的架构里。
最后分享一个实际操作中的小习惯:每次改完代码和约束,上板验证之前,先跑一遍时序报告看有没有时序违规。HDMI相关逻辑跑在几百MHz的时钟下,即使布局布线通过了,也不代表时序真的收敛了。我见过太多人把代码写完后不看时序报告直接上板,结果被花屏和闪屏折磨几天,最后发现是约束写错了“create_clock”的周期。先看时序报告,再上板调试,这个顺序能帮你省掉非常多的时间。
有朋友私信问过,做FPGA视频处理有没有什么好的学习方法,我个人建议你把这次实验作为起点,把链路里的每一个细节吃透,然后去尝试扩展不同的功能模块。当你能熟练地让视频数据在FPGA内部“按你的想法流动”时,图像处理的大门就基本为你敞开了。