FPGA基础之9----如何用VGA接口乳法?

1、什么是VGA接口

1.1、VGA接口介绍

VGA,英文全称“Video Graphics Array”,译为视频图形阵列,是一种使用模拟信号进行视频传输的标准协议,由 IBM 公司于 1987 年推出,因其分辨率高、显示速度快、颜色丰富等优点,广泛应用于彩色显示器领域。

VGA接口样式如图:

FPGA基础之9----如何用VGA接口乳法?

VGA 接口管脚如下图:

FPGA基础之9----如何用VGA接口乳法?

 各管脚定义如下:

FPGA基础之9----如何用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 显示器显示图像,并不是直接让图像在显示器上显示出来,而是采用扫描的方 式,将构成图像的像素点,在行同步信号和场同步信号的同步下,按照从上到下、由左到 右的顺序扫描到显示屏上,如下所示:

FPGA基础之9----如何用VGA接口乳法?

 

 1.2、VGA接口时序

在VGA视频传输标准中,视频图像被分解为红、绿、蓝三原色信号,经过数模转换之后, 在行同步(HSYNC)和场同步(VSYNC)信号的同步下分别在三个独立通道传输。VGA在传输程中的同步时序分为行时序和场时序,以下分别是行同步、场同步的时序图:

FPGA基础之9----如何用VGA接口乳法?

  • HSync的周期为行扫描周期。
  • 一个完整的行扫描周期,包含 6 部分:Sync(同步)、Back Porch(后沿)、Left Border(左边框)、“Addressable” Video(有效图像)、Right Border(右边框)、Front Porch(前沿),这 6 部分的基本单位是 pixel(像素),即一个像素时钟周期
  • 在一个完整的行扫描周期中,Video 图像信息在 HSync 行同步信号的同步下完成一行图像的扫描显示,Video 图像信息只有在“Addressable” Video(有效图像)阶段,图像信息 有效,其他阶段图像信息无效 
  • HSync 行同步信号在 Sync(同步)阶段,维持低电平,其他阶段均保持高电平

FPGA基础之9----如何用VGA接口乳法? 

  • 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 分辨率时序参数如下:

FPGA基础之9----如何用VGA接口乳法?

2、乳法实例

接下来就用FPGA生成彩条(法国FLAG)通过VGA接口显示出来,分辨率为640*480@60,时钟25M。 

设计的模块如下:

FPGA基础之9----如何用VGA接口乳法?

顶层模块用于例化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,有一说一还挺好看的):

FPGA基础之9----如何用VGA接口乳法?

 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了:

FPGA基础之9----如何用VGA接口乳法?

 

 

 

 

上一篇:VTK-图像创建


下一篇:MyDLNote-Transformer: 局部和全局的 Transformer - Transformer in Transformer