HDMI显示器驱动设计与验证

1 HDMI简介

在此附上HDMI协议的数据手册链接,更有1.4的中文版:

https://pan.baidu.com/s/1CdEQuJzYXnESNZK60k7aVQ?pwd=6666https://pan.baidu.com/s/1CdEQuJzYXnESNZK60k7aVQ?pwd=6666链接:https://pan.baidu.com/s/1CdEQuJzYXnESNZK60k7aVQ?pwd=6666

提取码:6666

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 信号传输,不能让广色域的显示终端发挥出最佳性能;出于兼容性考虑,预留了不少引脚以支持模拟设备,造成接口体积较大;只能传输图像信号,对于数字音频信号的支持完全没有考虑。

**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的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的传输速度。三种接口如图所示。

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

引脚 定义 引脚 定义
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+)

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接口与HD MI A Type接口的各引脚名称、功能相同,只是引脚线序不同。

2 HDMI显示原理

HDMI 系统架构由信源端和接收端组成。某个设备可能有一个或多个 HDMI 输入,一个或多个 HDMI 输出。这些设备上,每个 HDMI 输入都应该遵循 HDMI 接收端规则, 每个 HDMI输出都应该遵循 HDMI 信源端规则。

如图所示,HDMI线缆和连接器提供四个差分线对组成TMDS数据和时钟通道,这些通道用于传递视频,音频和辅助数据; 另外, HDMI 提供一个 VESA DDC 通道,DDC是用于配置和在一个单独的信源端和一个单独的接收端交换状态;(发送端可以通过DDC通道读取EDIDROM关于显示模式的信息); 可选择的 CEC**(可以预留不使用)** 在用户的各种不同的音视频产品中, 提供高水平的控制功能; 可选择的 HDMI 以太网和音频返回(HEAC),在连接的设备中提供以太网兼容的网络数据和一个和 TMDS 相对方向的音频回返通道;此外还有热插拔检测信号HDP,当显示器等HDMI接口的显示设备通过HDMI接口与HDMI信源端相连或断开连接时,HDMI信源端能够通过HPD引脚检测出这一事件,并做出响应。

HDMI中的TMDS 传输系统分为两个部分:发送端和接收端TMDS 发送端收到HDMI 接口传来的表示 RGB 信号的24 位并行数据(TMDS 对每个像素的 RGB 三原色分别按 8bit 编码,即 R信号有 8 位,G 信号有 8 位,B 信号有 8 位) ,**然后对这些数据和时钟信号进行编码和并/串转换,再将表示 3 个 RGB 信号的数据和时钟信号分别分配到独立的传输通道发送出去。**接收端接收来自发送端的串行信号,对其进行解码和串/并转换,然后发送到显示器的控制端。与此同时也接收时钟信号,以实现同步。流程框图如图所示。

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

要实现TMDS通道传输,首先要将传入的8 位的并行数据进行编码、并/串转换,添加第9位编码位,如下图所示。

将 8 位并行数据发送到 TMDS 接收端;将接收到的8位数据并/串转换;随后进行最小化传输处理,加上第 9 位,即编码过程。

添加编码位的数据需要进行直流均衡处理。直流平衡(DC-balanced)就是指在编码过程中保证信道中直流偏移为零,使信道中传输数据包含的1与0的个数相同。方法是在添加编码位的 9 位数据的后面加上第 10 位数据,保证10位数据中1与0个数相同。这样,传输的数据趋于直流平衡,使信号对传输线的电磁干扰减少,提高信号传输的可靠性。

直流均衡处理后的10位数据需要进行单端转差分处理 。TMDS差分传动技术是一种利用2个引脚间电压差来传送信号的技术 。传输数据的数值("0"或者"1")由两脚间电压正负极性和大小决定。即采用 2 根线来传输信号,**一根线上传输原来的信号,另一根线上传输与原来信号相反的信号。这样接收端就可以通过让一根线上的信号减去另一根线上的信号的方式来屏蔽电磁干扰,从而得到正确的信号。**原理图如下图所示。

3 实验设计

实验目标:编写HDMI驱动,使用FPGA开发板驱动HDMI显示器显示十色等宽彩条, HDMI显示模式为640*480@60。

3.1 框图与波形图

FPGA部分承担的设计主要集中在下图所示功能:

设计步骤:

  1. 提供有效的图片数据,控制信号,时钟信号;
  2. 需要将上述数据做并串转换,编码,生成四组差分信号;

3.1.1 总体框图

本次实验工程中,HDMI显示模式为640*480@60,时钟频率为25MHz,而板卡晶振传入时钟频率为50MHz。时钟生成模块的作用就是将50MHz晶振时钟分频为25MHz的HDMI工作时钟;除此之外,还要生成25MHz时钟的5 倍频125MHz时钟,125MHz时钟的具体用途会在后文讲到。

3.1.2 hdmi_ctrl

HDMI驱动控制模块hdmi_ctrl是HDMI彩条显示的核心模块,功能是将VGA控制模块传入的行场同步信号、图像信息转换为HDMI能读取的差分信号,其内部实例化若干子模块,模块及模块简介,如图所示。

模块名称 功能描述
hdmi_ctrl HDMI驱动控制模块
en_code 编码模块,实现8b转10b编码
par_to_ser 并行转串行模块,实现并行数据到串行数据的转换
3.1.2.1 编码(en_code)模块
信号 位宽 类型 功能描述
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数据

代码编写,对于编码模块的代码编写,我们不再进行波形图的绘制,以官方手册的流程图为参照进行代码编写。编码模块参考流程图,具体见表格;

[HDMI]TMDS编码算法分析(2)_最小化传输编码-CSDN博客

HDMI编码的基本原理

HDMI的编码算法主要包括以下几个步骤:

  1. 数据编码(Data Encoding)
    • 对每个8位的数据(即每个像素的颜色或音频样本)进行编码,生成10位的数据流。这一步的核心是TMDS编码。
  2. 控制信号编码(Control Signal Encoding)
    • 对控制信号 C0C1 进行编码,用于指示何时传输有效数据或控制信息。
  3. 差分信号传输(Differential Signaling)
    • TMDS使用差分信号传输,每个信号对在传输线上产生的电压差表示传输的数据,从而减小了电磁干扰的影响。

TMDS编码算法

TMDS编码的核心目的是减少传输中的信号变化次数,以减少电磁干扰(EMI),并通过增加传输的数据位数来增强纠错能力。TMDS编码的步骤如下:

  1. 第一阶段:数据位的平衡(Data Bit Balancing)

    • 计算输入8位数据中'1'的个数,如果'1'的数量多于或等于'0'的数量(称为"权重"为正),则编码的第一阶段使用每一位的非操作(XOR)来处理数据;
    • 如果'0'的数量多于'1'的数量("权重"为负),则使用每一位的同或操作(XNOR)来处理数据。
    • 这样做是为了平衡编码后的数据位中'1'和'0'的数量,减少直流偏移(DC偏移)。
  2. 第二阶段:差分编码(Differential Encoding)

    • 对第一阶段的输出数据进行差分编码,将其与上一次编码的结果进行比较,以进一步减少传输过程中信号的变化次数。
    • 在差分编码中,系统会根据当前传输位的状态和前一位的状态,选择合适的编码方式,进一步降低信号变化频率。
  3. 第三阶段:控制信号的处理(Control Signals Handling)

    • 当传输的是控制信号而非数据时,TMDS编码会将控制信号直接映射到特定的10位编码值。

HDMI的编码结构

HDMI传输由三个TMDS数据通道和一个TMDS时钟通道组成,分别传输视频的红色、绿色和蓝色通道,以及同步信号。每个数据通道都采用上述的TMDS编码进行传输。

总结

HDMI的编码算法通过TMDS技术将8位数据编码为10位,平衡了数据位中的'1'和'0',并减少了传输信号中的跳变次数。这不仅提高了数据传输的可靠性和抗干扰能力,还确保了HDMI接口在高带宽、高速率下能够稳定工作。

HDMI的编码算法在实际应用中,保证了高质量的音视频信号在传输过程中的稳定性,成为数字音视频传输的行业标准。

差分编码(Differential Encoding)是一种减少信号跳变频率的方法,常用于通信系统中以提高抗干扰能力。在TMDS编码中,差分编码用于减少信号的变化次数,进而减少电磁干扰(EMI)。下面我通过具体的例子来解释差分编码的原理和方法。

差分编码的基本思路

差分编码的核心思路是,通过在当前位的值和前一位的值之间建立关系,减少信号的变化次数。这意味着,编码的输出不仅取决于当前的输入数据,还依赖于先前的数据输出。

差分编码的步骤

假设我们有一组原始数据比特b[n],差分编码的输出c[n]可以通过以下规则计算:

  • 初始条件 : c[0] = b[0] (第一位的编码结果直接取输入的第一位数据)
  • 后续比特 :
    • 如果b[n]c[n-1]相同,则c[n] = 0
    • 如果b[n]c[n-1]不同,则c[n] = 1

例子说明

假设我们有一组8位输入数据 b[7:0] = 10111001,下面是通过差分编码得到的输出:

  1. 原始数据 : b[7:0] = 10111001

  2. 编码步骤:

    • c[0] = b[0] = 1 (第一位直接取输入数据)
    • c[1]:
      • b[1] = 0c[0] = 1
      • b[1] ≠ c[0]c[1] = 1
    • c[2]:
      • b[2] = 1c[1] = 1
      • b[2] = c[1]c[2] = 0
    • c[3]:
      • b[3] = 1c[2] = 0
      • b[3] ≠ c[2]c[3] = 1
    • c[4]:
      • b[4] = 1c[3] = 1
      • b[4] = c[3]c[4] = 0
    • c[5]:
      • b[5] = 0c[4] = 0
      • b[5] = c[4]c[5] = 0
    • c[6]:
      • b[6] = 0c[5] = 0
      • b[6] = c[5]c[6] = 0
    • c[7]:
      • b[7] = 1c[6] = 0
      • b[7] ≠ c[6]c[7] = 1
  3. 差分编码结果 : c[7:0] = 11010001

编码分析

  • 原始数据 : 10111001
  • 差分编码结果 : 11010001

在差分编码后的数据中,相邻比特之间的变化次数减少,特别是在长串相同比特的情况下,这种方法极大地减少了信号的跳变频率。

3.1.2.2 并转串(par_to_ser)模块

使用编码模块可解决图像数据的编码问题,而并行转串行模块(par_to_ser.v)的主要功能就是实现并行串行转换、单端信号转差分信号、单沿采样转双沿采样。并行转串行模块框图,如图 41‑17所示;模块输入输出信号功能描述,具体见表格;

信号 位宽 类型 功能描述
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_data位宽10bit,若转换为串行信号,需要在时钟信号clk_1x的一个时钟周期内完成数据转换,转换后的串行数据信号的同步时钟频率必须为clk_1x的10倍,使用双沿采样则为5倍。

在模块框图小节,我们已经对并行转串行模块做了功能介绍。由框图可知,模块内部包含子模块的实例化,这是名为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核框图和接口信号描述,具体见图;ALTDDIO_OUT IP核时序图,如图所示:

模块输入的是位宽10bit的并行数据par_data,clk_1x时钟信号同步下的par_data数据是如何转换为clk_5x时钟信号下的datain_h、datain_l数据信号。

我们参照图为大家讲解并行数据转串行数据的实现方法。

第一步:将输入的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接口,输出的串行双沿采样信号与之前生成的串行双沿采样信号,构成差分信号对。

3.2 program

encode:

cpp 复制代码
module encode
(
    input wire          vga_clk     , //时钟信号
    input wire          sys_rst_n   , //复位信号,低有效
    input wire [7:0]    data_in     , //输入8bit待编码数据
    input wire          hsync       , //控制信号c0
    input wire          vsync       , //控制信号c1
    input wire          rgb_valid   , //使能信号
    
    output reg [9:0]    data_out      //输出编码后的10bit数据
);


//parameter define
parameter   DATA_OUT0   =   10'b1101010100 ,
            DATA_OUT1   =   10'b0010101011 ,
            DATA_OUT2   =   10'b0101010100 ,
            DATA_OUT3   =   10'b1010101011 ;

//reg define
reg     [3:0]   data_in_n1;
reg     [7:0]   data_in_reg;    //打拍在判断crtl_1的时候数据对齐
reg     [4:0]   cnt;

