开发软件:Vivado 2018.3
开发板:PYNQ-Z2
IP核:rgb2dvi(Digilent官方)
我一直想使用FPGA开发图像处理算法,在学习图像处理前要学会如何显示图像。我使用的开发板是PYNQ-Z2,在该开发板上带有两个HDMI接口(一个输入,一个输出)。其实这两个HDMI接口都可以用作输出,因为HDMI接口是直接连接到FPGA的IO引脚上的。
关于HDMI的原理,网上有很多资源,可参考这篇文章(点击文章可跳转到网页查看),在此不再赘述。接下来就介绍下我是如何实现HDMI输出图像的。
首先介绍下我实现了HDMI输出的图像像素是800*600,像素时钟为40MHz。而我使用了PYNQ-Z2上125MHz作为输入,之后使用MMCM经行分频,产生40MHz和200MHz。除了MMCM产生需要的时钟外,还使用了rgb2dvi IP核。该IP核实现了HDMI发送数据的编码及串行化。该IP 的配置如下:
在该IP核配置中我使用了外部产生串行化时钟,这里的外部是指不通过rgb2dvi IP核产生串行化时钟,而是使用了MMCM时钟管理单元产生。串行化时钟的频率为像素时钟的5倍,这里使用的是200MHz。
除了以上两个IP外,还需要产生图像数据。这里我使用了该文章作者的源代码。以下是源代码。
module color_bar(
input clk, //pixel clock
input rst, //reset signal high active
output hs, //horizontal synchronization
output vs, //vertical synchronization
output de, //video valid
output[7:0] rgb_r, //video red data
output[7:0] rgb_g, //video green data
output[7:0] rgb_b //video blue data
);
`define VIDEO_800_600
//video timing parameter definition
`ifdef VIDEO_1280_720
parameter H_ACTIVE = 16'd1280; //horizontal active time (pixels)
parameter H_FP = 16'd110; //horizontal front porch (pixels)
parameter H_SYNC = 16'd40; //horizontal sync time(pixels)
parameter H_BP = 16'd220; //horizontal back porch (pixels)
parameter V_ACTIVE = 16'd720; //vertical active Time (lines)
parameter V_FP = 16'd5; //vertical front porch (lines)
parameter V_SYNC = 16'd5; //vertical sync time (lines)
parameter V_BP = 16'd20; //vertical back porch (lines)
parameter HS_POL = 1'b1; //horizontal sync polarity, 1 : POSITIVE,0 : NEGATIVE;
parameter VS_POL = 1'b1; //vertical sync polarity, 1 : POSITIVE,0 : NEGATIVE;
`endif
//480x272 9Mhz
`ifdef VIDEO_480_272
parameter H_ACTIVE = 16'd480;
parameter H_FP = 16'd2;
parameter H_SYNC = 16'd41;
parameter H_BP = 16'd2;
parameter V_ACTIVE = 16'd272;
parameter V_FP = 16'd2;
parameter V_SYNC = 16'd10;
parameter V_BP = 16'd2;
parameter HS_POL = 1'b0;
parameter VS_POL = 1'b0;
`endif
//640x480 25.175Mhz
`ifdef VIDEO_640_480
parameter H_ACTIVE = 16'd640;
parameter H_FP = 16'd16;
parameter H_SYNC = 16'd96;
parameter H_BP = 16'd48;
parameter V_ACTIVE = 16'd480;
parameter V_FP = 16'd10;
parameter V_SYNC = 16'd2;
parameter V_BP = 16'd33;
parameter HS_POL = 1'b0;
parameter VS_POL = 1'b0;
`endif
//800x480 33Mhz
`ifdef VIDEO_800_480
parameter H_ACTIVE = 16'd800;
parameter H_FP = 16'd40;
parameter H_SYNC = 16'd128;
parameter H_BP = 16'd88;
parameter V_ACTIVE = 16'd480;
parameter V_FP = 16'd1;
parameter V_SYNC = 16'd3;
parameter V_BP = 16'd21;
parameter HS_POL = 1'b0;
parameter VS_POL = 1'b0;
`endif
//800x600 40Mhz
`ifdef VIDEO_800_600
parameter H_ACTIVE = 16'd800;
parameter H_FP = 16'd40;
parameter H_SYNC = 16'd128;
parameter H_BP = 16'd88;
parameter V_ACTIVE = 16'd600;
parameter V_FP = 16'd1;
parameter V_SYNC = 16'd4;
parameter V_BP = 16'd23;
parameter HS_POL = 1'b1;
parameter VS_POL = 1'b1;
`endif
//1024x768 65Mhz
`ifdef VIDEO_1024_768
parameter H_ACTIVE = 16'd1024;
parameter H_FP = 16'd24;
parameter H_SYNC = 16'd136;
parameter H_BP = 16'd160;
parameter V_ACTIVE = 16'd768;
parameter V_FP = 16'd3;
parameter V_SYNC = 16'd6;
parameter V_BP = 16'd29;
parameter HS_POL = 1'b0;
parameter VS_POL = 1'b0;
`endif
//1920x1080 148.5Mhz
`ifdef VIDEO_1920_1080
parameter H_ACTIVE = 16'd1920;
parameter H_FP = 16'd88;
parameter H_SYNC = 16'd44;
parameter H_BP = 16'd148;
parameter V_ACTIVE = 16'd1080;
parameter V_FP = 16'd4;
parameter V_SYNC = 16'd5;
parameter V_BP = 16'd36;
parameter HS_POL = 1'b1;
parameter VS_POL = 1'b1;
`endif
parameter H_TOTAL = H_ACTIVE + H_FP + H_SYNC + H_BP;//horizontal total time (pixels)
parameter V_TOTAL = V_ACTIVE + V_FP + V_SYNC + V_BP;//vertical total time (lines)
//define the RGB values for 8 colors
parameter WHITE_R = 8'hff;
parameter WHITE_G = 8'hff;
parameter WHITE_B = 8'hff;
parameter YELLOW_R = 8'hff;
parameter YELLOW_G = 8'hff;
parameter YELLOW_B = 8'h00;
parameter CYAN_R = 8'h00;
parameter CYAN_G = 8'hff;
parameter CYAN_B = 8'hff;
parameter GREEN_R = 8'h00;
parameter GREEN_G = 8'hff;
parameter GREEN_B = 8'h00;
parameter MAGENTA_R = 8'hff;
parameter MAGENTA_G = 8'h00;
parameter MAGENTA_B = 8'hff;
parameter RED_R = 8'hff;
parameter RED_G = 8'h00;
parameter RED_B = 8'h00;
parameter BLUE_R = 8'h00;
parameter BLUE_G = 8'h00;
parameter BLUE_B = 8'hff;
parameter BLACK_R = 8'h00;
parameter BLACK_G = 8'h00;
parameter BLACK_B = 8'h00;
reg hs_reg; //horizontal sync register
reg vs_reg; //vertical sync register
reg hs_reg_d0; //delay 1 clock of 'hs_reg'
reg vs_reg_d0; //delay 1 clock of 'vs_reg'
reg[11:0] h_cnt; //horizontal counter
reg[11:0] v_cnt; //vertical counter
reg[11:0] active_x; //video x position
reg[11:0] active_y; //video y position
reg[7:0] rgb_r_reg; //video red data register
reg[7:0] rgb_g_reg; //video green data register
reg[7:0] rgb_b_reg; //video blue data register
reg h_active; //horizontal video active
reg v_active; //vertical video active
wire video_active; //video active(horizontal active and vertical active)
reg video_active_d0; //delay 1 clock of video_active
assign hs = hs_reg_d0;
assign vs = vs_reg_d0;
assign video_active = h_active & v_active;
assign de = video_active_d0;
assign rgb_r = rgb_r_reg;
assign rgb_g = rgb_g_reg;
assign rgb_b = rgb_b_reg;
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
begin
hs_reg_d0 <= 1'b0;
vs_reg_d0 <= 1'b0;
video_active_d0 <= 1'b0;
end
else
begin
hs_reg_d0 <= hs_reg;
vs_reg_d0 <= vs_reg;
video_active_d0 <= video_active;
end
end
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
h_cnt <= 12'd0;
else if(h_cnt == H_TOTAL - 1)//horizontal counter maximum value
h_cnt <= 12'd0;
else
h_cnt <= h_cnt + 12'd1;
end
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
active_x <= 12'd0;
else if(h_cnt >= H_FP + H_SYNC + H_BP - 1)//horizontal video active
active_x <= h_cnt - (H_FP[11:0] + H_SYNC[11:0] + H_BP[11:0] - 12'd1);
else
active_x <= active_x;
end
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
v_cnt <= 12'd0;
else if(h_cnt == H_FP - 1)//horizontal sync time
if(v_cnt == V_TOTAL - 1)//vertical counter maximum value
v_cnt <= 12'd0;
else
v_cnt <= v_cnt + 12'd1;
else
v_cnt <= v_cnt;
end
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
hs_reg <= 1'b0;
else if(h_cnt == H_FP - 1)//horizontal sync begin
hs_reg <= HS_POL;
else if(h_cnt == H_FP + H_SYNC - 1)//horizontal sync end
hs_reg <= ~hs_reg;
else
hs_reg <= hs_reg;
end
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
h_active <= 1'b0;
else if(h_cnt == H_FP + H_SYNC + H_BP - 1)//horizontal active begin
h_active <= 1'b1;
else if(h_cnt == H_TOTAL - 1)//horizontal active end
h_active <= 1'b0;
else
h_active <= h_active;
end
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
vs_reg <= 1'd0;
else if((v_cnt == V_FP - 1) && (h_cnt == H_FP - 1))//vertical sync begin
vs_reg <= HS_POL;
else if((v_cnt == V_FP + V_SYNC - 1) && (h_cnt == H_FP - 1))//vertical sync end
vs_reg <= ~vs_reg;
else
vs_reg <= vs_reg;
end
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
v_active <= 1'd0;
else if((v_cnt == V_FP + V_SYNC + V_BP - 1) && (h_cnt == H_FP - 1))//vertical active begin
v_active <= 1'b1;
else if((v_cnt == V_TOTAL - 1) && (h_cnt == H_FP - 1)) //vertical active end
v_active <= 1'b0;
else
v_active <= v_active;
end
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
begin
rgb_r_reg <= 8'h00;
rgb_g_reg <= 8'h00;
rgb_b_reg <= 8'h00;
end
else if(video_active)
if(active_x == 12'd0)
begin
rgb_r_reg <= WHITE_R;
rgb_g_reg <= WHITE_G;
rgb_b_reg <= WHITE_B;
end
else if(active_x == (H_ACTIVE/8) * 1)
begin
rgb_r_reg <= YELLOW_R;
rgb_g_reg <= YELLOW_G;
rgb_b_reg <= YELLOW_B;
end
else if(active_x == (H_ACTIVE/8) * 2)
begin
rgb_r_reg <= CYAN_R;
rgb_g_reg <= CYAN_G;
rgb_b_reg <= CYAN_B;
end
else if(active_x == (H_ACTIVE/8) * 3)
begin
rgb_r_reg <= GREEN_R;
rgb_g_reg <= GREEN_G;
rgb_b_reg <= GREEN_B;
end
else if(active_x == (H_ACTIVE/8) * 4)
begin
rgb_r_reg <= MAGENTA_R;
rgb_g_reg <= MAGENTA_G;
rgb_b_reg <= MAGENTA_B;
end
else if(active_x == (H_ACTIVE/8) * 5)
begin
rgb_r_reg <= RED_R;
rgb_g_reg <= RED_G;
rgb_b_reg <= RED_B;
end
else if(active_x == (H_ACTIVE/8) * 6)
begin
rgb_r_reg <= BLUE_R;
rgb_g_reg <= BLUE_G;
rgb_b_reg <= BLUE_B;
end
else if(active_x == (H_ACTIVE/8) * 7)
begin
rgb_r_reg <= BLACK_R;
rgb_g_reg <= BLACK_G;
rgb_b_reg <= BLACK_B;
end
else
begin
rgb_r_reg <= rgb_r_reg;
rgb_g_reg <= rgb_g_reg;
rgb_b_reg <= rgb_b_reg;
end
else
begin
rgb_r_reg <= 8'h00;
rgb_g_reg <= 8'h00;
rgb_b_reg <= 8'h00;
end
end
endmodule
接下来的代码是顶层代码,这里主要是对各个IP经行实例化。
module top(
input clk,
output[2:0] TMDS_DATA_p,
output[2:0] TMDS_DATA_n,
output TMDS_CLK_p,
output TMDS_CLK_n
);
clk_wiz_0 u2(
.clk_in1(clk),
.clk_out1(clk_40m),
.clk_out2(clk_200m),
.reset(1'b0),
.locked(sys_rst)
);
wire[7:0] R,G,B;
/*hdmi_data_gen u3(
.pix_clk(clk_40m),
.VGA_R(R),
.VGA_G(G),
.VGA_B(B),
.VGA_HS(VGA_HS),
.VGA_VS(VGA_VS),
.VGA_DE(VGA_DE)
);*/
color_bar u4(
.clk(clk_40m), //pixel clock
.rst(~sys_rst), //reset signal high active
.hs(VGA_HS), //horizontal synchronization
.vs(VGA_VS), //vertical synchronization
.de(VGA_DE), //video valid
.rgb_r(R), //video red data
.rgb_g(G), //video green data
.rgb_b(B) //video blue data
);
rgb2dvi_0 u1(
.aRst_n(sys_rst),
.SerialClk(clk_200m),
.PixelClk(clk_40m),
.TMDS_Clk_p(TMDS_CLK_p),
.TMDS_Clk_n(TMDS_CLK_n),
.TMDS_Data_p(TMDS_DATA_p),
.TMDS_Data_n(TMDS_DATA_n),
.vid_pData({R,G,B}),
.vid_pHSync(VGA_HS),
.vid_pVSync(VGA_VS),
.vid_pVDE(VGA_DE)
);
endmodule
IO约束如下:
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property PACKAGE_PIN H16 [get_ports clk]
#set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
#set_property PACKAGE_PIN M15 [get_ports rst_n]
#############HDMI_O##################
set_property IOSTANDARD TMDS_33 [get_ports TMDS_CLK_n]
set_property PACKAGE_PIN L17 [get_ports TMDS_CLK_n]
set_property PACKAGE_PIN L16 [get_ports TMDS_CLK_p]
set_property IOSTANDARD TMDS_33 [get_ports TMDS_CLK_p]
set_property IOSTANDARD TMDS_33 [get_ports {TMDS_DATA_n[0]}]
set_property PACKAGE_PIN K18 [get_ports {TMDS_DATA_n[0]}]
set_property PACKAGE_PIN K17 [get_ports {TMDS_DATA_p[0]}]
set_property IOSTANDARD TMDS_33 [get_ports {TMDS_DATA_p[0]}]
set_property IOSTANDARD TMDS_33 [get_ports {TMDS_DATA_n[1]}]
set_property PACKAGE_PIN J19 [get_ports {TMDS_DATA_n[1]}]
set_property PACKAGE_PIN K19 [get_ports {TMDS_DATA_p[1]}]
set_property IOSTANDARD TMDS_33 [get_ports {TMDS_DATA_p[1]}]
set_property IOSTANDARD TMDS_33 [get_ports {TMDS_DATA_n[2]}]
set_property PACKAGE_PIN H18 [get_ports {TMDS_DATA_n[2]}]
set_property PACKAGE_PIN J18 [get_ports {TMDS_DATA_p[2]}]
set_property IOSTANDARD TMDS_33 [get_ports {TMDS_DATA_p[2]}]
需要注意的是HDMI接口输出了4组差分信号,约束的时候需要使用TMDS_33电平。
参考:https://www.cnblogs.com/ninghechuan/archive/2018/01/26/8353827.html