FPGA强化- HDMI显示器驱动设计与验证
12.HDMI显示器驱动设计与验证
在前面章节我们通过几个实验对VGA的相关知识做了系统性的介绍,读者务必理解掌握。由前文可知,VGA显示具有成本低、结构简单、应用灵活等优点,但缺点是VGA使用的模拟信号极易受到外界干扰源的影响,产生信号畸变,而且VGA接口体积较大,不利于便携设备的使用。
为了解决VGA接口的弊端,DVI、HDMI接口应运而生。在本章节,我们通过实验,学习掌握一下另一个重要的视频接口HDMI。
1024程序员节快乐,如果对你有帮助,感谢点赞、收藏、关注~
12.1. 理论学习
实战之前的理论学习是必不可少的,在本小结中,我们针对HDMI的基本知识和概念做一个系统性的介绍,希望读者能够理解掌握,这对后面的实战大有裨益。
12.1.1. HDMI简介
VGA接口体积较大,不利于便携设备的集成;且传输的模拟信号易受外界干扰,产生信号畸变。为了解决VGA接口的诸多问题,视频接口开始了一次革新。
VGA接口之后,首先推出的的是DVI接口,DVI是基于TMDS(Transition Minimized Differential Signaling,最小化传输差分信号)技术来传输数字信号。TMDS运用先进的编码算法把8bit数据(R、G、B中的每路基色信号)通过最小转换编码为10bit数据(包含 行场同步信息、时钟信息、数据DE、纠错等),经过直流均衡后,采用差分信号传输数据,它和LVDS、TTL相比有较好的电磁兼容性能,可以用低成本的专用电缆实现长距离、高质量的数字信号传输。数字视频接口(DVI)是一种国际开放的接口标准,在PC、DVD、高清晰电视(HDTV)、高清晰投影仪等设备上有广泛的应用。
DVI接口分为3大类:DVI-Analog(DVI-A)接口(12+5)只传输模拟信号,实质就是 VGA模拟传输接口规格;DVI-Digital(DVI-D)接口(18+1和24+1)是纯数字的接口,只能传输数字信号,不兼容模拟信号;DVI- Integrated(DVI-I)接口(18+5和24+5)是兼容数字和模拟接口的。
DVI 接口虽然是一种全数字化的传输技术,但是在开发之初,其最初目标就是要实现高清晰、无损压缩的数字信号传输。由于没有考虑到 IT 产品和 AV 产品融合的趋势,DVI 标准过分偏重于对计算机显示设备的支持而忽略了对数字平板电视等 AV 设备的支持。DVI 接口虽然成功的实现了无损高清传输这一目标,但是过于专一的定位也在相当程度上造成了整体性能的落后,暴露出诸多问题。
DVI接口设计之初考虑的对象是 PC,对于平板电视的兼容能力一般;只支持计算机领域的 RGB 数字信号,而对数字化的色差信号无法支持;只支持 8bit 的 RGB 信号传输,不能让广色域的显示终端发挥出最佳性能;出于兼容性考虑,预留了不少引脚以支持模拟设备,造成接口体积较大;只能传输图像信号,对于数字音频信号的支持完全没有考虑。
由于以上种种缺陷,DVI 接口已经不能更好的满足整个行业的发展需要。也正是基于这些原因,促使了 HDMI 标准的诞生。
HDMI 全称“High Definition Multimedia Interface 高清多媒体接口”。2002 年 4 月,来自电子电器行业的 7 家公司——日立、松下、飞利浦、Silicon Image、索尼、汤姆逊、东芝共同组建了 HDMI 高清多媒体接口接口组织HDMI Founders(HDMI 论坛),开始着手制定一种符合高清时代标准的全新数字化视频/音频接口技术。经过半年多时间的准备工作,HDMI founders 在 2002 年12 月 9 日正式发布了 HDMI 1.0 版标准,标志着 HDMI 技术正式进入历史舞台。
HDMI 标准的制定,并没有抛弃 DVI 标准中相对成熟且较易实现的部分技术标准,整个传输原理依然是基于TMDS 编码技术。针对DVI的诸多问题,HDMI做了大幅改进。HDMI接口体积更小,各种设备都能轻松安装可用于机顶盒 、DVD播放机 、个人计算机 、电视、游戏主机、综合扩大机、数字音响与电视机 等设备;抗干扰能力更强,能实现最长 20 米的无增益传输;针对大尺寸数字平板电视分辨率进行优化,兼容性好;拥有强大的版权保护机制(HDCP),有效防止盗版现象;支持 24bit 色深处理,(RGB、YCbCr4-4-4、YCbCr4-2-2);一根线缆实现数字音频、视频信号同步传输,有效降低使用成本和繁杂程度。
时代在发展,社会在进步,HDMI发展至今也推出了若干版本,性能更加出色,兼容性不断提高。HDMI 正在成为高清时代普及率最高、用途最广泛的数字接口。在现在任何一台平板电视上,HDMI 接口都成了标准化的配置。
12.1.2. HDMI接口及引脚定义
HDMI接口因为接口体积下、抗干扰能力强、兼容性好等优点,已逐步取代VGA和DVI接口,特别是在一些便携设备上,HDMI 接口都成了标准化的配置。HDMI接口具体见图 41‑1。

图 41‑1 HDMI接口
HDMI规格书中规定了HDMI的4种接口类型,但其中HDMI B Type接口类型未在市场中出现过,市面上流通最广的是HDMI A Type、HDMI C Type和HDMI D Type接口类型。
HDMI A Type接口,应用于HDMI1.0版本,总共有19pin,规格为4.45mm×13.9mm,为最常见的HDMI接头规格;HDMI C Type接口,俗称mini-HDMI,应用于HDMI1.3版本,总共有19pin,可以说是缩小版的HDMI A type,规格为2.42mm×10.42mm,但脚位定义有所改变。主要是用在便携式设备上,例如DV、数字相机、便携式多媒体播放机等。由于大小所限,一些显卡会使用mini-HDMI,用家须使用转接头转成标准大小的Type A再连接显示器;HDMI D Type接口,应用于HDMI1.4版本,总共有19pin,规格为2.8mm×6.4mm,但脚位定义有所改变。新的Micro HDMI接口将比现在19针MINI HDMI版接口小50%左右,可为相机、手机等便携设备带来最高1080p的分辨率支持及最快5GB的传输速度。三种接口如图 41‑2所示。

