基本概念
显示器的显示方式有两种 : A/N(Alphabet/Number:字符/数字)显示方式 和 APA(All Point Addressable:全点寻址) 显示方式,即文本显示方式和图形显示方 式。A/N 方式已淘汰不用,目前微机都采用 APA 图形方式。
扫描频率 显示器采用光栅扫描方式,即轰击荧光屏的电子束在 CRT 屏幕上从左到右(受水 平同步信号 HSYNC 控制)、从上到下(受 垂直同步信号 VSYNC 控制)做有规律的移动。光栅扫描又分逐行扫描和隔行扫描。电子束采用光栅扫描方式,从屏幕左上角一点 开始,向右逐点进行扫描,形成一条水平线;到达最右端后,又回到下一条水平线的左 端,重复上面的过程;当电子束完成右下角一点的扫描后,形成一帧。此后,电子束又回到左上方起点,开始下一帧的扫描。这种 方法也就是常说的逐行扫描显示。 而隔行扫描指电子束在扫描时每隔一 行扫一线,完成一屏后再返回来扫描剩下的 线,这与电视机的原理一样。隔行扫描的显示器比逐行扫描闪烁得更厉害,也会让使用者的眼睛更疲劳。目前微机所用显示器几乎都是逐行扫描。 完成一行扫描所需时间称为水平扫描时间,其倒数称为行频率;完成一帧(整屏) 扫描所需的时间称为垂直扫描时间,其倒数为垂直扫描频率,又称刷新频率,即刷新一屏的频率。常见的有 60Hz、75Hz 等,标准 VGA 显示的场频60Hz,行频为31.5kHz。
VGA 时序设计 在 VGA 中,水平同步脉冲在光栅扫描 线需要回到水平开始位置也就是屏幕的左边的时候插入,垂直同步脉冲在光栅扫描线需要回到垂直开始位置也就是屏幕的上方的时候插入。复合同步脉冲是水平同步脉冲与垂直同步信号的组合。RGB 为像素数据,在没有图像投射到屏幕时插入消隐信号,当消隐有效时,RGB 信号无效。水平时序 在水平时序中,包括以下几个时序参数:水平同步脉冲宽度;水平同步脉冲结束到水平门的开始之间的宽度;一个视频行可视区域的宽度;一个完整的视频行的宽度,从水平同步脉冲的开始到下一个水平同步脉冲的开始。
垂直时序 在垂直时序与水平时序类似,包括以下几个不同的时序参数:垂直同步脉冲宽度; 垂直同步结束到垂直门的开始之间的宽度; 一个视频帧可是区域的宽度;一个完整视频帧的宽度,从垂直同步脉冲到下一个垂直同步脉冲的开始。
VGA时序
显示器扫描方式分为逐行扫描和隔行扫描:逐行扫描是扫描从屏幕左上角一点开始,从左像右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT对电子束进行消隐,每行结束时,用行同步信号进行同步;当扫描完所有的行,形成一帧,用场同步信号进行场同步,并使扫描回到屏幕左上方,同时进行场消隐,开始下一帧。隔行扫描是指电子束扫描时每隔一行扫一线,完成一屏后在返回来扫描剩下的线,隔行扫描的显示器闪烁的厉害,会让使用者的眼睛疲劳。
完成一行扫描的时间称为水平扫描时间,其倒数称为行频率;完成一帧(整屏)扫描的时间称为垂直扫描时间,其倒数称为场频率,即刷新一屏的频率,常见的有60Hz,75Hz等等。标准的VGA显示的场频60Hz,行频31.5KHz。
行场消隐信号:是针对老式显像管的成像扫描电路而言的。电子枪所发出的电子束从屏幕的左上角开始向右扫描,一行扫完需将电子束从右边移回到左边以便扫描第二行。在移动期间就必须有一个信号加到电路上,使得电子束不能发出。不然这个回扫线会破坏屏幕图像的。这个阻止回扫线产生的信号就叫作消隐信号,场信号的消隐也是一个道理。
显示带宽:带宽指的显示器可以处理的频率范围。如果是60Hz刷新频率的VGA,其带宽达640x480x60=18.4MHz,70Hz的刷新频率1024x768分辨率的SVGA,其带宽达1024x768x70=55.1MHz。
时钟频率:以640x480@59.94Hz(60Hz)为例,每场对应525个行周期(525=10+2+480+33),其中480为显示行。每场有场同步信号,该脉冲宽度为2个行周期的负脉冲,每显示行包括800点时钟,其中640点为有效显示区,每一行有一个行同步信号,该脉冲宽度为96个点时钟。由此可知:行频为525*59.94=31469Hz,需要点时钟频率:525*800*59.94约25MHz.
module VGA(
RESET,
GCLKP1,
GCLKP2,
R,
G,
B,
VS,
HS
);
input RESET;
input GCLKP1,GCLKP2;
output [3:0]R,G,B;
wire [3:0]R,G,B;
output VS,HS;
wire VS,HS;
parameter H_PIXELS = 640; 水平像素
parameter H_FRONTPORCH = 16; 前沿
parameter H_SYNCTIME = 96; 同步脉冲
parameter H_BACKPORCH = 48; 后沿
parameter H_SYNCSTART = H_PIXELS + H_FRONTPORCH;
parameter H_SYNCEND = H_SYNCSTART + H_SYNCTIME;
parameter H_PERIOD = H_SYNCEND + H_BACKPORCH; //= 640 + 16 + 96 +48
parameter V_LINES = 480; //-- 604
parameter V_FRONTPORCH = 11; //-- -1
parameter V_SYNCTIME = 2; //-- 4
parameter V_BACKPORCH = 32; //-- 21
parameter V_SYNCSTART = V_LINES + V_FRONTPORCH;
parameter V_SYNCEND = V_SYNCSTART + V_SYNCTIME;
parameter V_PERIOD = V_SYNCEND + V_BACKPORCH; // = 480 +11 + 2 + 32
//------------------------------------------------
//------------------------------------------------
reg HsyncB;
reg VsyncB;
reg [9:0]Hcnt;
reg [9:0]Vcnt;
reg Enable;
reg TempR;
reg TempG;
reg TempB;
wire [3:0]ColorR;
wire [3:0]ColorG;
wire [3:0]ColorB;
//--Clock:
//reg [2:0]Count;
wire Period1uS, Period1mS;
reg CLK;
wire CLKTemp ;
//-- Globle Clock Assignment
reg [5:0]Count_clk; //-- 1MHz
reg [9:0]Count1_clk; //-- 1KHz
reg [9:0]Count2_clk; //-- 1Hz
//CLK 25 MHz
always@(posedge GCLKP1)
CLK <= ~CLK;
//-----------------------------------------------
//--GCLKP : 50MHz
//--Period: 1uS (Period1uS <= GCLKP1; )
always@(negedge RESET or posedge GCLKP2)
begin
if(!RESET)
Count_clk <= 6'b0;
else if(Count_clk > 6'b110000)
Count_clk <= 6'b0; //-- 1uS
else
Count_clk <= Count_clk + 1'b1;
end
assign Period1uS = Count_clk[5];
//-----------------------------------------------
//--Period: 1mS
always@(negedge RESET or posedge Period1uS)
begin
if(!RESET)
Count1_clk <= 10'b0;
else if(Count1_clk > 10'b1111100110)
Count1_clk <= 10'b0; //-- 1mS
else
Count1_clk <= Count1_clk + 1'b1;
end
assign Period1mS = Count1_clk[9];
//-----------------------------------------------
//--Period: 1S
always@(negedge RESET or posedge Period1mS)
begin
if(!RESET)
Count2_clk <= 10'b0;
else if(Count2_clk > 10'b1111100110)
Count2_clk <= 10'b0; //-- 1S
else
Count2_clk <= Count2_clk + 1'b1;
end
//-- 2 Hz
assign CLKTemp = Count2_clk[8];
//================================================================================================
//-- VGA Clock process
//--------------------------------------------------------------------------------------------------
//-- Horizontal counter 640x480水平像素扫描频率是25M Hz, CLK是25M Hz
Hcnt是扫描时行计数器
always@(negedge RESET or posedge CLK)
begin
if(!RESET)
Hcnt <= 10'b0;
else if(Hcnt < H_PERIOD) H_PERIOD = 640 + 16 + 96 +48
Hcnt <= Hcnt + 1'b1;
else
Hcnt <= 10'b0;
end
//------------------------------------------------
//-- Vertical counter Vcnt是帧计数器
always@(negedge RESET or posedge HsyncB) HsyncB在下面定义
begin
if(!RESET)
Vcnt <= 10'b0;
else if(Vcnt < V_PERIOD)
Vcnt <= Vcnt + 1'b1;
else
Vcnt <= 10'b0;
end
HsyncB是帧变化的激励信号
always@(negedge RESET or posedge CLK)
begin
if(!RESET)
HsyncB <= 1'b1;
else if( Hcnt>=(H_PIXELS + H_FRONTPORCH) && Hcnt<(H_PIXELS + H_FRONTPORCH + H_SYNCTIME) )
HsyncB <= 1'b0;
else
HsyncB <= 1'b1;
end
//------------------------------------------------
//-- Vertical Sync
always@(negedge RESET or posedge HsyncB)
begin
if(!RESET)
VsyncB <= 1'b1;
else if( (Vcnt >= (V_LINES + V_FRONTPORCH) ) && (Vcnt<(V_LINES + V_FRONTPORCH + V_SYNCTIME) ) )
VsyncB <= 1'b0;
else
VsyncB <= 1'b1;
end
assign HS = HsyncB; 在扫描一行结束时启动一个行同步信号
assign VS = VsyncB; 在帧结束时启动一个帧同步信号
//==============================================
//----------------------------------------------
Enable=1时,是正在向RGB引脚输出的状态。其它时刻不向RGB引脚输出
always@(posedge CLK)
begin
if(!RESET)
Enable <= 1'b0;
else if(Hcnt >= H_PIXELS || Vcnt >= V_LINES)
Enable <= 1'b0;
else
Enable <= 1'b1;
end
以下代码是当Enable==1时向RGB引脚输出
//------------------------------------------------
reg [2:0]Count_2;
always@(negedge RESET or posedge CLKTemp)
begin
if(!RESET)
Count_2 <= 3'b0;
else
Count_2 <= Count_2 + 1'b1;
end
//------------------------------------------------
always@(Hcnt[7:5])
begin
case(Hcnt[7:5])
3'b000:
begin
TempR <= 1'b1;
TempG <= 1'b1;
TempB <= 1'b1;
end
3'b001:
begin
TempR <= 1'b0;
TempG <= 1'b0;
TempB <= 1'b0;
end
3'b010:
begin
TempR <= 1'b1;
TempG <= 1'b0;
TempB <= 1'b0;
end
3'b011:
begin
TempR <= 1'b0;
TempG <= 1'b0;
TempB <= 1'b1;
end
3'b100:
begin
TempR <= 1'b0;
TempG <= 1'b1;
TempB <= 1'b0;
end
3'b101:
begin
TempR <= 1'b1;
TempG <= 1'b0;
TempB <= 1'b1;
end
3'b110:
begin
TempR <= 1'b1;
TempG <= 1'b1;
TempB <= 1'b0;
end
3'b111:
begin
TempR <= 1'b1;
TempG <= 1'b1;
TempB <= 1'b1;
end
default:
begin
TempR <= 1'b0;
TempG <= 1'b0;
TempB <= 1'b0;
end
endcase
end
//-------------------------------------------------
assign R = (Enable != 1'b1)? 4'b0:
(TempR == 1'b1)?{Count_2,1'b1}:4'b0;
assign G = (Enable != 1'b1)? 4'b0:
(TempG == 1'b1)?{1'b1,Count_2}:4'b0;
assign B = (Enable != 1'b1)? 4'b0:
(TempB == 1'b1)?{1'b1,Count_2[0],Count_2[1],Count_2[2]}:4'b0;
endmodule