VGA

1  相关理论

1. 1 VGA介绍

VGA(Video Graphics Array)即视频图形阵列。

1.2  VGA管脚

VGA接口是一种D型接口,采用非对称分布的15pin连接方式,共有15针,分成3排,每排5个孔,是显卡上应用最为广泛的接口类型,绝大多数显卡都带有此种接口。它传输红、绿、蓝模拟信号以及同步信号(水平和垂直信号)。一般在VGA接头上,会有1,5,6,10,11,15等标明每个接口编号。如果没有,如上图所示编号。

VGA接口15根针,其对应接口定义如下:

1.红基色red

2.绿基色green

3.蓝基色blue

4.地址码ID Bit(也有部分是RES,或者为ID2显示器标示位2)

5.自测试(各家定义不同)(一般为GND)

6.红地

7.绿地

8.蓝地

9.保留(各家定义不同)

10.数字地

11.地址码(ID0显示器标示位0)

12.地址码(ID1显示器标示位1)

13.行同步

14.场同步

15.地址码( ID3或显示器标示位3 )

对于FPGA逻辑设计来说,我们关注的信号是:红基色、绿基色、蓝基色、行同步和场同步信号。其他信号都是原理图和PCB设计时关注。通过控制红基色、绿基色、蓝基色、行同步和场同步信号这5个接口,就能让显示器显示丰富的色彩,显示各种视频图像。

1.3  VGA色彩原理

在中学的物理课中我们可能做过棱镜的试验,白光通过棱镜后被分解成多种颜色逐渐过渡的色谱,颜色依次为红、橙、黄、绿、青、蓝、紫,这就是可见光谱。其中人眼对红、绿、蓝最为敏感,人的眼睛就像一个三色接收器的体系,大多数的颜色可以通过红、绿、蓝三色按照不同的比例合成产生。同样,绝大多数单色光也可以分解成红绿蓝三种色光。这是色度学的最基本原理,即三基色原理。

红绿蓝三种基色是相互独立的,任何一种基色都不能由其它两种颜色合成。红绿蓝三基色按照不同的比例相加合成混色,称为相加混色。

三基色混色原理示意图如下图所示:

三基色颜色编码:

颜色

绿

R

0

0

1

1

0

0

1

1

G

0

0

0

0

1

1

1

1

B

0

1

0

1

0

1

0

1

以上RBG一共有8组合,也就是可以产生8种颜色。但显示器显示的色彩却是非常丰富,远远多于8种颜色,这是如何做到的呢?原因就是对于显示器来说,RGB三个信号其实是模拟信号,其电压的高低,可以表示颜色的深浅。利用这个原理,我们就可以产生丰富的色彩。例如,如果R=3.3V,G=0V,B=0V,则显示器会显示非常鲜艳的红色。如果G和B仍然是0V,而R改为1.8V,则显示器会显示比较浅的红色。R、G、B的电压范围从0~3.3V,任意组合就可以表示非常多的颜色了。

1.4  显示器扫描方式

通过控制红绿蓝三基色,就可以控制1个像素的颜色。一幅图像是由非常多的像素组成的。例如640*480分辨率的图像,是由一行640个、一共480行,这么多像素组合起来显示的图像。为了让显示器显示这么一幅图像,那么就要控制显示器的扫描枪一个一个像素地将颜色显示起来。像素变化的时间非常快,从而使人眼误认为所有像素是一起显示的。

下面是CRT(阴极射线管)显示器的控制框图:

 

工作原理:显示器采用光栅扫描方式,即轰击荧光屏的电子束在CRT屏幕上从左到右(受水平同步信号HSYNC控制)、从上到下(受垂直同步信号VSYNC控制)做有规律的移动。电子束采用光栅扫描方式,从屏幕左上角一点开始,向右逐点进行扫描,形成一条水平线;到达最右端后,又回到下一条水平线的左端,重复上面的过程;当电子束完成右下角一点的扫描后,形成一帧。此后,电子束又回到左上方起点,开始下一帧的扫描。这种方法也就是常说的逐行扫描显示

