VGA显示器驱动设计与验证


由EGo1开发板进行验证

VGA简介

VGA,Video Graphics Array,即视频图形阵列,是一种使用模拟信号进行视频传输的标准协议

VGA接口及引脚定义

在这里插入图片描述

在这里插入图片描述

引脚定义引脚定义
1红基色(RED)9保留
2绿基色(GREEN)10数字地(GND)
3蓝基色(BLUE)11地址码0(ID BIT0)
4地址码2(ID BIT2)12地址码1(ID BIT1)
5自测试13行同步(HYNC)
6红色地(RGND)14场同步(VSYNC)
7绿色地(GGND))15地址码3(ID BIT3)
8蓝色地(BGND)

VGA显示原理

VGA显示器采用图像扫描的方式进行图像显示,将构成图像的像素点,在行同步信号和场同步信号的同步下,按照从上到下、从左到右的顺序扫描到显示屏上
在这里插入图片描述

VGA时序标准

一个行扫描周期分为6个部分,基本单位是一个像素点,每一个周期传递一个像素信息
在这里插入图片描述
一个场扫描的基本单位是一行,一行表示一个完成的行扫描周期
在这里插入图片描述
只要当Hsync和Vsync都处于图像有效阶段的时候,图像信息才有效,其他阶段信息无效,进行图像的同步和消隐
在这里插入图片描述

VGA显示模式及相关参数

在这里插入图片描述
640 * 480 @60
640:在一个完整的行扫描周期中,有效显示图像每一行有640个像素点
480:一帧完整的图像有480行
640*480 ≈ 300000个像素点,每个时钟进行一次像素点的扫描
60:帧率,每秒刷新图像60次,每秒显示60帧

行扫描周期 × 场扫描周期 × 帧率 = 每秒扫描像素点个数 = 时钟频率
ex. 840 × 500 × 75 = 31.5MHz
在这里插入图片描述

模块设计

在这里插入图片描述
实现640 * 480 @60规格
最终显示的结果为10个不同颜色的等宽彩条

  • clk_gen
    调用PPL锁相环,25MHz时钟生成模块
  • vga_pic
    根据传入的有效图像坐标信息,产生要显示的图像数据
  • vga_ctrl
    vga驱动模块,在25MHz工作时钟下,产生横纵坐标信号(pix_x, pix_y),(0, 0)~(639, 479),传送给vga_pic模块,并且生成行场同步信号hsync和sync,再将vga_pic模块传过来的像素点信息pix_data输出到rgb端口

vga_ctrl模块以及测试

