1.VGA标准介绍
计算机的显示器有很多标准,常见的有VGA,SVGA等,在现在,大部分台式机计算机的显示屏都采用VGA接口,VGA的应用之广,那么本文我们就用VGA接口来控制显示器,VGA是video graphics adapter的缩写,即为视频图形阵列,VGA接口使用15针的DB15接口,下面列举该接口引脚的功能:
VGA扫描方式
VGA标准中,常见的彩色显示器一般由CRT构成,色彩由RGB三基色构成,显示是用逐行扫描的方式解决,阴极射线发射电子束打到涂有荧光粉的显示屏上,这样就产生了RGB三色,合成一个彩色像素,扫描从屏幕左上方开始,从左到右,从上到下进行扫描,每扫完一行,电子束回到屏幕的左边下一行的起始位置,这个过程中,还要进行消隐操作. 每行结束时,用行同步信号进行行同步;扫描完所有行,用场同步信号进行场同步,并使扫描回到屏幕的左上方,同时进行场消隐,预备下一场的扫描。
对于普通的显示器,共有5个信号:RGB三基色信号,行同步信号HS,场同步信号vs,对于时序驱动,VGA显示器要严格遵循标准,即68048060hz模式。
VGA标准时序分析
下图是VGA行扫描,场扫描的时序图。
行扫描时序要求(单位:输出一个像素的时间间隔,即像素时钟):
Ta (行同步头):96
Tb :40
Tc :8
Td行图像:640
Te :8
Tf :8
Tg :800
场扫描时序要求(单位:输出一行line的时间间隔):
Ta场同步头 :2
Tb :25
Tc :8
Td场图像 :480
Te :8
Tf :2
Tg :525
VGA工业标准所要求的频率如下:时钟频率 25.175Mhz—>(像素输出的频率)行频率—> 31469hz 场频率 —>59.94hz->(每秒图像刷新频率)
在设计时,可用两个计数器进行计数(行,场扫描技数器),行计数器的驱动时钟为25Mhz,场计数器的驱动时钟为行计数器的溢出信号。计数器的同时控制行,场同步信号输出。并在适当的时候送出数据,就能显示相应的图像,显示器的刷新频率为25Mhz/800/525=59.52hz
本开发平台的开发板VGA模块采用的是GM7123芯片,GM7123芯片有3个10位的DAC通道的各高8位作为数据输入的端口,低两位舍弃。默认最高支持RGB888格式,24位色。该模块为开发用户提供了VGA_HS,VGA_VS,CLOCK,BLANK,R[7:0],B[7:0],G[7:0],GND信号,所以在设计VGA控制时应该正确连接这些信号。
控制器使用RGB888的数据模式和GM7123模块连接时,简易连接图如下所示:
使用此接口时,VGA接口三基色信号RGB使用24位数据线,则可以显示2^24种颜色。RGB数据格式如下表:
下表是常见的几种颜色对应的数据编码:
原理小结:
VGA驱动采用行列扫描方法,使用两个计数器分别进行行,场计数,根据计数值确定像素数据内容和行,场同步信号和电平状态。同时,要显示不同颜色,只要给D0-D24不同数据即可。
VGA控制器设计
行扫描计数器即每个像素时钟自动加1,只要加满到799,计数器清零重新计数:
2. 场扫描计数器
因为场扫描计数器是在每次一行扫描完成后加1,即场扫描计数器的自加条件是行扫描计数器溢出。即场扫描计数器自加条件为行扫描完成——>hcount_r = 10’d799 :
3. 行场同步信号
每一个完整的VGA帧都包含了数据段和消隐段,在消隐段期间,行同步信号和场同步信号有一段行同步头和场同步头。在同步期间,对应行同步信号或者场同步信号为低电平,所以可以根据行场计数器的值来确定行场同步信号的电平,行同步头为一行扫描的前96个像素时钟周期,则行同步信号设置为:
对于场同步信号,其场同步头为一行扫描的前2个像素时钟周期,则场同步信号设置为:
4. 数据输出
在行,场消隐期间,需要保证输出到VGA的RGB数据线上的数据全为0,因此可以设置一个二选一选择器,只有在非消隐阶段,VGA控制器才直接输出图像数据。
首先产生一个图像数据有效标志信号,然后使用标志信号控制VGA输出数据的内容。
dat_act为图像数据有效标志信号
module VGA_CTRL(
Clk25M, //系统输入时钟25MHZ
Rst_n, //复位输入,低电平复位
data_in, //待显示数据
hcount, //VGA行扫描计数器
vcount, //VGA场扫描计数器
VGA_RGB, //VGA数据输出
VGA_HS, //VGA行同步信号
VGA_VS //VGA场同步信号
);
//----------------模块输入端口----------------
input Clk25M; //系统输入时钟25MHZ
input Rst_n;
input [7:0]data_in; //待显示数据
//----------------模块输出端口----------------
output [9:0]hcount;
output [9:0]vcount;
output [7:0]VGA_RGB; //VGA数据输出
output VGA_HS; //VGA行同步信号
output VGA_VS; //VGA场同步信号
//----------------内部寄存器定义----------------
reg [9:0] hcount_r; //VGA行扫描计数器
reg [9:0] vcount_r; //VGA场扫描计数器
//----------------内部连线定义----------------
wire hcount_ov;
wire vcount_ov;
wire dat_act;//有效显示区标定
//VGA行、场扫描时序参数表
parameter VGA_HS_end=10'd95,
hdat_begin=10'd143,
hdat_end=10'd783,
hpixel_end=10'd799,
VGA_VS_end=10'd1,
vdat_begin=10'd34,
vdat_end=10'd514,
vline_end=10'd524;
assign hcount=hcount_r-hdat_begin;
assign vcount=vcount_r-vdat_begin;
//**********************VGA驱动部分**********************
//行扫描
always@(posedge Clk25M or negedge Rst_n)
if(!Rst_n)
hcount_r<=10'd0;
else if(hcount_ov)
hcount_r<=10'd0;
else
hcount_r<=hcount_r+10'd1;
assign hcount_ov=(hcount_r==hpixel_end);
//场扫描
always@(posedge Clk25M or negedge Rst_n)
if(!Rst_n)
vcount_r<=10'd0;
else if(hcount_ov) begin
if(vcount_ov)
vcount_r<=10'd0;
else
vcount_r<=vcount_r+10'd1;
end
else
vcount_r<=vcount_r;
assign vcount_ov=(vcount_r==vline_end);
//数据、同步信号输出
assign dat_act=((hcount_r>=hdat_begin)&&(hcount_r=vdat_begin)&&(vcount_rVGA_HS_end);
assign VGA_VS=(vcount_r>VGA_VS_end);
assign VGA_RGB=(dat_act)?data_in:8'h00;
endmodule
VGA控制器仿真验证
对应testbench只要产生一个25Mhz的时钟信号,在data_in端口上给一个固定的数据编码。
`timescale 1ns/1ns
`define clk_period 40
module VGA_CTRL_tb;
//----------------模块输入端口----------------
reg Clk25M; //系统输入时钟25MHZ
reg Rst_n;
reg [7:0]data_in; //待显示数据
//----------------模块输出端口----------------
wire [9:0]hcount;
wire [9:0]vcount;
wire [7:0]VGA_RGB; //VGA数据输出
wire VGA_HS; //VGA行同步信号
wire VGA_VS; //VGA场同步信号
reg [11:0]V_cnt = 0;//扫描行数统计计数器
VGA_CTRL VGA_CTRL(
.Clk25M(Clk25M),//系统输入时钟25MHZ
.Rst_n(Rst_n),
.data_in(data_in),//待显示数据
.hcount(hcount),//VGA行扫描计数器
.vcount(vcount),//VGA场扫描计数器
.VGA_RGB(VGA_RGB),//VGA数据输出
.VGA_HS(VGA_HS),//VGA行同步信号
.VGA_VS(VGA_VS)//VGA场同步信号
);
initial Clk25M = 0;
always #(`clk_period/2) Clk25M = ~Clk25M;
initial begin
Rst_n = 0;
data_in = 8'd0;
#(`clk_period *20 +1);
Rst_n = 1;
data_in = 8'hff;
end
initial begin
wait(V_cnt == 3);//等待扫描2帧后结束仿真
$stop;
end
always @(posedge VGA_VS)//统计总扫描帧数
V_cnt <= V_cnt + 1'b1;
endmodule
至此,VGA控制器设计就完成了,编写顶层文件,就可以在VGA上显示图案了。
关注微信公众号“FPGA科技室”,获取更多内容: