VGA(Video Graphics Array) 是视频图像阵列,通常指代VGA接口。就是常见的老式电脑中,主机和显示器相连的带有左右两个螺丝的连接线。虽然现在的显示屏大多已经采用DVI和HDMI方案,但其实VGA在另一个地方还有应用,那就是大屏的LCD。目前4.3寸以上的TFT基本都是VGA接口,这样在完成一个FPGA系统设计时,选择一个VGA接口的TFT用来显示便是最简单方便的方案。
VGA的内部信号
最重要的 5 个信号,分别是R 、G、 B 红绿蓝三色的模拟信号,以及行同步信号和场同步信号。 其他信号一般来说板子自己已经帮我们在内部接好了,我们不用去给它信号。
行和场 就可以类比于 显示屏的 宽和高
如上图所示的这样的VGA接口其实是通过模拟VGA接口连接,计算机内部以数字方式生成图像信息。这里最主要的就是要实现R、G、B 三原色信号的数模转换。
RGB是一种颜色标准,红®、绿(G)、蓝(B)。
其他各种各样的颜色都是用这三个颜色来互相叠加产生的。显示器大都是采用了RGB颜色标准,在显示器上,是通过电子枪打在屏幕的红、绿、蓝三色发光极上来产生色彩的。一般显示器可以显示2^(32)种颜色(理论上)。
电脑屏幕上的所有颜色,都由这红色绿色蓝色三种色光按照不同的比例混合而成的。一组红色绿色蓝色就是一个最小的显示单位。屏幕上的任何一个像素点颜色都可以由一组RGB值来记录和表达。
在电脑中,RGB的所谓“多少”就是指亮度,并使用整数来表示。通常情况下,RGB各有256级亮度,用数字表示为从0、1、2…直到255。
RGB的格式:
对一种颜色进行编码的方法统称为 色彩空间
RGB555:RGB555是一种16位的RGB格式,但是只有用到了5+5+5,剩下的一位其实是没有用到的。R、G、B 每个分量都用5位表示。
RGB565:RGB565使用16位表示一个像素,这16位中的5位用于R,6位用于G,5位用于B。这个比555好在 更加充分利用16位的资源
RGB888(也叫RGB24):RGB分量都用8位表示,取值范围为0-255。
RGB444:每个颜色通道用4位来表示,
virtex5用的RGB888。
参考:http://t.csdn.cn/7uHis
VGA显示器一般从左上角开始扫描,从左向右逐点扫描,每扫描完一行后,向下移动一行,继续扫描。在这期间,CRT对电子束进行消隐,每行结束时,用行同步信号进行同步;当扫描完所有行,形成一帧图片时,用场同步信号进行同步,使扫描回到屏幕的左上角,同时进行消隐,开始下一帧。
完成一行扫描的时间为水平扫描时间,倒数为行频率;完成一帧的扫描时间为垂直扫描时间,倒数为场频率。基本上用场频率来表示显示屏的刷新频率。
当计算设计的FPGA的时钟频率时需要考虑到无效显示区。
行时序/场时序都需要同步脉冲、显示后沿、显示时序段和显示前沿四部分。VGA工业标准显示模式要求:行同步,场同步都为负极性,即同步脉冲要求是负脉冲。
对于我们输入不同的行和场的同步信号,显示器会自动匹配我们的信号 产生图像。但我们的输入应该满足如下的配置。
现在以640*480,60hz编写相应的程序
`timescale 1ns / 1ps
//VGA Format 640*480@60HZ
`define H_ACTIVE 640
`define H_FRONT_PORCH 16
`define H_SYNCH_PULSE 96
`define H_BACK_PORCH 48
`define H_TOTAL ( `H_SYNCH_PULSE + `H_BACK_PORCH + `H_ACTIVE + `H_FRONT_PORCH )
//800 // pixels
`define V_ACTIVE 480
`define V_FRONT_PORCH 11
`define V_SYNCH_PULSE 2
`define V_BACK_PORCH 31
`define V_TOTAL (`V_SYNCH_PULSE + `V_BACK_PORCH + `V_ACTIVE + `V_FRONT_PORCH )
//524 // lines
module vga_driver(
input pixel_clk ,
input rst ,
output reg de ,
output reg h_sync ,
output reg v_sync ,
output reg [10:0] y_cnt ,
output reg [10:0] x_cnt
);
//=========== 行计数器 ================
always@(posedge pixel_clk)begin
if(rst)
x_cnt <= 0;
else if( x_cnt == (`H_TOTAL-1) )
x_cnt <= 0;
else
x_cnt <= x_cnt + 1;
end
//========== 场计数器 ==================
always@(posedge pixel_clk)begin
if(rst)
y_cnt <= 0;
else if(x_cnt == (`H_TOTAL-1))begin
if(y_cnt == (`V_TOTAL - 1) )
y_cnt <= 0;
else
y_cnt <= y_cnt + 1;
end
end
//======== 判断输出有效信号 ==========
reg h_en;
always@(posedge pixel_clk)begin
if(x_cnt == (`H_TOTAL-1) )
h_en <= 1;
else if(x_cnt == (`H_ACTIVE-1))
h_en <= 0;
end
reg v_en;
always@(posedge pixel_clk)begin
if(x_cnt == (`H_TOTAL-1)) begin
if(y_cnt == (`V_TOTAL-1) )
v_en <= 1;
else if(y_cnt == (`H_ACTIVE-1))
v_en <= 0;
end
end
always@(posedge pixel_clk) begin
de <= v_en & h_en;
end
//================ 设置SYN的时间段为低电平================
always@(posedge pixel_clk)begin
if(x_cnt == (`H_ACTIVE+`H_FRONT_PORCH-1))
h_sync <=0;
else if(x_cnt == (`H_ACTIVE+`H_FRONT_PORCH+`H_SYNCH_PULSE-1) )
h_sync <=1;
end
always@(posedge pixel_clk)begin
if(rst)
v_sync <=1;
else begin
if(x_cnt == `H_TOTAL-1) begin
if(y_cnt == (`V_ACTIVE+`V_FRONT_PORCH-1))
v_sync <=0;
else if(y_cnt == (`V_ACTIVE+`V_FRONT_PORCH+`V_SYNCH_PULSE-1) )
v_sync <=1;
else
v_sync<=v_sync;
end
else
v_sync <=v_sync;
end
end
endmodule
测试程序:
`timescale 1ns / 1ps
module vga_driver_tb;
reg clk,rst;
wire de;
wire hs,vs;
wire [10:0] x_cnt;
wire [10:0] y_cnt;
vga_driver u0(
. pixel_clk ( clk ) ,
.rst ( rst ) ,
.de ( de ) ,
.h_sync ( hs ) ,
.v_sync ( vs ) ,
.y_cnt ( y_cnt ) ,
.x_cnt ( x_cnt )
);
always #30 clk=~clk;
initial begin
clk = 0;
rst = 1;
#100 rst = 0;
end
endmodule
ise仿真:
使用ise自带的仿真。
看 hs什么时候低电平 什么时候重新拉高,如果是理想情况,一个是x_cnt到达h_active+h_front_porch-1 的时候也就是(640+16)-1=656-1=655的时候拉低,再过h_sync_pause=96 也就是(656+96)-1=752-1=751的时候拉高。
现在写一个顶层模块,同时把RGB颜色输出,但是,在此之前,我们应该给出一个60MHZ的时钟才行,注意,这里的60是根据我们之前讨论的 在不同分辨率下的VGA时序参数那里得到的。此处我们可以用ise自带的时钟IP核来完成时钟频率的转换。
1、DCM实际上就是一个DLL,可以对输入时钟进行相位移动,补偿,产生倍频和分频时钟,但是5以及以后的产品不用了。
2、PLL相对于DCM,除了不能相移时钟,其它的都一样,但是PLL产生时钟的频率比DCM更加精准,而且时钟的jitter也更好。
3、MMCM实际上就是PLL+DCM相移功能的结合体。7系列的FPGA还会在临近I/O部分放置一些PLL,专门给MIG来产生DDR时钟。
这里使用PLL
这里使用PLL出现了很多问题,一个问题是ERROR:Xst:2035 - Port
top文件:
module top(
output [7:0] R,G,B,
output h_sync,v_sync,
input clk,rst
);
wire clk60M ;
wire clk33M;
wire de ;
clock u1 (
.CLKIN1_IN(clk),
.RST_IN(1'b0),
.CLKOUT0_OUT(clk60M),
.CLKOUT1_OUT(clk33M),
.LOCKED_OUT()
);
wire [10:0]x_cnt ;
wire [9:0]y_cnt ;
vga_driver u0(
. pixel_clk ( clk60M ) ,
.rst ( rst ) ,
.de ( de ) ,
.h_sync ( h_sync ) ,
.v_sync ( v_sync ) ,
.y_cnt ( y_cnt ) ,
.x_cnt ( x_cnt )
);
reg[3:0] Rr , Gr , Br ;
always@ (posedge clk33M)
case ( x_cnt ) //对于x_cnt在不同的位置,我们赋予不同的颜色,就会形成彩条
80*0 : { Rr , Gr , Br } <={ 8'b00000000 , 8'b00000000 , 8'b00000000 } ;
80*1 : { Rr , Gr , Br } <={ 8'b00000000 , 8'b00000000 , 8'b11111111 } ;
80*2 : { Rr , Gr , Br } <={ 8'b00000000 , 8'b11111111 , 8'b00000000 } ;
80*3 : { Rr , Gr , Br } <={ 8'b00000000 , 8'b11111111 , 8'b11111111 } ;
80*4 : { Rr , Gr , Br } <={ 8'b11111111 , 8'b00000000 , 8'b00000000 } ;
80*5 : { Rr , Gr , Br } <={ 8'b11111111 , 8'b00000000 , 8'b11111111 } ;
80*6 : { Rr , Gr , Br } <={ 8'b11111111 , 8'b11111111 , 8'b00000000 } ;
80*7 : { Rr , Gr , Br } <={ 8'b11111111 , 8'b11111111 , 8'b11111111 } ;
endcase
assign B = ( de == 0 ) ? 0 : Br ;
assign G = ( de == 0 ) ? 0 : Gr ;
assign R = ( de == 0 ) ? 0 : Rr ;
endmodule
编写约束文件:
# PlanAhead Generated physical constraints
NET "B[7]" LOC = AD7;
NET "B[6]" LOC = AC7;
NET "B[5]" LOC = AB5;
NET "B[4]" LOC = AA5;
NET "B[3]" LOC = AB7;
NET "B[2]" LOC = AB6;
NET "B[1]" LOC = AC5;
NET "B[0]" LOC = AC4;
# PlanAhead Generated IO constraints
NET "B[7]" IOSTANDARD = LVCMOS33;
NET "B[6]" IOSTANDARD = LVCMOS33;
NET "B[5]" IOSTANDARD = LVCMOS33;
NET "B[4]" IOSTANDARD = LVCMOS33;
NET "B[3]" IOSTANDARD = LVCMOS33;
NET "B[2]" IOSTANDARD = LVCMOS33;
NET "B[1]" IOSTANDARD = LVCMOS33;
NET "B[0]" IOSTANDARD = LVCMOS33;
# PlanAhead Generated physical constraints
NET "G[7]" LOC = AE6;
NET "G[6]" LOC = AD6;
NET "G[5]" LOC = Y7;
NET "G[4]" LOC = AA6;
NET "G[3]" LOC = AD5;
NET "G[2]" LOC = AD4;
NET "G[1]" LOC = Y9;
NET "G[0]" LOC = Y8;
# PlanAhead Generated IO constraints
NET "G[7]" IOSTANDARD = LVCMOS33;
NET "G[6]" IOSTANDARD = LVCMOS33;
NET "G[5]" IOSTANDARD = LVCMOS33;
NET "G[4]" IOSTANDARD = LVCMOS33;
NET "G[3]" IOSTANDARD = LVCMOS33;
NET "G[2]" IOSTANDARD = LVCMOS33;
NET "G[1]" IOSTANDARD = LVCMOS33;
NET "G[0]" IOSTANDARD = LVCMOS33;
# PlanAhead Generated physical constraints
NET "R[7]" LOC = W11;
NET "R[6]" LOC = Y11;
NET "R[5]" LOC = AG6;
NET "R[4]" LOC = AH5;
NET "R[3]" LOC = V7;
NET "R[2]" LOC = W7;
NET "R[1]" LOC = AF5;
NET "R[0]" LOC = AG5;
# PlanAhead Generated IO constraints
NET "R[7]" IOSTANDARD = LVCMOS33;
NET "R[6]" IOSTANDARD = LVCMOS33;
NET "R[5]" IOSTANDARD = LVCMOS33;
NET "R[4]" IOSTANDARD = LVCMOS33;
NET "R[3]" IOSTANDARD = LVCMOS33;
NET "R[2]" IOSTANDARD = LVCMOS33;
NET "R[1]" IOSTANDARD = LVCMOS33;
NET "R[0]" IOSTANDARD = LVCMOS33;
# PlanAhead Generated physical constraints
NET "clk" LOC = AH17;
NET "v_sync" LOC = Y6;
NET "h_sync" LOC = AE7;
NET "rst" LOC = AK6;
# PlanAhead Generated IO constraints
NET "clk" IOSTANDARD = LVCMOS33;
NET "h_sync" IOSTANDARD = LVCMOS33;
NET "rst" IOSTANDARD = LVCMOS33;
NET "v_sync" IOSTANDARD = LVCMOS33;
生成bit流下载到板子上,再连接显示器观察。