基于FPGA----VGA显示跳动的小白框设计

前言:

之前已经学习过VGA显示部分了,这里在重新对VGA模块进行总结,同时使用参数,数据是手册,严格依据时序进行代码书写,对前面仅仅追求功能的实现进行小小总结。(所有欠下的,迟早都要还......)。本文参看Kevin的VIP教程

实现原理:

这里的VGA驱动原理很简单,就是控制行场信号的拉高与拉低。同时在显示区域显示要显示的数据即可。

VGA系列之一:VGA显示器驱动篇图片原理这里面都讲的很清楚。可以参考实现代码的书写。

基于FPGA----VGA显示跳动的小白框设计_第1张图片

小白框的移动这里采用控制最左上角位置来控制起始点,同时移动的速度采用数帧图片显示完成后在进行移动这种方式,这里的帧数可以自己定义,定义的越短,方框移动的速度越快。

基于FPGA----VGA显示跳动的小白框设计_第2张图片

这里简单的画一个实现的效果图,具体在屏幕显示的时候很炫酷。可以自己观察。

移动的方向问题

可以通过一个信号来确定移动的方向。具体看代码的实现。

代码实现:

这里需要一直注意的一个问题就是数数字是从0开始数的,0-9一共十个数!!!!!!

因为这个问题非常常见,但是同时也是特别容易弄混的一个问题。为了后面的数据读出,这里加入了读使能。

因为读数据使能必须延时一个时钟周期出来,所以采用在白框显示提前一个时钟周期,进行读取数据。

module VGA_driver(

input 				vga_clk 		,
input 				vga_rst_n		,

output 				vga_hs			,
output 				vga_vs			,
output 	reg	[15:0]  vga_rgb			,
//读数据使能
output 	reg 		ram_rd_en 		,
input 		[7:0] 	ram_data 		
    );
//---------------------------------移动---------------------------------
reg 	[8:0]		block_x 			;
reg 	 			x_move_direction 	;

reg 	[8:0]		block_y 			;
reg 	 			y_move_direction 	;

reg  				move_flag 			;	
reg 	[1:0] 		move_cnt 			;
reg 				pic_end_flag 		;
//---------------------------------常规---------------------------------
reg 	[9:0] 		cnt_h		;
reg 	[9:0]		cnt_v		;
parameter Hor_Total_Time 		= 800	;		//行显示帧长

parameter Hor_Sync		 		= 96	;		//行同步脉冲
parameter Hor_Back_Porch 		= 48	;		//行显示后沿
//这里为了方便控制,我将前面的两个时间段合成为一个时间段方便控制,后面同样类似
parameter Hor_Addr_Time 		= 640	;		//行显示区域
parameter Hor_Front_Porch		= 16	;		//行显示前沿

parameter Ver_Total_Time 		= 525	;		//列显示帧长

parameter Ver_Sync		 		= 2		;		//列同步脉冲
parameter Ver_Back_Porch 		= 33	;		//列显示后沿
parameter Ver_Addr_Time 		= 480	;		//列显示区域
parameter Ver_Front_Porch		= 10	;		//列显示前沿+列显示前门列
parameter PIC_SIZES  			= 198 	;		//图片尺寸大小

