图像显示模块是我们最重要的环节之一,因为图像显示是将我们图像处理后的结果进行可视化显示,我们可以很直观的看到是否为我们预期处理的效果。我们使用VGA接口来驱动液晶屏。
VGA(Video Graphics Array,视频图像阵列)是一种视频传输标准,具有分辨率高、颜色饱和丰富以及刷新速率快等优点,主要作为计算机显卡传输图像到显示器的桥梁,将显卡处理后的图像传输给显示器进行实时显示。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常见显示模式时序表
根据前面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模拟彩条驱动代码,运行在我们开发板上的显示效果如下图所示,可以看得出我们代码是正确的。