使用zedboard完成PL部分驱动VGA彩条显示实验。使用的代码在下面的帖子里的代码的基础上做了少量修改。
FPGA基础之VGA(二)彩条显示_不会一直在门外的博客-CSDN博客
实验效果如下图:
VGA接口的时序
关于VGA的内容,下面这篇文章已经写的很好了,我就不再多细讲了:
简单说一下下面的五个信号在工作时的时序吧:
HS | 行同步信号 |
VS | 场同步信号 |
R | 四位红色信号 |
G | 四位绿色信号 |
B | 四位蓝色信号 |
工作时序如下:
简单来讲,一个周期为四段,分别为a、b、c、d。
以行时序举例:1、行同步信号在a段为低电平b、c、d段为高电平。
2、R、G、B信号只在c段发送有效信号,其他的a、b、d段时要严格保持信号值为0。
VGA彩条显示实验完整代码
下面的为分频模块,可以将100M时钟分频为25M
div_clk.v
module div_clk(
rst_n,
sys_clk,
clk
);
input rst_n;
input sys_clk;
output clk;
reg clk;
reg[1:0] h;
always@(posedge sys_clk)
if(!rst_n)
begin
h <= 0;
clk <= 0;
end
else
if(h == 2'b00)
begin
clk <= 0;
h <= h + 1;
end
else if(h == 2'b01)
begin
clk <= 0;
h <= h + 1;
end
else if(h == 2'b10)
begin
clk <= 1'b1;
h <= h + 1;
end
else
begin
clk <= 1'b1;
h <= h + 1;
end
endmodule
内容比较简单,就是0-3循环计数,计数到0、1时输出低电平信号,2、3时输出高电平信号。
下面为VGA显示模块:
vga_ctrl.v
`define VGA_640x480x60 // choose different video standard,revise PLL clk ,alter cnt WIDTH
module vga_ctrl (
input wire clk,
input wire rst_n,
output reg [11:0] vga_rgb,
output reg vga_hs,
output reg vga_vs
);
//================ VGA_680X480X60 =========================================================
`ifdef VGA_640x480x60 // PLL clk = 25M = 640x480x60
localparam HS_A = 96; // synchronous pulse, horizontal
localparam HS_B = 48; // back porch pulse
localparam HS_C = 640; // display interval
localparam HS_D = 16; // Front porch
localparam HS_E = 800; // horizontal cycles
localparam VS_A = 2; // synchronous pulse, vertical
localparam VS_B = 33;
localparam VS_C = 480;
localparam VS_D = 10;
localparam VS_E = 525;
localparam HS_WIDTH = 10;
localparam VS_WIDTH = 10;
`endif
reg [HS_WIDTH - 1:0] cnt_hs; // counter for horizontal synchronous signal
reg [VS_WIDTH - 1:0] cnt_vs; // counter for vertical synchrous signal
wire en_hs; // dsiplay horizontal enable
wire en_vs; // display vertical enable
wire en; // effective display zone
/*行计数器*/
always @ (posedge clk, negedge rst_n)
if (!rst_n)
cnt_hs <= 0;
else
if (cnt_hs < HS_E - 1)
cnt_hs <= cnt_hs + 1'b1; //行计数器的值小于上限,行计数器加一
else
cnt_hs <= 0; //行计数器的值等于上限,行计数器清零
/*场计数器*/
always @ (posedge clk, negedge rst_n)
if (!rst_n)
cnt_vs <= 0;
else
if (cnt_hs == HS_E - 1) //如果一行扫描结束
if (cnt_vs < VS_E - 1)
cnt_vs <= cnt_vs + 1'b1; //列计数器的值少于上限,列计数加一
else
cnt_vs <= 0; //列计数器的值等于上限,列计数清零
else
cnt_vs <= cnt_vs; //如果一行扫描未结束,那列计保持不变
always @ (posedge clk, negedge rst_n)
if (!rst_n)
vga_hs <= 1'b1;
else
if (cnt_hs < HS_A - 1) //每个行周期开头,都有一段时间的同步低电平时间
vga_hs <= 1'b0;
else
vga_hs <= 1'b1; //同步低电平时间结束
always @ (posedge clk, negedge rst_n)
if (!rst_n)
vga_vs <= 1'b1;
else
if (cnt_vs < VS_A - 1)
vga_vs <= 1'b0; //每个场周期开头,都有一段时间的同步低电平时间
else
vga_vs <= 1'b1; //同步低电平时间结束
//生成使能信号,在使能信号高电平时,才发送RGB信号,否则RGB信号为0
assign en_hs = (cnt_hs > HS_A + HS_B - 1) && (cnt_hs < HS_E - HS_D);
assign en_vs = (cnt_vs > VS_A + VS_B - 1) && (cnt_vs < VS_E - VS_D);
assign en = en_hs && en_vs;
always @ (posedge clk, negedge rst_n)
if (!rst_n)
vga_rgb <= 12'b0000_0000_0000;
else
if (en) //使能信号为高时,RGB信号正常输出
if(cnt_hs < HS_A + HS_B + 213)
vga_rgb <= 12'b1111_0000_0000; //每行640个像素点,0-212显示红色
else if(cnt_hs < HS_A + HS_B + 427)
vga_rgb <= 12'b0000_1111_0000; //每行640个像素点,213-426显示绿色
else
vga_rgb <= 12'b0000_0000_1111; //每行640个像素点,427-640显示绿色
else //使能信号为高时,RGB信号为0,消隐
vga_rgb <= 12'b0000_0000_0000;
endmodule
这段代码给了很多注释,相信初学者也可以看懂。
下面为顶层代码:
vga_stripes_top.v
module vga_stripes_top (
input wire clk,
input wire rst_n,
output wire [11:0] vga_rgb,
output wire vga_hs,
output wire vga_vs
);
wire clk_25m;
//分频模块,将100M时钟分频为25M
div_clk u0 (
.rst_n ( ~rst_n ),
.sys_clk ( clk ),
.clk ( clk_25m )
);
//vga显示模块
vga_ctrl u1 (
.clk (clk_25m ),
.rst_n (~rst_n ),
.vga_rgb (vga_rgb ),
.vga_hs (vga_hs ),
.vga_vs (vga_vs )
);
endmodule
下面为约束文件
vga.xdc
set_property IOSTANDARD LVCMOS33 [get_ports {vga_rgb[11]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_rgb[10]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_rgb[9]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_rgb[8]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_rgb[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_rgb[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_rgb[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_rgb[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_rgb[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_rgb[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_rgb[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_rgb[0]}]
set_property PACKAGE_PIN V20 [get_ports {vga_rgb[11]}]
set_property PACKAGE_PIN U20 [get_ports {vga_rgb[10]}]
set_property PACKAGE_PIN V19 [get_ports {vga_rgb[9]}]
set_property PACKAGE_PIN V18 [get_ports {vga_rgb[8]}]
set_property PACKAGE_PIN AB22 [get_ports {vga_rgb[7]}]
set_property PACKAGE_PIN AA22 [get_ports {vga_rgb[6]}]
set_property PACKAGE_PIN AB21 [get_ports {vga_rgb[5]}]
set_property PACKAGE_PIN AA21 [get_ports {vga_rgb[4]}]
set_property PACKAGE_PIN Y21 [get_ports {vga_rgb[3]}]
set_property PACKAGE_PIN Y20 [get_ports {vga_rgb[2]}]
set_property PACKAGE_PIN AB20 [get_ports {vga_rgb[1]}]
set_property PACKAGE_PIN AB19 [get_ports {vga_rgb[0]}]
set_property PACKAGE_PIN Y9 [get_ports clk]
set_property PACKAGE_PIN T18 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property PACKAGE_PIN AA19 [get_ports vga_hs]
set_property PACKAGE_PIN Y19 [get_ports vga_vs]
set_property IOSTANDARD LVCMOS33 [get_ports vga_hs]
set_property IOSTANDARD LVCMOS33 [get_ports vga_vs]
这个部分是根据zedboard的原理图编写的。原理图如下: