理解VGA时序
2014/4/12
(学号:136-finema)
1. 前言
最近一段时间,看见EEPW-CPLD群里的很多网友和我一样在弄CPLD 的VGA实验,和很多人一样,我一开始也是搞不掂,后来经过咨询51FPGA版主、查看网上的资料、多次仿真才弄清楚了VGA是怎么回事。撰写本文,主要是为了自己理清所学内容,希望也能对大家有帮助。
在此先感谢51FPGA和EEPW,谢谢他们把我这菜鸟带进了 CPLD-FPGA这多姿多彩的世界。
2. 背景知识
本文以学习生成 640*480@60Hz VGA信号为目标,要继续阅读,首先你要对老式VGA显示器是如何显示东西的,以及对VGA的场、行周期、刷新率等概念搞清楚,不清楚去查Baidu或Google。
也可看:http://www.cnblogs.com/spartan/archive/2011/08/16/2140546.html
(我的理解就是:1 场= 1帧; 而1帧 有 480行,每1行有640列;完成一行扫描需要的时间就是行周期;刷新率就是 1秒中刷新了多少帧。)
3. 时序总览
相信你看了资料,也会理解不透彻,特别是时序部分,关键是动起手来。
废话少说了,先看一下ModelSim仿真的时序图(见图1),比你看资料中的手工画的时序图直观多了。
图1
图1中红线部分就是“场信号(vsync)”, 蓝线部分就是“行信号(hsync)”也叫“水平信号”。
图中我们可以数出有3段“场信号(vsync)”即3帧。
4. 场信号时序(vsync)
那么场信号时序包含了什么呢,我们看一下VGA定的标准。
见http://tinyvga.com/vga-timing/640x480@60Hz
整理得到640*480@60Hz的场信号定义,见表1。
同步脉冲
|
后沿
|
显示脉冲
(即可见部分)
|
前沿
|
合计
|
单位
|
2
|
33
|
480
|
10
|
525
|
行周期
|
表1
从表1我们得知“场信号”的同步脉冲有2个行周期长(注意:这里指的是行周期,而不是时钟周期),好了我们放大一下图1,可以清楚的验证这一点。见图2。
图2
也就是说2个行周期后,1帧的场同步信号就结束了,再经过 33+480+10=523个行周期后,下1帧的场同步信号才会来。
5. 行信号时序(hsync)
那么1行的周期到底有多长呢,行信号的时序又包含那些东西呢?我们还是看VGA的标准。见http://tinyvga.com/vga-timing/640x480@60Hz
整理得到640*480@60Hz的行信号定义,见表2。
同步脉冲
|
后沿
|
显示脉冲
(即可见部分)
|
前沿
|
合计
|
单位
|
96
|
48
|
640
|
16
|
800
|
时钟周期
|
表2
我们把图1的“行信号(hsync)”波形放大,见图3,可看到“hcount=96”,即“行信号”的同步脉冲有96个时钟周期,与表2的描述相符。
图3
6. 可见部分
可见部分(即显示脉冲)是什么,我们把表1和表2的数据合并得到表3。
|
同步脉冲
|
后沿
|
显示脉冲
(即可见部分)
|
前沿
|
合计
|
单位
|
场信号
|
2
|
33
|
480
|
10
|
525
|
行周期
|
行信号
|
96
|
48
|
640
|
16
|
800
|
时钟周期
|
表3
所谓可见部分,就是每1帧中,场信号的计数“hcount”在35到515之间(区间长度为480)并且行信号的计数“vcount”在144到784之间(区间长度为640),RGB的值才有效,其它范围VGA显示器不会显示RGB的值。
那么不在范围内的计数器的值有什么作用呢,老式VGA显示器就是靠他们来进行消隐的,对于液晶显示器,我想没什么可用之地。
7. 时钟
那么我们要生成 640*480@60Hz 的VGA信号,需要多快的时钟才能起作用呢,我们来算一下:
525*800*60Hz=25200000Hz=25.2MHz≈25MHz
更精确点的计算:
525*800*60Hz *1000/1001=25174825.174825174825174825174825Hz≈25MHz
也就是说至少需要25MHz的时钟,我们才能正确生成640*480@60Hz 的VGA信号。(我们会看到一些接了50MHz 晶振的CPLD,其HDL代码就有分频处理。)
那么显示1帧需要多长时间呢?精确点的计算就是:
525*800/25.174≈16683微妙
8. 源代码 8.1. Verilog代码
代码源于网络,自身修改了一下,以便于仿真,参照前面所说,自己琢磨代码。
//VGA 640*480@60Hz彩色信号显示
module VGA2(
sys_rst,
clock,
switch,
disp_RGB,
hsync,
vsync
);
input sys_rst; //复位信号
input clock; //系统输入时钟 50MHz
input [1:0]switch;
output [2:0]disp_RGB; //VGA数据输出
output hsync; //VGA行同步信号
output vsync; //VGA场同步信号
reg [9:0] hcount; //VGA行扫描计数器
reg [9:0] vcount; //VGA场扫描计数器
reg [2:0] data;
reg [2:0] h_dat;
reg [2:0] v_dat;
//reg [9:0] timer;
//reg flag;
wire hcount_ov;
wire vcount_ov;
wire dat_act;
wire hsync;
wire vsync;
reg vga_clk; //VGA时钟
//VGA行、场扫描时序参数表
parameter hsync_end = 10'd95,
hdat_begin = 10'd143,
hdat_end = 10'd783,
hpixel_end = 10'd799,
vsync_end = 10'd1,
vdat_begin = 10'd34,
vdat_end = 10'd514,
vline_end = 10'd524;
//always @(posedge clock)
//begin
// vga_clk = ~vga_clk;
// //vga_clk <= ~vga_clk;
//end
always @(posedge clock,negedge sys_rst)
begin
if (!sys_rst)
begin
vga_clk = 0;
end
else
begin
vga_clk = ~vga_clk; //50MHz 分频成 25MHz
//vga_clk <= ~vga_clk;
end
end
//************************VGA驱动部分*******************************
//行扫描
//always @(posedge clock,negedge sys_rst)
//begin
// if (!sys_rst)
// begin
// hcount <= 10'd0;
// end
// else
// begin
// if (hcount_ov)
// hcount <= 10'd0;
// else
// hcount <= hcount + 10'd1;
// end
//end
always @(posedge vga_clk,negedge sys_rst)
begin
if (!sys_rst)
begin
hcount <= 10'd0;
end
else
begin
if (hcount_ov)
hcount <= 10'd0;
else
hcount <= hcount + 10'd1;
end
end
assign hcount_ov = (hcount == hpixel_end);
//场扫描
//always @(posedge clock,negedge sys_rst)
//begin
// if (!sys_rst)
// begin
// vcount <= 10'd0;
// end
// else
// begin
// if (hcount_ov)
// begin
// if (vcount_ov)
// vcount <= 10'd0;
// else
// vcount <= vcount + 10'd1;
// end
// end
//end
always @(posedge vga_clk,negedge sys_rst)
begin
if (!sys_rst)
begin
vcount <= 10'd0;
end
else
begin
if (hcount_ov)
begin
if (vcount_ov)
vcount <= 10'd0;
else
vcount <= vcount + 10'd1;
end
end
end
assign vcount_ov = (vcount == vline_end);
//数据、同步信号输
assign dat_act = ((hcount >= hdat_begin) && (hcount < hdat_end))
&& ((vcount >= vdat_begin) && (vcount < vdat_end));
assign hsync = (hcount > hsync_end);
assign vsync = (vcount > vsync_end);
assign disp_RGB = (dat_act) ? data : 3'h00;
//************************显示数据处理部分*******************************
//图片显示延时计数器
/*always @(posedge vga_clk)
begin
flag <= vcount_ov;
if(vcount_ov && ~flag)
timer <= timer + 1'b1;
end
*/
always @(posedge vga_clk)
begin
case(switch[1:0])
2'd0: data <= h_dat; //选择横彩条
2'd1: data <= v_dat; //选择竖彩条
2'd2: data <= (v_dat ^ h_dat); //产生棋盘格
2'd3: data <= (v_dat ~^ h_dat); //产生棋盘格
endcase
end
always @(posedge vga_clk) //产生竖彩条
begin
if(hcount < 223)
v_dat <= 3'h7; //白
else if(hcount < 303)
v_dat <= 3'h6; //黄
else if(hcount < 383)
v_dat <= 3'h5; //青
else if(hcount < 463)
v_dat <= 3'h4; //绿
else if(hcount < 543)
v_dat <= 3'h3; //紫
else if(hcount < 623)
v_dat <= 3'h2; //红
else if(hcount < 703)
v_dat <= 3'h1; //蓝
else
v_dat <= 3'h0; //黑
end
always @(posedge vga_clk) //产生横彩条
begin
if(vcount < 94)
h_dat <= 3'h7; //白
else if(vcount < 154)
h_dat <= 3'h6; //黄
else if(vcount < 214)
h_dat <= 3'h5; //青
else if(vcount < 274)
h_dat <= 3'h4; //绿
else if(vcount < 334)
h_dat <= 3'h3; //紫
else if(vcount < 394)
h_dat <= 3'h2; //红
else if(vcount < 454)
h_dat <= 3'h1; //蓝
else
h_dat <= 3'h0; //黑
end
endmodule
8.2. ModelSim测试激励代码
`timescale 1 ns/ 1 ps
module VGA2_test();
// constants
// general purpose registers
// test vector input registers
reg clock;
reg sys_rst;
reg [1:0] switch;
// wires
wire [2:0] disp_RGB;
wire hsync;
wire vsync;
//时钟 50MHz
initial
begin
clock = 0;
forever #10 clock = ~clock;
end
//复位信号
initial
begin
sys_rst = 1;
# 10 sys_rst = 0;
# 50 sys_rst = 1;
end
initial
begin
switch[1:0] = 2'd1;
end
// assign statements (if any)
VGA2 VGA2_inst (
.sys_rst(sys_rst),
.clock(clock),
.disp_RGB(disp_RGB),
.hsync(hsync),
.switch(switch),
.vsync(vsync)
);
endmodule
8.3. TCL脚本文件
注意:本文件内容只适用于 EEPW-CPLD-DIY活动的板子。如果用于其它板子,请自己修改相关内容。
#CPLD 时钟脚
set_location_assignment PIN_12 -to clock
#CPLD 复位脚
set_location_assignment PIN_14 -to sys_rst
#行同步信号
set_location_assignment PIN_1 -to hsync
#场同步信号
set_location_assignment PIN_2 -to vsync
#RGB脚
set_location_assignment PIN_8 -to disp_RGB[0]
set_location_assignment PIN_5 -to disp_RGB[1]
set_location_assignment PIN_4 -to disp_RGB[2]
#拨码开关控制显示效果
set_location_assignment PIN_29 -to switch[0]
set_location_assignment PIN_30 -to switch[1]
9. 显示效果
9.1. 横条
9.2. 竖条
9.3. 方格
10. 参考资料
http://bbs.****.com/BLOG_ARTICLE_219303.HTM
http://www.epanorama.net/documents/pc/vga_timing.html
http://tinyvga.com/vga-timing/640x480@60Hz
http://share.eepw.com.cn/share/download/id/165148