HDMI接口应用

1.简介

HDMI接口的全称是高清多媒体接口(High Definition Multimedia Interface),最初在2004年出现,能够同时传输视频与音频信号,而且质量很高。比起VGA的模拟信号传输,HDMI传输数字信号可提供质量更好的图像信号;比起DVI仅传输视频信号,HDMI可以传输除视频信号之外的音频信号、字幕信号或其他的控制信号。目前的常用HDMI接口有HDMI1.4、2.0和2.1,分别支持最高2k60hz、4K60hz和4K120hz(8K60hz)。

使用FPGA实现HDMI接口可以采用芯片实现,如ADV7513,CH7301,采用这种方案只需要将RGB信号接入到芯片就可以,芯片自动完成编码和电平转换等工作,有一个缺陷是占用的管脚过多(大概需要30根左右)。从Xilinx 在 Spartan-3A 系列之后的器件中,加入了对TMDS接口标准的支持,用于在FPGA内部实现DVI和HDMI接口,使得仅需9根线即可完成HDMI的最简应用,由此TMDS编码的工作自然需要FPGA电路实现了。需要注意的一点是,当作为输出设备时,应通过HDMI接口输出5V电源;作为输入设备时,接口上的5V电源将由外部连接设备提供,同时,HDMI_HPD 将输出高电平,用于指示外部设备接入时HDMI连接的状态。

标准的HDMI接口有以下四种,分别称为ABCD四种接口,如图所示,也称作standard HDMI、Mini HDMI、Micro HDMI。

常见的A型接口有19根线,如图所示,1-9九根线是三个数据差分对加一个数据屏蔽线,10-12是时钟差分线和时钟屏蔽线。13是CEC管脚,指的是用户电气控制(Consumer Electronics Control),它用于 HDMI 连接线上的设备之间进行信息交换。当一个设备的状态发生变化时,CEC可以使用远程控制或自动改变设置来命令连接的关联设备的状态发生相应的变化。19是HPD管脚,指的是热拔插检测(Hot Plug Detect),当视频设备与接收设备通过 HDMI 连接时,接收设备将 HPD 置为高电平,通知发送设备。当发送设备检测到HPD为低电平时,表明断开连接。18是5V电压管脚,作为输出设备时,将为接收设备提供5V电压。15 16是DDC管脚即HDMI接口的显示数据通道(DDC,Display Data Channel),用于HDMI发送端和接收端之间交换一些配置信息,通过I2C协议通信,一根是SDA线一根是SCL线。发送端通过DDC通道,读取接收端保存在EEPROM中的EDID数据(EDID有其特定的规范格式),获取接收端的信息,确认接收端终端显示的设置和功能,决定跟接收端之间以什么格式传输音/视频数据。

2.场景与步骤

使用FPGA驱动HDMI设备,构建一个场景是,用显示屏通过HDMI连接线与带有HDMI接口FPGA板卡相连,使显示屏以1280*720@60Hz显示一个彩条,彩条的顺序是绿蓝红黑白。该应用可用作新制板卡的接口测试,也可以换做其他的数据源做摄像头类应用,也可以换做其他格式的分辨率。

从“简介”中可以看到,驱动HDMI接口就是与HDMI相连的FPGA管脚输出要求的数据即可。主要是通过三对差分数据线,和一对时钟差分线。将FPGA驱动HDMI显示彩条分为以下几个步骤,即①准备源数据,即按照VESA规定的标准视频时序参数,生成行场同步信号、数据使能信号以及彩条的RGB像素数据;②根据数据使能信号为像素数据和行场同步信号编码,编码规则为TMDS编码(具体随后介绍);③将编码后的数据进行10:1的串并转换,随后进行单端转差分的操作,即可发送到数据线上,并在对端相连的屏幕上看到彩色条纹的显示。关于什么是TMDS编码?为啥要做10:1的串并转换,将在下一小节结合代码做阐述。

3.代码与原理

其工程的代码结构如上图所示,Top模块完成时钟管理模块、v_fer、v_driver三个子模块的例化。代码如下。时钟管理模块是通过clk ip核完成的,板卡上的晶振输入为50MHz,生成74.25Mhz(绿色),和371.25Mhz(蓝色),前者用于像素时钟,这是由标准的时序参数决定的,不同分辨率和帧率对应不同的像素时钟,可以在http://tinyvga.com/vga-timing查询,时序参数包括了像素时钟和行显示后沿,显示有效时间等参数。后者用于串并转换,这是由于10:1的串并转换关系加上使用DDR模式决定该时钟是五倍于像素时钟。

module hdmi_top(input        sys_clk,output       tmds_clk_p,    // TMDS 时钟通道output       tmds_clk_n,output [2:0] tmds_data_p,   // TMDS 数据通道output [2:0] tmds_data_n// output       tmds_oen      // TMDS 输出使能(用于HDMI输入输出选择));
wire    pix_clk;
wire    sys_rst_n;
clk_wiz_0 u0_clk(// Clock out ports.clk_out1(pix_clk),     // output clk_out1.clk_out2(pix_5clk),     // output clk_out2.reset(1'b0),           // input reset.locked(sys_rst_n),       // output locked.clk_in1(sys_clk));      // input clk_in1wire    video_hs;
wire    video_vs;
wire    video_de;
wire[23:0]video_rgb;
//根据标准视频1280*720@60Hz时序参数, 产生行场同步信号
v_driver u1_driver(.pix_clk        (pix_clk),.sys_rst_n      (sys_rst_n),.video_hs       (video_hs),.video_vs       (video_vs),.video_de       (video_de),.video_rgb      (video_rgb)
);
v_xfer u2_xfer(.pix_clk        (pix_clk),.pix_5clk       (pix_5clk),.sys_rst_n      (sys_rst_n),        .video_rgb      (video_rgb),.video_hs       (video_hs), .video_vs       (video_vs),.video_de       (video_de),.tmds_clk_p     (tmds_clk_p),.tmds_clk_n     (tmds_clk_n),.tmds_data_p    (tmds_data_p),.tmds_data_n    (tmds_data_n)// .tmds_oen       (tmds_oen));
endmodule

v_driver模块用来产生彩条数据及按钮标准视频时序参数产生行场同步信号和数据使能信号,使用parameter定义各个视频显示时序参数,可以在显示其他分辨率时仅修改此处定义。该模块逻辑简单清晰,由像素时钟上升沿触发,计h_cnt和v_cnt两个数,分别由这两个数和时序参数比较得到行场同步信号video_hs和video_vs,视频显示使能信号video_de,以及当前扫描坐标pos_x和pos_y。根据当前扫描坐标将彩条的RGB数据赋值给像素数据pix_data,传出模块做下一步处理。

module v_driver(input   pix_clk,input   sys_rst_n,output  video_hs,output  video_vs,output  video_de,output  [23:0]video_rgb// output  [10:0] pos_x,// output  [10:0] pos_y);
//标准时序参数parameter       HH_SYNC = 11'd40;  //行同步parameter       HH_BACK = 11'd220; //行显示后沿,靠近行同步parameter       HH_VALID= 11'd1280;//行有效数据parameter       HH_FRONT= 11'd110; //行显示前沿parameter       HH_TOTAL= 11'd1650;//行扫描周期parameter       VV_SYNC = 11'd5;   //场同步parameter       VV_BACK = 11'd20;  //场显示后沿parameter       VV_VALID= 11'd720; //场有效数据parameter       VV_FRONT= 11'd5;   //场显示前沿parameter       VV_TOTAL= 11'd750; //场扫描周期
//颜色数据localparam WHITE  = 24'b11111111_11111111_11111111;  //RGB888 白色localparam BLACK  = 24'b00000000_00000000_00000000;  //RGB888 黑色localparam RED    = 24'b11111111_00001100_00000000;  //RGB888 红色localparam GREEN  = 24'b00000000_11111111_00000000;  //RGB888 绿色localparam BLUE   = 24'b00000000_00000000_11111111;  //RGB888 蓝色    reg     [10:0]h_cnt;reg     [10:0]v_cnt;always @(posedge pix_clk or negedge sys_rst_n) beginif(!sys_rst_n)beginh_cnt <= 1'd0;end else if(h_cnt < HH_TOTAL - 1'd1)beginh_cnt <= h_cnt + 11'd1;end else h_cnt <= 11'd0;endalways @(posedge pix_clk or negedge sys_rst_n) beginif(!sys_rst_n)beginv_cnt <= 1'd0;end else if(h_cnt == HH_TOTAL - 1'd1)beginif(v_cnt < VV_TOTAL - 1'd1)v_cnt <= v_cnt + 1'd1;else v_cnt <= 11'd0;end else v_cnt <= v_cnt;end//产生行场同步信号assign video_hs= (h_cnt < HH_SYNC) ? 1'b0 : 1'b1;assign video_vs= (v_cnt < VV_SYNC) ? 1'b0 : 1'b1;//产生HDMI数据使能信号assign video_de= (h_cnt>=HH_SYNC + HH_BACK)&&(h_cnt<HH_SYNC + HH_BACK + HH_VALID)&&(v_cnt>=VV_SYNC + VV_BACK)&&(v_cnt<VV_SYNC + VV_BACK + VV_VALID)?  1'b1 : 1'b0;//产生坐标wire [10:0]pos_x;wire [10:0]pos_y;assign pos_x = video_de ? (h_cnt - (HH_SYNC + HH_BACK)) : 11'b0;assign pos_y = video_de ? (v_cnt - (VV_SYNC + VV_BACK)) : 11'b0;//根据坐标产生数据reg [23:0]pix_data;assign video_rgb = video_de ? pix_data : 24'd0;always @(*) begin//屏幕颜色顺序为绿、蓝、红、黑、白if((pos_x >=0)&&(pos_x < (HH_VALID/5)*1))   //把屏幕分成5份,这是第一份pix_data = GREEN;else if((pos_x >= (HH_VALID/5)*1)&&pos_x < (HH_VALID/5)*2)pix_data = BLUE;else if((pos_x >= (HH_VALID/5)*2)&&pos_x < (HH_VALID/5)*3)pix_data = RED;else if((pos_x >= (HH_VALID/5)*3)&&pos_x < (HH_VALID/5)*4)pix_data = BLACK;elsepix_data = WHITE;endila_0 my_monitor (.clk(pix_clk), // input wire clk.probe0(video_rgb[23:0]), // input wire [23:0]  probe0  .probe1({video_de,video_hs,video_vs}), // input wire [2:0]  probe1 .probe2({pos_x,pos_y}) // input wire [21:0]  probe2
);
endmodule

v_xfer模块由top模块例化,接收v_driver模块产生的像素数据和行场同步信号,通过对v_encoder、serializer_10_to_1模块以及OBUFDS原语的例化,完成数据的编码、串并转换、单端转差分,最终将数据发送到管脚,完成HDMI彩条显示,代码如下。

module v_xfer(input   pix_clk,input   pix_5clk,input   sys_rst_n,input[23:0]video_rgb,input   video_hs,input   video_vs,input   video_de,output  tmds_clk_p,output  tmds_clk_n,output[2:0]tmds_data_p,output[2:0]tmds_data_n);//对三个颜色通道进行编码
//并行数据
wire [9:0]  red_10bit;
wire [9:0]  green_10bit;
wire [9:0]  blue_10bit;
wire [9:0]  clk_10bit;
v_encoder encoder_b (.clk_in     (pix_clk),.reset_n    (sys_rst_n),.din        (video_rgb[7:0]),.tmds_c0    (video_hs),.tmds_c1    (video_vs),.tmds_de    (video_de),.dout       (blue_10bit)) ;
v_encoder encoder_g (.clk_in     (pix_clk),.reset_n    (sys_rst_n),.din        (video_rgb[15:8]),.tmds_c0    (1'b0),.tmds_c1    (1'b0),.tmds_de    (video_de),.dout       (green_10bit)) ;
v_encoder encoder_r (.clk_in     (pix_clk),.reset_n    (sys_rst_n),.din        (video_rgb[23:16]),.tmds_c0    (1'b0),.tmds_c1    (1'b0),.tmds_de    (video_de),.dout       (red_10bit)) ;
wire [2:0]  tmds_data_serial;
wire        tmds_clk_serial;
assign clk_10bit = 10'b1111100000;
// assign clk_10bit = 10'b1010101010;
serializer_10_to_1 serializer_b(.reset              (!sys_rst_n),                // 复位,高有效.paralell_clk       (pix_clk),                 // 输入并行数据时钟.serial_clk_5x      (pix_5clk),              // 输入串行数据时钟.paralell_data      (blue_10bit),           // 输入并行数据.serial_data_out    (tmds_data_serial[0])   // 输出串行数据);
serializer_10_to_1 serializer_g(.reset              (!sys_rst_n),.paralell_clk       (pix_clk),.serial_clk_5x      (pix_5clk),.paralell_data      (green_10bit),.serial_data_out    (tmds_data_serial[1]));
serializer_10_to_1 serializer_r(.reset              (!sys_rst_n),.paralell_clk       (pix_clk),.serial_clk_5x      (pix_5clk),.paralell_data      (red_10bit),.serial_data_out    (tmds_data_serial[2]));
serializer_10_to_1 serializer_clk(.reset              (!sys_rst_n),.paralell_clk       (pix_clk),.serial_clk_5x      (pix_5clk),.paralell_data      (clk_10bit),.serial_data_out    (tmds_clk_serial));
//转换差分信号
OBUFDS #(.IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
) TMDS0 (.I                  (tmds_data_serial[0]),.O                  (tmds_data_p[0]),.OB                 (tmds_data_n[0])
);
OBUFDS #(.IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
) TMDS1 (.I                  (tmds_data_serial[1]),.O                  (tmds_data_p[1]),.OB                 (tmds_data_n[1])
);
OBUFDS #(.IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
) TMDS2 (.I                  (tmds_data_serial[2]), .O                  (tmds_data_p[2]), .OB                 (tmds_data_n[2])
);
OBUFDS #(.IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
) TMDS3 (.I                  (tmds_clk_serial), .O                  (tmds_clk_p),.OB                 (tmds_clk_n)
);
endmodule

