VGA时序与模拟彩条实现
图像显示模块是我们最重要的环节之一,因为图像显示是将我们图像处理后的结果进行可视化显示,我们可以很直观的看到是否为我们预期处理的效果。我们使用VGA接口来驱动液晶屏。
VGA(Video Graphics Array,视频图像阵列)是一种视频传输标准,具有分辨率高、颜色饱和丰富以及刷新速率快等优点,主要作为计算机显卡传输图像到显示器的桥梁,将显卡处理后的图像传输给显示器进行实时显示。VGA接口作为一种视频图像信号传输的标准接口,被广泛使用在各类视频显示场合。
VGA接口采用15针的D型接口,分三排,一排五个管脚。如下图为VGA接口公头(左)和母头(右),下表为对应的管脚说明。
VGA管脚说明
编号 | 管脚名 | 详情 |
1
|
RED
|
红色信号输入线
|
2
|
GREEN
|
绿色信号输入线
|
3
|
BLUE
|
蓝色信号输入线
|
4
|
ID Bit
|
地址码线
|
5
|
Self_Test
|
自测试信号线
|
6
|
RGND
|
红基色信号地线
|
7
|
GGND
|
绿基色信号地线
|
8
|
BGND
|
蓝基色信号地线
|
9
|
RESERVED
|
保留
|
10
|
SGND
|
数字信号地线
|
11
|
ID0
|
显示器标志位0
|
12
|
ID1
|
显示器标志位1
|
13
|
HSYNC
|
行同步信号线
|
14
|
VSYNC
|
场同步信号线
|
15
|
ID3
|
显示器标志位3
|
VGA时序及驱动
VGA最重要的信号是:RED、GREEN、BLUE及HSYNC和VSYNC,其中前面三种是数据信号,后面两种是控制信号,数据信号的传输是靠控制信号来进行同步控制的,下面介绍行和场控制信号如何对数据信号进行同步的。
一个完整行的扫描周期由a、b、c、d四部分组成,其中H_SYNC(a)为行同步阶段,对行扫描地址进行复位;H_BACK(b)为行消隐后肩,是扫描地址转移后的准备期;H_DISP(c)为行显示阶段,此阶段像素数据为有效;H_FRONT(d)为行消隐前肩,为扫描地址转移的准备期;H_TOTAL(e)为完成一次行扫描的总时间。扫描时候,先进行行同步,然后才进行数据的传输,其时序如下图。
VGA行扫描时序图
场扫描与行扫描时序类似,场扫描周期由n个行扫描周期组成,并且一次场扫描周期有其自身的时序规律,完成一定行数的行扫描后,进行一次场同步
该行数与VGA分辨率有关,如下图为VGA场扫描时序图。
VGA行扫描时序图
VGA显示分辨率与刷新频率是紧密联系的,是VGA时序标准,下表为VGA常用分辨率和刷新频率及场行时序之间的关系。
VGA常见显示模式时序表
显示模式 | 时钟
(MHZ) | 行时序(像素数) | 帧时序(行数) | ||||||||
a | b | c | d | e | o | p | q | r | s | ||
640x480@60 | 25.175 | 96 | 48 | 640 | 16 | 800 | 2 | 33 | 480 | 10 | 525 |
640x480@75 | 31.5 | 64 | 120 | 640 | 16 | 840 | 3 | 16 | 480 | 1 | 500 |
800x600@60 | 40.0 | 128 | 88 | 800 | 40 | 1056 | 4 | 23 | 600 | 1 | 628 |
800x600@75 | 49.5 | 80 | 160 | 800 | 16 | 1056 | 3 | 21 | 600 | 1 | 625 |
1024x768@60 | 65 | 136 | 160 | 1024 | 24 | 1344 | 6 | 29 | 768 | 3 | 806 |
1024x768@75 | 78.8 | 176 | 176 | 1024 | 16 | 1312 | 3 | 28 | 768 | 1 | 800 |
1280x1024@60 | 108.0 | 112 | 248 | 1280 | 48 | 1688 | 3 | 38 | 1024 | 1 | 1066 |
1280x800@60 | 83.46 | 136 | 200 | 1280 | 64 | 1680 | 3 | 24 | 800 | 1 | 828 |
1440x900@60 | 106.47 | 152 | 232 | 1440 | 80 | 1904 | 3 | 28 | 900 | 1 | 932 |
根据前面VGA显示模式时序表,我们在Vivado里使用Verilog编写相应的代码,生成相应的驱动时序,这里我们通过经典的模拟彩条来进行对VGA进行驱动与验证。
为了方便测试不同分辨率(不同帧率)下的驱动情况,我们将不同显示模式都保存在一个.v文件中,使用 `include "VGA_para.v" 来包含常用的场行时序常数,然后通过宏定义来选择不同的分辨率。
下面是选择640x480的宏定义代码。
//------------------------------------
//vga parameter define
`define VGA_640_480_60FPS_25MHz
//`define VGA_800_600_72FPS_50MHz
//`define VGA_1024_768_60FPS_65MHz
//`define VGA_1440_900_60FPS_105MHz
//`define VGA_1280_1024_60FPS_105MHz
//---------------------------------
根据前面选择的宏定义,下面的代码便是640x480的时序生成。
//---------------------------------
// 640 * 480
`ifdef VGA_640_480_60FPS_25MHz
`define H_FRONT 11'd16
`define H_SYNC 11'd96
`define H_BACK 11'd48
`define H_DISP 11'd640
`define H_TOTAL 11'd800
`define V_FRONT 11'd10
`define V_SYNC 11'd2
`define V_BACK 11'd33
`define V_DISP 11'd480
`define V_TOTAL 11'd525
`endif
//---------------------------------
同理,如果想要使用1440x900的分辨率,我们只需将VGA常见显示模式时序表中的时序常数对应修改,便可以转换到相应的分辨率显示模式。
因为我们的开发板上VGA的接口预留的是RGB444,因此我们需要定义颜色分量值,下面代码是定义不同颜色的RGB值。
//define colors RGB--4|4|4
`define RED 12'h800 /*1111,0000,0000 */
`define GREEN 12'hF0 /*0000,1111,0000 */
`define BLUE 12'h00F /*0000,0000,1111*/
`define WHITE 12'hFFF /*1111,1111,1111*/
`define BLACK 12'h000 /*0000,0000,0000 */
`define YELLOW 12'hFF0 /*1111,1111,0000*/
`define CYAN 12'hF0F /*1111,0000,1111*/
`define ROYAL 12'h0FF /*0000,1111,1111*/
对于场行同步信号的生成,我们只需编写相应的时序即可,下面是其代码。
//----------------行扫描进程-------------------
always@(posedge clk_out)
begin
if(hcount == `H_TOTAL) // 行计数结束
hcount_ov <= 1; // 行计数结束标志
else
hcount_ov <= 0;
end
always@(posedge clk_out)
begin
if(hcount_ov == 1)
hcount <= 12'b0; //行计数从新开始
else
hcount <= hcount + 1;
end
//----------------行同步信号的生成-------------------
//always@(hcount)
always@(posedge clk_out)
begin
if(hcount > `H_SYNC)
hsync <= 1;
else
hsync <= 0; //低电平同步
end
//----------------场扫描进程---------------------
always@(posedge clk_out)
begin
if(vcount == `V_TOTAL) // 场计数结束
vcount_ov <= 1; // 场计数结束标志
else
vcount_ov <= 0;
end
always@(posedge clk_out)
begin
if(hcount_ov == 1) begin
if(vcount_ov == 1)
vcount <= 12'b0; //场计数从新开始
else
vcount <= vcount + 1;
end
end
//----------------场同步信号生成-------------------
always@(posedge clk_out)
begin
if(vcount > `V_SYNC)
vsync <= 1;
else
vsync <= 0; //低电平同步
end
我们选择640x480@60显示模式,也就是驱动时钟为25MHz,场行消隐以及有效显示都应该严格符合时序表中的时序要求。下面为VGA彩条显示代码。
always@(posedge clk) begin
if((hcounter < H_DISP) && (vcounter< H_DISP))begin
if (hcounter <= H_DISP /5) begin colour <= `C_RED; end
else if(hcounter <= 2* H_DISP /5) begin colour <= `C_GREEN; end
else if(hcounter <= 3* H_DISP /5) begin colour <= `C_BLUE; end
else if(hcounter <= 4* H_DISP /5) begin colour <= `C_WHITE; end
else begin colour <= `C_BLACK; end
end else
colour <= `C_BLACK;
end
最终运行我们的VGA模拟彩条驱动代码,运行在我们开发板上的显示效果如下图所示,可以看得出我们代码是正确