
做视频相关实验之前我一直觉得HDMI这个接口特别“黑盒”一头插电脑另一头接显示器画面就出来了中间发生了啥基本靠猜。直到我拿到黑金的FPGA开发板打算自己动手做一个“HDMI视频输入与环路输出”的实验把信号从输入口收进来、再从输出口送出去才真正把HDMI这条链路搞明白。这个实验最妙的地方在于它把一个看似简单的“视频直通”拆成了信号检测、EDID握手、TMDS解码、并行RGB恢复、再编码发送一整套流程每一步都在FPGA内部看得见摸得着。这篇文章我就结合自己的实操过程把电路选型、时序参数、代码结构、常见坑位一次性梳理清楚希望能帮到正在入门FPGA图像处理的朋友。1. 这个实验到底在做什么——环路输出的工程意义1.1 什么是HDMI环路输出环路输出Loop Out这个概念搞监控项目或视频矩阵的人应该不陌生。它的本质就一句话把一路HDMI输入信号同时送到两个出口一个接到本地显示器另一个接到下一级设备或者处理器形成“信号复制”。最典型的场景是监控机房一台NVR的HDMI输出需要同时给大屏和值班显示器各送一份又不想额外配一台分配器那就直接用带环出功能的设备。在FPGA实验里环路输出还有一个更“教学化”的玩法输入信号进FPGA后分成两路一路原封不动送出去另一路先存进FIFO或BRAM做简单处理再送出去。这样一来你既得到了一条可用的视频通路又能顺手做抓帧、叠加OSD、色彩转换等图像处理实验。黑金这套教程把“环路输出”作为FPGA视频处理的基础课本质上是想让你先跑通“视频进来、视频出去”的完整链路再谈其他花活。1.2 视频输入链路的完整数据流HDMI输入环路输出实验数据流大概是这样的HDMI信号源电脑、机顶盒、树莓派都行通过HDMI线送到开发板输入接口板上的接收电路把TMDS差分信号转成FPGA能处理的并行像素数据FPGA内部一边把这份数据重新发送到输出接口一边还可以把它送给调试模块做参数分析。整个链路里信号形态会经过三次变化物理层HDMI线上的4对TMDS差分信号3对数据1对时钟这是高速串行差分信号。并行层接收端解串后得到的24位RGB数据或36位深色模式、行同步HS、场同步VS、数据使能DE以及恢复出来的像素时钟。再编码层发送端把这些并行数据再做一次TMDS编码变成差分信号从输出口送出去。这三层转换正是FPGA视频实验的精华所在。你不需要像做PCIE那样去关心复杂的协议栈只需盯着像素时钟和同步信号就能直观地理解一块屏幕是怎么被“点亮”的。1.3 为什么用FPGA做环路输出而不是买一颗HDMI分配芯片市面上有专门的HDMI分配芯片比如TI的TMDS442接上去能直接完成一路输入到两路输出的信号分配做产品芯片选型时这确实是省事方案。但FPGA实验的价值在于专用芯片是个黑盒你没法在中间插入任何自己的处理逻辑。而FPGA方案里信号在经过FPGA时是“打开”的——你可以抓取任意一帧画面、修改颜色分量、叠加字符、做边缘检测这些全是后续图像处理项目的基础。所以如果只想要一个能用分配器别折腾FPGA。但如果你想搞懂HDMI时序、想做图像算法、想在任何一款显示器上调试你自己的视频通路那这个实验就是最好的练手起点。2. 动手之前必须搞懂的几件事HDMI基础与硬件选型2.1 HDMI接口的物理层TMDS差分信号与四对线HDMI 1.4及以下版本物理层核心就是4对差分线其中3对是数据通道通道0、1、21对是像素时钟。每一对差分线都用TMDS编码方式传输简单理解就是发送端把一个8位像素数据经过8b/10b编码变成10位串行流再以差分形式打出去接收端对差分信号做解串恢复出数据。这里要重点理解“像素时钟”这个家伙。在HDMI链路里像素时钟决定了一秒钟能传多少个像素。以1080p60为例一帧1920×1080的画面加上行消隐和场消隐实际总像素约2200×1125乘起来再乘60帧就得到了148.5MHz的像素时钟。所有数据通道的比特率都是这个时钟的10倍因为8b/10b也就是说每对数据线实际跑了1.485Gbps。这个带宽概念在后面调试时非常有用。开发板上HDMI连接器的引脚定义是有标准的但我不建议直接对着连接器引脚写代码实际开发中一般会经过电平转换或保护电路再进FPGA。你需要关注的只是FPGA侧的管脚分配哪些引脚是差分时钟对哪些引脚是三个差分数据对以及与之配套的I2C引脚DDC通道和热插拔检测引脚HPD。2.2 两种常见的硬件方案专用收发芯片 vs FPGA直采做HDMI输入实验硬件上通常有两种选择很多初学者在这上面犹豫很久我把它们的差异直接摆出来。方案A是使用外部HDMI接收芯片和发送芯片。接收侧比如ADV7611、Silicon Image的SiI9022它们能把HDMI输入的TMDS信号完整解码成并行RGB行场同步像素时钟输出给FPGA发送侧比如ADV7513、SIL9134接收FPGA的并行数据再编码成TMDS输出。这个方案的优点是信号质量好、调试难度低缺点是成本高、可玩性降低——因为HDMI解码的活儿全被芯片干了FPGA只拿到并行数据少了挑战性。方案B是FPGA直接从引脚接收TMDS信号。现在很多FPGA的高速BANK引脚支持LVDS、TMDS这样的电平标准允许你把HDMI的差分数据对直接接进FPGA通过内部的ISERDES把高速串行信号转成并行数据再自己写逻辑做8b/10b解码、通道对齐、像素组装。这个方案难度直线上升但学习价值也直线上升——你会把HDMI彻底吃透。黑金这套基础实验使用的实际上是方案A的思路板上集成了HDMI接收发送芯片FPGA只需要处理并行视频数据不涉及高速串行解码。这在入门阶段是合理的因为实验目的是搞懂视频时序和环路逻辑而不是先被高速信号处理劝退。2.3 EDID、HPD与DDC通道HDMI握手的关键HDMI链路里最容易被忽略但又最影响成败的是“握手”过程。信号源电脑怎么知道该输出多大的分辨率答案是通过DDC通道读取显示设备的EDID信息。DDC本质是一条I2C总线SCL和SDA在HDMI连接器上有专门引脚EDID存在接收端开发板这一侧的一颗EEPROM或FPGA内部的ROM里I2C地址是0x50。电脑上电后会去读这个地址里的128字节数据里面写清楚了支持的分辨率、像素时钟、色深等信息。那HPD热插拔检测又是干嘛的HDMI连接器的第19脚就是HPD它是一个带方向性的检测信号源端通过一个上拉电阻检测接收端是否连接。接收端上电后会把HPD拉高源端检测到高电平就知道有设备插上了这才开始读EDID、输出视频。如果HPD没拉起来电脑会判断“没接显示器”整个链路根本不会启动。这个握手机制是排查“无视频输入”问题的第一个入口。我在实验里遇到过几次显示器不亮十有八九是HPD电平不对或者EDID读取失败后面调试章节我会细讲。2.4 关于HDMI供电电流的一个小提醒很多人在硬件检查时忽略的是HDMI连接器的5V引脚。这个5V是源端提供给接收端使用的标准要求在50mA左右实际因设备而异主要用于给接收端的DDC电路和HPD上拉供电。有些开发板为了省事5V引脚不给电或者电流能力不够就会导致HPD一直为低、EDID读不出来。如果遇到信号源识别不到显示器的情况可以用万用表量一下HDMI座的5V引脚和HPD引脚电压这是成本最低的诊断手段。3. 基础实验从HDMI输入到环路输出的完整实现3.1 实验平台与整体框图这个实验在硬件上需要一块带HDMI输入输出接口的中高端FPGA开发板我用的是黑金的AN108系列逻辑资源足够、自带HDMI收发芯片和外设接口。如果手头是其他品牌的板子也没关系只要满足“有HDMI输入接口”“有HDMI输出接口”“FPGA逻辑资源够跑一个小规模视频通路”这几个条件就行。工程整体可以按模块拆解成四块输入侧接口模块接收HDMI解码芯片输出的24位RGB、行场同步、数据使能DE和像素时钟。核心处理模块这部分可以设计成“直通”就是把输入数据原封不动打到输出侧也可以加一个行缓存做简单的像素级处理。输出侧接口模块把并行RGB和同步信号打包发送给HDMI编码芯片。调试辅助模块用ILA逻辑分析仪抓内部信号或者用板载按键控制环出模式的开关。第一步不是写代码而是先确认开发板原理图上HDMI输入输出芯片的引脚连接记录下芯片型号和关键引脚编号。这一步一定要做得仔细因为我见过不少人写代码前不看原理图凭感觉猜引脚最后上板一脸懵。3.2 上电后的信号检测与复位时序整个实验的第一步是等接收芯片完成初始化和信号锁定。以常见HDMI接收芯片为例上电后它会有一段时间的内部初始化这个时候输出给FPGA的数据还不稳定。因此工程里至少要有一个简单的复位状态机上电后延迟一段时间比如100ms等芯片初始化完成再释放FPGA内部复位。接着要检测HDMI解调芯片输出的像素时钟是否有效。很多接收芯片会把TMDS时钟恢复出来以像素时钟的形式直接给FPGA。如果这个时钟一直是低电平说明HDMI信号源根本没有进入工作状态这时候优先检查EDID和HPD而不是去查代码逻辑。我在调试时习惯先用ILA抓一下“像素时钟是否翻转”和“DE信号是否有高电平脉冲”这能把问题范围快速缩小到“信号有没有进来”和“数据解析对不对”两个层面。3.3 从TMDS解码到并行视频时序核心信号的含义当接收芯片锁定信号后FPGA拿到的是一组并行总线。最核心的四个信号是像素时钟PIX_CLK所有信号的基准数据都在这个时钟的上升沿变化。数据使能DE高电平表示当前像素处于有效显示区低电平处于消隐区这期间的数据无效通常是0或黑电平。行同步HS每行开始时的同步脉冲用来标定一行数据的起点。场同步VS每帧开始时的同步脉冲用来标定一帧画面的起点。理解这四者的关系就能看懂一幅画面在FPGA内部是如何流动的。以1080p60为例每一行总共2200个像素时钟周期其中1920个是高DE的有效像素280个是消隐每一帧总共有1125行其中1080行是有效视频行45行是场消隐。HS和VS就藏在这些消隐区里面它们和DE配合描述了一帧画面的完整骨架。很多入门朋友第一次看到DE拉低的那段数据会慌以为是自己代码写错了。其实那是正常的消隐区拉低正是DE的作用。环路输出的时候你甚至不需要知道消隐区里具体是什么值直接把并行数据原样搬过去就行显示器的时序控制器自己会处理。3.4 环路输出的两种实现无缓存直通与带缓存转发环路输出本身有两种实现方式建议都做一遍体验完全不一样。第一种是无缓存直通逻辑最简单。输入侧的RGB、HS、VS、DE直接用寄存器打一拍同步到输出端送给HDMI发送芯片。不需要FIFO不需要跨时钟域代码量不到二十行。这种方式的优点是零延迟、资源占用几乎为零缺点是没法做任何图像处理而且对输入输出芯片的时钟要求是同一个像素时钟——如果输入像素时钟是148.5MHz输出也必须跑148.5MHz。第二种是带缓存转发稍微复杂但更有实用价值。做法是输入端按输入像素时钟把有效像素写入FIFO或BRAM输出端按输出像素时钟把数据读出来。这样做的最大好处是解耦了输入输出的时钟域允许输入是1080p、输出是720p中间再做缩放。环路实验只需要做到“写入一行、读出一行”就行了输入DE有效时写FIFO输出DE有效时读FIFO空满标志控制读写使能。我个人的建议把无缓存直通先调通确认整条链路OK再加FIFO做缓存转发。一次引入太多东西出了问题根本不知道该查输入还是查输出。3.5 关键代码结构与时序约束要点环路直通的核心代码非常短本质上就是“输入打拍输出”但好的代码风格会让调试省心很多。时钟和复位最好统一用同步复位跨时钟域信号一律打两拍DE和同步信号要和数据对齐。这些是FPGA基本功但在这个实验里特别容易踩雷。时序约束方面我推荐至少做这样几条对芯片输出的像素时钟创建时钟约束告诉工具这个时钟频率是多少。720p是74.25MHz1080p60是148.5MHz。对输入总线的数据信号设置相对像素时钟的输入延迟set_input_delay这个值可以参考接收芯片数据手册的输出时序来估算。对输出信号做输出延迟约束set_output_delay保证FPGA送出去的数据能满足发送芯片的建立保持时间。时序约束写得好不好直接决定上板能不能稳定跑尤其是1080p分辨率下像素时钟高了一倍布局布线稍有闪失就容易出雪花点。3.6 环路输出实验的板级验证程序烧进去之后把HDMI输出接到显示器输入接到电脑或机顶盒。显示器点亮的那一瞬间整个链路才算真正打通。验证通过的标准不光是“有画面”还要确认画面没有闪烁、没有彩色噪点、没有错位。电脑桌面文字边缘是否清晰、灰阶是否平滑都是很好的判断依据。为了证明环路里数据确实经过FPGA而不是开发板的物理布线直接透传可以加一个简单的颜色翻转模块在DE有效时把RGB取反再输出。如果显示器上出现的是反色画面就说明数据确实经过了FPGA逻辑环路实验的意义就体现出来了。4. 调试实录实验中最容易踩的五个坑4.1 显示器提示“无视频输入”这是我遇到过的最高频问题没有之一。排查顺序很重要不要一上来就改代码。先确认HDMI线的连接然后用万用表量HDMI连接器的HPD引脚电压正常应该在2.4V以上。如果HPD不拉高检查接收芯片的供电、5V引脚和HPD的上拉电阻。HPD正常但还是黑屏就要查EDID有没有被正确读出来——可以在信号源电脑的显卡设置里看是否识别出了“数字显示器”如果显示“未连接”基本就是DDC通道的问题重点检查I2C引脚的上下拉和电平是否正常。4.2 画面有彩色噪点或雪花这个问题通常是信号链路质量差导致的优先怀疑三大原因第一像素时钟约束写错或没写导致布局布线没有按高频优化第二TMDS差分对的引脚约束错误比如把差分对正负极接反第三HDMI线的质量太次或太长超过3米的廉价线在1080p下很容易出噪点。排查时先用720p分辨率测试如果720p正常、1080p有噪点大概率是时序约束或信号完整性问题。4.3 画面偏移、有黑边或撕裂这类问题基本就是同步信号的问题。画面整体向左或向右偏移一般是HS和有效像素数据之间的相位关系没对齐。撕裂现象则表明输入输出两侧的帧不同步直通模式下如果出现撕裂重点检查发送芯片的配置对同步信号是否做了额外的处理比如有些芯片会强制插入自己的同步信号。使用FIFO缓存时出现撕裂则要检查FIFO读出的数据是否在帧边界对齐简单粗暴的解决办法是加一个“检测到VS后再开始读FIFO”的逻辑。4.4 EDID读取失败如果你在FPGA内部实现了EDID逻辑而不是用一个外挂EEPROM那么I2C时序就是关键。HDMI的DDC通道规范要求I2C时钟最高100kHz读操作是标准的“写地址后重复起始再读”的时序。很多人的问题出在响应了错误的I2C地址或者对0x00地址不响应。另外EDID数据里一定要正确填写显示器名称、分辨率支持列表、像素时钟数据如果数据畸变电脑虽然识别到了设备但可能默认输出一个很保守的分辨率比如640×480。4.5 环路输出分辨率与信号源不匹配有些场景下你希望信号源输出1080p但显示器的EDID里没有声明支持1080p那么源端就会降级输出720p甚至更低。这正是EDID在链路里的最终权力显示器支持什么源端才会输出什么。做环路实验如果发现实际分辨率不对不用怀疑FPGA逻辑你只需要用逻辑分析仪或者在线调试工具抓取DE高电平持续了多少个像素时钟再数一下行数和帧数就能推算出实际分辨率。这个“用DE测分辨率”的小技巧是我调试时使用频率最高的基本功。5. 实验延伸从环出到图像处理调通环路输出之后恭喜你你已经拥有了一个可以随意“拦截”视频画面的平台。接下来的扩展方向非常明确而且每一个都足够写一篇新文章加OSD字符叠加模块在有效视频区把像素替换成ASCII字模数据可以实现“在视频上显示时间戳或自定义文本”的功能这是监控设备和仪器仪表显示的基础功能。加灰度转换或二值化模块对RGB三个通道按权重求和得到Y分量输出只保留亮度信息是图像处理入门最简单的算法。加行缓存做边缘检测用两三个Line Buffer配合3x3卷积窗口能实现经典的Sobel边缘提取观察实时视频画面的轮廓效果。加大容量帧缓存如果你嫌弃BRAM不够用可以挂DDR3/DDR4控制器把整帧数据存进DDR再读出来实现帧率转换、格式转换甚至视频录制。我个人经验不要急着一步到图像算法。先把环路无缓存直通跑熟再换FIFO缓存模式确保自己闭着眼都能画出“输入-缓存-输出”的数据流图再去碰算法和DDR。视频处理项目最大的难点从来不是某个算法本身而是你在分析问题时脑袋里有没有一个清晰的数据通路图。另外建议统一用一套标准的调试方法ILA抓信号时优先抓“像素时钟、DE、HS、VS”这四个先把骨架搭清楚再抓数据。任何画面异常都可以先用这四个信号把问题定位到输入端还是输出端。有几次我抓到眼冒金星回头发现只是ILA触发条件写错了DE下降沿触发被写成上升沿抓出来的波形自然怎么都对不上。这种基础错误很蠢但每个人都会犯。这个实验做完你会对HDMI接口、视频时序、时钟域处理、常见调试手段都有一个全新的认识。以后再看到“FPGA图像处理”项目就不再是仰望黑盒而是心里有了一张清晰的地图。