v_encoder模块由v_xfer模块例化,理清这段代码的逻辑,先要对TMDS编码规则有了解。TMDS 编码包含两个大的内容,传输控制信号的控制段和用来传输图像像素数据的数据段。一个完整的图像传输 TMDS 模块包含三个相同的编码和发送模块。每个发送模块包含 8 位的像素数据,对应 24 位像素数据中单个颜色的 8 位数据。2 个控制信号,这两个控制信号可以分别接行同步(HSYNC)、场同步(VSYNC)信号,也可以空置接 0。另外还有一个数据有效信号 DE(video_de信号),该信号用来区分控制数据和像素数据。当 DE 信号为高电平的时候,表明当前数据有效,编码器对 8 位的 Data 数据进行编码。当 DE 信号为低电平的时候,则对 2 位的控制信号进行编码。

TMDS(Transition-minimized differential signaling)最小化传输差分信号,所谓最小化传输,其本质就是要通过对输入数据的变换,得到一个跳变次数最少的新数据。这样做的好处有两点,第一,信号跳变过程中就必然会出现信号线高低电平的变化,信号的变化就会产生磁场。对信号线产生电磁干扰。所以,越少的信号翻转次数,就会产生越小的电磁干扰。第二,TMDS 数据通道传送的是一个连续的10bit TMDS字符流,在行场消隐期间,传送4个有显著特征的字符,它们直接对应编码器的2个控制信号的4个可能的状态。在数据有效期间,10bit的字符包含8bit的像素数据,编码的字符提供近似的DC平衡,所谓直流平衡,就是指信号在传输中0和1的数据个数相同,则发送方和接收方之间就不会有直流电流的传递,在通信系统中,直流平衡可以有效避免由于收发两端电压不稳引起的问题。

