目录

1.理论

2.实操

2.1 顶层模块

2.2 时钟生成模块

2.3 HDMI 驱动控制模块

2.3.1 编码模块

2.3.2 并行转串行模块

2.4 顶层仿真验证

3.总结

1.理论

HDMI简介

VGA 接口体积较大;且传输的模拟信号易受外界干扰。因此在VGA 接口之后,首先推出的是 DVI 接口, DVI 是基于 TMDS(Transition MinimizedDifferential Signaling, 最小化传输差分信号)技术来传输数字信号。

DVI 接口设计之初考虑的对象是 PC,对于平板电视的兼容能力一般;只支持计算机领域的 RGB 数字信号,而对数字化的色差信号无法支持;只支持 8bit 的 RGB 信号传输,不能让广色域的显示终端发挥出最佳性能;出于兼容性考虑,预留了不少引脚以支持模拟设备,造成接口体积较大(比VGA还大);只能传输图像信号,对于数字音频信号完全没有考虑。
       由于以上种种缺陷, 促使了 HDMI 标准的诞生。
       HDMI 全称“High Definition Multimedia Interface 高清多媒体接口”。 2002 年 4 月,来自电子电器行业的 7 家公司——日立、松下、飞利浦、 等七家共同组建了 HDMI 高清多媒体接口接口组织 HDMI Founders(HDMI 论坛),开始着手制定一种符合高清时代标准的全新数字化视频/音频接口技术。

HDMI 标准的制定,没有抛弃 DVI 标准中相对成熟且较易实现的部分技术标准,整个传输原理依然是基于 TMDS 编码技术。 针对 DVI 的诸多问题, HDMI 做了大幅改进具有众多优点。

HDMI接口及引脚定义

在一些便携设备上, HDMI 接口都成了标准化的配置。 HDMI 接口具体见下图。

HDMI 规格书中规定了 HDMI 的 4 种接口类型,但其中 HDMI B Type 接口类型未在市场中出现过,市面上流通最广的是 HDMI A Type、 HDMI C Type 和 HDMI D Type 接口类型。

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

上面看来接口的外观,下面了解串口的引脚定义。

表一: 接口引脚定义

HDMI 接口共有 19 个引脚,分上下两排,奇数在上,偶数在下,穿插排布。根据其功能,可以将引脚分为 4 类。

TMDS 通道:引脚 1-引脚 12。负责发送音频、视频及各种辅助数据
       DDC 通道:引脚 15、 16、 17。 DDC 译文为 Display Data Channel,译为“显示数据通道”; 发送端与接收端可利用 DDC 沟道得知彼此的发送与接收能力(HDMI 仅需单向获知接收端器)。

CEC 通道:引脚 13、 17。 CEC 译文为 Consumer Electronics Control, CEC 通道为必须
预留线路,但可以不必实现,作用是用来发送工业规格的 AV Link 协议信号。

其他通道:引脚 14 位保留引脚,无连接;引脚 18 为+5V 电源;引脚 19 位热插拔检测引脚。

注:另外两类型的HDMI c Type 接口、HDMI D Type 接口与HDMI A Type 接口的各引脚名称、功能相同,只是引脚线序不同。

HDMI显示原理

HDMI 系统架构由信源端(发送端)和接收端组成。

   HDMI 数据传输框图

如上图所示, HDMI 线缆和连接器提供四个差线对,组成 TMDS 数据和时钟通道, 这些通道用于传递视频,音频和辅助数据; 另外, HDMI 提供一个 VESA DDC 通道, DDC 是用于配置和在一个单独的信源端和一个单独的接收端交换状态; 可选择的 CEC在用户的各种不同的音视频产品中, 提供高水平的控制功能; 可选择的 HDMI 以太网和音频返回(HEAC),在连接的设备中提供以太网兼容的网络数据和一个和 TMDS 相对方向的音频回返通道;此外还有热插拔检测信号 HDP, 当显示器等 HDMI 接口的显示设备通过 HDMI 接口与 HDMI 信源端相连或断开连接时, HDMI 信源端能够通过 HPD 引脚检测出这一事件,并做出响应。
       HDMI 采用和 DVI 相同的传输原理 ——TMDS( Transition Minimized Differential signal),最小化传输差分信号。HDMI 中的 TMDS 传输系统分为两个部分:发送端和接收端。(我们需要完成的是发送端的功能) TMDS 发送端表示 RGB 信号的 24 位并行数据(TMDS 对每个像素的 RGB 三原色分别按8bit 编码,即 R 信号有 8 位, G 信号有 8 位, B 信号有 8 位),然后对这些数据和时钟信号进编码和并/串转换,再将表示 3 个 RGB 信号的数据和时钟信号分别分配到独立的传输通道发送出去。接收端接收来自发送端的串行信号,对其进行解码和串/并转换,然后发送到显示器的控制端。与此同时也接收时钟信号,以实现同步。 流程框图如下图所示。

