FPGA实现VGA转HDMI功能的IP,配详细的接口和使用说明

先来点硬核的------VGA接口那堆行场同步信号和模拟RGB,在FPGA里处理起来简直就是数字电路基础考试题。咱们今天不整虚的,直接拿Verilog搞个实时转换IP,把640x480@60Hz的VGA信号怼进HDMI显示器。

看这个接口定义先:
verilog
module vga2hdmi (
input wire vga_clk, // 25.175MHz原VGA时钟
input wire vga_hsync, // 行同步低有效
input wire vga_vsync, // 场同步低有效
input wire [7:0] vga_r, // 8位红色分量
input wire [7:0] vga_g,
input wire [7:0] vga_b,
output wire hdmi_clk, // HDMI TMDS时钟
output wire [3:0] hdmi_d // TMDS差分对
);
// 搞个DCM生成HDMI需要的像素时钟5倍频
clock_gen u_clkgen(
.clk_in(vga_clk),
.clk_x5(hdmi_clk)
);
// 颜色空间转换直接查表,省得算浮点
wire [9:0] tmds_r, tmds_g, tmds_b;
color_conv u_conv_r(.data(vga_r), .tmds(tmds_r));
color_conv u_conv_g(.data(vga_g), .tmds(tmds_g));
color_conv u_conv_b(.data(vga_b), .tmds(tmds_b));
// TMDS编码器三连击
tmds_encoder u_enc_r(
.clk(vga_clk),
.hsync(vga_hsync),
.vsync(vga_vsync),
.data(tmds_r),
.channel(hdmi_d[2])
);
tmds_encoder u_enc_g(/* 同上 */);
tmds_encoder u_enc_b(/* 同上 */);
// 控制通道塞点固定值
assign hdmi_d[3] = 1'b1;
endmodule
重点在TMDS编码这块,用查表代替算法能省不少逻辑单元。比如颜色转换模块:
verilog
module color_conv(
input [7:0] data,
output [9:0] tmds
);
// 简单粗暴的伽马校正LUT
reg [9:0] conv_table[0:255];
initial $readmemh("gamma_lut.hex", conv_table);
assign tmds = conv_table[data];
endmodule
使用时注意三点:
- VGA时钟必须精确到25.175MHz±0.5%
- HDMI输出端需要外接DDC电平转换芯片
- 同步信号必须做两时钟域处理
实测中发现个坑:VGA消隐期间得手动插入同步包。在编码器里加个状态机:
verilog
always @(posedge clk) begin
case(state)
ACTIVE: if(!vga_de) state <= SYNC;
SYNC: begin
tmds_out <= 10'b1101010101; // HDMI同步控制符号
if(vga_de) state <= ACTIVE;
end
endcase
end
调试时用Signaltap抓波形,重点看HSYNC到TMDS编码的延迟是否在5个像素周期内。这玩意儿在Artix-7上跑起来资源占用大概15%左右,关键路径在时钟域交叉那块。