//---------------------------------
always@(posedge vga_clk or negedge vga_rst_n)
begin
	if(!vga_rst_n)
		cnt_h <= 'd0;
	else 
		if(cnt_h == ( Hor_Total_Time - 1'b1) )
			cnt_h <= 'd0;
		else 
			cnt_h <= cnt_h + 1'b1;
end
always@(posedge vga_clk or negedge vga_rst_n)
begin
	if(!vga_rst_n)
		cnt_v <= 'd0;
	else 
		if(cnt_v == (Ver_Total_Time - 1'b1) && (cnt_h == Hor_Total_Time - 1'b1) )
			cnt_v <= 'd0;
		else 
			if(cnt_h == (Hor_Total_Time - 1'b1))
				cnt_v <= cnt_v + 1'b1;
end 

assign vga_hs 	=	(cnt_h < Hor_Sync)	?	1'b1	:	1'b0; //从0开始计数,当到达96的时候,拉低
assign vga_vs 	=	(cnt_v < Ver_Sync)	?	1'b1	:	1'b0;

//读数据使能,需要提前一个时钟周期

always@(posedge vga_clk or negedge vga_rst_n)
begin
	if(!vga_rst_n)
		ram_rd_en <= 1'b0;
	else
		if(				cnt_v >= 	(Ver_Sync + Ver_Back_Porch + block_y  - 1'b1) && 
						cnt_v < 	(Ver_Sync + Ver_Back_Porch + block_y  + PIC_SIZES  - 1'b1) &&
						cnt_h >= 	(Hor_Sync + Hor_Back_Porch + block_x  - 'd2) && 
						cnt_h < 	(Hor_Sync + Hor_Back_Porch + block_x  + PIC_SIZES - 'd2) )
			ram_rd_en <= 1'b1;
		else 
			ram_rd_en <= 1'b0;
end
reg vga_en;
always@(posedge vga_clk)
begin
	vga_en <= (cnt_h >= (Hor_Sync + Hor_Back_Porch) && cnt_h < (Hor_Sync + Hor_Back_Porch + Hor_Addr_Time) && cnt_v >= (Ver_Sync + Ver_Back_Porch) && cnt_v < (Ver_Sync + Ver_Back_Porch + Ver_Addr_Time));
end
always@(*)
begin
	if(vga_en)
		begin 
				if(	cnt_v >= 	(Ver_Sync + Ver_Back_Porch + block_y  - 1'b1) && 
					cnt_v < 	(Ver_Sync + Ver_Back_Porch + block_y  + PIC_SIZES  - 1'b1) &&
					cnt_h >= 	(Hor_Sync + Hor_Back_Porch + block_x - 1'b1) && 
					cnt_h < 	(Hor_Sync + Hor_Back_Porch + block_x + PIC_SIZES - 1'b1) )
						vga_rgb <= {ram_data[7:5],2'h0,ram_data[4:2],3'h0,ram_data[1:0],2'h0};
				else 
					if(cnt_v >= (Ver_Sync + Ver_Back_Porch  - 1'b1) && 
						cnt_v < (Ver_Sync + Ver_Back_Porch  + 159 - 1'b1) )
							vga_rgb <= {5'h1f,11'h0};
					else 
						if(cnt_v >= (Ver_Sync + Ver_Back_Porch  + 159 - 1'b1) && 
							cnt_v < (Ver_Sync + Ver_Back_Porch  + 319 - 1'b1) )
 							vga_rgb <= {5'h0,6'h3f,5'h0};
 						else 
 							if(cnt_v >= (Ver_Sync + Ver_Back_Porch  + 319 - 1'b1) && 
								cnt_v < (Ver_Sync + Ver_Back_Porch  + 480 - 1'b1) )
 								vga_rgb <= {5'h0,6'h0,5'h1f};
 							else 
 								vga_rgb <= 16'd0;
 			end 
	else 
		vga_rgb <= 16'd0;
end
//控制起始位置的点的移动
always@(posedge vga_clk or negedge vga_rst_n)
begin
	if(!vga_rst_n)
		block_x <= 'd0;
	else 
		if(x_move_direction && move_flag)
			block_x <= block_x + 1'b1;
		else 
			if(!x_move_direction && move_flag)
				block_x <= block_x - 1'b1;
			else 
				block_x <= block_x;
end 
always@(posedge vga_clk or negedge vga_rst_n)
begin
	if(!vga_rst_n)
		block_y <= 'd0;
	else 
		if(y_move_direction && move_flag)
			block_y <= block_y + 1'b1;
		else 
			if(!y_move_direction && move_flag)
				block_y <= block_y - 1'b1;
			else 
				block_y <= block_y;
end 
//控制两者移动的方向
always@(posedge vga_clk or negedge vga_rst_n)
begin
	if(!vga_rst_n)
		x_move_direction <= 1'b1;
	else 
		if(block_x == 'd440)
			x_move_direction <= 1'b0;
		else 
			if(block_x == 'd0)
				x_move_direction <= 1'b1;
			else 
				x_move_direction <= x_move_direction;
end 
always@(posedge vga_clk or negedge vga_rst_n)
begin
	if(!vga_rst_n)
		y_move_direction <= 1'b1;
	else 
		if(block_y == 'd280)
			y_move_direction <= 1'b0;
		else 
			if(block_y == 'd0)
				y_move_direction <= 1'b1;
			else 
				y_move_direction <= y_move_direction;
end 
//控制信号move_flag拉高条件
always@(posedge vga_clk or negedge vga_rst_n)
begin
	if(!vga_rst_n)
		move_flag <= 'd0;
	else 
		if(move_cnt == 'd2 && pic_end_flag)
			move_flag <= 'd1;
		else
			move_flag <= 'd0;
end 

always@(posedge vga_clk or negedge vga_rst_n)
begin
	if(!vga_rst_n)
		move_cnt <= 'd0;
	else 
		if(move_cnt == 'd3)
			move_cnt <= 'd0;
		else 
			if(pic_end_flag)
				move_cnt <= move_cnt + 1'b1;
			else 
				move_cnt <= move_cnt;
end 
//一帧图像显示结束标志信号
always@(posedge vga_clk or negedge vga_rst_n)
begin
	if(!vga_rst_n)
		pic_end_flag <= 'd0;
	else 
		if(cnt_v == (Ver_Total_Time - 1'b1) && cnt_h == (Hor_Total_Time - 1'b1) )
			pic_end_flag <= 1'b1;
		else 	
			pic_end_flag <= 1'b0;
end 
endmodule

总结与思考

这里没有和我之前写的方块移动采用一样的方式。同时这里的这个方式更好。因为之前的方块移动很简单就是计数器,同时,改变初始点的位置,这里使用数帧图像传输结束后在进行移动位置,不会出现数据位置错位的情况。同时组合与时序逻辑使用是必须有一定的思考,这里设计流水线,及组合时序代码的时延,电路时序的稳定等等问题,后文CPU的代码书写中补坑。

你可能感兴趣的:(#,图像处理(fpga))