HDMI接口和TMDS傳輸標準詳解

1.HDMI是新一代的多媒體接口標準。

High-Definition Multimedia Interface:高清多媒體接口
        能夠同時傳輸視頻和音頻,簡化了設備的接口和連線;
        提供了更高的數據傳輸帶寬,可以傳輸無壓縮的數字音頻及高分辨率視頻信號;
下面是HDMI線公頭和母頭示意圖:
 

2.HDMI向下兼容DVI

DVI(數字視頻接口)只能用來傳輸視頻,而不能同時傳輸音頻;
DVI和HDMI接口協議在物理層均使用TMDS標準傳輸音視頻數據。
下面是DVI和HDMI接口示意圖:

3.TMDS: 最小化傳輸差分信號

Transition Minimized Differential Signaling
 
TMDS是Silicon Image公司開發的一項高速數據傳輸技術,在DVI和HDMI視頻接口中使用差分信號傳輸高速串行數據。
 
TMDS差分傳輸技術使用兩個引腳來傳輸一路信號,利用這兩個引腳間的電壓差的正負極性和大小來決定傳輸數據的數值(0或1)。
DVI或HDMI視頻傳輸所使用的TMDS連接通過四個串行通道實現,
獨立的TMDS時鐘通道爲接收端提供接收的參考頻率,保證數據在接收端能夠正確恢復。
TMDS連接從邏輯功能上可以劃分成兩個階段:“編/解碼” 和 “並/串轉換” 。
 
在編碼階段,編碼器將視頻源中的像素數據、HDMI的音頻/附加數據,以及行同步和場同步信號分別編碼成10位的字符流。
 
在並串轉換階段將上述的10位字符流轉換成串行數據流,並將其從三個差分輸出通道發送出去。
 
這個10:1的並轉串過程所生成的串行數據速率是實際像素時鐘速率的10倍。
如下圖所示:
 

4.TMDS編碼算法

TMDS和LVDS、TTL相比有較好的電磁兼容性能:
這種算法可以減小傳輸信號過程的上衝和下衝;
而DC平衡使信號對傳輸線的電磁干擾減少;
可以用低成本的專用電纜實現長距離、高質量的數字信號傳輸。

5.HDMI模塊框圖

CEC: Consumer Electronics Control 用戶電氣控制
DDC:用來獲取被接設備的信息EDID(擴展顯示標識數據 
 
CEC線在HDMI插上的可以控制從設備的一些狀態,比如開機
 
DDC是IIC接口,可以結合HDMI的19管腳(熱拔插信號)來讀取被接設備的一些有用信息,比如分辨率。FPGA檢測到顯示器後可以通過DDC讀取顯示器的分辨率,從而達到智能識別分辨率,FPGA設備可以根據分辨率調整輸出(比如我們把筆記本的HDMI接到1080p的顯示器和720p的顯示器筆記本的輸出會自己做調整適應不同的顯示器)
 

6.HDMI引腳定義

 

7.程序設計

下面是程序框圖

衆所周知,HDMI既可以傳輸視頻信號,也可以傳輸音頻信號,FPGA產生的Video Source經過TMDS編碼算法和CTRL編碼算把三組RGB數據、HS、VS信號進行編碼,編碼過後的10bit數據需要並串轉換模塊Serializer把並行的10bit信號發送出去,這裏Serializer用的FPGA的OSERDESE2原語,並要使能DDR功能(因爲我們給的時鐘是5倍時鐘,所以要使能雙倍數據速率)。這樣HDMI接收端只會接收到源的視頻信號,如果需要傳輸音頻信號,則需要FPGA產生Audio data並且需要TERC4算法進行編碼發送,這裏我也沒研究。Video Source是標準的VGA時序,只是多了一個VDE(Video Data Enable)信號,也就是VGA的數據使能信號。時鐘信號是把標準的10'b11110_00000的數據通過OSERDESE2進行並串轉換輸出就是真實的時鐘。
這樣我們FPGA程序需要以下模塊:

   1.VGA生成模塊

   2.TMDS和CTRL編碼模塊(需要例化三對)

   3.並串轉換模塊(需要例化四對)

   4.單端轉差分模塊(需要例化四對)

   5.頂層模塊

1.VGA時序生成模塊

module video_driver(
    input           pixel_clk,
    input           sys_rst_n,
    
    //RGB接口
    output          video_hs,     //行同步信號
    output          video_vs,     //場同步信號
    output          video_de,     //數據使能
    output  [23:0]  video_rgb,    //RGB888顏色數據
    
    input   [23:0]  pixel_data,   //像素點數據
    output  [10:0]  pixel_xpos,   //像素點橫座標
    output  [10:0]  pixel_ypos    //像素點縱座標
);

//parameter define

//1280*720 分辨率時序參數
parameter  H_SYNC   =  11'd40;   //行同步
parameter  H_BACK   =  11'd220;  //行顯示後沿
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 define
reg  [10:0] cnt_h;
reg  [10:0] cnt_v;

//wire define
wire       video_en;
wire       data_req;

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

assign video_de  = video_en;

assign video_hs  = ( cnt_h < H_SYNC ) ? 1'b0 : 1'b1;  //行同步信號賦值
assign video_vs  = ( cnt_v < V_SYNC ) ? 1'b0 : 1'b1;  //場同步信號賦值

//使能RGB數據輸出
assign video_en  = (((cnt_h >= H_SYNC+H_BACK) && (cnt_h < H_SYNC+H_BACK+H_DISP))
                 &&((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
                 ?  1'b1 : 1'b0;

//RGB888數據輸出
assign video_rgb = video_en ? pixel_data : 24'd0;

//請求像素點顏色數據輸入
assign data_req = (((cnt_h >= H_SYNC+H_BACK-1'b1) && 
                    (cnt_h < H_SYNC+H_BACK+H_DISP-1'b1))
                  && ((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
                  ?  1'b1 : 1'b0;

//像素點座標
assign pixel_xpos = data_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 11'd0;
assign pixel_ypos = data_req ? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 11'd0;

//行計數器對像素時鐘計數
always @(posedge pixel_clk ) begin
    if (!sys_rst_n)
        cnt_h <= 11'd0;
    else begin
        if(cnt_h < H_TOTAL - 1'b1)
            cnt_h <= cnt_h + 1'b1;
        else 
            cnt_h <= 11'd0;
    end
end

//場計數器對行計數
always @(posedge pixel_clk ) begin
    if (!sys_rst_n)
        cnt_v <= 11'd0;
    else if(cnt_h == H_TOTAL - 1'b1) begin
        if(cnt_v < V_TOTAL - 1'b1)
            cnt_v <= cnt_v + 1'b1;
        else 
            cnt_v <= 11'd0;
    end
end

endmodule

2.TMDS和CTRL編碼模塊(需要例化三對)

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)
    ) ;

3.並串轉換模塊(需要例化四對)

 
//對編碼後的數據進行並串轉換
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)
    );

 4.單端轉差分模塊(需要例化四對)

//轉換差分信號  
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) 
);
  

5.頂層模塊

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,
    output       tmds_oen       // TMDS 輸出使能,0:表示是HDMI輸入 1:表示是HDMI輸出
);

附錄(TMDS編碼算法):

此算法可以自己研究。

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 

 

 
 
 
 
 
 
 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章