1、什么是VGA接口
1.1、VGA接口介绍
VGA,英文全称“Video Graphics Array”,译为视频图形阵列,是一种使用模拟信号进行视频传输的标准协议,由 IBM 公司于 1987 年推出,因其分辨率高、显示速度快、颜色丰富等优点,广泛应用于彩色显示器领域。
VGA接口样式如图:
VGA 接口管脚如下图:
各管脚定义如下:
VGA 接口共有 15 个引脚,分为 3 排,每排各 5 个, 按照自上而下、从左 向右的顺序排列。其中第一排的引脚 1、2、3 和第三排的引脚 13、14 最为重要。 VGA 使用工业界通用的 RGB 色彩模式作为色彩显示标准,这种色彩显示标准是根据 三原色中红色、绿色、蓝色所占比例多少及三原色之间的相互叠加得到各式各样的颜色。
- 引脚 1 红基色(RED)、引脚 2 绿基色(GREEN)、引脚 3 蓝基色(BLUE)就是 VGA 接口中负责 传输三原色的传输通道。要注意的是,这 3 个引脚传输的是模拟信号
- 引脚 13 行同步信号(HSYNC)、引脚 14 场同步信号(VSYNC),这两个信号,是在 VGA 显示图像时,负责同步图像色彩信息的同步信号
- 引脚 5、9:这两个引脚分别是 VGA 接口的自测试和预留接口,不过不同生产厂家对 这两个接口定义不同,在接线时,两引脚可悬空不接
- 引脚 4、11、12、15:这四个是 VGA 接口的地址码,可以悬空不接
- 引脚 6、7、8、10:这四个引脚接地
VGA 显示器显示图像,并不是直接让图像在显示器上显示出来,而是采用扫描的方 式,将构成图像的像素点,在行同步信号和场同步信号的同步下,按照从上到下、由左到 右的顺序扫描到显示屏上,如下所示:
1.2、VGA接口时序
在VGA视频传输标准中,视频图像被分解为红、绿、蓝三原色信号,经过数模转换之后, 在行同步(HSYNC)和场同步(VSYNC)信号的同步下分别在三个独立通道传输。VGA在传输程中的同步时序分为行时序和场时序,以下分别是行同步、场同步的时序图:
- HSync的周期为行扫描周期。
- 一个完整的行扫描周期,包含 6 部分:Sync(同步)、Back Porch(后沿)、Left Border(左边框)、“Addressable” Video(有效图像)、Right Border(右边框)、Front Porch(前沿),这 6 部分的基本单位是 pixel(像素),即一个像素时钟周期
- 在一个完整的行扫描周期中,Video 图像信息在 HSync 行同步信号的同步下完成一行图像的扫描显示,Video 图像信息只有在“Addressable” Video(有效图像)阶段,图像信息 有效,其他阶段图像信息无效
- HSync 行同步信号在 Sync(同步)阶段,维持低电平,其他阶段均保持高电平
- VSync的周期为场扫描周期。
- 一个完整的场扫描周期,也包含 6 部分:Sync(同步)、Back Porch(后沿)、Top Border(上边框)、“Addressable” Video(有效图像)、Bottom Border(底边框)、Front Porch(前沿),与行同步信号不同的是,这 6 部分的基本单位是 line(行),即一个完整 的行扫描周期。
- 在一个完整的场扫描周期中,Video 图像信息在 HSync(行同步信号)和 VSync(场 同步信号)的共同作用下完成一帧图像的显示,Video 图像信息只有在“Addressable” Video (有效图像)阶段,图像信息有效,其他阶段图像信息无效。
- VSync 行同步信号在 Sync(同步)阶段,维持低电平,其他阶段均保持高电平,完成 一个场扫描周期后,进入下一帧图像的扫描
需要注意的有2点:
1、行时序是以”像素”为单位的, 场时序是以”行”为单位的
2、VGA 工业标准显示模式要求:行同步,场同步都为负极性,即同步脉冲要求是负脉冲
行同步时序可分为 6 个阶段,对于这 6 个阶段的参数是有严格定义的。VGA 显示器可支持多种分辨率,常用 VGA 分辨率时序参数如下:
2、乳法实例
接下来就用FPGA生成彩条(法国FLAG)通过VGA接口显示出来,分辨率为640*480@60,时钟25M。
设计的模块如下:
顶层模块用于例化vga_fra和vga_driver模块,并生成一个25M的时钟做VGA的驱动时钟(这里最好是用PLL生成)。vga_fra模块根据vga_driver传回的行、场坐标判断该坐标的颜色并输出给vga_driver模块,vga_driver模块将颜色信息通过VGA的规定时序输出到VGA接口。
vga_driver模块:驱动模块,把输入的像素信息同步到VGA时序下。
- vga_clk:VGA时钟,25M
- sys_rst_n:复位信号、低电平有效
- pixel_data:输入RGB数据
- h_sync :行同步信号
- v_sync :场同步信号
- pixel_x:行坐标
- pixel_y :场坐标
- rgb_data :在VGA时序下的RGB数据
vga_fra:根据vga_driver模块传入的行、场坐标断该坐标的颜色并输出给vga_driver模块。
- vga_clk:VGA时钟
- sys_rst_n:复位信号、低电平有效
- pixel_x:行坐标
- pixel_y:场坐标
- pixel_data:输入RGB数据
顶层模块:生成25M时钟,并分别例化ga_driver模块,vga_driver模块。
- sys_clk:系统时钟50M
- sys_rst_n:系统复位
- h_sync:行同步信号
- v_sync:场同步信号
- rgb_data:VGA输出的RGB565信息
2.2、Verilog代码
因为一个完整行扫描周期为 800 个像素时钟周期 (640*480@60),我们可以利用计数器以像素时钟周期进行计数,每一个像素时钟周期自加 1,计数范围为 0-799,共计数 800 次,与完整行扫描周期数相吻合。只要在行同步阶段(计 数范围 0-95)赋值 hsync 信号为高电平,其他阶段为低电平,就可以实现符合时序要求的行同步信号 hsync。然后当计数器计数到有效周期时拉高有效信号,并输出RGB像素信息。场时序同理。
vga_driver模块代码:
//==================================================================
//--VGA驱动程序
//==================================================================
//------------<模块及端口声明>----------------------------------------
module vag_driver(
input vga_clk , //VGA时钟
input sys_rst_n , //复位信号、低电平有效
input [15:0] pixel_data , //输入RGB数据
output h_sync , //行同步信号
output v_sync , //场同步信号
output [10:0] pixel_x , //行坐标
output [9:0] pixel_y , //场坐标
output [15:0] rgb_data //在VGA时序下的RGB数据
);
//---------640*480@60模式下的参数定义----------------------------------
//行同步参数定义
localparam H_SYNC = 10'd96 , //行同步
H_BACK = 10'd40 , //行时序后沿
H_LEFT = 10'd8 , //行时序左边框
H_VALID = 10'd640 , //行有效数据
H_RIGHT = 10'd8 , //行时序右边框
H_FRONT = 10'd8 , //行时序前沿
H_TOTAL = 10'd800 ; //行扫描周期
//场同步参数定义
localparam V_SYNC = 10'd2 , //场同步
V_BACK = 10'd25 , //场时序后沿
V_TOP = 10'd8 , //场时序上边框
V_VALID = 10'd480 , //场有效数据
V_BOTTOM = 10'd8 , //场时序下边框
V_FRONT = 10'd2 , //场时序前沿
V_TOTAL = 10'd525 ; //场扫描周期
//------------<reg定义>------------------------------------------------
reg [10:0] cnt_h; //行计数器,位宽11,最大兼容1440*900@60
reg [9:0] cnt_v; //场计数器,位宽10,最大兼容1440*900@60
//------------<wire定义>------------------------------------------------
wire rgb_valid; //RGB数据有效标志信号
wire pix_data_req; //RGB数据请求信号,比rgb_valid提前一拍
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//行计数器,用作时序指示
//每时钟单位+1,最大值H_TOTAL
always@(posedge vga_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cnt_h <= 11'd0;
else if(cnt_h == H_TOTAL - 1'b1)
cnt_h <= 11'd0;
else
cnt_h <= cnt_h + 1'd1;
end
//场计数器,用作时序指示
//每行+1,最大值V_TOTAL
always@(posedge vga_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cnt_v <= 10'd0;
else if(cnt_v == V_TOTAL - 1'b1 && cnt_h == H_TOTAL - 1'b1)
cnt_v <= 10'd0;
else if(cnt_h == H_TOTAL - 1'b1)
cnt_v <= cnt_v + 1'd1;
else
cnt_v <= cnt_v;
end
//在行同步区间内拉低,其他时间拉高
assign h_sync = (cnt_h <= H_SYNC -1'd1) ? 1'b0 : 1'b1;
//在场同步区间内拉低,其他时间拉高
assign v_sync = (cnt_v <= V_SYNC -1'd1) ? 1'b0 : 1'b1;
//在有效区间内拉高,其他时间拉低
assign rgb_valid = (((cnt_h >= H_SYNC + H_BACK + H_LEFT)
&& (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID))
&&((cnt_v >= V_SYNC + V_BACK + V_TOP)
&& (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID)))
? 1'b1 : 1'b0;
//在有效期间输出数据,其他时间则输出0(黑色)
assign rgb_data = rgb_valid ? pixel_data : 16'd0;
//提前一个时钟周期在有效区间内拉高,其他时间拉低(因为其他模块是时序信号)
assign pix_data_req = (((cnt_h >= H_SYNC + H_BACK + H_LEFT -1'd1)
&& (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID -1'd1))
&&((cnt_v >= V_SYNC + V_BACK + V_TOP)
&& (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID)))
? 1'b1 : 1'b0;
//在有效区间内生成行坐标传递给其他模块以生成坐标的RGB
assign pixel_x = (pix_data_req == 1'b1) ?
(cnt_h - (H_SYNC + H_BACK + H_LEFT - 1'b1)) : 11'd0;
//在有效区间内生成行坐标传递给其他模块以生成坐标的RGB
assign pixel_y = (pix_data_req == 1'b1) ?
(cnt_v - (V_SYNC + V_BACK + V_TOP )) : 10'd0;
endmodule
vag_fra模块:
比较简单,只要根据不同的坐标生成不同的颜色即可
//==================================================================
//--彩条生成模块
//==================================================================
module vag_fra(
input vga_clk , //VGA时钟
input sys_rst_n , //复位信号、低电平有效
input [10:0] pixel_x , //行坐标
input [9:0] pixel_y , //场坐标
output reg [15:0] pixel_data //输入RGB数据
);
//---------行场参数定义----------------------------------
localparam H_VALID = 10'd640 , //行有效数据
V_VALID = 10'd480 ; //场有效数据
//---------颜色的参数定义--------------------------------
localparam RED = 16'hF800 , //红色
ORANGE = 16'hFC00 , //橙色
YELLOW = 16'hFFE0 , //黄色
GREEN = 16'h07E0 , //绿色
CYAN = 16'h07FF , //青色
BLUE = 16'h001F , //蓝色
PURPPLE = 16'hF81F , //紫色
BLACK = 16'h0000 , //黑色
WHITE = 16'hFFFF , //白色
GRAY = 16'hD69A ; //灰色
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//根据坐标点输出不同的RGB颜色
always@(posedge vga_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
pixel_data <= BLACK;
else if(pixel_x >= 0 && pixel_x < (H_VALID/3)*1) //从0到1/3画面
pixel_data <= BLUE; //输出蓝色
//从1/3到2/3画面
else if(pixel_x >= (H_VALID/3)*1 && pixel_x < (H_VALID/3)*2)
pixel_data <= WHITE; //输出白色
else //剩余1*/3画面
pixel_data <= RED; //输出红色
end
endmodule
顶层vag_bar模块:生成25M时钟,并分别例化ga_driver模块,vga_driver模块。
//==================================================================
//--VGA彩条显示实例
//==================================================================
//------------<模块及端口声明>----------------------------------------
module vag_bar(
input sys_clk , //系统时钟50M
input sys_rst_n , //系统复位
output h_sync , //行同步信号
output v_sync , //场同步信号
output [15:0] rgb_data //VGA输出的RGB565信息
);
//---------<reg定义>----------------------------------------------------
reg clk_25M ; //VGA时钟
//---------<wire定义>----------------------------------------------------
wire [15:0] pixel_data ; //VGA 像素点色彩信息
wire [10:0] pixel_x ; //VGA 有效显示区域 X 轴坐标
wire [9:0] pixel_y ; //VGA 有效显示区域 Y 轴坐标
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//25M时钟生成模块,用作该时序下VGA的时钟
//这里最好使用PLL生成25M时钟,我纯属懒
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
clk_25M <= 1'b0;
else
clk_25M <= ~clk_25M;
end
//例化彩条生成模块
vag_fra u_vag_fra
(
.vga_clk (clk_25M) ,
.sys_rst_n (sys_rst_n) ,
.pixel_x (pixel_x) ,
.pixel_y (pixel_y) ,
.pixel_data (pixel_data)
);
//例化VGA驱动模块
vag_driver u_vag_driver
(
.vga_clk (clk_25M) ,
.sys_rst_n (sys_rst_n) ,
.pixel_data (pixel_data),
.h_sync (h_sync) ,
.v_sync (v_sync) ,
.pixel_x (pixel_x) ,
.pixel_y (pixel_y) ,
.rgb_data (rgb_data)
);
endmodule
下面是代码下载到我的开发板中显示器显示的彩条图案(法国Flag,有一说一还挺好看的):
3、乳法
可能看到这里,有的朋友就问了:这哪里乳法了。
不急,接下来修改vag_fra模块的代码,如下:
//==================================================================
//--彩条生成模块
//==================================================================
module vag_fra(
input vga_clk , //VGA时钟
input sys_rst_n , //复位信号、低电平有效
input [10:0] pixel_x , //行坐标
input [9:0] pixel_y , //场坐标
output reg [15:0] pixel_data //输入RGB数据
);
//---------行场参数定义----------------------------------
localparam H_VALID = 10'd640 , //行有效数据
V_VALID = 10'd480 ; //场有效数据
//---------颜色的参数定义--------------------------------
localparam WHITE = 16'hFFFF , //白色
BLACK = 16'h0000 ; //黑色
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//根据坐标点输出不同的RGB颜色
always@(posedge vga_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
pixel_data <= BLACK; //复位黑色
else
pixel_data <= WHITE; //全部输出白色
end
endmodule
重新编译,下载到开发板中,显示器现在显示的就是纯正的法国Flag了: