ZYNQ-FPGA-HDMI

一、HDMI简介

        HDMI有19个引脚,其中1-12中3个3个的出现,例如1是数据2+ 2是隔离线 3是数据2-

        其中数据2+和数据2-是差分信号,屏蔽信号用于减少干扰。三路数据分别输入RGB信号,第四路输入时钟信号。

        CEC(Consumer Electronics Control)是一套完整的单总线协议,电子设备可以借着CEC信号让使用可控制HDMI接口上所连接的装置,比如单键播放(One Touth Play),系统待机(System Standby),可以实现由单一遥控器控制所有HDMI连接的装置。最多可以控制15个设备。允许HDMI设备在没有用户干扰情况下互相命令控制。

        SCL,SDA:这两支脚位是用来让source(DVD) 和 display (TV)作沟通,在电视内部都有一个记忆体,内部存放了有关这台电视所支持的分辨率,例如:720P 或1080P,如果source(DVD)不知道 目前所连接的电视所支持的电视分辨率是多少时,source(DVD) 就不知道要放送出什么分辨率的讯号,因此在一开始source(DVD)会透过这两支脚位去读取电视所支持的分辨率,当source(DVD) 知道后source(DVD) 才放送出符合电视分辨率的影像画面。其中SCL是时钟(CLOCK)脚位,SDA是资料脚位

        热插拔信号:HDMI屏幕可以拔了再插立刻恢复

        HDMI传输分为了音频传输和视频传输本次只探究了视频传输

        TMDS数据分为RGB和clk信号。

        B是蓝色端,其中包含8位数据位,2位行场同步信号

        G是绿色端,其中包含8位数据位,2位C0 C1控制信号

        R是红色段,其中包含8位数据位,2位C0 C1控制信号

        Auxiliary DAT不用管,因为本次无音频数据

        TMDS编码是将八位数据编码为十位信号,第九位是编码模式(异或1 同或0)第十位是交直流平衡数据(让每个数据中0-1平衡,避免长时间出现直流信号,因为fpga内部有电容结构,长时间出现0或者1是直流电会导致开路)。

        TMDS编码流程:

二、系统框图

 系统分为如下几个模块:

1.显示模块:负责输出显示数据,给驱动

2.显示驱动:负责驱动屏幕,控制屏幕扫描坐标,数据使能信号,行场同步信号,再把有效数据输出给后面的编码模块

3.编码模块:把八位数据编码为十位数据

4.并转串模块:把编码的数据转为串行数据

5.差分信号模块:把串行信号差分输出到HDMI

6.异步复位,同步释放:复位可以异步但是开始需要同时,并且该模块把复位转为高电平

7.顶层模块:历化所有模块

三、模块代码

720p的显示是有效数据位1280x720个像素点,且时钟是74.25HZ本文中使用75HZ

3.1显示驱动

一个视频信号分为:1.行场同步信号2.后沿3.有效数据4.前沿

其中行场同步信号时序行场同步信号需要0(有效)其余时间拉高;

有效数据时进行数据传输(即de信号拉高,但我们需要提前一个周期告诉其他模块进行数据准备,因此需要一个data_req模块比de模块早一个周期)

过程:首先行场计数器进行计数。筛选出有效显示区域,在有效显示区域前一个周期data_req拉高,打一拍de信号拉高,在de拉高时候输出有效数据。

module    hdmi_output_drive(
        
        input               pixel_clk    , 
        input               pixel_rst    ,
        input       [23:0]  pixel_data   , //24位数据是固定的是代表rgb各八位
        output   reg        data_req     ,
        output   reg        video_de     ,//有效数据使能
        output      [23:0]  video_rgb    ,
        output      [10:0]  pixel_xpos   ,
        output      [10:0]  pixel_ypos   ,
        output              video_hs     ,
        output              video_vs


);
//11位是因为1650是11位,因此为了统一都取11位数据
parameter       H_SYNC  = 11'd40     ;              //行同步
parameter       H_BACK  = 11'd220    ;              //行显示后沿 260 1280 1540 1539 1538
parameter       H_DISP  = 11'd1280   ;              //行显示有效数据
parameter       H_FRONT = 11'd110    ;              //行显示前沿
parameter       H_TOTAL = 11'd1650   ;              //行总和(周期)

parameter       V_SYNC  = 11'd5      ;              //场同步
parameter       V_BACK  = 11'd20     ;              //场显示后沿
parameter       V_DISP  = 11'd720    ;              //场显示有效数据
parameter       V_FRONT = 11'd5      ;              //场显示前沿
parameter       V_TOTAL = 11'd750    ;              //场总和(周期)


           reg       [10:0]           h_cnt;
           reg       [10:0]           v_cnt;
           
