上一篇介绍了vga显示图像的基础知识。这一节我们来实现vga彩条显示。实现步骤:
1、接口分析。module的接口,除了一般的clk,rst,还需要输出水平同步信号,垂直同步信号,以及RGB颜色值。
2、扫描过程各个参数的值。这个可以通过查阅VESA手册(见附件),得到想要输出分辨率下的各个参数值。主要有时钟频率、同步信号极性、同步信号宽度、后沿宽度、边框、显示区域、前沿等。
3、调用PLL。VGA时钟频率一般和输入钟频率不同,为了得到比较精确的VGA始终频率,需要使用PLL输出。
4、水平和垂直扫描计数。通过设置水平和垂直两个计数器来确定当前扫描位置,也可以看作当前扫描像素的坐标。
5、同步信号输出。根据当前扫描点位置和第二步中参数值来决定水平、垂直同步信号的输出。
6、判断是否在显示区域。根据当前扫描点的位置和各个参数值,判断行是否在显示区域,列是否在显示区域,如果行、列都在显示区域,则当前扫描点在显示区域。
7、输出RGB值。当扫描点进入显示区域后,输出RGB的值。否则,RGB应当输出零。

图1

图2
程序参考代码如下:
module VGA_colorbar(
input wire sysclk, //系统时钟,也就是板子的时钟
input wire rst,
output reg [3:0] R, //输出的颜色值
output reg [3:0] G, //输出的颜色值
output reg [3:0] B, //输出的颜色值
output wire HS, //水平同步信号
output wire VS //垂直同步信号
);
//显示分辨率 640*480@60Hz
//时钟25.175M
localparam HPOLA = 0; //水平同步信号极性
localparam VPOLA = 0; //垂直同步信号极性
localparam HTOTAL = 800;//水平总像素数
localparam HSYNCP = 96; //水平同步信号像素数
localparam HBACKP = 40; //扫描后沿像素数
localparam LBorder = 8; //左边框
localparam HDISP = 640;//显示区域
localparam RBorder = 8; //右边框
localparam HFRONTP = 8; //扫描前沿像素数
localparam VTOTAL = 525;//垂直总像素数
localparam VSYNCP = 2; //垂直同步信号
localparam VBACKP = 25; //扫描后沿像素数
localparam TBorder = 8; //顶边框
localparam VDISP = 480;//显示区域
localparam BBorder = 8; //底边框
localparam VFRONTP = 2; //扫描前沿像素数
wire vgaclk;
wire locked;
wire vgarst;
assign vgarst = ~locked; //locked为高表示时钟正常,此时不复位
//使用锁相环来输出指定时钟
pll pll_inst (
.areset ( rst ),
.inclk0 ( sysclk ),
.c0 ( vgaclk ),
.locked ( locked )
);
reg [9:0] hcnt; //行计数
reg [9:0] vcnt; //场计数
//行计数
always@(posedge vgaclk or posedge vgarst) begin
if(vgarst)
hcnt <= 0;
else
if(hcnt == HTOTAL - 1)
hcnt <= 0;
else
hcnt <= hcnt + 1;
end
//场计数
always@(posedge vgaclk or posedge vgarst) begin
if(vgarst)
vcnt <= 0;
else
if((hcnt == HTOTAL - 1) && (vcnt == VTOTAL - 1))
vcnt <= 0;
else if(hcnt == HTOTAL - 1)
vcnt <= vcnt + 1;
else
vcnt <= vcnt;
end
//行同步信号输出
assign HS = (hcnt <= HSYNCP - 1) ? HPOLA : ~HPOLA;
//场同步信号输出
assign VS = (vcnt <= VSYNCP - 1) ? VPOLA : ~VPOLA;
//显示区域的判断
wire h_disp;
assign h_disp = (hcnt > (HSYNCP + HBACKP + LBorder - 1) &&
hcnt < (HSYNCP + HBACKP + LBorder + HDISP - 1)) ? 1'b1 : 1'b0;
wire v_disp;
assign v_disp = (vcnt > (VSYNCP + VBACKP + TBorder - 1) &&
vcnt < (VSYNCP + VBACKP + TBorder + VDISP- 1)) ? 1'b1 : 1'b0;
wire disp;
assign disp = h_disp && v_disp;
//如果扫描到显示区域内部,则输出颜色值
always@(posedge vgaclk or posedge vgarst) begin
if(vgarst)
begin
R <= 4'b0000;
G <= 4'b0000;
B <= 4'b0000;
end
else
if(disp)
begin
if(hcnt < (HSYNCP + HBACKP + LBorder) + HDISP/5 - 1)
begin
R <= 4'b1111;
G <= 4'b0000;
B <= 4'b0000;
end
else if(hcnt < (HSYNCP + HBACKP + LBorder) + HDISP*2/5 - 1)
begin
R <= 4'b0000;
G <= 4'b1111;
B <= 4'b0000;
end
else if(hcnt < (HSYNCP + HBACKP + LBorder) + HDISP*3/5 - 1)
begin
R <= 4'b0000;
G <= 4'b0000;
B <= 4'b1111;
end
else if(hcnt < (HSYNCP + HBACKP + LBorder) + HDISP*4/5 - 1)
begin
R <= 4'b1111;
G <= 4'b1111;
B <= 4'b0000;
end
else if(hcnt < (HSYNCP + HBACKP + LBorder) + HDISP - 1)
begin
R <= 4'b1111;
G <= 4'b1111;
B <= 4'b1111;
end
end
else
begin
R <= 4'b0000;
G <= 4'b0000;
B <= 4'b0000;
end
end
endmodule

图3
原理图:

图4

图5