HDMI是一个数字视频接口,通过FPGA可以很快的实现驱动。下面可以看到HDMI是怎么工作的.
connector
标准的HDMI连接器称为"A型",有19个引脚,其中8个引脚是需要注意的,组成了四个差分对TMDS(Transition Min imized Differential Signaling)[过度调制差分信号]。
我们链接HDMI和FPGA很简单,只要使用其中的4对差分信号就可以了。
video signal
现在需要插入一个640x480 RGB 24bpp(一个像素需要的bit数) @ 60Hz。所以一帧数据需要307200像素,而且每个像素需要24bit(RGB各8bit),在60Hz中HDMI链路中有用信号信号的速率是0.44Gbps。同时在视频信号中存在“off-area”区域,在所有的HDMI驱动控制中是非常有用的。640x480格式的实际上需要800x525格式。
TMDS signals
FPGA的TMDS驱动有4对不同的差分对。TMDS时钟是像素时钟 ,所以这里使用的是25MHz。其他3个差分对转化输出是红、绿、蓝信号。
但是事实上每一bit都会更复杂,HDMI要求使用“TMDS编码”来实现对应的显示数据,其中需要加2bit的加扰数据,所以实际上每通道是10bit而不是8bit,一帧数据传输需要30bit数据。
Source code
1、视频生成
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;
2、加入HSYNC和VSYNC同步信号
wire hSync = (CounterX>=656) && (CounterX<752);
wire vSync = (CounterY>=490) && (CounterY<492);
wire DrawArea = (CounterX<640) && (CounterY<480);
3、生成红、绿、蓝信号(每个颜色8bit)
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];
4、通过"TMDS编码"将8bit数据展诚10bit
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));
5、每个时钟需要去发送10bit的数据,我们使用的时钟是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
6、三个RGB的移位寄存器是在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
Higher resolutions
如果使用640x480分辨率,我们需要使用250MHz时钟的串行时钟, 但是如果要更高的分辨率,需要更高的频率,但是会超出FPGA本身的工作频率。可以选择使用一些特殊的FPGA IO引脚,譬如DDR输出和IO串行器。在更高频率是还有其他问题是,如何将像素时钟的数据转换到串行工作区中。一种可行的技术是使用一个浅(小)的FIFO。具体可以看xilinx XAPP460 和 XAPP495,可以得到相关的信息。