实现TMDS编码按照一定的算法即可。如下图所示,为编码器的示意图。三个相同的编码器,均为输入8bit像素数据,一个数据使能信号DE,以及两个控制数据。对于行场消隐期间(即DE信号为低电平时)传输的传输的控制信号(含行场同步信号、配音信号、字幕信息或其他控制信号)的编码规则为,因为只有两位,因此对应四种可能,“00”对应10’b110101;0100;“01”对应10’b0010101011;“10”对应10’b0101010100;“11”对应10’b1010101011。

对于DE信号为高电平时,传输的像素数据的编码规则,在 Xilinx 应用笔记 XAPP460 中会提供编码模块,或者在网上资源很多的,下面我简单概括和描述一下。TMDS 通过逻辑算法将 8 位字符数据通过最小转换编码为 10 位字符数据。首先,计算8bit像素数据中1的个数,并根据像素数据中1的个数做出编码算法的第一次选择(异或或同或,第一个数保留,从第二个数开始,与前一位数同或或异或得出当前位数的值),同时据此得出bit9的值。然后计算得出当前9bit中的1的个数n1q_m和0的个数n0q_m以及上一次的状态值cnt,根据此做第二次算法选择,得出bit[9:0]的值,完成编码,输出编码后的数据dout。

module v_encoder(input       clk_in,input       reset_n,input  [7:0]din,input       tmds_c0,//控制数据1input       tmds_c1,//控制数据2input       tmds_de,//图像数据和控制数据指示信号output reg[9:0]dout//编码后数据);//步骤1:计算像素数据中1的个数reg [3:0] n1_cnt; //number of 1s in dinreg [7:0] din_q;always @ (posedge clk_in) beginn1_cnt <=#1 din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];din_q <=#1 din;end//步骤2:根据像素数据中1的个数做出编码方向判断wire d1_direct;assign d1_direct = (n1_cnt >4'd4) | ((n1_cnt == 4'h4) & (din_q[0] == 1'b0));//步骤3: 根据已经做出的决断,计算bit9wire [8:0]q_m;assign q_m[0] = din_q[0];assign q_m[1] = (d1_direct) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]);assign q_m[2] = (d1_direct) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]);assign q_m[3] = (d1_direct) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]);assign q_m[4] = (d1_direct) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]);assign q_m[5] = (d1_direct) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]);assign q_m[6] = (d1_direct) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]);assign q_m[7] = (d1_direct) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]);assign q_m[8] = (d1_direct) ? 1'b0 : 1'b1;//步骤4: 计算9bit中的一的个数和0的个数reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_malways @ (posedge clk_in) beginn1q_m  <=#1 q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7];n0q_m  <=#1 4'h8 - (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//步骤5:根据bit9中的01个数以及上一次的状态cnt标识,提供编码方向reg [4:0] cnt; //disparity counter, MSB is the sign bitwire d2_direct,d3_direct;assign d2_direct = (cnt == 5'h0) | (n1q_m == n0q_m);//cnt可以理解为上一次的状态  01个数相等//步骤6:zuochu bit10的决断assign d3_direct = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m));reg       de_q, de_reg;reg       c0_q, c1_q;reg       c0_reg, c1_reg;reg [8:0] q_m_reg;always @ (posedge clk_in) begin//所有信号全部同步de_q    <=#1 tmds_de;de_reg  <=#1 de_q;c0_q    <=#1 tmds_c0;c0_reg  <=#1 c0_q;c1_q    <=#1 tmds_c1;c1_reg  <=#1 c1_q;q_m_reg <=#1 q_m;endparameter CTRLTOKEN0 = 10'b1101010100;parameter CTRLTOKEN1 = 10'b0010101011;parameter CTRLTOKEN2 = 10'b0101010100;parameter CTRLTOKEN3 = 10'b1010101011;//10bit数据输出always @ (posedge clk_in or negedge reset_n) beginif(!reset_n) begindout <= 10'h0;cnt <= 5'h0;end else beginif (de_reg) begin//de信号为高指示像素数据if(d2_direct) begindout[9]   <=#1 ~q_m_reg[8]; dout[8]   <=#1 q_m_reg[8]; dout[7:0] <=#1 (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0];cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m);end else beginif(d3_direct) begindout[9]   <=#1 1'b1;dout[8]   <=#1 q_m_reg[8];dout[7:0] <=#1 ~q_m_reg[7:0];cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m);end else begindout[9]   <=#1 1'b0;dout[8]   <=#1 q_m_reg[8];dout[7:0] <=#1 q_m_reg[7:0];cnt <=#1 cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m);endendend else begin//de为低指示控制数据case ({c1_reg, c0_reg})2'b00:   dout <=#1 CTRLTOKEN0;2'b01:   dout <=#1 CTRLTOKEN1;2'b10:   dout <=#1 CTRLTOKEN2;default: dout <=#1 CTRLTOKEN3;endcasecnt <=#1 5'h0;endendend
endmodule

Serializer_10_to_1模块用于进行10:1串并转换,由于一个OSERDESE2原语最多只能实现8:1串并转化,因此需要两个原语级联实现10:1的串并转换,采用DDR模式则可以选择5倍于像素时钟的串行时钟即可。

module serializer_10_to_1(input           reset,              // 复位,高有效input           paralell_clk,       // 输入并行数据时钟input           serial_clk_5x,      // 输入串行数据时钟input   [9:0]   paralell_data,      // 输入并行数据output          serial_data_out     // 输出串行数据);
//wire define
wire        cascade1;     //用于两个OSERDESE2级联的信号
wire        cascade2;//例化OSERDESE2原语,实现并串转换,Master模式
OSERDESE2 #(.DATA_RATE_OQ   ("DDR"),       // 设置双倍数据速率.DATA_RATE_TQ   ("SDR"),       // DDR, BUF, SDR.DATA_WIDTH     (10),           // 输入的并行数据宽度为10bit.SERDES_MODE    ("MASTER"),    // 设置为Master,用于10bit宽度扩展.TBYTE_CTL      ("FALSE"),     // Enable tristate byte operation (FALSE, TRUE).TBYTE_SRC      ("FALSE"),     // Tristate byte source (FALSE, TRUE).TRISTATE_WIDTH (1)             // 3-state converter width (1,4)
)
OSERDESE2_Master (.CLK        (serial_clk_5x),    // 串行数据时钟,5倍时钟频率.CLKDIV     (paralell_clk),     // 并行数据时钟.RST        (reset),            // 1-bit input: Reset.OCE        (1'b1),             // 1-bit input: Output data clock enable.OQ         (serial_data_out),  // 串行输出数据.D1         (paralell_data[0]), // D1 - D8: 并行数据输入.D2         (paralell_data[1]),.D3         (paralell_data[2]),.D4         (paralell_data[3]),.D5         (paralell_data[4]),.D6         (paralell_data[5]),.D7         (paralell_data[6]),.D8         (paralell_data[7]),.SHIFTIN1   (cascade1),         // SHIFTIN1 用于位宽扩展.SHIFTIN2   (cascade2),         // SHIFTIN2.SHIFTOUT1  (),     .OFB        (),                 // 以下是未使用信号.T1         (1'b0),             .T2         (1'b0),.T3         (1'b0),.T4         (1'b0),.TBYTEIN    (1'b0),             .TCE        (1'b0),             .TBYTEOUT   (),                 .TFB        (),                 .TQ         ()
);
//例化OSERDESE2原语,实现并串转换,Slave模式
OSERDESE2 #(.DATA_RATE_OQ   ("DDR"),       // 设置双倍数据速率.DATA_RATE_TQ   ("SDR"),       // DDR, BUF, SDR.DATA_WIDTH     (10),           // 输入的并行数据宽度为10bit.SERDES_MODE    ("SLAVE"),     // 设置为Slave,用于10bit宽度扩展.TBYTE_CTL      ("FALSE"),     // Enable tristate byte operation (FALSE, TRUE).TBYTE_SRC      ("FALSE"),     // Tristate byte source (FALSE, TRUE).TRISTATE_WIDTH (1)             // 3-state converter width (1,4)
)
OSERDESE2_Slave (.CLK        (serial_clk_5x),    // 串行数据时钟,5倍时钟频率.CLKDIV     (paralell_clk),     // 并行数据时钟.RST        (reset),            // 1-bit input: Reset.OCE        (1'b1),             // 1-bit input: Output data clock enable.OQ         (),                 // 串行输出数据.D1         (1'b0),             // D1 - D8: 并行数据输入.D2         (1'b0),.D3         (paralell_data[8]),.D4         (paralell_data[9]),.D5         (1'b0),.D6         (1'b0),.D7         (1'b0),.D8         (1'b0),.SHIFTIN1   (),                 // SHIFTIN1 用于位宽扩展.SHIFTIN2   (),                 // SHIFTIN2.SHIFTOUT1  (cascade1),         // SHIFTOUT1: 用于位宽扩展.SHIFTOUT2  (cascade2),         // SHIFTOUT2.OFB        (),                 // 以下是未使用信号.T1         (1'b0),             .T2         (1'b0),.T3         (1'b0),.T4         (1'b0),.TBYTEIN    (1'b0),             .TCE        (1'b0),             .TBYTEOUT   (),                 .TFB        (),                 .TQ         ()
);
endmodule

然后数据在逻辑上返回到v_xfer模块,通过调用OBUFDS原语实现信号单端转差分的变化。这便是最终的差分信号。

4.注意事项与效果

①IO约束、OBUFDS原语调用是TMDS33电平标准;

②调试时请注意选择屏幕的信号源为HDMI;

③使用其他的数据源只需要把v_driver模块换成其他的数据源接入即可;

④在约束中差分信号仅对p端子约束即可;

⑤该程序没有使用音频或其他控制信号,是向下兼容DVI接口的;

⑥注意给HDMI接口的差分信号的时钟频率是像素时钟,而不是五倍频时钟。

H1--HDMI接口测试应用2022-07-15相关推荐

  1. P1422 小玉家的电费--2022.03.15

    /* P1422 小玉家的电费--2022.03.15 https://www.luogu.com.cn/problem/P1422 */ #include <bits/stdc++.h> ...

  2. Xilinx HLS 导出IP失败的最新解决方案(2022.1.15)

    Xilinx HLS 导出IP失败的最新解决方案(2022.1.15) 作者主页: 从2022年1月1日起,所有版本的XIlinx HLS或者是Vitis HLS中的导出IP功能在使用时均会报错,同时 ...

  3. 信奥学习规划 信息学竞赛之路(2022.07.31)

    信奥(CSP-J/S初赛)公益讲座精选系列之考试形式介绍 信奥(CSP-J/S初赛)公益讲座精选系列之考试形式介绍_哔哩哔哩_bilibili 2022年信息学奥赛学习规划讲座 2022年信息学奥赛学 ...

  4. 信奥中的数学 相关资料汇总(2022.07.08)

    信奥中的数学 数论篇 相关资料汇总(2022.07.07) 信奥中的数学 数论篇 相关资料汇总(2022.07.07)_dllglvzhenfeng的博客-CSDN博客 信奥中的数学 组合篇 相关资料 ...

  5. Windows批处理命令快速获取文件夹下特定类型的文件名(2022.5.15)

    Windows批处理命令快速获取文件夹下特定类型的文件名 (2022.5.15) 1.需求分析 2.batch简介 3.代码实现 3.1 八种编程语言分别实现 3.1.1 C# 实现 3.1.2 C+ ...

  6. vp视频结构化框架(2022/9/15更新)

    完成多路视频并行接入.解码.多级推理.结构化数据分析.上报.编码推流等过程,插件式/pipe式编程风格,功能上类似英伟达的deepstream和华为的mxvision,但底层核心不依赖复杂难懂的gst ...

  7. 少儿python编程和少儿c++编程学哪个比较好(2022.07.18)

    关于少儿编程编程语言的选择,python还是c++ 关于少儿编程编程语言的选择,python还是c++_dllglvzhenfeng的博客-CSDN博客 14岁孩子学python还是c++好_有编程基 ...

  8. English Learning - L1-4 从此没有不会的表达(下) 2022.12.15 周四

    English Learning - L1-4 从此没有不会的表达(下) 2022.12.15 周四 5 动名词 5.1 动名词都有什么作用 作主语 做表语 做定语 动名词做定语有啥讲究? 做宾语 5 ...

  9. 最新苹果二手报价单(2022.2.15)

    2022.2.15换换回收iPhone苹果二手机报价单(按照内存.靓机.小花.大花.花机四个等级进行报价)如下所示

  10. 硬件大熊原创合集(2022/07更新)

    2022/07月份更新的篇章 面板显示技术:LCD与OLED 屏:框贴.0贴合.全贴合 屏:全贴合工艺之GFF.OGS.Oncell.Incell 技术工程师每每自诩:"我们的技术到哪里都可 ...