图 41‑2 三种HDMI接口图
HDMI接口之间使用HDMI信号线连接,不同类型的HDMI接口之间也可以使用连接线进行转接。HDMI连接线如图 41‑3所示。

图 41‑3 HDMI连接线
虽然已经见识过HDMI接口的外观,但对接口各引脚功能并没有进一步的认识,下面,我们以HDMI A Type接口为例,结合HDMI接口引脚图和各引脚定义表格,对HDMI接口各引脚做一下简单介绍,具体见图 41‑4及表格 41‑1。

图 41‑4 HDMI A Type接口引脚图
表格 41‑1 HDMI A Type接口引脚定义
| 引脚 | 定义 | 引脚 | 定义 |
|---|---|---|---|
| 1 | 数据2+ (TMDS Data2+) | 11 | 时钟屏蔽 (TMDS Clock Shield) |
| 2 | 数据2屏蔽 (TMDS Data2 Shield) | 12 | 时钟- (TMDS Clock–) |
| 3 | 数据2- ( TMDS Data2-) | 13 | CEC |
| 4 | 数据1+ (TMDS Data1+) | 14 | 保留 |
| 5 | 数据1屏蔽 (TMDS Data1 Shield) | 15 | DDC时钟线(SCL) |
| 6 | 数据1- ( TMDS Data1-) | 16 | DDC数据线(SDA) |
| 7 | 数据0+ (TMDS Data0+) | 17 | DDC/CEC地 (DDC/CEC GND) |
| 8 | 数据0屏蔽 (TMDS Data0 Shield) | 18 | +5V电源 (Power) |
| 9 | 数据0- ( TMDS Data0-) | 19 | 热插拔检测 (Hot Plug Detect) |
| 10 | 时钟+ (TMDS Clock+) |
由图表可知,HDMI接口共有19个引脚,分上下两排,奇数在上,偶数在下,穿插排布。根据其功能,可以将引脚分为4类。
TMDS通道:引脚1-引脚12。负责发送音频、视频及各种辅助数据;遵循DVI 1.0规格的信号编码方式;视频像素带宽从25 MHz到340 MHz(Type A, HDMI 1.3)或至680MHz (Type B)。带宽低于25MHz的视频信号如NTSC 480i 将以倍频方式输出;每个像素的容许数据量从24位至48位。支持每秒120张画面1080p分辨率画面发送以及WQSXGA分辨率;支持RGB 、YCbCr 4:4:4(8-16 bits per component)、YCbCr 4:2:2(12 bits per component)、 YCbCr 4:2:0(HDMI 2.0)等多种像素编码方式;音频采样率支持32kHz、44.1kHz、 48kHz、 88.2kHz、96kHz、176.4kHz、192kHz、1536kHz(HDMI 2.0);音频声道数量最大8声道。HDMI 2.0支持32声道。音频流规格为IEC61937兼容流,包括高流量无损信号如Dolby TrueHD、DTS -HD Master Audio。
DDC通道:引脚15、16、17。DDC全文为Display Data Channel,译为“显示数据通道”;发送端与接收端可利用DDC沟道得知彼此的发送与接收能力,但HDMI仅需单向获知接收端(显示器)的能力;DDC通道使用100kHz时钟频率的I²C信号,发送数据结构为VESA Enhanced EDID(V1.3)。
CEC通道:引脚13、17。CEC全文为Consumer Electronics Control,CEC通道为必须预留线路,但可以不必实现,作用是用来发送工业规格的AV Link协议信号,以便支持单一遥控器操作多台AV机器,为单芯线双向串列总线。
其他通道:引脚14位保留引脚,无连接;引脚18为+5V电源;引脚19位热插拔检测引脚。
注:另外两类型的HDMI接口与HDMI A Type接口的各引脚名称、功能相同,只是引脚线序不同。
12.1.3. HDMI显示原理
HDMI 系统架构由信源端和接收端组成。某个设备可能有一个或多个 HDMI 输入,一个或多个 HDMI 输出。这些设备上,每个 HDMI 输入都应该遵循 HDMI 接收端规则, 每个 HDMI输出都应该遵循 HDMI 信源端规则。
如图 41‑5所示, HDMI 线缆和连接器提供四个差分线对,组成TMDS数据和时钟通道,这些通道用于传递视频,音频和辅助数据;另外, HDMI 提供一个 VESA DDC 通道,DDC是用于配置和在一个单独的信源端和一个单独的接收端交换状态;可选择的 CEC 在用户的各种不同的音视频产品中, 提供高水平的控制功能; 可选择的 HDMI 以太网和音频返回(HEAC),在连接的设备中提供以太网兼容的网络数据和一个和 TMDS 相对方向的音频回返通道;此外还有热插拔检测信号HDP,当显示器等HDMI接口的显示设备通过HDMI接口与HDMI信源端相连或断开连接时,HDMI信源端能够通过HPD引脚检测出这一事件,并做出响应。

图 41‑5 HDMI数据传输框图
在前文中我们提到过,HDMI采用和DVI 相同的传输原理 — TMDS(Transition Minimized Differential signal),最小化传输差分信号。在此之前我们只是提及这一传输原理,并未做过系统性的讲解,下面我们就来详细说明一下TMDS的相关知识。
HDMI中的TMDS 传输系统分为两个部分:发送端和接收端。 TMDS 发送端收到HDMI 接口传来的表示 RGB 信号的24 位并行数据(TMDS 对每个像素的 RGB 三原色分别按 8bit 编码,即 R信号有 8 位,G 信号有 8 位,B 信号有 8 位),然后对这些数据和时钟信号进行编码和并/串转换,再将表示 3 个 RGB 信号的数据和时钟信号分别分配到独立的传输通道发送出去。接收端接收来自发送端的串行信号,对其进行解码和串/并转换,然后发送到显示器的控制端。与此同时也接收时钟信号,以实现同步。流程框图如图 41‑6所示。

图 41‑6 TMDS信道连接图
TMDS通道包括 3 个RGB 数据传输通道和 1 个时钟信号传输通道。每一通道都通过编码算法,将 8 位的视频、音频数据转换成最小化传输、直流平衡的 10 位数据,8 位数据经过编码和直流平衡得到 10 位最小化数据,看似增加了冗余位,对传输链路的带宽要求会更高,但事实上,通过这种算法得到的 10 位数据在更长的同轴电缆中传输的可靠性增强了。最小化传输差分信号是通过异或及异或非等逻辑算法将原始 8位数据转换成 10 位数据,前 8位数据由原始信号经逻辑运算后逻辑得到,第 9 位指示运算的方式,第 10 位用来对应直流平衡。
要实现TMDS通道传输,首先要将传入的8 位的并行数据进行编码、并/串转换,添加第9位编码位,如下图所示。

图 41‑7 添加编码位
将 8 位并行数据发送到 TMDS 接收端;将接收到的8位数据并/串转换;随后进行最小化传输处理,加上第 9 位,即编码过程。
添加编码位的数据需要进行直流均衡处理。直流平衡(DC-balanced)就是指在编码过程中保证信道中直流偏移为零,使信道中传输数据包含的1与0的个数相同。方法是在添加编码位的 9 位数据的后面加上第 10 位数据,保证10位数据中1与0个数相同。这样,传输的数据趋于直流平衡,使信号对传输线的电磁干扰减少,提高信号传输的可靠性。
直流均衡处理后的10位数据需要进行单端转差分处理。TMDS差分传动技术是一种利用2个引脚间电压差来传送信号的技术。传输数据的数值(“0”或者“1”)由两脚间电压正负极性和大小决定。即采用 2 根线来传输信号,一根线上传输原来的信号,另一根线上传输与原来信号相反的信号。这样接收端就可以通过让一根线上的信号减去另一根线上的信号的方式来屏蔽电磁干扰,从而得到正确的信号。原理图如下图所示。

图 41‑8 TMDS差分信号原理
经过上述处理,我们得到了可以进行TMDS通道传输的差分信号,使用这种方法对24位图像数据(8 位R信号、8 位G 信号、8 位B 信号)和时钟信号进行处理,将4对差分信号通过HDMI接口发到接收设备;接收设备通过解码等一系列操作,实现图像后音频再现。
12.2. 实战演练
在上一小节中,我们对HDMI接口及接口定义、HDMI显示原理做了详细讲解,望读者认真揣摩、理解掌握,这些理论基础对后面的实战至关重要,纸上谈兵之后,我们进入实战演练,在实战中加深对概念的理解。
12.2.1. 实验目标
实验目标:编写HDMI驱动,使用FPGA开发板驱动HDMI显示器显示十色等宽彩条, HDMI显示模式为640*480@60。
实验效果,具体见图 41‑9。

图 41‑9 HDMI彩条实验效果图
12.2.2. 硬件资源
HDMI接口部分位于板卡的中下部,如图 41‑10所示,HDMI原理图如图 41‑11所示。关于HDMI接口的引脚说明,我们在理论学习小节已经做了详细介绍,此处不再赘述。

图 41‑10 板载HDMI接口图

图 41‑11 HDMI部分原理图
12.2.3. 程序设计
硬件资源介绍完毕,我们开始实验工程的程序设计。在本小节,我们采用先整体概括,再局部说明的方式对实验工程的各个模块进行讲解,详细内容如下。
12.2.3.1. 整体说明
注:本实验选用HDMI 640*480@60显示模式,时钟频率为25MHz,;接下来讲解中的相关参数与此显示模式相对应,事先告知,后续不再声明。
在本小节,我们先要对整个实验工程有一个整体认识,首先来看一下HDMI彩条显示实验工程的整体框图,具体见图 41‑12。

图 41‑12 HDMI彩条显示实验整体框图
由上图可知,本实验工程包括5个模块,各模块简介,具体见表格 41‑2。
表格 41‑2 HDMI彩条显示工程模块简介
| 模块名称 | 功能描述 |
|---|---|
| hdmi_colorbar | 顶层模块 |
| clk_gen | 时钟生成模块,生成VGA驱动时钟 |
| vga_ctrl | VGA时序控制模块,驱动VGA图像显示 |
| vga_pic | 图像数据生成模块,生成VGA待显示图像 |
| hdmi_ctrl | HDMI驱动控制模块,生成HDMI待显示图像 |
由图 41‑12、表格 41‑2可知,HDMI的彩条显示是基于VGA彩条显示的基础上的,是在VGA彩条显示工程的基础上修改的得到的。其中改动较大的有两部分:一是时钟生成模块的输出时钟频率和时钟个数做了改动;二是增加了HDMI驱动控制模块hdmi_ctrl。这两部分改动会在后文做详细介绍。
12.2.3.2. 时钟生成模块
时钟生成模块依然使用调用IP核的生成方式,输出两路时钟信号。由上文可知,本次实验工程中,HDMI显示模式为640*480@60,时钟频率为25MHz,而板卡晶振传入时钟频率为50MHz。时钟生成模块的作用就是将50MHz晶振时钟分频为25MHz的HDMI工作时钟;除此之外,还要生成25MHz时钟的5 倍频125MHz时钟,125MHz时钟的具体用途会在后文讲到。
时钟生成模块框图,具体见图 41‑13。

图 41‑13 时钟生成模块框图
12.2.3.3. HDMI驱动控制模块
模块框图
HDMI驱动控制模块hdmi_ctrl是HDMI彩条显示的核心模块,功能是将VGA控制模块传入的行场同步信号、图像信息转换为HDMI能读取的差分信号,其内部实例化若干子模块,模块及模块简介,如图 41‑14、表格 41‑3所示。

图 41‑14 HDMI驱动控制模块
表格 41‑3 HDMI驱动控制模块简介
| 模块名称 | 功能描述 |
|---|---|
| hdmi_ctrl | HDMI驱动控制模块 |
| en_code | 编码模块,实现8b转10b编码 |
| par_to_ser | 并行转串行模块,实现并行数据到串行数据的转换 |
由图 41‑14可知,HDMI驱动控制模块共有17路输入输出信号,输入信号9路,输出信号8路。输入输出信号简介具体见表格 41‑4。
表格 41‑4 HDMI驱动控制模块输入输出信号信号功能描述
| 信号 | 位宽 | 类型 | 功能描述 |
|---|---|---|---|
| clk_1x | 1Bit | Input | 工作时钟,频率25MHz |
| clk_5x | 1Bit | Input | 工作时钟,频率125MHz |
| sys_rst_n | 1Bit | Input | 复位信号,低电平有效 |
| hsync | 1Bit | Input | 行同步信号 |
| vsync | 1Bit | Input | 场同步信号 |
| de | 1Bit | Input | 使能信号 |
| rgb_red | 8Bit | Input | RGB图像色彩信息红色分量 |
| rgb_green | 8Bit | Input | RGB图像色彩信息绿色分量 |
| rgb_blue | 8Bit | Input | RGB图像色彩信息蓝色分量 |
| hdmi_clk_p | 1Bit | Output | HDMI时钟差分信号 |
| hdmi_clk_n | 1Bit | Output | HDMI时钟差分信号 |
| hdmi_r_p | 1Bit | Output | HDMI红色分量差分信号 |
| hdmi_r_n | 1Bit | Output | HDMI红色分量差分信号 |
| hdmi_g_p | 1Bit | Output | HDMI绿色分量差分信号 |
| hdmi_g_n | 1Bit | Output | HDMI绿色分量差分信号 |
| hdmi_b_p | 1Bit | Output | HDMI蓝色分量差分信号 |
| hdmi_b_n | 1Bit | Output | HDMI蓝色分量差分信号 |
代码编写
HDMI驱动控制模块就是HDMI驱动控制部分的顶层模块,内部实例化编码模块和并行转串行模块,连接各自对应信号,代码编写较为简单,无需波形图绘制。HDMI驱动控制模块参考代码具体见代码清单 41‑1。
代码清单 41‑1 HDMI驱动控制模块参考代码(hdmi_ctrl.v)
module hdmi_ctrl ( input wire clk_1x , //输入系统时钟 input wire clk_5x , //输入5倍系统时钟 input wire sys_rst_n , //复位信号,低有效 input wire [7:0] rgb_blue , //蓝色分量 input wire [7:0] rgb_green , //绿色分量 input wire [7:0] rgb_red , //红色分量 input wire hsync , //行同步信号 input wire vsync , //场同步信号 input wire de , //使能信号output wire hdmi_clk_p , output wire hdmi_clk_n , //时钟差分信号 output wire hdmi_r_p , output wire hdmi_r_n , //红色分量差分信号 output wire hdmi_g_p , output wire hdmi_g_n , //绿色分量差分信号 output wire hdmi_b_p , output wire hdmi_b_n //蓝色分量差分信号 );//// //\* Parameter and Internal Signal \// //// wire [9:0] red ; //8b转10b后的红色分量 wire [9:0] green ; //8b转10b后的绿色分量 wire [9:0] blue ; //8b转10b后的蓝色分量//// //\* Instantiate \// //// //------------- encode_inst0 ------------- encode encode_inst0 ( .sys_clk (clk_1x ), .sys_rst_n (sys_rst_n ), .data_in (rgb_blue ), .c0 (hsync ), .c1 (vsync ), .de (de ), .data_out (blue ) );//------------- encode_inst1 ------------- encode encode_inst1 ( .sys_clk (clk_1x ), .sys_rst_n (sys_rst_n ), .data_in (rgb_green ), .c0 (hsync ), .c1 (vsync ), .de (de ), .data_out (green ) );//------------- encode_inst2 ------------- encode encode_inst2 ( .sys_clk (clk_1x ), .sys_rst_n (sys_rst_n ), .data_in (rgb_red ), .c0 (hsync ), .c1 (vsync ), .de (de ), .data_out (red ) );//------------- par_to_ser_inst0 ------------- par_to_ser par_to_ser_inst0 ( .clk_5x (clk_5x ), .par_data (blue ),.ser_data_p (hdmi_b_p ), .ser_data_n (hdmi_b_n ) );//------------- par_to_ser_inst1 ------------- par_to_ser par_to_ser_inst1 ( .clk_5x (clk_5x ), .par_data (green ),.ser_data_p (hdmi_g_p ), .ser_data_n (hdmi_g_n ) );//------------- par_to_ser_inst2 ------------- par_to_ser par_to_ser_inst2 ( .clk_5x (clk_5x ), .par_data (red ),.ser_data_p (hdmi_r_p ), .ser_data_n (hdmi_r_n ) );//------------- par_to_ser_inst3 -------------par_to_ser par_to_ser_inst3(.clk_5x (clk_5x ),.par_data (10'b1111100000),.ser_data_p (hdmi_clk_p ),.ser_data_n (hdmi_clk_n ));endmodule |
12.2.3.4. 编码模块
模块框图
上一小节我们介绍了HDMI驱动控制模块,实际就是HDMI驱动控制的顶层模块,功能是实现VGA图像信息到HDMI图像信息的转化。实现这一功能的转化,需要对输入的VGA图像信息进行编码、并行串行转换、单端信号转差分信号、单沿采样转双沿采样。
而编码模块就是为了完成VGA图像数据8b转10b的编码,关于8b转10b编码的理论知识我们在前面的理论小节已经做了详细介绍。编码模块模块框图,具体见图 41‑15;模块输入输出信号功能描述,具体见表格 41‑5。

图 41‑15 编码模块模块框图
表格 41‑5 编码模块输入输出信号信号功能描述
| 信号 | 位宽 | 类型 | 功能描述 |
|---|---|---|---|
| sys_clk | 1Bit | Input | 工作时钟,频率25MHz |
| sys_rst_n | 1Bit | Input | 复位信号,低有效 |
| c0 | 1Bit | Input | 输入控制信号(hsync) |
| c1 | 1Bit | Input | 输入控制信号(vsync) |
| de | 1Bit | Input | 输入使能信号 |
| data_in | 8Bit | Input | 输入待编码8bit数据 |
| data_out | 10Bit | Output | 输出编码后的10bit数据 |
代码编写
对于编码模块的代码编写,我们不再进行波形图的绘制,以官方手册的流程图为参照进行代码编写。编码模块参考流程图,具体见表格 41‑6、图 41‑16。
表格 41‑6 编码模块参考流程图中各参数说明
| D,C0,C1,DE | 编 | 器输入数据。 | D是八位 | 像素数据,C1和C0是通道的控制数据,DE是数据使能。 | |
| cnt | 寄存器,用来跟踪数据 | 流的不一致,正值表示发送的1的个数超过的数目,负 | 数表示发送的0的个数超过的数目。表达式cnt{t-1}表 | 示相对于输入数据前一个集的前一个不一致值。表达式 | cnt{t}表示相对于输入数据当前集的新的不一致设置。 | |
| q_out | 完成编码后的10bit数据。 | |
| N1{x} | 返回参数x中“1”的个数。 | |
| N0{x} | 返回参数x中“1”的个数。 | |

图 41‑16 编码模块参考流程图
编码模块参考代码,具体见代码清单 41‑2。
代码清单 41‑2 编码模块参考代码(encode.v)
module encode
(
input wire sys_clk , //时钟信号
input wire sys_rst_n , //复位信号,低有效
input wire [7:0] data_in , //输入8bit待编码数据
input wire c0 , //控制信号c0
input wire c1 , //控制信号c1
input wire de , //使能信号output reg [9:0] data_out //输出编码后的10bit数据
);////
//\* Parameter and Internal Signal \//
////
//parameter define
parameter DATA_OUT0 = 10'b1101010100,
DATA_OUT1 = 10'b0010101011,
DATA_OUT2 = 10'b0101010100,
DATA_OUT3 = 10'b1010101011;//wire define
wire condition_1 ; //条件1
wire condition_2 ; //条件2
wire condition_3 ; //条件3
wire [8:0] q_m ; //第一阶段转换后的9bit数据//reg define
reg [3:0] data_in_n1 ; //待编码数据中1的个数
reg [7:0] data_in_reg ; //待编码数据打一拍
reg [3:0] q_m_n1 ; //转换后9bit数据中1的个数
reg [3:0] q_m_n0 ; //转换后9bit数据中0的个数
reg [4:0] cnt ; //视差计数器,0-1个数差别,最高位为符号位
reg de_reg1 ; //使能信号打一拍
reg de_reg2 ; //使能信号打两拍
reg c0_reg1 ; //控制信号c0打一拍
reg c0_reg2 ; //控制信号c0打两拍
reg c1_reg1 ; //控制信号c1打一拍
reg c1_reg2 ; //控制信号c1打两拍
reg [8:0] q_m_reg ; //q_m信号打一拍////
//\* Main Code \//
////
//data_in_n1:待编码数据中1的个数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_in_n1 <= 4'd0;
else
data_in_n1 <= data_in[0] + data_in[1] + data_in[2]
+ data_in[3] + data_in[4] + data_in[5]
+ data_in[6] + data_in[7];//data_in_reg:待编码数据打一拍
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_in_reg <= 8'b0;
else
data_in_reg <= data_in;//condition_1:条件1
assign condition_1 = ((data_in_n1 > 4'd4) \|\| ((data_in_n1 == 4'd4)
&& (data_in_reg[0] == 1'b1)));//q_m:第一阶段转换后的9bit数据
assign q_m[0] = data_in_reg[0];
assign q_m[1] = (condition_1) ? (q_m[0] ^~ data_in_reg[1]) : (q_m[0] ^ data_in_reg[1]);
assign q_m[2] = (condition_1) ? (q_m[1] ^~ data_in_reg[2]) : (q_m[1] ^ data_in_reg[2]);
assign q_m[3] = (condition_1) ? (q_m[2] ^~ data_in_reg[3]) : (q_m[2] ^ data_in_reg[3]);
assign q_m[4] = (condition_1) ? (q_m[3] ^~ data_in_reg[4]) : (q_m[3] ^ data_in_reg[4]);
assign q_m[5] = (condition_1) ? (q_m[4] ^~ data_in_reg[5]) : (q_m[4] ^ data_in_reg[5]);
assign q_m[6] = (condition_1) ? (q_m[5] ^~ data_in_reg[6]) : (q_m[5] ^ data_in_reg[6]);
assign q_m[7] = (condition_1) ? (q_m[6] ^~ data_in_reg[7]) : (q_m[6] ^ data_in_reg[7]);
assign q_m[8] = (condition_1) ? 1'b0 : 1'b1;//q_m_n1:转换后9bit数据中1的个数
//q_m_n0:转换后9bit数据中0的个数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
q_m_n1 <= 4'd0;
q_m_n0 <= 4'd0;
end
else
begin
q_m_n1<=q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7];
q_m_n0<=4'd8-(q_m[0]+q_m[1]+q_m[2]+q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]);
end//condition_2:条件2
assign condition_2 = ((cnt == 5'd0) \|\| (q_m_n1 == q_m_n0));//condition_3:条件3
assign condition_3 = (((~cnt[4] == 1'b1) && (q_m_n1 > q_m_n0))
\|\| ((cnt[4] == 1'b1) && (q_m_n0 > q_m_n1)));//数据打拍,为了各数据同步
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)beginde_reg1 <= 1'b0;de_reg2 <= 1'b0;c0_reg1 <= 1'b0;c0_reg2 <= 1'b0;c1_reg1 <= 1'b0;c1_reg2 <= 1'b0;q_m_reg <= 9'b0;endelsebeginde_reg1 <= de;de_reg2 <= de_reg1;c0_reg1 <= c0;c0_reg2 <= c0_reg1;c1_reg1 <= c1;c1_reg2 <= c1_reg1;q_m_reg <= q_m;end//data_out:输出编码后的10bit数据//cnt:视差计数器,0-1个数差别,最高位为符号位always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)begindata_out <= 10'b0;cnt <= 5'b0;endelsebeginif(de_reg2 == 1'b1)beginif(condition_2 == 1'b1)begindata_out[9] <= ~q_m_reg[8];data_out[8] <= q_m_reg[8];data_out[7:0]<=(q_m_reg[8])?q_m_reg[7:0] : ~q_m_reg[7:0];cnt<=(~q_m_reg[8])?(cnt+q_m_n0-q_m_n1):(cnt+q_m_n1-q_m_n0);endelsebeginif(condition_3 == 1'b1)begindata_out[9] <= 1'b1;data_out[8] <= q_m_reg[8];data_out[7:0] <= ~q_m_reg[7:0];cnt<=cnt+{q_m_reg[8],1'b0}+(q_m_n0-q_m_n1);endelsebegindata_out[9] <= 1'b0;data_out[8] <= q_m_reg[8];data_out[7:0] <= q_m_reg[7:0];cnt<= cnt-{~q_m_reg[8],1'b0}+(q_m_n1-q_m_n0);endendendelsebegincase ({c1_reg2, c0_reg2})2'b00: data_out <= DATA_OUT0;2'b01: data_out <= DATA_OUT1;2'b10: data_out <= DATA_OUT2;default:data_out <= DATA_OUT3;endcasecnt <= 5'b0;endendendmodule
|
12.2.3.5. 并行转串行模块
模块框图
使用编码模块可解决图像数据的编码问题,而并行转串行模块(par_to_ser.v)的主要功能就是实现并行串行转换、单端信号转差分信号、单沿采样转双沿采样。并行转串行模块框图,如图 41‑17所示;模块输入输出信号功能描述,具体见表格 41‑7。

图 41‑17 并行转串行模块框图
表格 41‑7 并行转串行模块输入输出信号信号功能描述
| 信号 | 位宽 | 类型 | 功能描述 |
|---|---|---|---|
| clk_5x | 1Bit | Input | 工作时钟,频率125MHz |
| par_data | 10Bit | Input | 传入待转换并行数据 |
| ser_data_p | 1Bit | Output | 传出转换后的串行差分数据 |
| ser_data_n | 1Bit | Output | 传出转换后的串行差分数据 |
注:传入的时钟信号clk_5x,频率125MHz,为输出串行差分信号ser_data_p、ser_data_n的同步时钟;传入的并行数据信号par_data,同步时钟信号为clk_1x,频率25MHz,未传入本模块。时钟信号clk_1x与clk_5x,时钟频率为5倍关系,因为并行数据信号par_da ta位宽10bit,若转换为串行信号,需要在时钟信号clk_1x的一个时钟周期内完成数据转换,转换后的串行数据信号的同步时钟频率必须为clk_1x的10倍,使用双沿采样则为5倍。
波形图绘制及IP核调用
在模块框图小节,我们已经对并行转串行模块做了功能介绍。由框图图 41‑17可知,模块内部包含子模块的实例化,这是名为ALTDDIO_OUT的IP核。
ALTDDIO_OUT是Altera提供的双数据速率 (DDR) IP核的一部分,双数据速率 (DDR) IP核可以用于在逻辑资源中实现DDR寄存器。其中ALTDDIO_IN可实现DDR输入接口, ALTDDIO_OUT可实现DDR输出接口,ALTDDIO_BIDIR可实现双向DDR输入输出接口。
本模块使用的是ALTDDIO_OUT IP核,用以实现DDR输出接口,将两路单沿信号,转换为双沿信号,在参考时钟的上升沿和下降沿发送数据。ALTDDIO_OUT IP核框图和接口信号描述,具体见图 41‑18、图 41‑19;ALTDDIO_OUT IP核时序图,如图 41‑20所示。

图 41‑18 ALTDDIO_OUT IP核框图

图 41‑19 ALTDDIO_OUT IP核接口信号描述

图 41‑20 ALTDDIO_OUT IP核时序图
了解了ALTDDIO_OUT IP核的相关知识后,我们开始进行IP核的配置。

图 41‑21 ALTDDIO_OUT IP核配置(一)
如图 41‑21所示,在IP核搜索界面标注①处搜索ALTDDIO;选中标注②处的ALTDDIO_OUT;在标注③处选择IP核文件存储位置并命名IP核;点击标注④处的“Next”,进行后续配置。

图 41‑22 ALTDDIO_OUT IP核配置(二)
如图 41‑22所示,在标注①处选择输入输出数据位宽,本模块中选择位宽为1bit;在标注②处选择“Not used”,不使用“aclr”“aset”信号;点击标注③处“Next”,进行后续配置。

图 41‑23 ALTDDIO_OUT IP核配置(三)
如图 41‑23所示,本页面不进行任何参数设置,直接点击“Next”进入下一步。

图 41‑24 ALTDDIO_OUT IP核配置(四)
如图 41‑24所示,标注①处为IP核仿真模型的相关描述;本页面同样不进行任何参数配置,点击标注②处“Next”,进入后续配置。

图 41‑25 ALTDDIO_OUT IP核配置(五)
如图 41‑25所示,标注①处为IP核生成后产生文件的文件列表,默认全部勾选,读者也可自定义选择;点击标注②处“Finish”完成IP核的生成。
读者要注意的是,我们只使用了ALTDDIO_OUT IP核的部分功能,有关其详细的配置资料读者可点击图 41‑25标注③,查找IP核的官方数据手册。
IP核生成完毕后,打开IP核实例化文件,如图 41‑26所示。其中,信号datain_h为输入的时钟上升沿待输出数据,位宽为1bit,信号datain_l为输入的时钟下降沿待输出数据,位宽为1bit ,dataout为输出的串行双沿采样数据,同步时钟为outclock。

图 41‑26 IP核实例化文件
讲到这里,读者会心存疑虑,模块输入的是位宽10bit的并行数据par_data,clk_1x时钟信号同步下的par_data数据是如何转换为clk_5x时钟信号下的datain_h、datain_l数据信号。下面我们会为读者详细说明。

图 41‑27 并行转串行波形图
我们参照图 41‑27为大家讲解并行数据转串行数据的实现方法。
第一步:将输入的10bit并行数据par_data拆分为两个位宽5bit的数据信号。拆分规则:将会在时钟上升沿输出的par_data[8]、par_data[6]、par_data[4]、par_data[2]、par_data[0]赋值给变量data_rise[4:0];将会在时钟下降沿输出的pa r_data[9]、par_data[7]、par_data[5]、par_data[3]、par_data[1]赋值给变量data_fall[4:0]。
第二步:声明计数器cnt,以clk_5x为计数时钟进行循环计数,计数范围0-4,每个时钟周期自加1。当cnt计数值为最大值4时,将拆分得到的变量data_rise、data_fall分别赋值给data_rise_s、data_fall_s;
第三步:将data_rise_s[0]、data_fall_s[0]分别写入ALTDDIO_OUT IP核的datain_h、datain_l接口;同时,每个时钟周期将data_rise_s、data_fall_s右移一位。
经过上述三步操作后,位宽10bit的并行数据par_data转换为两路串行数据传入ALTDDIO_OUT IP核的datain_h、datain_l接口,经过IP核处理后,输出以clk_5x为同步时钟的串行双沿采样信号。
同时再次调用ALTDDIO_OUT IP核,将~data_rise_s[0]、~data_fall_s[0]分别写入ALTDDIO_OUT IP核的datain_h、datain_l接口,输出的串行双沿采样信号与之前生成的串行双沿采样信号,构成差分信号对。
到了这里,本模块已完成并行数据par_data向串行差分信号对ser_data_p、der_data_n的转化。
代码编写
并行转串行模块参考代码,具体见代码清单 41‑3。
代码清单 41‑3 并行转串行模块参考代码(par_to_ser.v)
module par_to_ser
(
input wire clk_5x , //输入系统时钟
input wire [9:0] par_data , //输入并行数据output wire ser_data_p , //输出串行差分数据
output wire ser_data_n //输出串行差分数据
);//////\* Parameter and Internal Signal \////////wire definewire [4:0] data_rise = {par_data[8],par_data[6],par_data[4],par_data[2],par_data[0]};wire [4:0] data_fall = {par_data[9],par_data[7],par_data[5],par_data[3],par_data[1]};//reg definereg [4:0] data_rise_s = 0;reg [4:0] data_fall_s = 0;reg [2:0] cnt = 0;always @ (posedge clk_5x)begincnt <= (cnt[2]) ? 3'd0 : cnt + 3'd1;data_rise_s <= cnt[2] ? data_rise : data_rise_s[4:1];data_fall_s <= cnt[2] ? data_fall : data_fall_s[4:1];end//////\* Instantiate \////////------------- ddio_out_inst0 -------------ddio_out ddio_out_inst0(.datain_h (data_rise_s[0] ),.datain_l (data_fall_s[0] ),.outclock (~clk_5x ),.dataout (ser_data_p ));//------------- ddio_out_inst1 -------------ddio_out ddio_out_inst1(.datain_h (~data_rise_s[0]),.datain_l (~data_fall_s[0]),.outclock (~clk_5x ),.dataout (ser_data_n ));endmodule
|
12.2.3.6. 顶层模块
代码编写
实验工程的各子功能模块均已讲解完毕,在本小节对顶层模块做一下介绍。hdmi_colorbar顶层模块主要是对各个子功能模块的实例化,以及对应信号的连接。代码编写较为容易,无需波形图的绘制。顶层参考代码,具体见代码清单 41‑4。
代码清单 41‑4 顶层模块参考代码(hdmi_colorbar.v)
module hdmi_colorbar
(
input wire sys_clk , //输入工作时钟,频率50MHz
input wire sys_rst_n , //输入复位信号,低电平有效output wire ddc_scl ,
output wire ddc_sda ,
output wire tmds_clk_p ,
output wire tmds_clk_n , //HDMI时钟差分信号output wire [2:0] tmds_data_p ,output wire [2:0] tmds_data_n //HDMI图像差分信号);//////\* Parameter and Internal Signal \////////wire definewire vga_clk ; //VGA工作时钟,频率25MHzwire clk_5x ;wire locked ; //PLL locked信号wire rst_n ; //VGA模块复位信号wire [11:0] pix_x ; //VGA有效显示区域X轴坐标wire [11:0] pix_y ; //VGA有效显示区域Y轴坐标wire [15:0] pix_data; //VGA像素点色彩信息wire hsync ; //输出行同步信号wire vsync ; //输出场同步信号wire [15:0] rgb ; //输出像素信息wire rgb_valid;//rst_n:VGA模块复位信号assign rst_n = (sys_rst_n & locked);assign ddc_scl = 1'b1;assign ddc_sda = 1'b1;//////\* Instantiation \////////------------- clk_gen_inst -------------clk_gen clk_gen_inst(.areset (~sys_rst_n ), //输入复位信号,高电平有效,1bit.inclk0 (sys_clk ), //输入50MHz晶振时钟,1bit.c0 (vga_clk ), //输出VGA工作时钟,频率25MHz,1bit.c1 (clk_5x ),.locked (locked ) //输出pll locked信号,1bit);//------------- vga_ctrl_inst -------------vga_ctrl vga_ctrl_inst(.vga_clk (vga_clk ), //输入工作时钟,频率25MHz,1bit.sys_rst_n (rst_n ), //输入复位信号,低电平有效,1bit.pix_data (pix_data ), //输入像素点色彩信息,16bit.pix_x (pix_x ), //输出VGA有效显示区域像素点X轴坐标,10bit.pix_y (pix_y ), //输出VGA有效显示区域像素点Y轴坐标,10bit.hsync (hsync ), //输出行同步信号,1bit.vsync (vsync ), //输出场同步信号,1bit.rgb_valid (rgb_valid ),.rgb (rgb ) //输出像素点色彩信息,16bit);//------------- vga_pic_inst -------------vga_pic vga_pic_inst(.vga_clk (vga_clk ), //输入工作时钟,频率25MHz,1bit.sys_rst_n (rst_n ), //输入复位信号,低电平有效,1bit.pix_x (pix_x ), //输入VGA有效显示区域像素点X轴坐标,10bit.pix_y (pix_y ), //输入VGA有效显示区域像素点Y轴坐标,10bit.pix_data (pix_data ) //输出像素点色彩信息,16bit);//------------- hdmi_ctrl_inst -------------hdmi_ctrl hdmi_ctrl_inst(.clk_1x (vga_clk ), //输入系统时钟.clk_5x (clk_5x ), //输入5倍系统时钟.sys_rst_n (rst_n ), //复位信号,低有效.rgb_blue ({rgb[4:0],3'b0} ), //蓝色分量.rgb_green ({rgb[10:5],2'b0} ), //绿色分量.rgb_red ({rgb[15:11],3'b0} ), //红色分量.hsync (hsync ), //行同步信号.vsync (vsync ), //场同步信号.de (rgb_valid ), //使能信号.hdmi_clk_p (tmds_clk_p ),.hdmi_clk_n (tmds_clk_n ), //时钟差分信号.hdmi_r_p (tmds_data_p[2] ),.hdmi_r_n (tmds_data_n[2] ), //红色分量差分信号.hdmi_g_p (tmds_data_p[1] ),.hdmi_g_n (tmds_data_n[1] ), //绿色分量差分信号.hdmi_b_p (tmds_data_p[0] ),.hdmi_b_n (tmds_data_n[0] ) //蓝色分量差分信号);endmodule
|
顶层模块参考代码理解起来较为简单,在此不再过多叙述。
RTL视图
实验工程通过仿真验证后,使用Quartus软件对实验工程进行编译,编译完成后,我们查看一下RTL视图, RTL视图展示信息与顶层模块框图一致,各信号正确连接,具体见图 41‑28、图 41‑29。

图 41‑28 RTL视图(一)

图 41‑29 RTL视图(二)
12.2.3.7. 仿真验证
仿真代码编写
顶层模块参考代码介绍完毕,开始对顶层模块进行仿真,对顶层模块的仿真就是对实验工程的整体仿真。顶层模块仿真参考代码,具体见代码清单 41‑5。
代码清单 41‑5 顶层模块仿真参考代码(tb_hdmi_colorbar.v)
module tb_hdmi_colorbar(); //// //\* Parameter and Internal Signal \// //// //wire define wire ddc_scl ; wire ddc_sda ; wire tmds_clk_p ; wire tmds_clk_n ;wire [2:0] tmds_data_p ;wire [2:0] tmds_data_n ;//reg definereg sys_clk ;reg sys_rst_n ;//////\* Clk And Rst \////////sys_clk,sys_rst_n初始赋值initialbeginsys_clk = 1'b1;sys_rst_n <= 1'b0;#200sys_rst_n <= 1'b1;end//sys_clk:产生时钟always #10 sys_clk = ~sys_clk ;//////\* Instantiation \////////------------- hdmi_colorbar_inst -------------hdmi_colorbar hdmi_colorbar_inst(.sys_clk (sys_clk ), //输入工作时钟,频率50MHz.sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效.ddc_scl (ddc_scl ),.ddc_sda (ddc_sda ),.tmds_clk_p (tmds_clk_p ),.tmds_clk_n (tmds_clk_n ), //HDMI时钟差分信号.tmds_data_p (tmds_data_p),.tmds_data_n (tmds_data_n) //HDMI图像差分信号);endmodule |
顶层模块仿真参考代码内部实例化各子功能模块,连接各子功能模块对应信号,模拟产生50MHz时钟信号和复位信号,理解较为容易,不再讲解。
仿真波形分析
使用ModelSim软件对代码进行仿真,VGA部分相关模块再前面章节已经验证通过,clk_gen为调用IP核,无需仿真,在顶层模块的仿真波形分析,我们只查看encode模块、par_to_ser模块的相关信号。由一下仿真波形图可知,各模块波形正常,仿真通过。

图 41‑30 encode模块仿真波形图

图 41‑31 par_to_ser模块仿真波形图

图 41‑32 DDIO_OUT仿真波形
12.3. 上板验证
12.3.1. 引脚约束
仿真验证通过后,准备上板验证,上板验证之前先要进行引脚约束。工程中各输入输出信号与开发板引脚对应关系如表格 41‑8所示。
表格 41‑8 引脚分配表
| 信号名 | 信号类型 | 对应引脚 | 备注 |
|---|---|---|---|
| sys_clk | Input | E1 | 输入系统时钟 |
| sys_rst_n | Input | M15 | 复位信号 |
| ddc_scl | Output | P15 | DDC时钟 |
| dda_sda | Output | N14 | DDC数据 |
| tmds_clk_p | Output | R16 | 时钟差分信号 |
| tmds_clk_n | Output | P16 | 时钟差分信号 |
| tmds_data_p[2] | Output | K15 | 红色分量差分信号 |
| tmds_data_n[2] | Output | K16 | 红色分量差分信号 |
| tmds_data_p[1] | Output | L15 | 绿色分量差分信号 |
| tmds_data_n[1] | Output | L15 | 绿色分量差分信号 |
| tmds_data_p[0] | Output | N15 | 蓝色分量差分信号 |
| tmds_data_n[0] | Output | N16 | 蓝色分量差分信号 |
下面进行管脚分配,管脚的分配方法在前面章节已有所讲解,在此就不再过多叙述,管脚的分配如下图 41‑33所示。

图 41‑33 管脚分配
12.3.1.1. 结果验证
如图 41‑34所示,开发板连接12V直流电源、USB-Blaster下载器JTAG端口以及HDMI显示器。线路正确连接后,打开开关为板卡上电。

图 41‑34 程序下载连线图
如图 41‑35所示,使用“Programmer”为开发板下载程序。

图 41‑35 程序下载窗口
程序下载完成后,如图 41‑36所示,HDMI显示器显示出十色彩条,和预期实验效果一致。

图 41‑36 HDMI彩条实验效果图
12.4. 章末总结
到这里,本章节讲解完毕,通过实验,相信读者对于HDMI显示的基本知识和概念,以及TMDS传输原理已经理解掌握,读者要勤加练习,做到学以致用。
12.5. 拓展训练
1、更改相关参数,实现HDMI其他分辨率下的彩条显示
2、将VGA图像显示的其他工程移植到HDMI显示上。
