fpga实现VGA显示

计算机显示区的显示有许多标准,常见的有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。
在这里插入图片描述

fpga实现VGA显示_第1张图片

如图2所示为VGA图像显示扫描示意图,在设计时,可用两个计数器进行计数(行、场扫描计数器),行计数器的驱动时钟为25MHz,场计数器的驱动时钟为行计数器的溢出信号。计数的同时控制行、场同步信号输出,并在适当的时候送出数据,就能显示相应的图像。注意消隐期间送出的R、G、B信号为0x00。
fpga实现VGA显示_第2张图片

在这里插入代码片
产生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 
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

你可能感兴趣的:(笔记)