//行场计数器
assign video_rgb = (video_de == 1  ) ?  pixel_data : 24'b0;
assign video_hs  = (h_cnt < H_SYNC ) ? 1'b0 :1'b1;//0有效
assign video_vs  = (v_cnt < V_SYNC ) ? 1'b0 :1'b1;//0有效
assign pixel_xpos = (video_de == 1 ) ? (h_cnt + 1'b1 - H_SYNC - H_BACK) : 11'b0;
assign pixel_ypos = (video_de == 1 ) ? (v_cnt + 1'b1 - V_SYNC - V_BACK) : 11'b0;


always @(posedge  pixel_clk  or  negedge pixel_rst  ) begin
    if(pixel_rst == 1'b0)
        h_cnt <= 11'b0;
    else if (h_cnt < H_TOTAL - 11'd1)
        h_cnt <= h_cnt + 11'b1;
    else if (h_cnt == H_TOTAL - 11'd1)
        h_cnt <= 11'b0;
    else 
        h_cnt <= 11'b0;
end

always @(posedge  pixel_clk  or  negedge pixel_rst  ) begin
    if(pixel_rst == 1'b0)
        v_cnt <= 11'b0;  
    else if (h_cnt == H_TOTAL - 11'd1) begin 
        if ( v_cnt < V_TOTAL - 11'd1)
            v_cnt <= v_cnt + 11'd1;
        else
            v_cnt <= 11'b0;  
    end
end

//data_req数据准备,我们只需要将有效区域左移一个周期即减一即可,但由于非阻塞赋值
always @(posedge  pixel_clk  or  negedge pixel_rst  ) begin
    if(pixel_rst == 1'b0)
        data_req <= 1'b0;
    else if (( h_cnt  >= H_SYNC + H_BACK - 11'd2 )&&( h_cnt < H_SYNC+H_BACK+H_DISP - 11'd2)&&(v_cnt>=V_SYNC+V_BACK)&&(v_cnt <V_SYNC+V_BACK+V_DISP))
        data_req <= 1'b1;
    else
        data_req <= 1'b0;
end
//data_req打一拍得到de信号
always @(posedge  pixel_clk  or  negedge pixel_rst  ) begin
    if(pixel_rst == 1'b0)
        video_de <= 1'b0;
    else
        video_de <= data_req;
end
endmodule

3.2显示模块

        通过判断扫描点pixel_xpos和pixel_ypos的位置来设定显示内容,本次是显示彩条

module video_display(
        input          [10:0]  pixel_xpos      ,
        input          [10:0]  pixel_ypos      ,
        input                  pixel_clk       ,
        input                  pixel_rst       ,
        output      reg[23:0]  pixel_data    //通过坐标判断需要输出的数据
       
);
parameter           H_DISP    =     11'd1280     ;//行分辨率
parameter           V_DISP    =     11'd720      ;//列分辨率

localparam          WHITE     =     24'b11111111_11111111_11111111  ;
localparam          BLACK     =     24'b00000000_00000000_00000000  ;
localparam          RED       =     24'b11111111_00001100_00000000  ;
localparam          GREEN     =     24'b00000000_11111111_00000000  ;
localparam          BLUE      =     24'b00000000_00000000_11111111  ;



//显示数据(后面还要通过驱动给后面的模块编码)
always @(posedge  pixel_clk or negedge  pixel_rst ) begin 
    if(pixel_rst == 1'b0)
        pixel_data <= 24'd0;
    else begin 
        if((pixel_xpos >= 11'd0)&&(pixel_xpos< (H_DISP/5)))
            pixel_data <= GREEN;
        else if ((pixel_xpos >= H_DISP/5)&&(pixel_xpos< (H_DISP/5)*2))
            pixel_data <= WHITE;
        else if ((pixel_xpos >= (H_DISP/5)*2)&&(pixel_xpos< (H_DISP/5)*3))
            pixel_data <= RED;
        else if ((pixel_xpos >= (H_DISP/5)*3)&&(pixel_xpos< (H_DISP/5)*4))
            pixel_data <= GREEN;
        else if ((pixel_xpos >= (H_DISP/5)*4)&&(pixel_xpos< H_DISP))
            pixel_data <= BLUE;            
    end




end

 






endmodule

3.3编码模块

编码主要是把RGB的三通道八位数据转化为三通道时位数据,同时把时钟信号也编码成10位,这部分比较难,可以按照上面的流程图写 

`timescale 1 ps / 1ps

module dvi_encoder (
  input            clkin,    // pixel clock input
  input            rstin,    // async. reset input (active high)
  input      [7:0] din,      // data inputs: expect registered
  input            c0,       // c0 input
  input            c1,       // c1 input
  input            de,       // de input
  output reg [9:0] dout      // data outputs
);


// Counting number of 1s and 0s for each incoming pixel
// component. Pipe line the result.
// Register Data Input so it matches the pipe lined adder
// output

reg [3:0] n1d; //number of 1s in din
reg [7:0] din_q;

//计算像素数据中“1”的个数
always @ (posedge clkin) begin
  n1d <=#1 din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];

  din_q <=#1 din;
end

///
// Stage 1: 8 bit -> 9 bit
// Refer to DVI 1.0 Specification, page 29, Figure 3-5
///
wire decision1;

assign decision1 = (n1d > 4'h4) | ((n1d == 4'h4) & (din_q[0] == 1'b0));

wire [8:0] q_m;
assign q_m[0] = din_q[0];
assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]);
assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]);
assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]);
assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]);
assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]);
assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]);
assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]);
assign q_m[8] = (decision1) ? 1'b0 : 1'b1;

/
// Stage 2: 9 bit -> 10 bit
// Refer to DVI 1.0 Specification, page 29, Figure 3-5
/
reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_m
always @ (posedge clkin) begin
  n1q_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

parameter CTRLTOKEN0 = 10'b1101010100;
parameter CTRLTOKEN1 = 10'b0010101011;
parameter CTRLTOKEN2 = 10'b0101010100;
parameter CTRLTOKEN3 = 10'b1010101011;

reg [4:0] cnt; //disparity counter, MSB is the sign bit
wire decision2, decision3;

assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m);
/
// [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)]
/
assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m));


// pipe line alignment

reg       de_q, de_reg;
reg       c0_q, c1_q;
reg       c0_reg, c1_reg;
reg [8:0] q_m_reg;

always @ (posedge clkin) begin
  de_q    <=#1 de;
  de_reg  <=#1 de_q;
  
  c0_q    <=#1 c0;
  c0_reg  <=#1 c0_q;
  c1_q    <=#1 c1;
  c1_reg  <=#1 c1_q;

  q_m_reg <=#1 q_m;
end

///
// 10-bit out
// disparity counter
///
always @ (posedge clkin or posedge rstin) begin
  if(rstin) begin
    dout <= 10'h0;
    cnt <= 5'h0;
  end else begin
    if (de_reg) begin
      if(decision2) begin
        dout[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 begin
        if(decision3) begin
          dout[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 begin
          dout[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);
        end
      end
    end else begin
      case ({c1_reg, c0_reg})
        2'b00:   dout <=#1 CTRLTOKEN0;
        2'b01:   dout <=#1 CTRLTOKEN1;
        2'b10:   dout <=#1 CTRLTOKEN2;
        default: dout <=#1 CTRLTOKEN3;
      endcase

      cnt <=#1 5'h0;
    end
  end
end
  
endmodule 

3.4并行转串行

这里主要是运用了 原语 (原语可以理解为库函数可以调用,但是不需要IP核那样配置)

10位并转串的原理是运用10倍时钟如下图

但十倍时钟在更高画质很难提升上去因此我们选择双边缘检测因此只需5倍时钟

(需要PLL来产生75HZ时钟与375HZ时钟)

OSERDESE2原语在单个的情况下只能实现8bit的并转串,但是如果级联的化可以实现14bit以内并转串,级联方法如下:

        第一个原语选择主模式,并在输入模块接上从模式的输出,第二个选择从模式,但注意的是从模块必须从D3开始接,这也是为什么两个级联最多14bit的原因。其余都在代码上有注释。

`timescale 1ns / 1ps

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;
  
//*****************************************************
//**                    main code
//***************************************************** 
    
//例化OSERDESE2原语,实现并串转换,Master模式,一个OSERDESE2只能进行8-1   因此10-1需要两个OSERDESE2串联
OSERDESE2 #(
    .DATA_RATE_OQ   ("DDR"),       // 设置双倍数据速率,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 用于位宽扩展      主模式的in接到从模式的out,因为从模式拓展的两位输出到主模式
    .SHIFTIN2   (cascade2),         // SHIFTIN2
    .SHIFTOUT1  (),                 // SHIFTOUT1: 用于位宽扩展
    .SHIFTOUT2  (),                 // SHIFTOUT2
        
    .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]),   //串联时候从模式D3才能开始拓展,D1 D2不能用
    .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

3.5异步复位同步释放

先把复位信号转化为高电平触发(新设置了复位信号),再把复位信号打一拍,保证复位失效时候(系统开始运行)都在下一个时钟上升沿同时开始。


module asyn_rst_syn(
    input clk,          //目的时钟域
    input reset_n,      //异步复位,低有效
    
    output syn_reset    //高有效
    );
    
//reg define
reg reset_1;
reg reset_2;
    
//*****************************************************
//**                    main code
//**************************************** ************* 
assign syn_reset  = reset_2;
    
//对异步复位信号进行同步释放,并转换成高有效
always @ (posedge clk or negedge reset_n) begin
    if(!reset_n) begin
        reset_1 <= 1'b1;
        reset_2 <= 1'b1;
    end
    else begin
        reset_1 <= 1'b0;
        reset_2 <= reset_1;    //打两排
    end
end
    
endmodule

3.6差分模块

        由于差分模块较简单,因此差分模块作为数据处理的顶层模块用于历化编码与并转串还有异步复位同步释放模块。

module dvi_transmitter_top(
    input        pclk,           // pixel clock
    input        pclk_x5,        // pixel clock x5
    input        reset_n,        // reset
    
    input [23:0] video_din,      // RGB888 video in
    input        video_hsync,    // hsync data
    input        video_vsync,    // vsync data
    input        video_de,       // data enable
    
    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 输出使能
    );
    
//wire define    
wire        reset;
    
//并行数据
wire [9:0]  red_10bit;
wire [9:0]  green_10bit;
wire [9:0]  blue_10bit;
wire [9:0]  clk_10bit;  
  
//串行数据
wire [2:0]  tmds_data_serial;
wire        tmds_clk_serial;

//*****************************************************
//**                    main code
//***************************************************** 
assign tmds_oen = 1'b1;  
assign clk_10bit = 10'b1111100000;

//异步复位,同步释放
asyn_rst_syn reset_syn(
    .reset_n    (reset_n),
    .clk        (pclk),
    
    .syn_reset  (reset)    //高有效
    );
  
//对三个颜色通道进行编码
dvi_encoder encoder_b (
    .clkin      (pclk),
    .rstin	    (reset),
    
    .din        (video_din[7:0]),
    .c0			(video_hsync),
    .c1			(video_vsync),
    .de			(video_de),
    .dout		(blue_10bit)
    ) ;

dvi_encoder encoder_g (
    .clkin      (pclk),
    .rstin	    (reset),
    
    .din		(video_din[15:8]),
    .c0			(1'b0),
    .c1			(1'b0),
    .de			(video_de),
    .dout		(green_10bit)
    ) ;
    
dvi_encoder encoder_r (
    .clkin      (pclk),
    .rstin	    (reset),
    
    .din		(video_din[23:16]),
    .c0			(1'b0),
    .c1			(1'b0),
    .de			(video_de),
    .dout		(red_10bit)
    ) ;
    
//对编码后的数据进行并串转换      RGB clk四个通道分别并转串

serializer_10_to_1 serializer_b(
    .reset              (reset),                // 复位,高有效
    .paralell_clk       (pclk),                 // 输入并行数据时钟
    .serial_clk_5x      (pclk_x5),              // 输入串行数据时钟
    .paralell_data      (blue_10bit),           // 输入并行数据

    .serial_data_out    (tmds_data_serial[0])   // 输出串行数据
    );    
    
serializer_10_to_1 serializer_g(
    .reset              (reset),
    .paralell_clk       (pclk),
    .serial_clk_5x      (pclk_x5),
    .paralell_data      (green_10bit),

    .serial_data_out    (tmds_data_serial[1])
    );
    
serializer_10_to_1 serializer_r(
    .reset              (reset),
    .paralell_clk       (pclk),
    .serial_clk_5x      (pclk_x5),
    .paralell_data      (red_10bit),

    .serial_data_out    (tmds_data_serial[2])
    );
            
serializer_10_to_1 serializer_clk(
    .reset              (reset),
    .paralell_clk       (pclk),
    .serial_clk_5x      (pclk_x5),
    .paralell_data      (clk_10bit),

    .serial_data_out    (tmds_clk_serial)
    );                                     //时钟信号无需TMDS编码因此输出1111100000十位数据即可
    
//转换差分信号  
OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
) TMDS0 (
    .I                  (tmds_data_serial[0]),  //输入10位串行
    .O                  (tmds_data_p[0]),       //输出差分1
    .OB                 (tmds_data_n[0])        //输出差分2
);

OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
) TMDS1 (
    .I                  (tmds_data_serial[1]),   //输入10位串行
    .O                  (tmds_data_p[1]),        //输出差分1
    .OB                 (tmds_data_n[1])         //输出差分2
);

OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
) TMDS2 (
    .I                  (tmds_data_serial[2]),   //输入10位串行
    .O                  (tmds_data_p[2]),        //输出差分1
    .OB                 (tmds_data_n[2])         //输出差分2
);

OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
) TMDS3 (
    .I                  (tmds_clk_serial), //输入10位串行
    .O                  (tmds_clk_p),      //输出差分1
    .OB                 (tmds_clk_n)       //输出差分2
);
  
endmodule

3.7顶层模块

历化了显示模块,驱动模块,与3.6的差分顶层模块


module  hdmi_colorbar_top(
    input        sys_clk,
    input        sys_rst_n,
    
    output       tmds_clk_p,    // TMDS 时钟通道
    output       tmds_clk_n,
    output [2:0] tmds_data_p,   // TMDS 数据通道
    output [2:0] tmds_data_n
);

//wire define
wire          pixel_clk;
wire          pixel_clk_5x;
wire          clk_locked;

wire  [10:0]  pixel_xpos_w;
wire  [10:0]  pixel_ypos_w;
wire  [23:0]  pixel_data_w;

wire          video_hs;
wire          video_vs;
wire          video_de;
wire  [23:0]  video_rgb;

//*****************************************************
//**                    main code
//*****************************************************

//例化MMCM/PLL IP核
clk_wiz_0  clk_wiz_0(
    .clk_in1        (sys_clk),
    .clk_out1       (pixel_clk),        //像素时钟
    .clk_out2       (pixel_clk_5x),     //5倍像素时钟
    
    .reset          (~sys_rst_n), 
    .locked         (clk_locked)
);

//例化视频显示驱动模块
hdmi_output_drive  u_hdmi_output_drive(
    .pixel_clk      ( pixel_clk ),
    .pixel_rst      ( sys_rst_n ),                
                                                  
    .video_hs       ( video_hs ),                 
    .video_vs       ( video_vs ),                 
    .video_de       ( video_de ),                 
    .video_rgb      ( video_rgb ),                
	.data_req		(),                           
                                                  
    .pixel_xpos     ( pixel_xpos_w ),             
    .pixel_ypos     ( pixel_ypos_w ),             
	.pixel_data     ( pixel_data_w )   
);

//例化视频显示模块
video_display  u_video_display(                
    .pixel_clk      (pixel_clk),               
    .pixel_rst      (sys_rst_n),               
                                               
    .pixel_xpos     (pixel_xpos_w),            
    .pixel_ypos     (pixel_ypos_w),
    .pixel_data     (pixel_data_w)
    );

//例化HDMI驱动模块
dvi_transmitter_top u_rgb2dvi_0(
    .pclk           (pixel_clk),
    .pclk_x5        (pixel_clk_5x),
    .reset_n        (sys_rst_n & clk_locked),
                
    .video_din      (video_rgb),
    .video_hsync    (video_hs), 
    .video_vsync    (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       ()                        //预留的端口,本次实验未用到
    );

endmodule 

四、实验结果

哦对了还有PLL模块自己加一下以及管脚的约束

set_property PACKAGE_PIN U18 [get_ports {sys_clk}]
set_property IOSTANDARD LVCMOS33 [get_ports {sys_clk}]
create_clock -period 20.000 -waveform {0.000 10.000} [get_ports sys_clk]


set_property IOSTANDARD TMDS_33 [get_ports tmds_clk_n]
set_property PACKAGE_PIN N18 [get_ports tmds_clk_p]
set_property IOSTANDARD TMDS_33 [get_ports tmds_clk_p]


set_property IOSTANDARD TMDS_33 [get_ports {tmds_data_n[0]}]
set_property PACKAGE_PIN V20 [get_ports {tmds_data_p[0]}]
set_property IOSTANDARD TMDS_33 [get_ports {tmds_data_p[0]}]


set_property IOSTANDARD TMDS_33 [get_ports{tmds_data_n[1]}]
set_property PACKAGE_PIN T20 [get_ports {tmds_data_p[1]}]
set_property IOSTANDARD TMDS_33 [get_ports {tmds_data_p[1]}]


set_property IOSTANDARD TMDS_33 [get_ports {tmds_data_n[2]}]
set_property PACKAGE_PIN N20 [get_ports {tmds_data_p[2]}]
set_property IOSTANDARD TMDS_33 [get_ports {tmds_data_p[2]}]


set_property PACKAGE_PIN N15 [get_ports sys_rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports sys_rst_n]

成功!

五、总结

        1.HDMI驱动的原理以及各个模块作用

        2.信号并转串、差分原语运用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值