1.5  VGA时序

行同步信号的时序图

行同步信号的时序如上图。行同步信号周期性地产生高低电平,其一共可分成4个阶段:同步脉冲a、显示后沿b、显示时序c和显示前沿d。同步脉冲a表示着一行的结束,同时也是下一行的开始。显示时序c是真正图像显示的区域,在此阶段,像素逐个显示出来,也就是说在此阶段,我们要控制红、绿、蓝三基色信号,输出对应像素的RGB值。在显示后沿b和显示前沿d这两个阶段,是消隐时刻,此时红、绿、蓝三基色信号都要求为0。

场同步信号的时序与行同步信号相似,如下图。

场同步信号的时序图

场同步信号也是周期性地产生高低电平,其一共可分成4个阶段:同步脉冲a、显示后沿b、显示区域c和显示前沿d。但注意的是,场同步信号的变化单位是“一行”。例如,一个“场同步脉冲”时间包含多个“行脉冲周期”。

针对容易出错的理解,这里再次强调一下VGA时序。

  1. 场同步的变化是以“一行”为单位的。例如说,如果一行时间是800个时钟,场同步脉冲a的值为2,则场同步脉冲的时间是2*800=1600个时钟。
  2. 真正的显示区域是在:场同步信号处于显示区域,并且行同步信号也处于显示区域。其他区域,红、绿、蓝基色都要给低电平。

看时序的时候,不仅要关注其变化点,还要关注持续的时间。在行场同步的四个阶段,其时间分别是多少呢?下面是常见分辨率的参数。

分辨率

行/列

同步脉冲

显示后沿

显示区域

显示前沿

帧长

单位

640*480

/60Hz

96

48

640

16

800

基准时钟

2

33

480

10

525

800*600

/72Hz

120

64

800

56

1040

基准时钟

6

23

600

37

666

800*600

/60Hz

128

88

800

40

1056

基准时钟

4

23

600

1

628

1024*768

/60Hz

136

160

1024

24

1344

基准时钟

6

29

768

3

806

以640*480/60Hz为例,640*480/60Hz是指刷新频率为60Hz,分辨率为640X480。这个是标准VGA显示驱动。

(1)刷新频率为60 Hz,是指1秒显示60幅图像。

(2)从表中可以看出,该分辨率行同步信号,同步脉冲是96个基准时钟,显示后沿是48个基准时钟,显示区域是640个基准时钟,显示前沿是16个基准时钟,那么一行一共有800个基准时钟。

(3)该分辨率场同步信号,同步脉冲是2行(2*800个基准时钟),显示后沿是33行(33*800个基准时钟),显示区域为480行(480*800个基准时钟),显示前沿为10行(10*800个基准时钟),一共有525行(525*800个基准时钟)。

(4)基准时钟是多少呢?由于1秒显示60幅图像,所以一幅图像显示的时间是1/60秒。一幅图像占用了525*800个基准时钟,所以基准时钟周期=(1/60)/(525*800)秒,约为39.6825ns。那么基准时钟周期就约为25.175 MHz,实验中我们取25M。

以分辨率为640x480为例,刷新速率为60Hz,每幅图像每行有800个clk,有525个行,完成一幅图像的时间是1s/60=16.6ms,完成一行的时间为16.6/525=31.75us,完成一个像素的时间约为31.75us/800=40ns(16.6/(525*800))。因此为了方便设计,接口时钟设置为25MHz,每个时钟送一个数据。

1.6  VGA原理图

FPGA是数字芯片,管脚输出的都是0和1的数字信号,只有高电平和低电平。为了控制RGB电压的高低,我们就必须用到数模转换DA芯片。可以用数字信号控制数模转换芯片的输入端,从而让其输出不同幅度的电压值。

