官方论坛
官方淘宝
官方博客
微信公众号
点击联系吴工 点击联系周老师
您的当前位置:主页 > FPGA行业资讯 >

基于FPGA的HDMI设计-明德扬科教(mdy-edu.com)

发布时间:2019-12-10   作者:admin 浏览量:

HDMI是一种数字视频接口,易于从现代FPGA驱动。
让我们看看它是如何工作的。

连接器

标准的HDMI连接器称为“A型”,有19个引脚。在19个引脚中,有8个是特别感兴趣的,因为它们形成了4个TMDS差分对来传输实际的高速视频信息。

  • TMD时钟+和时钟-
  • TMDs data 0+和data 0-
  • TMDs data 1+和data 1-
  • TMDs data 2+和data 2-

我们从FPGA到HDMI连接器的连接非常简单.我们使用8个FPGA引脚配置为4个差分TMDS输出。

视频信号

让我们创建一个640x480 RGB 24 bpp@60 Hz视频信号。这是每帧307200像素,因为每个像素都有24位(红色、绿色和蓝色8位),在60 Hz时,HDMI链路传输0.44Gbps的“有用”数据。

但是视频信号通常也有一个“离屏”区域,HDMI接收器(电视或监视器)用于一些家务管理。我们的640x480帧实际上是作为800x525帧发送的。

考虑到这一点,我们需要一个24.5MHz像素时钟来达到每秒钟60帧,但是HDMI指定了一个25 MHz的最小像素时钟,所以我们使用它(这可以获得61 Hz的帧速率)。

TMD信号

FPGA有4个TMDS差分对来驱动。
首先,TMDS时钟只是像素时钟,所以它在25 MHz运行。另外3对发出红色、绿色和蓝色的信号,所以我们得到了类似的信号。

其实事情有点复杂。HDMI要求我们对数据进行置乱,并在每条彩色车道上增加2位,所以我们有10位而不是8位,而链路以每像素30位的速度结束。hdmi接收机需要加扰和额外的位,以便正确地同步和获取dvi和hdmi规范中的每个车道模式细节。

源代码

首先是视频发生器。我们使用了几个计数器,它们穿过800x525像素区域.

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;

创建hsync和vsync信号.。

wire hSync = (CounterX>=656) && (CounterX<752); wire vSync = (CounterY>=490) && (CounterY<492); wire DrawArea = (CounterX<640) && (CounterY<480);

产生一些红,绿,蓝的信号(每个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];

通过三个“TMDS_编码器”实例将其扩展到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));

现在,我们为每个像素时钟周期发送三个10位值。我们把25 MHz的时钟乘以10来产生一个250 MHz的时钟.

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

在250 MHz频率下使用三个移位寄存器.

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数据发送到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));

完整的源代码是可用的。这里.

较高分辨率

对于640x480,我们使用250 MHz时钟串行化,但对于更高的分辨率,我们需要更高的频率,这可以迅速超过FPGA的能力。解决办法是使用一些特殊的FPGA IO特性,如DDR输出和IO串行化。

在较高频率上的另一个问题是如何可靠地将数据从像素时钟域传输到序列化器域。一种可能的技术是使用浅层FIFO。检查XilinxXAPP 460(斯巴达-3A)及XAPP 495(对于斯巴达-6)申请说明,以获得一些想法。

截图

以下是几张用数码相机拍摄的由Pluto-IIxHDMI驱动的液晶显示器的照片。
我们有乒乓球比赛...

为了好玩,经典的吃豆人街机游戏.。以前可以从fpgaarcade.com但是这个网站最近被重新设计了。仍然可以使用回车机.

下面是我们测试板的一张图片(PlutoIIxHDMI加载了一个可选的HDMI适配器,因此我们实际上有两个HDMI输出要处理.)。

  •   
  •   
  •   
  •  
  • FPGA教育领域第一品牌
  • 咨询热线:020-39002701
  • 技术交流Q群:544453837