reg     [8:0]   q_m_reg;
reg     [3:0]   q_m_n1;
reg     [3:0]   q_m_n0;
reg             de_reg1;
reg             de_reg2;
reg             hsync_reg1;
reg             hsync_reg2;
reg             vsync_reg1;
reg             vsync_reg2;


//wire define
wire    ctrl_1;
wire    ctrl_2;
wire    ctrl_3;
wire    [8:0]   q_m;



always@(posedge vga_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];

always@(posedge vga_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_in_reg <= 8'd0;
    else
        data_in_reg <= data_in;
        
assign ctrl_1 = ((data_in_n1 > 4'd4) || (data_in_n1 == 4'd4 && data_in_reg[0] == 1'b0)) ? 1'b1 : 1'b0;

//q_m:第一阶段转换后9bit数据     
assign q_m[0] = data_in_reg[0] ;
assign q_m[1] = (ctrl_1 == 1'b1) ? (q_m[0] ^~ data_in_reg[1]) : (q_m[0] ^ data_in_reg[1]) ;
assign q_m[2] = (ctrl_1 == 1'b1) ? (q_m[1] ^~ data_in_reg[2]) : (q_m[1] ^ data_in_reg[2]) ;
assign q_m[3] = (ctrl_1 == 1'b1) ? (q_m[2] ^~ data_in_reg[3]) : (q_m[2] ^ data_in_reg[3]) ;
assign q_m[4] = (ctrl_1 == 1'b1) ? (q_m[3] ^~ data_in_reg[4]) : (q_m[3] ^ data_in_reg[4]) ;
assign q_m[5] = (ctrl_1 == 1'b1) ? (q_m[4] ^~ data_in_reg[5]) : (q_m[4] ^ data_in_reg[5]) ;
assign q_m[6] = (ctrl_1 == 1'b1) ? (q_m[5] ^~ data_in_reg[6]) : (q_m[5] ^ data_in_reg[6]) ;
assign q_m[7] = (ctrl_1 == 1'b1) ? (q_m[6] ^~ data_in_reg[7]) : (q_m[6] ^ data_in_reg[7]) ;
assign q_m[8] = (ctrl_1 == 1'b1) ? 1'b0 : 1'b1 ;

always@(posedge vga_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

assign ctrl_2 = ((cnt == 5'd0) || (q_m_n0 == q_m_n1)) ? 1'b1 : 1'b0; assign ctrl_3 = (((cnt > 5'd0) && (q_m_n1 > q_m_n0)) || ((cnt < 5'd0) && (q_m_n0 > q_m_n1))) ? 1'b1 : 1'b0;   

//信号打拍  
always@(posedge vga_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0) begin
        q_m_reg <= 9'd0;
        de_reg1 <= 1'd0;
        de_reg2 <= 1'd0;
        hsync_reg1 <= 1'd0;
        hsync_reg2 <= 1'd0;
        vsync_reg1 <= 1'd0;
        vsync_reg2 <= 1'd0;
        end
    else begin
        q_m_reg <= q_m;
        de_reg1 <= rgb_valid;
        de_reg2 <= de_reg1;
        hsync_reg1 <= hsync;
        hsync_reg2 <= hsync_reg1;
        vsync_reg1 <= vsync;
        vsync_reg2 <= vsync_reg1;
        end

//data_out:输出编码后的10bit数据
//cnt:视差计数器,0-1个数差别,最高位为符号位
always@(posedge vga_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            data_out <= 10'b0 ;
            cnt      <= 5'd0  ;
        end
    else
        begin   
            if(de_reg2 == 1'b1)
                begin
                    if(ctrl_2 == 1'b1)
                        begin
                            data_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] == 1'b0) ? (cnt + q_m_n0 - q_m_n1) : (cnt + q_m_n1 - q_m_n0);
                        end
                    else
                        begin
                            if(ctrl_3 == 1'b1)
                                begin
                                    data_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);
                                end
                            else
                                begin
                                    data_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);
                                end
                            
                        end
                end
            else
                begin
                    case({vsync_reg2,hsync_reg2})
                        2'b00:  data_out <= DATA_OUT0;
                        2'b01:  data_out <= DATA_OUT1;
                        2'b10:  data_out <= DATA_OUT2;
                        default:data_out <= DATA_OUT3;
                    endcase
                    cnt <=  5'b0;                    
                end
        end    
endmodule

hdmi_colorbar:

cpp 复制代码
module hdmi_colorbar
(
    input   wire        sys_clk     ,
    input   wire        sys_rst_n   ,
    
    output  wire        ddc_scl     ,
    output  wire        ddc_sda     ,//scl与sda是iic协议用来读取显示器内部状态,不用需要拉高输出
    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     
);

wire    clk_1x;
wire    clk_5x;
wire    locked;
wire    rst_n;
wire    hsync;
wire    vsync;
wire    rgb_valid;
wire    [11:0]  pix_x;
wire    [11:0]  pix_y;
wire    [15:0]  pix_data;
wire    [15:0]  rgb;


assign  rst_n = (sys_rst_n & locked);
assign  ddc_scl = 1'b1;
assign  ddc_sda = 1'b1;

//--------clk_gen_inst--------
clk_gen	clk_gen_inst (
	.areset (~sys_rst_n),
	.inclk0 (sys_clk   ),
	.c0     (clk_1x    ),
	.c1     (clk_5x    ),
	.locked (locked    )
);

//--------vga_pic_inst--------
vga_pic vga_pic_inst
(
    .vga_clk     (clk_1x  ),
    .sys_rst_n   (rst_n   ),
    .pix_x       (pix_x   ),    
    .pix_y       (pix_y   ),
  
    .pix_data    (pix_data)
);
//--------vga_crtl_inst--------
vga_crtl    vga_crtl_inst
(
    .vga_clk     (clk_1x   ),
    .sys_rst_n   (rst_n    ),
    .pix_data    (pix_data ),
 
    .pix_x       (pix_x    ),
    .pix_y       (pix_y    ),
    .hsync       (hsync    ),
    .vsync       (vsync    ),
    .rgb_valid   (rgb_valid),
    .vga_rgb     (rgb      )
);

hdmi_ctrl   hdmi_ctrl_inst
(
    .clk_1x     (clk_1x           ), //输入系统时钟
    .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            ), //场同步信号
    .rgb_valid  (rgb_valid        ), //使能信号

    .hdmi_clk_p (hdmi_clk_p       ),
    .hdmi_clk_n (hdmi_clk_n       ), //时钟差分信号
    .hdmi_r_p   (hdmi_r_p         ),
    .hdmi_r_n   (hdmi_r_n         ), //红色分量差分信号
    .hdmi_g_p   (hdmi_g_p         ),
    .hdmi_g_n   (hdmi_g_n         ), //绿色分量差分信号
    .hdmi_b_p   (hdmi_b_p         ),
    .hdmi_b_n   (hdmi_b_n         )  //蓝色分量差分信号
);

endmodule

hdmi_ctrl:

cpp 复制代码
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       rgb_valid  , //使能信号
    
    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 encode_inst0 -------------
encode encode_inst0
(
    .vga_clk    (clk_1x   ),
    .sys_rst_n  (sys_rst_n),
    .data_in    (rgb_blue ),
    .hsync      (hsync    ),
    .vsync      (vsync    ),
    .rgb_valid  (rgb_valid),
    .data_out   (blue     )
);

//------------- encode encode_inst1 -------------
encode encode_inst1
(
    .vga_clk    (clk_1x   ),
    .sys_rst_n  (sys_rst_n),
    .data_in    (rgb_green),
    .hsync      (hsync    ),
    .vsync      (vsync    ),
    .rgb_valid  (rgb_valid),
    .data_out   (green    )
);

//------------- encode encode_inst2 -------------
encode encode_inst2
(
    .vga_clk    (clk_1x   ),
    .sys_rst_n  (sys_rst_n),
    .data_in    (rgb_red  ),
    .hsync      (hsync    ),
    .vsync      (vsync    ),
    .rgb_valid  (rgb_valid),
    .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

par_to_ser:

cpp 复制代码
module par_to_ser
(
    input   wire        clk_5x      , //输入系统时钟
    input   wire [9:0]  par_data    , //输入并行数据
    
    output  wire        ser_data_p  , //输出串行差分数据
    output  wire        ser_data_n    //输出串行差分数据
);

//直接赋值,就省略了复位信号赋初值
//wire define
wire [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 define
reg [4:0] data_rise_s = 0;
reg [4:0] data_fall_s = 0;
reg [2:0] cnt = 0;


always @ (posedge clk_5x)
    begin
        cnt <= (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 

//------------- ddio_out_inst0 -------------
ALTDOIO_OUT	ALTDOIO_OUT_inst0(
	.datain_h (data_rise_s[0]),
	.datain_l (data_fall_s[0]),
	.outclock (~clk_5x       ),
	.dataout  (ser_data_p    )
);
//------------- ddio_out_inst1 -------------
ALTDOIO_OUT	ALTDOIO_OUT_inst1(
	.datain_h (~data_rise_s[0]),
	.datain_l (~data_fall_s[0]),
	.outclock (~clk_5x        ),
	.dataout  (ser_data_n     )
);

endmodule

vga_crtl

cpp 复制代码
module vga_crtl
(
    input   wire            vga_clk     ,
    input   wire            sys_rst_n   ,
    input   wire    [15:0]  pix_data    ,
    
    output  wire    [9:0]   pix_x       ,
    output  wire    [9:0]   pix_y       ,
    output  wire            hsync       ,
    output  wire            vsync       ,
    output  wire            rgb_valid   ,
    output  wire    [15:0]  vga_rgb     
);

parameter H_SYNC    = 10'd96   ,
          H_BACK    = 10'd40   ,
          H_LEFT    = 10'd8    ,
          H_VAILD   = 10'd640  ,
          H_RIGHT   = 10'd8    ,
          H_FRONT   = 10'd8    ,
          H_TOTAL   = 10'd800  ;
          
parameter V_SYNC    = 10'd2    ,
          V_BACK    = 10'd25   , 
          V_TOP     = 10'd8    ,
          V_VAILD   = 10'd480  ,  
          V_BOTTOM  = 10'd8    ,
          V_TOTAL   = 10'd525  ;  


reg     [9:0]   cnt_h       ;
reg     [9:0]   cnt_v       ;
//wire            rgb_valid   ;
wire            pix_data_req;   //因为在仿真中pix_data会因为时序逻辑延迟一拍,所以通过pix_data_req将pix_x都提前一个值开始计数,这样仿真中的pix_data可以提前一拍被赋值,正好弥补时序逻辑延迟的一拍;

always@(posedge vga_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_h <= 10'd0;
    else if(cnt_h == H_TOTAL - 1'b1)
        cnt_h <= 10'd0;
    else
        cnt_h <= cnt_h + 1'b1;
        
always@(posedge vga_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_v <= 10'd0;
    else if((cnt_v == V_TOTAL - 1'b1) && (cnt_h == H_TOTAL - 1'b1))
        cnt_v <= 10'd0;
    else if(cnt_h == H_TOTAL - 1'b1)
        cnt_v <= cnt_v + 1'b1;
    else
        cnt_v <= cnt_v;
        
assign rgb_valid = (((cnt_h >= H_SYNC + H_BACK + H_LEFT) 
                    && (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VAILD))
                    && ((cnt_v >= V_SYNC + V_BACK + V_TOP)
                    && (cnt_v < V_SYNC + V_BACK + V_TOP + V_VAILD)))
                    ? 1'b1 : 1'b0;
                    
//pix_data_req:像素点色彩信息请求信号,超前rgb_valid信号一个时钟周期
assign pix_data_req = (((cnt_h >= H_SYNC + H_BACK + H_LEFT - 1'b1) 
                    && (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VAILD - 1'b1))
                    && ((cnt_v >= V_SYNC + V_BACK + V_TOP)
                    && (cnt_v < V_SYNC + V_BACK + V_TOP + V_VAILD)))
                    ? 1'b1 : 1'b0;
                    
assign pix_x = (pix_data_req == 1'b1) ? (cnt_h - (H_SYNC + H_BACK + H_LEFT - 1'b1)):10'h3ff;
assign pix_y = (pix_data_req == 1'b1) ? (cnt_v - (V_SYNC + V_BACK + V_TOP)):10'h3ff;
assign hsync = (cnt_h <= H_SYNC - 1'd1) ? 1'b1 : 1'b0;
assign vsync = (cnt_v <= V_SYNC - 1'd1) ? 1'b1 : 1'b0;
assign vga_rgb = (rgb_valid == 1'b1) ? pix_data : 16'h0000;

endmodule

vga_pic

cpp 复制代码
module  vga_pic
(
    input   wire            vga_clk     ,
    input   wire            sys_rst_n   ,
    input   wire    [9:0]   pix_x       ,    
    input   wire    [9:0]   pix_y       ,

    output  reg    [15:0]   pix_data   
);

parameter   H_VALID = 10'd640,
            V_VALID = 10'd480;
            
parameter   RED      = 16'hF800,
            ORANGE   = 16'hFC00,
            YELLOW   = 16'hFFE0,
            GREEN    = 16'h07E0,
            CREEN    = 16'h07FF,
            CYAN     = 16'h07FF,
            BLUE     = 16'h001F,
            PURPPLE  = 16'hF81F,
            BLACK    = 16'h0000,
            WHITE    = 16'hFFFF,
            GRAY     = 16'hD69A;

always@(posedge vga_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        pix_data <= BLACK;
    else    if(pix_x >= 0 && pix_x < ((H_VALID / 10) * 1))
        pix_data <= RED;
    else    if(pix_x >= ((H_VALID / 10) * 1) &&  pix_x < ((H_VALID / 10) * 2))
        pix_data <= ORANGE;
    else    if(pix_x >= ((H_VALID / 10) * 2) &&  pix_x < ((H_VALID / 10) * 3))
        pix_data <= YELLOW;
    else    if(pix_x >= ((H_VALID / 10) * 3) &&  pix_x < ((H_VALID / 10) * 4))
        pix_data <= GREEN;
    else    if(pix_x >= ((H_VALID / 10) * 4) &&  pix_x < ((H_VALID / 10) * 5))
        pix_data <= CYAN;
    else    if(pix_x >= ((H_VALID / 10) * 5) &&  pix_x < ((H_VALID / 10) * 6))
        pix_data <= BLUE;
    else    if(pix_x >= ((H_VALID / 10) * 6) &&  pix_x < ((H_VALID / 10) * 7))
        pix_data <= PURPPLE;
    else    if(pix_x >= ((H_VALID / 10) * 7) &&  pix_x < ((H_VALID / 10) * 8))
        pix_data <= BLACK;
    else    if(pix_x >= ((H_VALID / 10) * 8) &&  pix_x < ((H_VALID / 10) * 9))
        pix_data <= WHITE;
    else    if(pix_x >= ((H_VALID / 10) * 9) &&  pix_x < (H_VALID))
        pix_data <= GRAY;
    else
        pix_data <= BLACK;

endmodule

抱歉 项目吃紧,写的有点乱,挖个坑,有空来优化

相关推荐
我想学LINUX11 小时前
基于Zynq FPGA对雷龙SD NAND的测试
嵌入式硬件·学习·fpga开发·sd nand·雷龙开发
apple_ttt1 天前
SystemVerilog学习——构造函数new
fpga开发·fpga·systemverilog·uvm
zxfeng~1 天前
AG32 FPGA部分简单开发
fpga开发·嵌入式·ag32
CodingCos1 天前
【OpenOCD 与 FT4232H 专栏 4 -- FT4232H 实现 USB 转 GPIO】
fpga开发·ft432h·usb 转 gpio
乘风~&2 天前
fpga异步fifo
fpga开发
szxinmai主板定制专家2 天前
基于ARM+FPGA的电力通信管理机IEC61850规约通信机的实现
fpga开发
stm 学习ing2 天前
FPGA 第7讲 简单组合逻辑译码器
stm32·嵌入式硬件·学习·fpga开发·c#·学习方法·fpga
水饺编程2 天前
【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,2-24
linux·嵌入式硬件·fpga开发·硬件架构
KKK3号2 天前
Verilog HDL学习笔记
嵌入式硬件·fpga开发·verilog hdl·1024程序员节
乘风~&2 天前
fpga 同步fifo
fpga开发