例如下图中,FPGA产生RGB三种信号,这时RGB都是多位的数字信号,这些信号将给DA芯片,DA会根据这个数字信号产生不同电压的模拟信号 rgb。模拟信号rgb再连到显示器上,就可以显示丰富的颜色了。

在这里,只要记住,FPGA可以通过数字信号控制DA芯片,DA芯片就可以产生不同电平。

 

VGA接口的原理图如下:

行同步管脚连到信号VGA_HSYNC,场同步信号连到信号VGA_VSYNC,红基管脚连到VGA_RED,蓝基管脚连到信号VGA_BLUE,绿基管脚连到信号VGA_GREEN。

VGA_HSYNC和VGA_VSYNC信号,另一端分别连到FPGA的管脚上。换句话说,就是FPGA控制管脚的输出,就能控制VGA接口的行场同步了。

VGA_RED、VGA_BLUE和VGA_GREEN信号,其原理图如下:

由图可见,开发板使用常用的RGB565,也就是 16 位高彩色 VGA 显示,红色占 5 位,绿色占 6 位,蓝色占 5 位。该部分电路使用电阻网络代替了DA芯片,即通过电阻匹配网络实现 RGB 数字信号到模拟信号的转换。其中,VGA_RED是VGA_R0~VGA_R4与电阻并联产生的,VGA_GREEN是VGA_G0~VGA_G5与电阻并联产生,VGA_BLUE是VGA_B0~VGA_B4与电阻并联产生。而VGA_R0~VGA_R4、VGA_G0~VGA_G5、VGA_B0~VGA_B4是连接到FPGA管脚的数字信号,每个信号都只有0V和3.3V两种可能。那么FPGA通过控制这些信号,也就控制了VGA的红基、绿基和蓝基管脚的电压。

VGA_RED电压       = (VGA_R0/2 + VGA_R1/4 + VGA_R2/8 + VGA_R3/16 + VGA_R4/32)*3.3V。

VGA_GREEN电压  = (VGA_G0/2 + VGA_G1/4 + VGA_G2/8 + VGA_G3/16 + VGA_G4/32+VGA_G5/64)*3.3V。

VGA_BLUE电压      = (VGA_B0/2 + VGA_B1/4 + VGA_B2/8 + VGA_B3/16 + VGA_B4/32)*3.3V。

颜色

VGA_R4~R0

VGA_G5~G0

VGA_B4~B0

白色

5’b11111

6’b111111

5’b11111

黑色

5’b0

6’b0

5’b0

蓝色

5’b0

6’b0

5’b11111

绿色

5’b0

6’b111111

5’b0

红色

5’b11111

6’b0

5’b0

 Xilinx AX516开发板的VGA电路图:

 2  设计目标

通过VGA连接线,将显示器和开发板的VGA接口相连。然后FPGA产生分辨率为640*480,刷新频率为60Hz的VGA时序,让显示器显示一幅完整的红色图像,即参数列表中的第一种参数。提示:显示器一般都有自适应功能,无须设置就能识别不同分辨率的图像。其中,行的单位为“基准时钟”,即频率为25MHz、周期为40ns的时钟,注意列的单位为“行”。

效果图如下图所示:

3  设计实现

3.1  顶层信号

新建目录:E:\FPGA\Project\AX516_Verilog\VGA_ColorDisplay。在该目录中,新建一个名为VGA_ColorDisplay.v的文件,并用GVIM打开,开始编写代码。

我们要实现的功能,概括起来就是:FPGA产生VGA时序,即控制VGA_R4~R0、VGA_G5~G0、VGA_B4~B0、VGA_HSYNC和VGA_VSYNC,让显示器显示红色。其中,VGA_HSYNC和VGA_VSYNC,FPGA可根据时序产生高低电平。而颜色数据,由于是固定的红色,FPGA也能自己产生,不需要外部输入图像数据。那么我们的FPGA工程,可以定义输出信号hys表示行同步,用输出信号vys表示场同步,定义一个16位的信号lcd_rgb,其中lcd_rgb[15:11]表示VGA_R4~0,、lcd_rgb[10:5]表示VGA_G5~0,、lcd_rgb[4:0]表示VGA_B4~0。此外,还需要时钟信号和复位信号来进行工程控制。

