VGA(video graphics array)即视频图形阵列,是IBM在1987年随PS/2机一起推出的一种视频传输标准,具有分辨率高、显示速度快、颜色丰富等优点,在彩色显示器领域得到了广泛的应用。
1.CRT显示器原理
CRT(cathode ray tube)阴极射线管,其基本结构包括显像管、控制电路、外壳三部分。其中显像管是关键部件,主要由后端的电子枪、中部的偏转线圈、前端的荧光屏构成。工作时通过电子枪,将从阴极发射出大量电子,经过强度控制,聚集和加速,使其形成电子流,再经过偏转线圈的控制,快速的轰击显示器的荧光屏,从而使荧光屏上的荧光粉发亮。
CRT显示器因为设计制造上的原因,只能接受模拟信号输入,这就需要显卡能输出模拟信号。VGA接口就是显卡上输出模拟信号的接口,VGA(Video Graphics Array)接口,也叫D-Sub接口。
2.VGA控制器设计
VGA接口是一种D型接口,上面共有15针孔,分成三排,每排五个。 其中,除了2根NC(Not Connect)信号、3根显示数据总线和5个GND信号,比较重要的是3根RGB彩色分量信号和2根扫描同步信号HSYNC和VSYNC针。VGA接口中彩色分量采用RS343电平标准。RS343电平标准的峰值电压为1V。
大多数计算机与外部显示设备之间都是通过模拟VGA接口连接,计算机内部以数字方式生成的显示图像信息,被显卡中的数字/模拟转换器转变为R、G、B三原色信号和行、场同步信号,信号通过电缆传输到显示设备中。对于模拟显示设备,如模拟CRT显示器,信号被直接送到相应的处理电路,驱动控制显像管生成图像。而对于LCD、DLP等数字显示设备,显示设备中需配置相应的A/D(模拟/数字)转换器,将模拟信号转变为数字信号。在经过D/A和A/D两次转换后,不可避免地造成了一些图像细节的损失。VGA接口应用于CRT显示器无可厚非,但用于连接液晶之类的显示设备,则转换过程的图像损失会使显示效果略微下降。
现在应该很少会看到专门使用VGA驱动芯片的了。使用电阻网络已经能获得不错的显示效果。FPGA需要处理的信号有行同步信号HSYNC和场同步信号VSYNC,以及R、G、B三组颜色控制信号。在驱动VGA之前,我们首先要确定自己的显示参数,分辨率及刷新率,比如800*600@60Hz的显示方式其时序参数如下所示:
由于VGA需要一个时钟来管理扫描的速度,因此这个时钟大小就应当为扫面面积*刷新率,如上例中的640*480@60Hz的显示方式需要的VGA时钟大小为800*525*60=25.2MHz(扫描时不仅包括显示区域,还有同步脉冲、显示前沿和显示后沿,因此整个区域要大于分辨率)。VGA时钟可以用PLL或分频等方法产生,通常要求不会太严苛,上例取整为25MHz即可。
在此科普一下帧率的概念,因为在显示动态图像的时候,实际屏幕都是一张图一张图地切换显示,帧率对应的就是每秒图片的刷新频率。一般当帧率小于30fps的时候能感觉到画面卡顿。
VGA常见刷新时序表:
a:行同步段;b:显示后沿段;c:显示有效段;d:显示前沿段;e:行周期(e=a+b+c+d)
o:帧同步段;p:显示后沿段;q:显示有效段;f:显示前沿段;s:帧周期(s=o+p+q+f)
行场时序图:
VGA采用逐行扫描,每个像素对应的点扫描。行与行之间存在消隐以及显示时期,场与场之间也类似。行扫描可分为以下几个阶段:同步、消隐后肩、显示期、消隐前肩再到下个周期同步为一个循环,对应的是屏幕上的一行。场同步类似,对应为屏幕显示的一帧。
//习惯是先将关键性数据用parameter定义出来
//-------------------------------------------------//
// 此处采用 VGA_800*600_60fps_40MHz:800为列数,体现在行扫描;600为行数,体现在帧扫描
//-------------------------------------------------//
parameter H_DISP =10'd800,//分辨率
parameter V_DISP =10'd600,
parameter H_SYNC=10'd128,//行同步
parameter H_BACK=10'd88,//行消隐后肩
parameter H_FRONT=10'd40,//行消隐前肩
parameter H_SHOW_START = 217; //显示区行开始像素点
parameter H_TOTAL=11'd1056,//行总周期
parameter V_SYNC=10'd4,//场同步
parameter V_BACK=10'd23,//场消隐后肩
parameter V_FRONT=10'd1,//场消隐前肩
parameter V_SHOW_START = 28; //显示区列开始像素点
parameter V_TOTAL=10'd628)//场总周期
//接下来进行行场扫描时序的实现,vcnt为场计数器,hcnt为行计数器
//VGA的时序驱动部分是相当固定的,只要我们使用VGA,肯定会加入下面这段代码。
//主要方法是定义两个计数器,一个管理行扫描,一个管理列扫描;
//水平扫描
always@(posedge clk_25M or negedge RSTn)
if(!RSTn) x_cnt <= 'd0;
else if (x_cnt == H_SYNC_TOTAL) x_cnt <= 'd0;
else x_cnt <= x_cnt + 1'b1;
//垂直扫描
always@(posedge clk_25M or negedge RSTn)
if(!RSTn) y_cnt <= 'd0;
else if (y_cnt == V_SYNC_TOTAL) y_cnt <= 'd0;
else if (x_cnt == H_SYNC_TOTAL) y_cnt <= y_cnt + 1'b1;
else y_cnt <= y_cnt;//固定y,每次x加1;水平扫描完成后y加1
//当行扫描计数器扫描完行同步脉冲后置高HSYNC信号;同理,当列扫描计数器扫描完列同步脉冲后置高VSYNC信号
//H_SYNC信号
always@(posedge clk_25M or negedge RSTn)
if(!RSTn) hsync <= 'd0;
else if (x_cnt == 'd0) hsync <= 1'b0;
else if (x_cnt == H_SYNC_END) hsync <= 1'b1;
else hsync <= hsync;
//V_SYNC信号
always@(posedge clk_25M or negedge RSTn)
if(!RSTn) vsync <= 'd0;
else if (y_cnt == 'd0) vsync <= 1'b0;
else if (y_cnt == V_SYNC_END) vsync <= 1'b1;
else vsync <= vsync;
//为了后面的显示方便,一种实用的方法是再定义两个寄存器,
//专门存储显示区域的坐标,即以可以显示的第一个像素点为坐标(0,0)
assign x_pos = x_cnt - H_SHOW_START;
assign y_pos = y_cnt - V_SHOW_START;
//最后根据坐标的值,给屏幕像素RGB颜色:
//输入图像像素
always@(posedge vga_clk or negedge rst_n)begin
if(!rst_n)begin
vga_R<=0;
vga_G<=0;
vga_B<=0;
end
else begin
if(x==0)begin
vga_R<=0;
vga_G<=0;
vga_B<=0;
end
else if(x<H_DISP/3)begin//黄色
vga_R<=8'd255;
vga_G<=8'd255;
vga_B<=8'd0;
end
else if(x<H_DISP*2/3)begin//蓝色
vga_R<=8'd0;
vga_G<=8'd255;
vga_B<=8'd255;
end
else begin//绿色
vga_R<=8'd0;
vga_G<=8'd255;
vga_B<=8'd0;
end
end
end