最新文章

  1. “AI”战疫在行动,一文盘点百度大脑增援疫情防控的AI操作
  2. “惊群”,看看nginx是怎么解决它的
  3. VMware 虚拟化编程(5) — VixDiskLib 虚拟磁盘库详解之一
  4. ThreadPoolExecutor源码解析(一)
  5. ppt科研绘图 图形布尔运算
  6. docker学习笔记(六)docker-compose
  7. Qt使用socket通信时接收的汉字信息显示时乱码
  8. boost互斥锁_boost锁使用总结
  9. oracle 无效数字 0,oracle to_namber之后报ora-01722:无效数字
  10. 孩子成绩不好,学艺术还是上中职?
  11. paip.汉字简化大法总结
  12. 2018第九届蓝桥杯C++省赛B组题目汇总
  13. 宝塔面板申请带有www的二级域名SSL证书失败的处理方法
  14. 智能家居Series
  15. centos7文件同步服务器,教你在 Centos7 中使用 Unison 同步文件
  16. 基础拍摄前期和基础后期修图
  17. 应届生如何做好一份简历?
  18. uniapp 来电显示悬浮窗插件(支持锁屏来电) Ba-CallerID
  19. Spring监听器的完整使用步骤
  20. 旧话重提,还来聊聊ERP

热门文章

  1. python-类的继承和多态-继承父类方法和属性的多种方法-继承多个类,继承顺序的研究
  2. 通用字体重量对应的font-weight值
  3. 如何用 Python 生成炫酷二维码及解析
  4. 安卓获取设备及系统信息小结
  5. Viewer.js点击按钮放大图片用法
  6. 计算机专业要不要读博?(转)
  7. 单端与差分的接线方法
  8. Django之创建项目
  9. 尾插法建立单链表 数据结构
  10. 区块链百科合集之 智 能 合 约