计算机显示区的显示有许多标准,常见的有VGA、SVGA等。在这里我们用VGA接口来控制显示器,VGA即Video Graphics Array的缩写,也就是视频图形阵列。作为一种标准的显示接口得到广泛的应用。
常见的彩色显示器一般由CRT(阴极射线管)构成,色彩是由R、G、B(红、黄、蓝)三基色组成。显示是用逐行扫描的方式解决,阴极射线枪发出电子束打在涂有荧光粉的荧光屏上,产生RGB三基色,合成一个彩色像素。扫描从屏幕的左上方开始,从左到右,从上到下,进行扫描,每扫完一行,电子束回到屏幕的左边下一行的起始位置,在这其间CRT对电子束进行消隐。每行结束时,用行同步信号进行同步;扫描完所有行,用场同步信号进行同步,并使扫描回到屏幕左上方,同时进行场消隐,预备下一场的扫描。
对于普通的VGA显示器,共有5个信号:R、G、B三基色;HS(行同步信号);VS(场同步信号)。对于时序驱动,VGA显示器要严格遵循“VGA”工业标准,即640x480@60Hz模式,否则可能会损害VGA显示器。
通常我们用的显示器都满足工业标准,因此我们设计VGA控制器时要参考显示器的技术规格。如图1所示是VGA行扫描、场扫描的时序图。
VGA工业标准要求频率:时钟频率 25.175MHz(像素输出的频率)
行扫描时序要求(单位:像素,即输出一个像素的时间间隔)
Sync:96;Back Porch:40;Left Borfer:8;Addr Time:640;Right Borfer:8;Front Porch:8。
场扫描时序要求(单位:行,即输出一行的时间间隔)
Sync:2;Back Porch:25;Top Borfer:8;Addr Time:480;Bottom Borfer:8;Front Porch:2。
如图2所示为VGA图像显示扫描示意图,在设计时,可用两个计数器进行计数(行、场扫描计数器),行计数器的驱动时钟为25MHz,场计数器的驱动时钟为行计数器的溢出信号。计数的同时控制行、场同步信号输出,并在适当的时候送出数据,就能显示相应的图像。注意消隐期间送出的R、G、B信号为0x00。
在这里插入代码片
产生25M时钟
```module gen_clk(
input wire clk_50M,
input wire rst_n,
output reg clk_25M
);
always@(posedge clk_50M or negedge rst_n)
if(!rst_n)
clk_25M <= 1'b0;
else
clk_25M <= ~clk_25M;
endmodule
/*
VGA工业标准要求频率:时钟频率 25.175MHz(像素输出的频率)
行扫描时序要求(单位:像素,即输出一个像素的时间间隔)
Sync:96;Back Porch:40;Left Borfer:8;Addr Time:640;Right Borfer:8;Front Porch:8。
场扫描时序要求(单位:行,即输出一行的时间间隔)
Sync:2;Back Porch:25;Top Borfer:8;Addr Time:480;Bottom Borfer:8;Front Porch:2。
一共公式800*525 其中640*480是显示器,其他相当于边界,
像素点相当于 144 <x <783
35< y <514
*/
module vga_ctrl(
input wire clk,//25M
input wire rst_n,
output reg H_sync,
output reg V_sync,
output reg [7:0]rgb_data
);
reg [9:0]h_cnt;//行计数器最大计数到800
reg [9:0]s_cnt;//场计数器最大到525
//行计数器
always@(posedge clk or negedge rst_n)
if(!rst_n)
h_cnt <= 10'd0;
else if(h_cnt == 10'd799)
h_cnt <= 10'd0;
else
h_cnt <= h_cnt + 1'd1;
//场计数器 ,一行结束之后加一
always@(posedge clk or negedge rst_n)
if(!rst_n)
s_cnt <= 10'd0;
else if(h_cnt == 10'd799&& s_cnt == 10'd524)
s_cnt <= 10'd0;
else if(h_cnt == 10'd799)
s_cnt <= s_cnt + 1'd1;
else
s_cnt <= s_cnt;
//产生行信号,
always@(posedge clk or negedge rst_n)
if(!rst_n)
H_sync <= 1;
else if(h_cnt == 10'd95)
H_sync <= 0;
else if(h_cnt == 10'd799)
H_sync <= 1;
else
H_sync <= H_sync;
//产生场信号
always@(posedge clk or negedge rst_n)
if(!rst_n)
V_sync <= 1;
else if(s_cnt == 10'd1 && h_cnt == 799)
V_sync <= 0;
else if(s_cnt == 10'd524&& h_cnt == 799)
V_sync <= 1;
else
V_sync <= V_sync;
always@(posedge clk or negedge rst_n)
if(!rst_n)
rgb_data <= 8'd0;
else if(h_cnt > 144 && h_cnt < 783 && s_cnt > 0&& s_cnt < 514)
rgb_data <= 8'b11001001;
endmodule
module vga(
input wire clk,//50M时钟
input wire rst_n,
output wire H_sync,
output wire V_sync,
output wire [7:0]rgb_data
);
wire clk_25M;
gen_clk gen_clk_inst(
.clk_50M(clk),
.rst_n(rst_n),
.clk_25M(clk_25M)
);
vga_ctrl vga_ctrl_inst(
.clk(clk_25M),//25M
.H_sync(H_sync),
.V_sync(V_sync),
.rgb_data(rgb_data),
.rst_n(rst_n)
);
endmodule
仿真模块:
`timescale 1ns/1ns
module tb_vga();
reg clk;
reg rst_n;
wire H_sync;
wire V_sync;
wire [7:0]rgb_data;
initial begin
rst_n = 0;
clk = 0;
#10
rst_n = 1;
end
always #10 clk =~clk;
vga vga_inst(
.clk(clk),//50M时钟
.rst_n(rst_n),
.H_sync(H_sync),
.V_sync(V_sync),
.rgb_data(rgb_data)
);
endmodule
仿真脚本
quit -sim
.main clear
vlib work
vlog ./tb_vga.v
vlog ./../design/*.v
vsim -voptargs=+acc tb_vga
add wave tb_vga/vga_inst/*
add wave -divider {w}
#产生一个分组也可以快捷键ctrl+G
run 10us