FPGA:HDMI接口
HDMI 是一種數(shù)字視頻接口,因此很容易從現(xiàn)代 FPGA 驅(qū)動(dòng)。讓我們看看它是如何工作的。
本文引用地址:http://m.butianyuan.cn/article/202401/454692.htm連接器
標(biāo)準(zhǔn) HDMI 連接器有 19 個(gè)引腳。 在 19 個(gè)引腳中,有 8 個(gè)特別值得關(guān)注,因?yàn)樗鼈冃纬?4 個(gè) TMDS 差分對(duì)來(lái)傳輸實(shí)際的高速視頻信息。
TMDS 時(shí)鐘+ 和時(shí)鐘-
TMDS data0+ 和 data0-
TMDS data1+ 和 data1-
TMDS data2+ 和 data2-
我們從FPGA到HDMI連接器的連接再簡(jiǎn)單不過(guò)了......我們使用 8 個(gè) FPGA 引腳,配置為 4 個(gè)差分 TMDS 輸出。
視頻信號(hào)
讓我們創(chuàng)建一個(gè) 640x480 RGB 24bpp @ 60Hz 的視頻信號(hào)。 這是每幀 307200 像素,由于每個(gè)像素有 24 位(紅色、綠色和藍(lán)色為 8 位),在 60Hz 時(shí),HDMI 鏈路傳輸 0.44Gbps 的“有用”數(shù)據(jù)。
但視頻信號(hào)通常也有一個(gè)“屏幕外”區(qū)域,HDMI接收器(電視或顯示器)使用它進(jìn)行一些內(nèi)務(wù)處理。 我們的 640x480 幀實(shí)際上是作為 800x525 幀發(fā)送的。
考慮到這一點(diǎn),我們需要一個(gè) 24.5MHz 的像素時(shí)鐘來(lái)實(shí)現(xiàn)每秒 60 幀,但 HDMI 指定了 25MHz 的最小像素時(shí)鐘,所以我們就這樣使用(這讓我們獲得了 61Hz 的幀速率)。
TMDS信號(hào)
FPGA 有 4 個(gè) TMDS 差分對(duì)可供驅(qū)動(dòng)。
首先,TMDS時(shí)鐘只是像素時(shí)鐘,因此它以25MHz運(yùn)行。 其他 3 對(duì)傳輸紅色、綠色和藍(lán)色 8 位信號(hào),因此我們得到類似的東西。
事實(shí)上,事情只是稍微復(fù)雜一些。 HDMI 要求我們對(duì)數(shù)據(jù)進(jìn)行加擾,并在每個(gè)顏色通道上添加 2 位,因此我們有 10 位而不是 8 位,鏈路最終每像素傳輸 30 位。 HDMI 接收器需要加擾和額外位才能正確同步和采集每個(gè)通道(請(qǐng)務(wù)必查看 DVI 和 HDMI 規(guī)格以獲取更多詳細(xì)信息)。
源代碼
首先是視頻生成器。 我們使用幾個(gè)穿過(guò) 800x525 像素區(qū)域的計(jì)數(shù)器......
reg [9:0] CounterX; // counts from 0 to 799
always @(posedge pixclk) CounterX <= (CounterX==799) ? 0 : CounterX+1;
reg [9:0] CounterY; // counts from 0 to 524
always @(posedge pixclk) if(CounterX==799) CounterY <= (CounterY==524) ? 0 : CounterY+1;
并創(chuàng)建 H-Sync 和 V-Sync 信號(hào)...
wire hSync = (CounterX>=656) && (CounterX<752);
wire vSync = (CounterY>=490) && (CounterY<492);
wire DrawArea = (CounterX<640) && (CounterY<480);
并生成一些紅色、綠色和藍(lán)色信號(hào)(每個(gè) 8 位)......
wire [7:0] red = {CounterX[5:0] & {6{CounterY[4:3]==~CounterX[4:3]}}, 2'b00};
wire [7:0] green = CounterX[7:0] & {8{CounterY[6]}};
wire [7:0] blue = CounterY[7:0];
通過(guò)三個(gè)“TMDS_encoder”實(shí)例分別擴(kuò)展到 10 位......
wire [9:0] TMDS_red, TMDS_green, TMDS_blue;
TMDS_encoder encode_R(.clk(pixclk), .VD(red ), .TMDS(TMDS_red) , .CD(2'b00) , .VDE(DrawArea));
TMDS_encoder encode_G(.clk(pixclk), .VD(green), .TMDS(TMDS_green), .CD(2'b00) , .VDE(DrawArea));
TMDS_encoder encode_B(.clk(pixclk), .VD(blue ), .TMDS(TMDS_blue) , .CD({vSync,hSync}), .VDE(DrawArea));
現(xiàn)在,我們?yōu)槊總€(gè)像素時(shí)鐘周期發(fā)送三個(gè) 10 位值。 我們將 25MHz 時(shí)鐘乘以 10 以生成 250MHz 時(shí)鐘......
wire clk_TMDS, DCM_TMDS_CLKFX;
DCM_SP #(.CLKFX_MULTIPLY(10)) DCM_TMDS_inst(.CLKIN(pixclk), .CLKFX(DCM_TMDS_CLKFX), .RST(1'b0));
BUFG BUFG_TMDSp(.I(DCM_TMDS_CLKFX), .O(clk_TMDS)); // 250 MHz
并使用三個(gè)時(shí)鐘頻率為250MHz的移位寄存器...
reg [3:0] TMDS_mod10; // modulus 10 counter
always @(posedge clk_TMDS) TMDS_mod10 <= (TMDS_mod10==9) ? 0 : TMDS_mod10+1;
reg TMDS_shift_load;
always @(posedge clk_TMDS) TMDS_shift_load <= (TMDS_mod10==9);
reg [9:0] TMDS_shift_red, TMDS_shift_green, TMDS_shift_blue;
always @(posedge clk_TMDS)begin
TMDS_shift_red <= TMDS_shift_load ? TMDS_red : TMDS_shift_red [9:1];
TMDS_shift_green <= TMDS_shift_load ? TMDS_green : TMDS_shift_green[9:1];
TMDS_shift_blue <= TMDS_shift_load ? TMDS_blue : TMDS_shift_blue [9:1];
end
將TMDS數(shù)據(jù)發(fā)送到FPGA外部。
OBUFDS OBUFDS_red (.I(TMDS_shift_red [0]), .O(TMDSp[2]), .OB(TMDSn[2]));
OBUFDS OBUFDS_green(.I(TMDS_shift_green[0]), .O(TMDSp[1]), .OB(TMDSn[1]));
OBUFDS OBUFDS_blue (.I(TMDS_shift_blue [0]), .O(TMDSp[0]), .OB(TMDSn[0]));
OBUFDS OBUFDS_clock(.I(pixclk), .O(TMDSp_clock), .OB(TMDSn_clock));
更高的分辨率
對(duì)于 640x480,我們使用了 250MHz 時(shí)鐘串行器,但為了獲得更高的分辨率,我們需要更高的頻率,這很快就會(huì)超過(guò) FPGA 的能力。 解決方法是使用一些特殊的FPGA IO功能,如DDR輸出和IO串行器。
在較高頻率下,另一個(gè)問(wèn)題是如何可靠地將數(shù)據(jù)從像素時(shí)鐘域傳輸?shù)酱衅饔颉?一種可能的技術(shù)是使用淺層FIFO。 查看 Xilinx XAPP460(用于 Spartan-3A)和 XAPP495(用于 Spartan-6)應(yīng)用筆記,了解一些想法。
截圖
以下是使用數(shù)碼相機(jī)拍攝的幾張照片,該相機(jī)拍攝了由Pluto-IIx HDMI驅(qū)動(dòng)的LCD顯示器。
我們有乒乓球比賽......
為了好玩,經(jīng)典的 PacMan 街機(jī)游戲......曾經(jīng)可以從 fpgaarcade.com 獲得,但該網(wǎng)站最近進(jìn)行了重新設(shè)計(jì)。 您仍然可以使用回溯機(jī)獲取原始源代碼。
這是我們測(cè)試板的圖片(Pluto-IIx HDMI加載了一個(gè)可選的HDMI適配器 - 所以我們實(shí)際上有兩個(gè)HDMI輸出可以玩......
評(píng)論