module vga_ctrl (
	input	wire			vga_clk,
	input	wire			sys_rst_n,
	input	wire	[15: 0]	pix_data,
	
	output	wire	[ 9: 0]	pix_x,
	output	wire	[ 9: 0]	pix_y,
	output	wire			hsync,
	output	wire			vsync,
	output	wire	[15: 0]	rgb
);
	
	parameter	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	;
				
	parameter	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		[ 9 :0]	cnt_h;
	reg		[ 9: 0]	cnt_v;
	wire			rgb_valid;		// 图像有效信号  显示640*480
	
	wire			pix_data_req;	// 数据请求信号
									// 需要超前数据有效信号一个时钟周期,解决时序逻辑的滞后问题
	
	// 行计数器
	always @ (posedge vga_clk  or negedge sys_rst_n)
		if (sys_rst_n == 1'b0)
			cnt_h	<=	10'd0;
		else	if (cnt_h == H_TOTAL - 1'b1)
			cnt_h	<=	10'd0;
		else
			cnt_h	<=	cnt_h + 1'b1;
	
	// 场计数器
	always @ (posedge vga_clk  or negedge sys_rst_n)
		if (sys_rst_n == 1'b0)
			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'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;
	// 数据请求信号
	// 行计数器往前推一个,代表超前一个时钟周期
	// 场计数器往前退一个,代表超前一行,不可取
	assign	pix_data_req =  ((cnt_h >= H_SYNC + H_BACK + H_LEFT - 1'b1)
							&& (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID - 1'b1)
							&& (cnt_v >= V_SYNC + V_BACK + V_TOP)
							&& (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID) )
							? 1'b1 : 1'b0;
	
	// 行坐标信号
	assign	pix_x = (pix_data_req == 1'b1) ? (cnt_h - (H_SYNC + H_BACK + H_LEFT - 1'b1)) : 10'h3ff;
	
	// 纵坐标信号
	assign	pix_y = (pix_data_req == 1'b1) ? (cnt_v - (V_SYNC + V_BACK + V_TOP))  : 10'h3ff;

	// 行同步信号
	assign	hsync = (cnt_h <= H_SYNC - 1'b1) ? 1'b1 : 1'b0;
	
	// 场同步信号
	assign	vsync = (cnt_v <= V_SYNC - 1'b1) ? 1'b1 : 1'b0;

	// 
	assign	rgb	  = (rgb_valid == 1'b1) ? pix_data : 16'h0000;

endmodule
module tb_vga_ctrl ( );
	
	reg				sys_clk		;
	reg				sys_rst_n	;
	
	wire			vga_clk	;
	wire			locked	;
	wire			rst_n	;

	reg		[15: 0]	pix_data;	
	wire	[ 9: 0]	pix_x	;
	wire	[ 9: 0]	pix_y	;
	wire			hsync	;
	wire			vsync	;
	wire	[15: 0]	rgb		;
	
	initial	begin
		sys_clk 	=	1'b0;
		sys_rst_n	=	1'b0;
		#20
		sys_rst_n	=	1'b1;
	end
	
	always #5 sys_clk = ~sys_clk;	// 100MHz
	
	assign	rst_n = sys_rst_n && locked;

	clk_gen instance_name
	(
    // Clock out ports
    .clk_out1	(vga_clk	),    	// output clk_out1
    // Status and control signals
    .reset		(~sys_rst_n	), 		// input reset
    .locked		(locked		),      // output locked
   // Clock in ports
    .clk_in1	(sys_clk	));     // input clk_in1
    
    vga_ctrl	vga_ctrl_inst (
    	.vga_clk	(vga_clk	),		// 25MHz
    	.sys_rst_n	(rst_n		),
    	.pix_data	(pix_data	),
    	
    	.pix_x		(pix_x		),
    	.pix_y		(pix_y		),
    	.hsync		(hsync		),
    	.vsync		(vsync		),
    	.rgb		(rgb		)
    );
    
    // 模拟生成图像数据
    // 这里使用时序逻辑
    // 出来的波形rgb会比rgb_valid滞后一个时钟周期
    // 产生的图像数据就会滞后这个坐标信号一个周期
    // 为了补偿滞后的一个时钟周期,将请求信号提前有效信号一个周期
    always @ (posedge vga_clk or negedge sys_rst_n)
    	if (sys_rst_n == 1'b0)
    		pix_data	<=	16'h0000;
    	else	if (pix_x >= 10'd0 && pix_x <= 10'd639
    				&& pix_y >= 10'd0 && pix_y <= 10'd479)
    		pix_data	<=	16'hffff;
    	else
    		pix_data	<=	16'h0000;
    				

endmodule

在这里插入图片描述

说明:

  1. vsync信号的一个高脉冲代表一帧数据显示结束
  2. 关注一个行显示周期,因为有组合逻辑的原因,需要设置req请求信号,将其设置为早于valid有效信号一个时钟
  3. 仿真的时候差不多设置17ms,就刚好是一帧图像的波形

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

vga_pic模块及测试

module vga_pic (
	input	wire			vga_clk,
	input	wire			sys_rst_n,
	input	wire	[ 9: 0]	pix_x,
	input	wire	[ 9: 0]	pix_y,
	
	output	reg		[15: 0]	pix_data
);

	// 分辨率  也就是坐标信号的最大值
	parameter	H_VALID	=	10'd460,
				V_VALID	=	10'd480;
				
	// 颜色参数定义		
	parameter	RED		=	16'hF800,
				ORANGE	=	16'hFC00,
				YELLOW	=	16'hFFE0,
				GREEN	=	16'h07E0,
				CYAN	=	16'h07FF,
				BLUE	=	16'h001F,
				PUPPLE	=	16'hF81F,
				BLACK	=	16'h0000,
				WHITE	=	16'hFFFF,
				GRAY	=	16'hD69A;
	
	// 输出信号赋值
	always @ (posedge vga_clk or negedge sys_rst_n)
		if (sys_rst_n == 1'b0)
			pix_data	<=	BLACK;		// 默认显示黑色
		else	if (pix_x >= 0 && pix_x < (H_VALID / 10) * 1)
			pix_data	<=	RED;
		else	if (pix_x >= (H_VALID / 10) * 1 && pix_x < (H_VALID / 10) * 2)
			pix_data	<=	ORANGE;`在这里插入代码片`
		else	if (pix_x >= (H_VALID / 10) * 2 && pix_x < (H_VALID / 10) * 3)
			pix_data	<=	YELLOW;
		else	if (pix_x >= (H_VALID / 10) * 3 && pix_x < (H_VALID / 10) * 4)
			pix_data	<=	GREEN;
		else	if (pix_x >= (H_VALID / 10) * 4 && pix_x < (H_VALID / 10) * 5)
			pix_data	<=	CYAN;	
		else	if (pix_x >= (H_VALID / 10) * 5 && pix_x < (H_VALID / 10) * 6)
			pix_data	<=	BLUE;
		else	if (pix_x >= (H_VALID / 10) * 6 && pix_x < (H_VALID / 10) * 7)
			pix_data	<=	PUPPLE;
		else	if (pix_x >= (H_VALID / 10) * 7 && pix_x < (H_VALID / 10) * 8)
			pix_data	<=	BLACK;
		else	if (pix_x >= (H_VALID / 10) * 8 && pix_x < (H_VALID / 10) * 9)
			pix_data	<=	WHITE;
		else	if (pix_x >= (H_VALID / 10) * 9 && pix_x < (H_VALID / 10) * 10)
			pix_data	<=	GRAY;	
		else
			pix_data	<=	BLACK;
			
endmodule
module tb_vga_ctrl_pic ( );
	
	reg				sys_clk		;
	reg				sys_rst_n	;
	
	wire			vga_clk	;
	wire			locked	;
	wire			rst_n	;

	wire	[15: 0]	pix_data;	
	wire	[ 9: 0]	pix_x	;
	wire	[ 9: 0]	pix_y	;
	wire			hsync	;
	wire			vsync	;
	wire	[15: 0]	rgb		;
	
	initial	begin
		sys_clk 	=	1'b0;
		sys_rst_n	=	1'b0;
		#20
		sys_rst_n	=	1'b1;
	end
	
	always #5 sys_clk = ~sys_clk;	// 100MHz
	
	assign	rst_n = sys_rst_n && locked;

	clk_gen instance_name
	(
    // Clock out ports
    .clk_out1	(vga_clk	),    	// output clk_out1
    // Status and control signals
    .reset		(~sys_rst_n	), 		// input reset
    .locked		(locked		),      // output locked
   // Clock in ports
    .clk_in1	(sys_clk	));     // input clk_in1
    
    vga_ctrl	vga_ctrl_inst (
    	.vga_clk	(vga_clk	),		// 25MHz
    	.sys_rst_n	(rst_n		),
    	.pix_data	(pix_data	),
    	
    	.pix_x		(pix_x		),
    	.pix_y		(pix_y		),
    	.hsync		(hsync		),
    	.vsync		(vsync		),
    	.rgb		(rgb		)
    );
    
    vga_pic		vga_pic_inst (
    	.vga_clk	(vga_clk	),		// 25MHz
    	.sys_rst_n	(rst_n		),
    	.pix_x		(pix_x		),
    	.pix_y		(pix_y		),
    	
    	.pix_data	(pix_data	)
    );
    
    // 不需要模拟生成图像数据,vga_pic会根据vga_ctrl控制模块送过来的坐标输出图像数据

endmodule

这是一个完整的行显示周期,可以看到按照“红橙黄绿青蓝紫黑白灰”的颜色设定进行输出
在这里插入图片描述

顶层模块

生成的原理图和visio画的是一样的
在这里插入图片描述

module vga_colorbar (
	input	wire			sys_clk,
	input	wire			sys_rst_n,
	
	output	wire			hsync,
	output	wire			vsync,
	output	wire	[15: 0]	rgb
);

	wire			vga_clk	;
	wire			locked	;
	wire			rst_n	;
	
	wire	[ 9: 0]	pix_x;
	wire	[ 9: 0]	pix_y;
	wire	[15: 0]	pix_data;
	
	assign	rst_n	=	sys_rst_n && locked;
	
	clk_gen instance_name (
    // Clock out ports
    .clk_out1	(vga_clk	),    	// output clk_out1
    // Status and control signals
    .reset		(~sys_rst_n	), 		// input reset
    .locked		(locked		),      // output locked
   // Clock in ports
    .clk_in1	(sys_clk	)		// input clk_in1
    ); 
    
    vga_ctrl	vga_ctrl_inst (
    	.vga_clk	(vga_clk	),		// 25MHz
    	.sys_rst_n	(rst_n		),
    	.pix_data	(pix_data	),
    	
    	.pix_x		(pix_x		),
    	.pix_y		(pix_y		),
    	.hsync		(hsync		),
    	.vsync		(vsync		),
    	.rgb		(rgb		)
    );
    
    vga_pic		vga_pic_inst (
    	.vga_clk	(vga_clk	),		// 25MHz
    	.sys_rst_n	(rst_n		),
    	.pix_x		(pix_x		),
    	.pix_y		(pix_y		),
    	
    	.pix_data	(pix_data	)
    );

endmodule
module tb_vga_colorbar ( );

	reg				sys_clk		;
	reg				sys_rst_n	;
	
	wire			hsync	;
	wire			vsync	;
	wire	[15: 0]	rgb		;
	
	initial	begin
		sys_clk		<=	1'b0;
		sys_rst_n	<=	1'b0;
		#20
		sys_rst_n	<=	1'b1;
	end
	
	always #5	sys_clk = ~sys_clk;
	
	vga_colorbar	vga_colorbar_inst (
		.sys_clk	(sys_clk	),
		.sys_rst_n	(sys_rst_n	),
	
		.hsync		(hsync		),
		.vsync		(vsync		),
		.rgb		(rgb		)
	);
	
endmodule

总结

使用VGA接口显示彩色像素点,主要是遵循VESA VGA时序标准,上面实现的是640 * 480 @60,也就是有效显示图像每一行有640个像素点,这样的行一共有480行,除去这些有效的像素点,还有四周消隐的部分,前后沿和左右边框。

关于显示还是不显示可以用一个数据有效信号valid来做标志,ctrl控制模块输出(pix_x, pix_y)坐标信号到pic模块,pic根据坐标信息输送特定的像素点信息,也就是颜色参数,ctrl模块还有valid有效信号、req数据请求信号,pic模块传过来的像素信息在valid有效信号的控制下选择是否赋值给rgb端口,也就是在下面的图像显示区域valid拉高,ctrl可以用rgb接收pic传过来的pix_data像素数据

那个req请求信号根据前面代码注释的说明,设置为超前valid信号一个时钟周期,这里其实挺绕的,如果不仔细放大,是不会发现个别信号有滞后或者超前的
在这里插入图片描述

上面的接口引脚图是参考老师的视频写的,RGB565:用16个bit表示一个像素,5个bit表示R(红色),6个bit表示G(绿色),5个bit表示B(蓝色),从高位到低位排列如下:
R R R R R G G G G G G B B B B B
根据vga_pic模块的参数定义,本来是要显示下面这样的
在这里插入图片描述

而EGo1板子 上的 VGA 接口(J1)通过14 位信号线与 FPGA 连接,红、绿、蓝三个,颜色信号各占 4 bit,另外还包括行同步和场同步信号

所以想要在EGo1上下板还需要改动一点
在这里插入图片描述
如果不关注具体显示颜色的话,可以随便给12bit的像素信号一些值,比如下面这样,rgb_reg就是vga_ctrl送出来的15bit的565模式像素信息,然后随便截取12bit的444模式像素信息到rgb,这个rgb送给顶层模块用于管脚约束,然后下板

	wire	[15: 0]	rgb_reg;	// 15:11 10:5 4:0
	assign	rgb = {rgb_reg[ 3: 0], rgb_reg[ 8: 5], rgb_reg[ 3: 0]};

在这里插入图片描述

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
使用Verilog实现OV7725摄像头与VGA显示器需要注意以下几点: 首先,需要了解OV7725摄像头的工作原理,以及它所能提供的图像数据类型和格式。OV7725摄像头是一款数字信号处理器芯片,可以通过I2C总线与外部系统进行通信。OV7725的图像数据格式有多种,包括VGA、QVGA和QQVGA等。在Verilog中,需要对OV7725进行初始化配置,以确保其输出的图像数据符合VGA显示器的要求。 其次,需要明确VGA接口的时序规格,了解其各信号线的输入输出方式和电气特性。在Verilog中,可以通过设计控制模块和状态机来生成VGA信号。常见的VGA信号包括水平同步信号HSYNC、垂直同步信号VSYNC和图像数据信号DATA,每个信号都需要按照时序要求进行生成和输出,以实现正确的图像显示效果。 最后,需要对OV7725摄像头和VGA显示器进行接口设计,以确保它们可以互联并正确传输数据。需要对OV7725的输出数据信号进行适当调整和转换,以适应VGA的输入信号要求。例如,在OV7725的输出信号中,每个像素通常包括红、绿、蓝三个颜色通道,需要将其转换为VGA信号中每个像素只包含一个亮度值的格式。同时,在接口设计中,还需要考虑时序匹配、电气兼容性等因素,以确保数据能够正确、稳定地传输。 综上所述,使用Verilog实现OV7725摄像头与VGA显示器需要仔细考虑多个方面的问题,并进行细致设计和实现。只有在充分考虑和处理好这些问题后,才能得到高质量的图像传输和显示效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值