功能概述:通过FPGA在显示器上显示一些条纹或图案,用CRT显示器显示横条纹、竖条纹以及棋盘格子图案。用一个按键来控制显示模式,每按下一次,屏幕上的图案改变一次,依次为横条纹、竖条纹以及棋盘格子图案。
VGA的英文全称是 Video Graphics Array,中文名宇叫视频图形阵列,是IBM在1987年随PS/2机一起推出的_ 种视频传输标准,具有分辨率高、显示速率快、颜色丰富等优点,不支持热插拔,不支持音频传输,在彩色显示器领域得到了广泛的应用。这个标准对于现今的个人电脑市场已经十分过时,现在有更高级的HDMI和DVI等,即使如此,VGA仍然是最多制造商所共同支持的一个标准, 个人电脑在加载自己的独特驱动程序之前,都必须支持VGA的标准。这也说明它在显示标准中的重要性和兼容性。
接口与引脚定义如下图
在这15个管脚中,其中比较重要的是3根RGB彩色分量信号和2根扫描同步信号HSYNC 和VSYNC。需要注意的是:VGA显示器上每一个像素点可以有多种颜色,由三基色 信号R、G、B组合构成,如果每个像素点采用3位二进制数表示(R、G、B信号各一位),则 总共可以显示成2^S=8种颜色,每个像素点采用8位二进制数表示(R、G、B信号各为3、 3、2 )则总共可以显示成8*8*4=256种颜色。如果VGA显示真彩色BMP图像,则要R、G、 B三个分量各8位,即24位表示一个像素值,很多情况下还采用32位表示一个像素值。为了 节省显存的存储空间,可采用髙彩色图像,即每个像素值由16位表示,R、G、B三个分量分别 使用5位、6位、5位,比真彩色图像数据量减少一半,同时又能满足显示效果。介绍完了 VGA 的接口管脚定义。
显示原理图如下
要显示某个图像首先要有这个图像,有了图像以后,可以通过VGA控制器来产生相对应的VGA时序将图像数据送出,送出去之后。然而显示器不能识别数字信号,只能识别模拟信号,因此还需 要一个DA来将数字信号转换成显示器所识别的模拟信号。位数较少的情况下可通过R-2R电阻网络分流模拟DA来实现。
本人所用开发板原理图:
VGA通信时序图
帧时序和行时序都产生了四个部分,帧时序的四个部分分别是:同步脉冲(Sync o)、显示后沿(Back porch p)、显示时序段(Display interval q)和显示 前沿(Front porch r)。其中同步脉冲(Sync o)、显示后沿(Back porch p)和显示前沿(Front porch r)是消隐区,RGB信号无效,屏幕不显示数据。显示时序段(Display interval q)是有效数据区。 行时序的四个部分分别是:同步脉冲(Sync a)、显示后沿(Back porch b)、显示时序段(Display interval c)和显示前沿(Front porch d)。其中同步脉冲(Sync a)、显示后沿(Back porch b)和显示 前沿(Front porch d)是消隐区,RGB信号无效,屏幕不显示数据。显示时序段(Display inteival c)是有效数据区。
设计思路:结合VGA显示原理,可将整个系统分成三个模块,分别是:
(1)PLL模块:定制一个PLL IP核,接入系统时钟,产生显示模式相对应的时钟,我所用的是800*600@60(即分辨率为800*600,刷新频率为60Hz)的显示模式 相应的时钟为40 MHz,可以按照行、帧时序计算得到,行扫描时序共1056个时钟周期,帧扫描时序共625个行扫描周期,每秒刷新60帧 ,时钟频率=1056*625*60=3960000Hz,因为PLL并非可以随意分频,所以取40MHz。
(2)KEY模块:用以控制显示模式,每按下一次,屏幕显示转换一次,主要解决按键消抖问题,把消抖后的控制信号传给VGA模块。
(3)VGA模块:根据VGA通信时序产生VGA通信时序波形,在数据有效的时间输出RGB分量信号,并接收来自KEY模块的控制信号
RTL视图如下:
顶层模块代码如下
module VGA_TOP
(
CLK_50M , RST_N , KEY, //输入端口
VGA_VSYNC , VGA_HSYNC , VGA_DATA //输出端口
);
input CLK_50M , RST_N , KEY;
output VGA_VSYNC , VGA_HSYNC ;
output [7:0] VGA_DATA ;
wire CLK_40M;
wire KEY_DATA;
KEY_Single_Button key
(
.KEY (KEY),
.KEY_DATA (KEY_DATA),
.RST_N (RST_N),
.CLK (CLK_40M)
);
PLL pll_50m_to_40m
(
.inclk0 (CLK_50M),
.c0 (CLK_40M)
);
VGA vga_control
(
.CLK_40M (CLK_40M),
.RST_N (RST_N),
.KEY_DATA (KEY_DATA),
.VGA_VSYNC (VGA_VSYNC),
.VGA_HSYNC (VGA_HSYNC),
.VGA_DATA (VGA_DATA)
);
endmodule
KEY模块代码
module KEY_Single_Button (KEY,KEY_DATA,RST_N,CLK);
input KEY;
input RST_N,CLK;
output reg KEY_DATA;
reg key_r , KEY_DATA_N;
reg [20:0] cnt,cnt_n;
wire key_an;
parameter time_40ms = 21'd4000000;
always @ (posedge CLK or negedge RST_N)
begin
if (!RST_N)
key_r <= 1'b0;
else
key_r <= KEY;
end
assign key_an = !key_r & KEY;
always @ (posedge CLK or negedge RST_N)
begin
if (!RST_N)
cnt <= 21'd0;
else
cnt <= cnt_n;
end
always @(*)
begin
if (cnt == time_40ms || key_an)
cnt_n = 21'd0;
else
cnt_n = cnt + 1'b1;
end
always @ (posedge CLK or negedge RST_N)
begin
if (!RST_N)
KEY_DATA <= 1'b0;
else
KEY_DATA <= KEY_DATA_N;
end
always @ (*)
begin
if (cnt == time_40ms)
KEY_DATA_N = key_r;
else
KEY_DATA_N = 1'b0;
end
endmodule
VGA模块代码
`define HSYNC_A 16'd128 // 128
`define HSYNC_B 16'd216 // 128 + 88
`define HSYNC_C 16'd1016 // 128 + 88 + 800
`define HSYNC_D 16'd1056 // 128 + 88 + 800 + 40
`define VSYNC_O 16'd4 // 4
`define VSYNC_P 16'd27 // 4 + 23
`define VSYNC_Q 16'd627 // 4 + 23 + 600
`define VSYNC_R 16'd628 // 4 + 23 + 600 + 1
module VGA
(
CLK_40M, RST_N, KEY_DATA , //输入端口
VGA_VSYNC , VGA_HSYNC , VGA_DATA //输出端口
);
//输入输出端口声明
input CLK_40M, RST_N,KEY_DATA;
output reg VGA_VSYNC , VGA_HSYNC ;
output reg [7:0] VGA_DATA ;
//内部端口声明
reg [15:0] cnt_hsync , cnt_hsync_n , cnt_vsync ,cnt_vsync_n;
reg [7:0] VGA_DATA_N ;
reg VGA_VSYNC_N , VGA_HSYNC_N ;
reg vga_en , vga_en_n ;
reg [1:0] control , control_n;
always @ (posedge CLK_40M or negedge RST_N)
begin
if (!RST_N)
control <= 2'd0;
else
control <= control_n;
end
always @ (*)
begin
if (KEY_DATA)
control_n = control + 1'b1;
else
control_n = control;
end
//行扫描计数器
always @ (posedge CLK_40M or negedge RST_N)
begin
if (!RST_N)
cnt_hsync <= 16'd0;
else
cnt_hsync <= cnt_hsync_n;
end
always @(*)
begin
if (cnt_hsync == `HSYNC_D)
cnt_hsync_n = 16'd0;
else
cnt_hsync_n = cnt_hsync + 1'b1;
end
//垂直扫描计数器
always @ (posedge CLK_40M or negedge RST_N)
begin
if (!RST_N)
cnt_vsync <= 16'd0;
else
cnt_vsync <= cnt_vsync_n;
end
always @(*)
begin
if ((cnt_vsync == `VSYNC_R) && (cnt_hsync == `HSYNC_D))
cnt_vsync_n = 16'd0;
else if (cnt_hsync == `HSYNC_D)
cnt_vsync_n = cnt_vsync + 16'd1;
else
cnt_vsync_n = cnt_vsync;
end
//VSYNC时序波形生成
always @ (posedge CLK_40M or negedge RST_N)
begin
if (!RST_N)
VGA_VSYNC <= 1'b0;
else
VGA_VSYNC <= VGA_VSYNC_N;
end
always @(*)
begin
if (cnt_vsync < `VSYNC_O)
VGA_VSYNC_N = 1'b0;
else
VGA_VSYNC_N =1'b1;
end
//HSYNC时序波形生成
always @ (posedge CLK_40M or negedge RST_N)
begin
if (!RST_N)
VGA_HSYNC <= 1'b0;
else
VGA_HSYNC <= VGA_HSYNC_N;
end
always @(*)
begin
if (cnt_hsync < `HSYNC_A)
VGA_HSYNC_N = 1'b0;
else
VGA_HSYNC_N =1'b1;
end
//判断显示有效区(即通信时序图的Q与重叠的区域),在显示有效区域对vga_en赋1
always @ (posedge CLK_40M or negedge RST_N)
begin
if (!RST_N)
vga_en <= 1'b0;
else
vga_en <= vga_en_n;
end
always @(*)
begin
if (((cnt_hsync > `HSYNC_B)&&(cnt_hsync < `HSYNC_C)) && ((cnt_vsync > `VSYNC_P)&&(cnt_vsync < `VSYNC_Q)))
vga_en_n = 1'b1;
else
vga_en_n =1'b0;
end
//对VGA_DATA赋值
always @ (posedge CLK_40M or negedge RST_N)
begin
if (!RST_N)
VGA_DATA <= 8'h0;
else
VGA_DATA <= VGA_DATA_N;
end
always @ (*)
begin
case (control)
2'b00: //横条纹
if (vga_en)
begin
if((cnt_vsync > `VSYNC_P) && (cnt_vsync <= `VSYNC_P + 10'd150)) //判断屏幕位置
VGA_DATA_N = 8'hE0; //红色
else if((cnt_vsync > `VSYNC_P + 10'd150) && (cnt_vsync <= `VSYNC_P + 10'd300))//判断屏幕位置
VGA_DATA_N = 8'h03; //黄色
else if((cnt_vsync > `VSYNC_P + 10'd300) && (cnt_vsync <= `VSYNC_P + 10'd450)) //判断屏幕位置
VGA_DATA_N = 8'hFC; //蓝色
else if((cnt_vsync > `VSYNC_P + 10'd450) && (cnt_vsync <= `VSYNC_P + 10'd600)) //判断屏幕位置
VGA_DATA_N = 8'h1C; //绿色
else
VGA_DATA_N = 8'h0; //黑色
end
else
VGA_DATA_N = 8'h0;
2'b01: //竖条纹
if (vga_en)
begin
if((cnt_hsync > `HSYNC_B) && (cnt_hsync <= `HSYNC_B + 10'd200)) //判断屏幕位置
VGA_DATA_N = 8'hE0; //红色
else if((cnt_hsync > `HSYNC_B + 10'd200) && (cnt_hsync <= `HSYNC_B + 10'd400))//判断屏幕位置
VGA_DATA_N = 8'h03; //黄色
else if((cnt_hsync > `HSYNC_B + 10'd400) && (cnt_hsync <= `HSYNC_B + 10'd600)) //判断屏幕位置
VGA_DATA_N = 8'hFC; //蓝色
else if((cnt_hsync > `HSYNC_B + 10'd600) && (cnt_hsync <= `HSYNC_B + 10'd800)) //判断屏幕位置
VGA_DATA_N = 8'h1C; //绿色
else
VGA_DATA_N = 8'h0; //黑色
end
else
VGA_DATA_N = 8'h0;
2'b10: //棋盘格
if (vga_en)
begin
if((cnt_hsync > `HSYNC_B) && (cnt_hsync <= `HSYNC_B + 10'd200)) //判断屏幕位置
begin
if ((cnt_vsync <= `VSYNC_P + 10'd100) || ((cnt_vsync > `VSYNC_P + 10'd200) && (cnt_vsync <= `VSYNC_P + 10'd300)) ||((cnt_vsync > `VSYNC_P + 10'd400) && (cnt_vsync <= `VSYNC_P + 10'd500)))
VGA_DATA_N = 8'hE0; //
else if (((cnt_vsync > `VSYNC_P + 10'd100) && (cnt_vsync <= `VSYNC_P + 10'd200))|| ((cnt_vsync > `VSYNC_P + 10'd300) && (cnt_vsync <= `VSYNC_P + 10'd400)) ||((cnt_vsync > `VSYNC_P + 10'd500) && (cnt_vsync <= `VSYNC_P + 10'd600)))
VGA_DATA_N = 8'h03; //
else
VGA_DATA_N = 8'h0;
end
else if((cnt_hsync > `HSYNC_B + 10'd200) && (cnt_hsync <= `HSYNC_B + 10'd400))//判断屏幕位置
begin
if ((cnt_vsync <= `VSYNC_P + 10'd100) || ((cnt_vsync > `VSYNC_P + 10'd200) && (cnt_vsync <= `VSYNC_P + 10'd300)) ||((cnt_vsync > `VSYNC_P + 10'd400) && (cnt_vsync <= `VSYNC_P + 10'd500)))
VGA_DATA_N = 8'hFC; //
else if (((cnt_vsync > `VSYNC_P + 10'd100) && (cnt_vsync <= `VSYNC_P + 10'd200))|| ((cnt_vsync > `VSYNC_P + 10'd300) && (cnt_vsync <= `VSYNC_P + 10'd400)) ||((cnt_vsync > `VSYNC_P + 10'd500) && (cnt_vsync <= `VSYNC_P + 10'd600)))
VGA_DATA_N = 8'h1C; //
else
VGA_DATA_N = 8'h0;
end
else if((cnt_hsync > `HSYNC_B + 10'd400) && (cnt_hsync <= `HSYNC_B + 10'd600)) //判断屏幕位置
begin
if ((cnt_vsync <= `VSYNC_P + 10'd100) || ((cnt_vsync > `VSYNC_P + 10'd200) && (cnt_vsync <= `VSYNC_P + 10'd300)) ||((cnt_vsync > `VSYNC_P + 10'd400) && (cnt_vsync <= `VSYNC_P + 10'd500)))
VGA_DATA_N = 8'h03; //
else if (((cnt_vsync > `VSYNC_P + 10'd100) && (cnt_vsync <= `VSYNC_P + 10'd200))|| ((cnt_vsync > `VSYNC_P + 10'd300) && (cnt_vsync <= `VSYNC_P + 10'd400)) ||((cnt_hsync > `VSYNC_P + 10'd500) && (cnt_vsync <= `VSYNC_P + 10'd600)))
VGA_DATA_N = 8'hE0; //
else
VGA_DATA_N = 8'h0;
end
else if((cnt_hsync > `HSYNC_B + 10'd600) && (cnt_hsync <= `HSYNC_B + 10'd800)) //判断屏幕位置
begin
if ((cnt_vsync <= `VSYNC_P + 10'd100) || ((cnt_vsync > `VSYNC_P + 10'd200) && (cnt_vsync <= `VSYNC_P + 10'd300)) ||((cnt_vsync > `VSYNC_P + 10'd400) && (cnt_vsync <= `VSYNC_P + 10'd500)))
VGA_DATA_N = 8'h1C; //
else if (((cnt_vsync > `VSYNC_P + 10'd100) && (cnt_vsync <= `VSYNC_P + 10'd200))|| ((cnt_vsync > `VSYNC_P + 10'd300) && (cnt_vsync <= `VSYNC_P + 10'd400)) ||((cnt_vsync > `VSYNC_P + 10'd500) && (cnt_vsync <= `VSYNC_P + 10'd600)))
VGA_DATA_N = 8'hFC; //
else
VGA_DATA_N = 8'h0;
end
else
VGA_DATA_N = 8'h0;
end
else
VGA_DATA_N = 8'h0;
2'b11:VGA_DATA_N = 8'h0;
default : VGA_DATA_N = 8'h0;
endcase
end
endmodule