综上所述,这个工程需要五个信号,时钟clk,复位rst_n,场同步信号lcd_vs、行同步信号lcd_hs和RGB输出信号lcd_rgb。

器件

电阻网络转换后

信号线

信号线

FPGA管脚

FPGA工程信号

CN1

VGA_RED

VGA_R4

 

lcd_rgb[15]

VGA_R3

 

lcd_rgb[14]

VGA_R2

 

lcd_rgb[13]

VGA_R1

 

lcd_rgb[12]

VGA_R0

 

lcd_rgb[11]

VGA_GREEN

VGA_G5

 

lcd_rgb[10]

VGA_G4

 

lcd_rgb[9]

VGA_G3

 

lcd_rgb[8]

VGA_G2

 

lcd_rgb[7]

VGA_G1

 

lcd_rgb[6]

VGA_G0

 

lcd_rgb[5]

VGA_BLUE

VGA_B4

 

lcd_rgb[4]

VGA_B3

 

lcd_rgb[3]

VGA_B2

 

lcd_rgb[2]

VGA_B1

 

lcd_rgb[1]

VGA_B0

 

lcd_rgb[0]

VGA_HSYNC

VGA_HSYNC

 

lcd_hs

VGA_VSYNC

VGA_VSYNC

 

lcd_vs

X1

 

SYS_CLK

 

clk

K1

 

SYS_RST

 

rst_n

  • 将module的名称定义为vga_color_display。并且我们已经知道该模块有五个信号:clk、rst_n、lcd_hs、lcd_vs和lcd_rgb。为此,代码如下:

1

2

3

4

5

6

7

module vga_color_display(

clk      ,

rst_n    ,

lcd_hs   ,

lcd_vs   ,

lcd_rgb

);

其中clk、rst_n是输入信号,lcd_hs、lcd_vs和lcd_rgb是输出信号,其中clk、rst_n、lcd_hs、lcd_vs的值是0或者1,一根线即可,lcd_rgb为16位位宽的,根据这些信息,补充输入输出端口定义。代码如下:

1

2

3

4

5

input                       clk            ;

input                       rst_n         ;

output                     lcd_hs       ;

output                     lcd_vs        ;

output  [15:0]          lcd_rgb       ;

3.2  架构设计

需要注意的是,输入进来的时钟clk是50MHz,而从分辨率参数表可知道,行单位的基准时钟是25 MHz。为此我们需要根据50MHz来产生一个25 MHz的时钟,然后再用于产生VGA时序。

为了得到这个25M时钟,我们需要一个PLL。PLL可以认为是FPGA内的一个硬核,它的功能是根据输入的时钟,产生一个或多个倍频和分频后的输出时钟,同时可以调整这些输出时钟的相位、占空比等。例如,输入进来是50M时钟,如果需要一个100M时钟,那么从逻辑上、代码上是不可能产生的,我们就必须用到PLL来产生了。

整个工程的结构图如下。

3.3  VGA驱动模块设计

3.3.1 模块接口

在目录:E:\FPGA\Project\AX516_Verilog\VGA_ColorDisplay中,建立一个color.v文件,并用GVIM打开,开始编写代码。

我们先分析功能。要控制显示器,让其产生红色,也就是让FPGA控制VGA_R0~4、VGA_G0~5、VGA_B0~4、VGA_VSYNC和VGA_HSYNC信号。那么VGA驱动模块,可以定义输出信号hys表示行同步,用输出信号vys表示场同步,定义一个16位的信号lcd_rgb,其中lcd_rgb[15:11]表示VGA_R4~0、lcd_rgb[10:5]表示VGA_G5~0、lcd_rgb[4:0]表示VGA_B4~0。同时该模块的工作时钟为25M,同时需要一个复位信号。

综上所述,VGA驱动模块需要五个信号,25M时钟clk,复位rst_n,场同步信号vys、行同步信号hys和RGB输出信号lcd_rgb。

3.3.2 信号设计

(1)先设计行同步信号hys,VGA时序中的场同步信号,其时序图如下:

hys就是一个周期性地高低变化的脉冲。我们使用的是640*480/60Hz,也就是同步脉冲a的时间是96个时钟周期,而显示后沿b是48个时钟周期,显示时序c是640个时钟周期,显示前沿是16个时钟周期,一共是800个时钟周期。

将时间信息填入图中,更新后的时序图如下:

很显然,我们需要1个计数器来产生这个时序,我们将该计数器命名为h_cnt。由于hys是不停地产生的,那么h_cnt就是不停地计数,所以认为该计数器的加1条件为“1”,可写成:assign add_h_cnt ==1。从上图可知,该计数器的周期是800。综上所述,该计数器的代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

always @(posedge clk or negedge rst_n)begin

if(!rst_n)begin

h_cnt <= 0;

end

else if(add_h_cnt)begin

if(end_h_cnt)

h_cnt <= 0;

else

h_cnt <= h_cnt + 1;

end

end

assign add_h_cnt = 1;

assign end_h_cnt = add_h_cnt && h_cnt== 800 - 1;

有了计数器h_cnt,那么hys信号就有了对齐的对象。从时序图可以发现,hys有两个变化点,一个是h_cnt数到96个时,由0变1;另一个是当h_cnt数到800个时,由1变0。所以,行同步信号hys的代码如下:

1

2

3

4

5

6

7

8

9

10

11

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin

hys <= 0;

end

else if(add_h_cnt && h_cnt == 96 -1)begin

hys <= 1'b1;

end

else if(end_h_cnt)begin

hys <= 1'b0;

end

end

(2)接下来设计场同步信号vys。该信号的时序图如下所示。

vys也是一个周期性地高低变化的脉冲。我们使用的是表中的第一种分辨率,查询表可知,同步脉冲a的时间是2行的时间,而显示后沿b是33行,显示时序c是480行,显示前沿是10行,一共是525行。其中,一“行”结束,也就是h_cnt数完了。

将时间信息填入图中,更新后的时序图如下:

很显然,我们还需要1个计数器来产生这个时序,我们将该计数器命名为v_cnt。该计数器是用来数有多少行的,所以加1条件就是一行结束,即end_h_cnt,可写成:assign add_v_cnt = end_h_cnt。从上图可知,该计数器的周期是525。综上所述,该计数器的代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

always @(posedge clk or negedge rst_n)begin

if(!rst_n)begin

v_cnt <= 0;

end

else if(add_v_cnt)begin

if(end_v_cnt)

v_cnt <= 0;

else

v_cnt <= v_cnt + 1;

end

end

assign add_v_cnt = end_h_cnt;

assign end_v_cnt = add_v_cnt && v_cnt== 525 - 1;

有了计数器v_cnt,那么vys信号就有了对齐的对象。从时序图可以发现,vys有两个变化点,一个是v_cnt数到2个时,由0变1;另一个是当h_cnt数到525个时,由1变0。所以,场同步信号的代码如下:

1

2

3

4

5

6

7

8

9

10

11

always  @(posedge clk or negedge rst_n)begin

if(!rst_n)begin

vys <= 1'b0;

end

else if(add_v_cnt && v_cnt == 2 - 1)begin

vys <= 1'b1;

end

else if(end_v_cnt)begin

vys <= 1'b0;

end

end

(3)最后我们还有一个信号需要设计,那就是lcd_rgb信号。我们要显示红色,即lcd_rgb输出的值为“16’b11111_000000_00000” 。但注意的是,要在“显示区域”才能赋给这个值,在其他区域要将lcd_rgb的值赋值为0。“显示区域”是什么时候?就是场同步信号vys和行同步信号都处于“显示时序c”阶段。结合时序图可知,就是h_cnt大于(96+48)并且小于(96+48+640),v_cnt大于(2+33)并且小于(2+33+480)。为了设计方便,添加一个信号red_area,当red_area=1就表示为此区域。

1

2

3

4

always  @(*)begin

red_area   = (h_cnt>=(96+48) && h_cnt<(96+48+640))&& (v_cnt>=(2+33) && v_cnt<(2+33+480));

end

 

有了red_area,设计lcd_rgb就好办了。当red_area=1时,lcd_rgb输出“16’b11111_000000_00000”,否则输出0。

1

2

3

4

5

6

7

8

9

10

11

always  @(posedge clk or negedge rst_n)begin

if(rst_n==1'b0)begin

lcd_rgb <= 16'h0;

end

else if(red_area)begin

lcd_rgb <= 16'b11111_111111_00000;

end

else begin

lcd_rgb <= 0;

end

end

此次,主体程序已经完成。接下来是将module补充完整。

3.3.3  信号定义

h_cnt是用always产生的信号,因此类型为reg。h_cnt计数的最大值为800,需要用10根线表示,即位宽是10位。因此代码如下:

1

reg    [9:0]           h_cnt  ;

add_h_cnt和end_h_cnt都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:

1

2

wire                   add_h_cnt;

wire                   end_h_cnt;

v_cnt是用always产生的信号,因此类型为reg。v_cnt计数的最大值为525,需要用10根线表示,即位宽是10位。因此代码如下:

1

reg    [9:0]           v_cnt  ;

add_v_cnt和end_v_cnt都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下:

1

2

wire                   add_v_cnt;

wire                   end_v_cnt;

lcd_rgb是用always方式设计的,因此类型为reg。并且它的位宽是16位,16根线表示即可。因此代码如下:

1

reg    [15:0]          lcd_rgb;

hys和vys是用always方式设计的,因此类型为reg。并且其值是0或1,需要1根线表示即可。因此代码如下:

1

2

reg                    hys    ;

reg                    vys    ;

red_area是用always方式设计的,因此类型为reg。并且其值是0或1,用一根线表示即可,因此代码如下:

1

reg                    red_area    ;

3.4  顶层模块设计

3.4.1 例化子模块

例化PLL IP核的代码

1

2

3

4

  vga_pll  pll_inst
   (// Clock in ports
    .CLK_IN1            (clk),          // IN 50MHz
    // Clock out ports
    .CLK_OUT1         (clk_o),     // OUT 25MHz
    .CLK_OUT2    ·    (),             // OUT 40MHz
    .CLK_OUT3         (),             // OUT 50MHz
    .CLK_OUT4         ()              // OUT 65MHz
     );  

例化驱动模块的代码

1

2

3

4

5

6

7

color   module_6(

.clk        (clk_o  ),

.rst_n      (rst_n  ),

.hys        (lcd_hs ),

.vys        (lcd_vs ),

.lcd_rgb    (lcd_rgb)

);

3.4.2 信号定义

clk_0是在例化文件中,因此类型为wire。并且其值是0或1,用一根线表示即可。因此代码如下:

1

wire                    clk_o        ;

lcd_hs和lcd_vs是在例化文件中,因此类型为wire。并且其值是0或1,用一根线表示即可。因此代码如下:

1

2

wire                    lcd_hs        ;

wire                    lcd_vs        ;

lcd_rgb是在例化文件中,因此类型为wire。它的位宽是16位的,用16根线表示即可。因此代码如下:

1

wire     [15:0]       lcd_rgb       ;

信号clk和rst_n不需要定义吗????

至此,整个代码的设计工作已经完成。下一步是新建工程和上板查看现象。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值