信源端内部流程框图:

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

要实现 TMDS 通道传输,首先要将传入的 8 位并行数据进行编码、并/串转换。

编码过程: 将 8 位并行数据发送到 TMDS 接收端,随后进行最小化传输处理,加上第 9 位,然后进行直流均衡处理,这样8bit数据就编码为10bit。 直流平衡(DC-balanced)就是指在编码过程中保证信道中直流偏移为零,使信道中传输数据包含的 1 与 0 的个数相同。方法是在添加编码位的 9位数据的后面加上第 10 位数据, 保证 10 位数据中 1 与 0 个数相同。这样传输的数据趋于直流平衡,使信号对传输线的电磁干扰减少,提高信号传输的可性。

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

使用上面的方式对 24位图像数据(8 位 R 信号、 8 位 G 信号、 8 位 B 信号)和时钟信号进行处理,将 4 对差分信号通过HDMI 接口发到接收设备;接收设备通过解码读取初始的8bit数据等一系列操作,实现图像和音频再现。


2.实操

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

 硬件资源:

HDMI 接口部分位于板卡的中下部,HDMI 原理图如下图所示。

 HDMI 部分原理图

2.1 顶层模块

 HDMI 彩条显示实验整体框图

由上图可知,本实验工程包括 5 个模块,各模块简介,具体见下表格。

表格 41-2 HDMI 彩条显示工程模块简介

观察上图可知,HDMI 的彩条显示是基于 VGA 彩条显示的基础上的,是在 VGA 彩条显示工程的基础上修改的得到的故vga_pic与vga_ctrl模块不重复说明。其中改动较大的有两部分:一是时钟生成模块的输出时钟频率和时钟个数做了改动;二是增加了 HDMI 驱动控制模块 hdmi_ctr。

RTL代码编写

`timescale  1ns/1nsmodule  hdmi_colorbar
(input   wire            sys_clk     ,   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图像差分信号);wire            vga_clk ;   //VGA工作时钟,频率25MHz
wire            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;assign  rst_n   = (sys_rst_n & (locked));
assign  ddc_scl = 1'b1;
assign  ddc_sda = 1'b1;clk_gen clk_gen_inst
(.RESET      (~sys_rst_n ),  //输入复位信号,高电平有效,1bit.CLK_IN1    (sys_clk    ),  //输入50MHz晶振时钟,1bit.CLK_OUT1   (vga_clk    ), //输出VGA工作时钟,频率25Mhz,1bit.CLK_OUT2   (clk_5x     ), //输出hdmi工作时钟,频率125M,1bit.LOCKED     (locked     )  //输出pll locked信号,1bit
);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 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   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

2.2 时钟生成模块

HDMI 显示模式为 640*480@60,时钟频率为 25MHz,而板卡晶振传入时钟频率为50MHz时钟生成模块的作用就是将 50MHz 晶振时钟分频为 25MHz 的 HDMI 工作时钟;除此之外,还要生成 25MHz 时钟的 5 倍频 125MHz 时钟,具体原因后面讲。

具体实现方法是使用PLL IP核,之前文章有讲解。

2.3 HDMI 驱动控制模块

HDMI 驱动控制模块 hdmi_ctrl 是 HDMI 彩条显示的核心模块,功能是将 VGA 控制模块传入的行场同步信号、图像信息转换为 HDMI 能读取的差分信号,也可以说成是实现 VGA 图像信息到 HDMI 图像信息的转化。实现这一功能的转化,需要对输入的VGA 图像信息进行编码、并行到串行转换、单端信号转差分信号、单沿采样转双沿采样。

  HDMI 驱动控制模块框图

HDMI 驱动控制模块简介:

由上可知HDMI 驱动控制模块共有 17 路输入输出信号,输入信号 9 路,输出信号 8 路信号。

RTL代码:

`timescale  1ns/1nsmodule  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        //蓝色分量差分信号
);wire    [9:0]   red     ;   //8b转10b后的红色分量
wire    [9:0]   green   ;   //8b转10b后的绿色分量
wire    [9:0]   blue    ;   //8b转10b后的蓝色分量//------------- 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

2.3.1 编码模块

完成 VGA 图像数据 8bit 转 10bit 的编码。

编码模块输入输出信号信号功能描述:

 编码模块参考流程图中各参数说明:

依据上面流程图编写编码代码,编码模块wei。

RTL代码:

