新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > FPGA:HDMI接口

FPGA:HDMI接口

作者:时间:2024-01-11来源:EEPW编译收藏

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

本文引用地址:http://www.eepw.com.cn/article/202401/454692.htm

连接器

标准 HDMI 连接器有 19 个引脚。 在 19 个引脚中,有 8 个特别值得关注,因为它们形成 4 个 TMDS 差分对来传输实际的高速视频信息。

  • TMDS 时钟+ 和时钟-

  • TMDS data0+ 和 data0-

  • TMDS data1+ 和 data1-

  • TMDS data2+ 和 data2-

我们从到HDMI连接器的连接再简单不过了......我们使用 8 个 引脚,配置为 4 个差分 TMDS 输出。

视频信号

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

但视频信号通常也有一个“屏幕外”区域,HDMI接收器(电视或显示器)使用它进行一些内务处理。 我们的 640x480 帧实际上是作为 800x525 帧发送的。

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

TMDS信号

FPGA 有 4 个 TMDS 差分对可供驱动。
首先,TMDS时钟只是像素时钟,因此它以25MHz运行。 其他 3 对传输红色、绿色和蓝色 8 位信号,因此我们得到类似的东西。

事实上,事情只是稍微复杂一些。 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;

并创建 H-Sync 和 V-Sync 信号...

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_encoder”实例分别扩展到 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 位值。 我们将 25MHz 时钟乘以 10 以生成 250MHz 时钟......

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

并使用三个时钟频率为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数据发送到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,我们使用了 250MHz 时钟串行器,但为了获得更高的分辨率,我们需要更高的频率,这很快就会超过 FPGA 的能力。 解决方法是使用一些特殊的FPGA IO功能,如DDR输出和IO串行器。

在较高频率下,另一个问题是如何可靠地将数据从像素时钟域传输到串行器域。 一种可能的技术是使用浅层FIFO。 查看 Xilinx XAPP460(用于 Spartan-3A)和 XAPP495(用于 Spartan-6)应用笔记,了解一些想法。

截图

以下是使用数码相机拍摄的几张照片,该相机拍摄了由Pluto-IIx HDMI驱动的LCD显示器。
我们有乒乓球比赛......

为了好玩,经典的 PacMan 街机游戏......曾经可以从 fpgaarcade.com 获得,但该网站最近进行了重新设计。 您仍然可以使用回溯机获取原始源代码。

这是我们测试板的图片(Pluto-IIx HDMI加载了一个可选的HDMI适配器 - 所以我们实际上有两个HDMI输出可以玩......



关键词: FPGA HDMI接口

评论


相关推荐

技术专区

关闭