`timescale  1ns/1nsmodule  encode
(input   wire            sys_clk     ,   //时钟信号input   wire            sys_rst_n   ,   //复位信号,低有效input   wire    [7:0]   data_in     ,   //输入8bit待编码数据input   wire            c0          ,   //控制信号c0input   wire            c1          ,   //控制信号c1input   wire            de          ,   //使能信号output  reg     [9:0]   data_out        //输出编码后的10bit数据
);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信号打一拍//data_in_n1:待编码数据中1的个数
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)data_in_n1  <=  4'd0;elsedata_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;elsedata_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'b0)));//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)beginq_m_n1  <=  4'd0;q_m_n0  <=  4'd0;endelsebeginq_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

说明:这里本人也没看懂仅贴图供大家学习,同时推荐观看B站野火视频 149-第四十三讲-HDMI显示器驱动设计与验证(五)_哔哩哔哩_bilibili。

2.3.2 并行转串行模块

实现并行到串行转换、单端信号转差分信号、单沿采样转双沿采样。

并行转串行模块输入输出信号信号功能描述

注意: 时钟信号 clk_1x 与 clk_5x,时钟频率为 5 倍关系,因为并行数据信号 par_data 位宽 10bit,若转换为串行信号,需要在时钟信号 clk_1x 的一个时钟周期内完成数据转换,故转换后的串行数据信号的同步时钟频率必须为 clk_1x 的 10 倍,使用双沿采样则为 5 倍。

不直接采用 10 倍时钟而是使用 5 倍时钟加上 ODDR2 双边沿转换的原因,是对于 10 倍信号内部逻辑约束难以达到要求,而且使用 5 倍时钟发热量也远低于 10 倍。

如何实现5 倍时钟双边沿输出数据以及单端转差分呢?

在软件中打开原语的模板,进行调用双数据速率原语 ODDR2 和单端转差分的原语,具体步骤如下图所示。 原语可以看成是一种 IP 核,只不过实现的功能较为简单,也更加接近硬件,能直接调用 FPGA 内部的一些器件,实现一些基础功能。

右边可看到 ODDR2 和OBUFDS 原语,选择复制添加到工程文件即可例化。

 波形图绘制

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

第一步:将输入的 10bit 并行数据 par_data 拆分为两个位宽 5bit 的数据信号,变量 data_rise[4:0]、变量 data_fall[4:0]。拆分规则 : 将 会 在 时 钟 上 升 沿 输 出 的 par_data[8] 、 par_data[6] 、 par_data[4] 、 par_data[2] 、par_data[0]赋值给变量 data_rise[4:0];将会在时钟下降沿输出的 par_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]分别写入 ODDR2 原语的 D0, D1 接口;同时,每个时钟周期将 data_rise_s、 data_fall_s 右移一位,进行输出。同时再调用 OBUFDS 原语,将串行双沿采样信号 data 写入 OBUFDS 原语的输入接口 I,输出的串行双沿采样信号 O 与 OB 与之前生成的串行双沿采样信号,构成差分信号对。

RTL代码编写

`timescale  1ns/1nsmodule 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 data;
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)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//ODDR2原语
//将单边沿时钟信号转换为双边沿时钟信号
//5倍时钟双边沿输出数据等价为10倍时钟单边沿输出数据
ODDR2 #(.DDR_ALIGNMENT("NONE"), //设置输出对齐方式 "NONE", "C0" or "C1" .INIT         (1'b0  ), // 设置初始化输出电平.SRTYPE       ("SYNC")  // 同步复位 "SYNC" or 异步复位"ASYNC" set/reset
) ODDR2_inst0 (.Q (data          ),   //输出ddr双边沿数据.C0(~clk_5x       ),   //上升沿时钟.C1(clk_5x        ),   // 下降沿时钟.CE(1'b1          ), // 使能输入.D0(data_rise_s[0]), // 上升沿数据.D1(data_fall_s[0]), // 上升沿数据.R (1'b0          ),   // 复位输入,不复位.S (1'b0          )    // 置位输入,不置位
);//OBUFDS原语
//将单端信号转换为差分信号,约束为TMDS33电平
OBUFDS #(.IOSTANDARD("TMDS_33") //约束电平为TMDS33
) OBUFDS_inst (.O (ser_data_p), //差分信号正极性输出.OB(ser_data_n), //差分信号正极性输出.I (data      )  //单端信号输入
);endmodule

2.4 顶层仿真验证

`timescale  1ns/1nsmodule  tb_hdmi_colorbar();
//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   define
reg             sys_clk     ;
reg             sys_rst_n   ;//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  ;//------------- 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

3.总结

HDMI显示是在vga的基础上,增加了TMDS传输技术的显示方式。难点就在于理解TMDS中的8bit转10bit编码技术。

说明:

本人使用的是野火家Xilinx Spartan6系列开发板及配套教程主要用于自我学习,以上内容如有疑惑或错误欢迎评论区指出,或者移步B站观看野火家视频教程。

开发软件:ise14.7     仿真:modelsim 10.5

如需上述资料私信或留下邮箱。

FPGA—HDMI 显示器驱动设计与验证(附代码)相关推荐

  1. HDMI显示器驱动设计与验证

    HDMI显示器驱动设计与验证 HDMI组成 HMDI数据传输图 TMDS信号连接图 HDMI彩条显示实验整体框图

  2. FPGA的学习:HDMI显示器驱动设计与验证

    HDMI的顶层模块设计如图所示: 其中包含了一个时钟产生电路,vga_pic模块,vga_ctrl模块,hdmi_ctrl模块. 各个模块的设计图如下: 其中还要用到一个encode模块: 还要用到p ...

  3. VGA显示器驱动设计与验证

    VGA显示器驱动设计与验证 1.VGA显示原理 VGA显示器显示图像,并不是直接让图像在显示器上显示出来,而是采用扫描的方式,将构成图像的像素点,在行同步信号和场同步信号的同步下,按照从上到下.由左到 ...

  4. VGA/HDMI显示器驱动设计

    浅谈VGA显示器 1.VGA显示器视频时序 2.VGA时序参数的定义 //480x272 9Mhz `ifdef VIDEO_480_272 parameter H_ACTIVE = 16'd480; ...

  5. HDMI显示驱动设计及验证

    项目场景: HDMI:High Definition Multimedia Interface(高清多媒体接口)2004 VGA:Video Graphics Array(视频图形阵列)1987 DV ...

  6. TFT显示屏驱动设计与验证

    TFT显示屏驱动设计与验证 RGB 接口的 TFT 屏扫描方式和 VGA (Video Graphics Array)标准类似,也是使用行列扫描的方式.在介绍 TFT 屏扫描原理之前,先来介绍下 VG ...

  7. TFT_LCD液晶屏驱动设计与验证

    TFT_LCD液晶屏驱动设计与验证 注:在本实验工程中,输出信号中包含 HV 同步模式下需要的行.场同步信号(hsync. vsync)和 DE 同步模式下的 tft_de 信号,各信号正确输出.读者 ...

  8. 线性序列机与串行接口ADC驱动设计与验证

    线性序列机与串行接口ADC驱动设计与验证 ADC128S022 型 ADC 内部工作原理 在 AC620 开发板上使用的模数转换器为逐次逼近型的低功耗芯片 ADC128S022,其具有 8 通道以及 ...

  9. 线性序列机与串口接口DAC驱动设计与验证

    线性序列机与串口接口DAC驱动设计与验证 TLV5618 型 DAC 内部工作原理 TLV5618 是一个基于电压输出型的双通道 12 位单电源数模转换器,其由串行接口.一个速度和电源控制器. 电阻网 ...

最新文章

  1. VS2017-MFC-生成二维码测试小程序
  2. js进阶 9-11 select选项框如何动态添加和删除元素
  3. 成都工业学院计算机工程学院院长,青春的交接礼——成都工业学院计算机工程学院...
  4. python封装exe 时间time问题_python模块之datetime
  5. php是阻塞模式吗,PHP非阻塞模式 - 黑白大熊猫的个人空间 - OSCHINA - 中文开源技术交流社区...
  6. for语句 2017-03-17
  7. docker compose详解
  8. 每日小记2017.2.20
  9. 在Nutz中如何配置多个数据库源,并且带事务控制
  10. 决策树-线性二分类+非线性二分类
  11. FMPlayer组件说明
  12. OA打造企业“最强大脑”
  13. 【java与智能设备】01_1Android简介与环境搭建
  14. 免苹果开发者账号申请iOS上架及证书打包ipa测试(2022最新详解)
  15. GHO是什么文件 与ISO镜像文件有什么不同
  16. 解决WiFi共享大师频繁掉线问题
  17. 创意简约土木黑灰配色PPT模板
  18. (10.3.5.6)软件验收报告文档模版
  19. 最好用的 Base64 在线工具
  20. 耶鲁大学开放课程:《金融市场》第3课

热门文章

  1. WinCC 变量记录和报警记录备份数据连接后不显示
  2. 代码审计--2--认证会话管理(上)
  3. 判断两个变量的正负号不同
  4. 架构设计主题阅读书目
  5. 网卡MAC地址怎么查
  6. border绘制边框
  7. wia Kodak 扫描仪应用 winform
  8. Javascript异步编程方法有哪些
  9. SuperMap GIS三维插件使用QA
  10. vue3 + vite引入scss全局变量