基于DE2平台的贪吃蛇游戏设计

4 篇文章 37 订阅
1 篇文章 0 订阅

一、设计内容及模块划分

在DE2开发平台上用按键、LCD及VGA制作贪吃蛇小游戏。

贪吃蛇游戏的设计可以分为两部分,第一部分是游戏的显示,另一部分是对游戏的控制

显示部分使用VGA驱动在屏幕上显示游戏画面,用LCD显示实时分数分数。因此需要VGA的驱动模块、VGA显示模块、LCD驱动模块、LCD显示模块。

游戏控制部分,首先游戏是由按键控制,所以需要一个按键模块。控制又分为游戏状态的控制,蛇身行进的控制,以及食物生成的控制。所以这一部分又分为这几个模块。

再按照每个模块所需要的功能进行硬件描述,最后在顶层文件实现例化调用。
在这里插入图片描述
在这里插入图片描述

二、各模块设计

2.1 按键模块

在这里插入图片描述
DE2品台上KEY0/1/2/3信号默认为高电平,当按键按下信号变为低电平,通过这个变化来产生一个控制信号。本设计不加按键消抖也不会对设计有影响,但为了严谨性,以及模块的可移植性,加入了按键消抖。

此设计中的消抖的思想属于软件消抖的思想,基于延时检测。通过将当前的按键状态存储在预先设置好的寄存器中。
通过Key_left <= Key_left2&&(!Left);只有当原先为高电平且15ms后为低电平才判定为按键按下。

这种方法其实十分地浪费资源,更多消抖方法可以查阅其它相关资料(如硬件架构的艺术)

在这里插入图片描述

module Key_check
(	
	input Clk_50mhz,
	input Rst_n,
	
	input Left,
	input Right,
	input Up,
	input Down,
	
	output reg Key_left,
	output reg Key_right,
	output reg Key_up,
	output reg Key_down
);

/******************************************************************/
	reg [31:0] Count1;
	
	reg Key_left2;
	reg Key_right2;
	reg Key_up2;
	reg Key_down2;

	always @ (posedge Clk_50mhz or negedge Rst_n)
	begin
		if(!Rst_n)
			begin		
				Count1 		<= 32'd0;
				
				Key_left 	<= 1'd0;
				Key_right 	<= 1'd0;
				Key_up 		<= 1'd0;
				Key_down 	<= 1'd0;
			
				Key_left2 	<= 1'd0;
				Key_right2 	<= 1'd0;
				Key_up2 	<= 1'd0;
				Key_down2 	<= 1'd0;						
			end
		else 		
		begin
			if(Count1 == 32'd750_000)		//15MS
			begin
				Count1      <= 32'd0;
				Key_left 	<= Key_left2&&(!Left);
				Key_right 	<= Key_right2&&(!Right);
				Key_up 		<= Key_up2&&(!Up);
				Key_down 	<= Key_down2&&(!Down);
				
				Key_left2 	<= Left;
				Key_right2 	<= Right;
				Key_up2 	<= Up;
				Key_down2 	<= Down;	
			end
			else
			begin
				Count1 		<= Count1 + 32'd1;
				Key_left 	<= 1'd0;
				Key_right 	<= 1'd0;
				Key_up 		<= 1'd0;
				Key_down 	<= 1'd0;
			end
		end	
	end	
endmodule

在这里插入图片描述

2.2 游戏控制模块

游戏控制指对游戏状态的控制,我们将游戏分为3个状态,分别是游戏开始的准备状态相当于空闲状态,游戏进行状态,以及游戏结束状态。通过一个二段式状态机来描述状态的跳转和每个状态所处理的事情。详细看代码注解部分:
在这里插入图片描述

module Game_ctrl			//game control
(
	input Clk_50,					//50MHz时钟
	input rst_n,						//复位信号低电平有效

	input Key_left,					//左按键按下
	input Key_right,					//右按键按下
	input Key_up,					//上按键按下
	input Key_down,					//下按键按下
	
	input Hit_wall,					//撞到墙的信号
	input Hit_body,					//撞到身体的信号

	output reg  [2:0] game_status,	//输出游戏状态
	output status_flg					//状态转换时产生脉冲信号(计分模块用)

);
	
	parameter 	START 	    = 3'b000;
	parameter 	PLAY		= 3'b001;
	parameter   DIE 		= 3'b010;

	reg [2:0] current_state,next_state;	

//状态转换时产生脉冲信号
assign status_flg=(game_status !=  next_state)? 1'b1:1'b0;		

//状态输出
always @(posedge Clk_50 or negedge rst_n)
		begin
   		 if(!rst_n)
       		game_status <= START;
   		 else
      			game_status <= current_state;
		end
/****************************************/
//FSM1-状态机跳转
	always @(posedge Clk_50 or negedge rst_n)
	begin
   	 if(!rst_n)
       		 current_state <= START;
   	 else	
      		 current_state <= next_state;
	end
/****************************************/
//FSM2
always @(*)
begin
    case(current_state)	//START	状态:有按键按下进入PLAY游戏状态
			START: 
				begin
					if(Key_left | Key_right | Key_up | Key_down) 
							next_state = PLAY;
                	else 	next_state = START;
				end
             		 //PLAY状态:出现死亡信号进入游戏结束状态(DIE)
			PLAY   : 
				begin
					if(Hit_wall| Hit_body) 
						next_state = DIE;				
                else 	next_state = PLAY;
				end
              	//游戏结束状态,有按键按下恢复到游戏准备状态
			DIE   : begin
					if(Key_left | Key_right | Key_up | Key_down) 
							next_state = START;
             	 	else 	next_state = DIE;
					end              
			default: next_state = START;
    endcase
end	
endmodul

2.3 蛇身行进控制模块

在这里插入图片描述

module snake_ctrl
(
	input Clk_50mhz,
	input Rst_n,
	input	[31:0]speed,
	
	input Key_left,
	input Key_right,
	input Key_up,
	input Key_down,
	
	output reg [1:0] Object,	// 00-NONE- 01-HEAD- 10-BODY- 11-WALL-
									
	input [10:0] Pixel_x,
	input [10:0] Pixel_y,
	
	output [5:0] Head_x,	
	output [5:0] Head_y,
	
	input Body_add_sig,
	
	input [2:0] Game_status,	//START-000   -PLAY-001  -END010
	
	output reg Hit_body_sig,
	output reg Hit_wall_sig
	
);
/***************************************************************************/
	
	localparam Up = 2'b00;
	localparam Down = 2'b01;
	localparam Left = 2'b10;
	localparam Right = 2'b11;
	
	
	localparam NONE = 2'b00;
	localparam HEAD = 2'b01;
	localparam BODY = 2'b10;
	localparam WALL = 2'b11;
	
	
	parameter   END = 3'b010;
	parameter   START = 3'b000;
	parameter   PLAY = 3'b001;
	parameter   x_num=6'd39;
	parameter   y_num=6'd29;	

/***************************************************************************/
	
	reg [6:0] Body_num;
	
	reg [31:0] Count;
	
	wire [1:0] Direct;
	reg [1:0] Direct_r;
	reg [1:0] Direct_next;
	
	assign Direct = Direct_r;
	
	
	reg Direct_left;
	reg Direct_right;
	reg Direct_up;
	reg Direct_down;
	
	reg [5:0] Body_x [15:0];
	reg [5:0] Body_y [15:0];
	reg [15:0] Snake_light_sig;
	
	assign Head_x = Body_x[0];
	assign Head_y = Body_y[0];
	
/***************************************************************************/
	
	always @ (posedge Clk_50mhz or negedge Rst_n)
	begin
		if(!Rst_n)
			Direct_r <= Right;//???????????
		else
			Direct_r <= Direct_next;
	end

/***************************************************************************/
	
	always @ (posedge Clk_50mhz or negedge Rst_n)
	begin
		if(!Rst_n)
		begin
			Count <= 32'd0;
		
			Body_x[0] <= 6'd20;
			Body_y[0] <= 6'd12;
			
			Body_x[1] <= 6'd19;
			Body_y[1] <= 6'd12;
			
			Body_x[2] <= 6'd18;
			Body_y[2] <= 6'd12;
			
			
			Body_x[3] <= 6'd0;
			Body_y[3] <= 6'd0;
			
			Body_x[4] <= 6'd0;
			Body_y[4] <= 6'd0;
			
			Body_x[5] <= 6'd0;
			Body_y[5] <= 6'd0;
			
			Body_x[6] <= 6'd0;
			Body_y[6] <= 6'd0;
			
			Body_x[7] <= 6'd0;
			Body_y[7] <= 6'd0;
			
			Body_x[8] <= 6'd0;
			Body_y[8] <= 6'd0;
			
			Body_x[9] <= 6'd0;
			Body_y[9] <= 6'd0;
			
			Body_x[10] <= 6'd0;
			Body_y[10] <= 6'd0;
			
			Body_x[11] <= 6'd0;
			Body_y[11] <= 6'd0;
			
			Body_x[12] <= 6'd0;
			Body_y[12] <= 6'd0;
			
			Body_x[13] <= 6'd0;
			Body_y[13] <= 6'd0;
			
			Body_x[14] <= 6'd0;
			Body_y[14] <= 6'd0;
			
			Body_x[15] <= 6'd0;
			Body_y[15] <= 6'd0;
			
			Hit_wall_sig <= 1'd0;
			Hit_body_sig <= 1'd0;
		end
			else if((Game_status == END))
			
					begin
						Body_x[0] <= Body_x[0];
						Body_y[0] <= Body_y[0] ;
						
						Body_x[1] <= Body_x[1] ;
						Body_y[1] <= Body_y[1];
						
						Body_x[2] <= Body_x[2] ;
						Body_y[2] <= Body_y[2];
						
						
						Body_x[3] <= Body_x[3] ;
						Body_y[3] <= Body_y[3];
						
						Body_x[4] <= Body_x[4] ;
						Body_y[4] <= Body_y[4] ;
						
						Body_x[5] <= Body_x[5] ;
						Body_y[5] <= Body_y[5];
						
						Body_x[6] <= Body_x[6] ;
						Body_y[6] <= Body_y[6];
						
						Body_x[7] <= Body_x[7];
						Body_y[7] <= Body_y[7] ;
						
						Body_x[8] <= Body_x[8];
						Body_y[8] <= Body_y[8];
						
						Body_x[9] <= Body_x[9] ;
						Body_y[9] <= Body_y[9];
						
						Body_x[10] <= Body_x[10];
						Body_y[10] <= Body_y[10];
						
						Body_x[11] <= Body_x[11] ;
						Body_y[11] <= Body_y[11];
						
						Body_x[12] <= Body_x[12];
						Body_y[12] <= Body_y[12] ;
						
						Body_x[13] <= Body_x[13];
						Body_y[13] <= Body_y[13];
						
						Body_x[14] <= Body_x[14];
						Body_y[14] <= Body_y[14];
						
						Body_x[15] <= Body_x[15];
						Body_y[15] <= Body_y[15];
						
						//Hit_wall_sig <= 1'd0;
						//Hit_body_sig <= 1'd0;

					end
		/************************88RST***************************/
		else	if(Count == speed) //0.02us*12'500'000 = 0.25s???????
				begin
					Count <= 32'd0;
		//************************************************????			
					if((Game_status == START))
				//if(Game_status == PLAY)
					begin
						Body_x[0] <= 6'd20;
						Body_y[0] <= 6'd12;
						
						Body_x[1] <= 6'd19;
						Body_y[1] <= 6'd12;
						
						Body_x[2] <= 6'd18;
						Body_y[2] <= 6'd12;
						
						
						Body_x[3] <= 6'd0;
						Body_y[3] <= 6'd0;
						
						Body_x[4] <= 6'd0;
						Body_y[4] <= 6'd0;
						
						Body_x[5] <= 6'd0;
						Body_y[5] <= 6'd0;
						
						Body_x[6] <= 6'd0;
						Body_y[6] <= 6'd0;
						
						Body_x[7] <= 6'd0;
						Body_y[7] <= 6'd0;
						
						Body_x[8] <= 6'd0;
						Body_y[8] <= 6'd0;
						
						Body_x[9] <= 6'd0;
						Body_y[9] <= 6'd0;
						
						Body_x[10] <= 6'd0;
						Body_y[10] <= 6'd0;
						
						Body_x[11] <= 6'd0;
						Body_y[11] <= 6'd0;
						
						Body_x[12] <= 6'd0;
						Body_y[12] <= 6'd0;
						
						Body_x[13] <= 6'd0;
						Body_y[13] <= 6'd0;
						
						Body_x[14] <= 6'd0;
						Body_y[14] <= 6'd0;
						
						Body_x[15] <= 6'd0;
						Body_y[15] <= 6'd0;
						
						Hit_wall_sig <= 1'd0;
						Hit_body_sig <= 1'd0;

					end
				
					/**************START-DIE***************************/
					else
					begin
						if(((Direct == Up) && (Body_y[0] == 6'd1)) | ((Direct == Down) && (Body_y[0] == (y_num-1))) | ((Direct == Left) && (Body_x[0] == 6'd1)) | ((Direct == Right) && (Body_x[0] == (x_num-1))))
									Hit_wall_sig <= 1'd1;
						else if(((Body_y[0] == Body_y[1]) && (Body_x[0] == Body_x[1]) && (Snake_light_sig[1] == 1'd1)) | 
								((Body_y[0] == Body_y[2]) && (Body_x[0] == Body_x[2]) && (Snake_light_sig[2] == 1'd1)) | 
								((Body_y[0] == Body_y[3]) && (Body_x[0] == Body_x[3]) && (Snake_light_sig[3] == 1'd1)) | 
								((Body_y[0] == Body_y[4]) && (Body_x[0] == Body_x[4]) && (Snake_light_sig[4] == 1'd1)) | 
								((Body_y[0] == Body_y[5]) && (Body_x[0] == Body_x[5]) && (Snake_light_sig[5] == 1'd1)) | 
								((Body_y[0] == Body_y[6]) && (Body_x[0] == Body_x[6]) && (Snake_light_sig[6] == 1'd1)) | 
								((Body_y[0] == Body_y[7]) && (Body_x[0] == Body_x[7]) && (Snake_light_sig[7] == 1'd1)) | 
								((Body_y[0] == Body_y[8]) && (Body_x[0] == Body_x[8]) && (Snake_light_sig[8] == 1'd1)) | 
								((Body_y[0] == Body_y[9]) && (Body_x[0] == Body_x[9]) && (Snake_light_sig[9] == 1'd1)) | 
								((Body_y[0] == Body_y[10]) && (Body_x[0] == Body_x[10]) && (Snake_light_sig[10] == 1'd1)) | 
								((Body_y[0] == Body_y[11]) && (Body_x[0] == Body_x[11]) && (Snake_light_sig[11] == 1'd1)) | 
								((Body_y[0] == Body_y[12]) && (Body_x[0] == Body_x[12]) && (Snake_light_sig[12] == 1'd1)) | 
								((Body_y[0] == Body_y[13]) && (Body_x[0] == Body_x[13]) && (Snake_light_sig[13] == 1'd1)) | 
								((Body_y[0] == Body_y[14]) && (Body_x[0] == Body_x[14]) && (Snake_light_sig[14] == 1'd1)) | 
								((Body_y[0] == Body_y[15]) && (Body_x[0] == Body_x[15]) && (Snake_light_sig[15] == 1'd1)))
								
								Hit_body_sig <= 1'd1;
						else
						begin
							Body_x[1] <= Body_x[0];
							Body_y[1] <= Body_y[0];
							
							Body_x[2] <= Body_x[1];
							Body_y[2] <= Body_y[1];
							
							Body_x[3] <= Body_x[2];
							Body_y[3] <= Body_y[2];
							
							Body_x[4] <= Body_x[3];
							Body_y[4] <= Body_y[3];
							
							Body_x[5] <= Body_x[4];
							Body_y[5] <= Body_y[4];
							
							Body_x[6] <= Body_x[5];
							Body_y[6] <= Body_y[5];
							
							Body_x[7] <= Body_x[6];
							Body_y[7] <= Body_y[6];
							
							Body_x[8] <= Body_x[7];
							Body_y[8] <= Body_y[7];
							
							Body_x[9] <= Body_x[8];
							Body_y[9] <= Body_y[8];
							
							Body_x[10] <= Body_x[9];
							Body_y[10] <= Body_y[9];
							
							Body_x[11] <= Body_x[10];
							Body_y[11] <= Body_y[10];
							
							Body_x[12] <= Body_x[11];
							Body_y[12] <= Body_y[11];
							
							Body_x[13] <= Body_x[12];
							Body_y[13] <= Body_y[12];
							
							Body_x[14] <= Body_x[13];
							Body_y[14] <= Body_y[13];
							
							Body_x[15] <= Body_x[14];
							Body_y[15] <= Body_y[14];
						
							case(Direct)					
								Up:
									begin
									if(Body_y[0] == 6'd1)
											Hit_wall_sig <= 1'd1;
										else
											Body_y[0] <= Body_y[0] - 6'd1;
									end
								
								Down:
									begin
										if(Body_y[0] == (y_num-1))
											Hit_wall_sig <= 1'd1;
										else
											Body_y[0] <= Body_y[0] + 6'd1;
									end
								Left:
									begin
										if(Body_x[0] == 6'd1)
											Hit_wall_sig <= 1'd1;
										else
											Body_x[0] <= Body_x[0] - 6'd1;
									end
								
								Right:
									begin
										if(Body_x[0] == (x_num-1))
											Hit_wall_sig <= 1'd1;
										else
											Body_x[0] <= Body_x[0] + 6'd1;
									end
							endcase																	
						end					
					end
			end 
		else
			Count <= Count + 32'd1;
	end
/***************************************************************************/
	
	always @ (posedge Clk_50mhz)
	begin
		if(Key_left == 1'd1)
				Direct_left <= 1'd1;
		else if(Key_right == 1'd1)
				Direct_right <= 1'd1;
		else if(Key_up == 1'd1)
				Direct_up <= 1'd1;
		else if(Key_down == 1'd1)
				Direct_down <= 1'd1;			
		else 
		begin
			Direct_left <= 1'd0;
			Direct_right <= 1'd0;
			Direct_up <= 1'd0;
			Direct_down <= 1'd0;
		end
	end
	/***************************************************************************/
	
	always @ (*)
	begin   
		Direct_next = Right;
		
		case(Direct)
			Up:
				begin
					if(Direct_left)
						Direct_next = Left;
					else if(Direct_right)
						Direct_next = Right;
					else
						Direct_next = Up;
				end
			
			Down:
				begin
					if(Direct_left)
						Direct_next = Left;
					else if(Direct_right)
						Direct_next = Right;
					else
						Direct_next = Down;
				end		
			Left:
				begin
					if(Direct_up)
						Direct_next = Up;
					else if(Direct_down)
						Direct_next = Down;
					else
						Direct_next = Left;
				end
			
			Right:
				begin
					if(Direct_up)
						Direct_next = Up;
					else if(Direct_down)
						Direct_next = Down;
					else
						Direct_next = Right;
				end		
		endcase
	end

/***************************************************************************/
	reg Eaten_sig;
	
	always @ (posedge Clk_50mhz or negedge Rst_n)
	begin
		if(!Rst_n)
		begin
			Snake_light_sig <= 16'd7;
			Body_num <= 7'd3;
			Eaten_sig <= 1'd0;
		end		
		else if(Game_status == END)
				begin
					Eaten_sig <= 1'd0;
					Snake_light_sig <= Snake_light_sig;
				end 
				else if(Game_status == START)
				begin
					Body_num <= 7'd3;
					Eaten_sig <= 1'd0;
					Snake_light_sig <= 16'd7;
				end

		else				
		begin
			case(Eaten_sig)
				1'd0	:
					begin
						if(Body_add_sig)
						begin
							Body_num <= Body_num + 7'd1;
							Snake_light_sig[Body_num] <= 1'b1;
							Eaten_sig <= 1'd1;
						end
						else
							Snake_light_sig[Body_num] <= Snake_light_sig[Body_num];
					end
				1'd1	:
					begin
						if(!Body_add_sig)
							Eaten_sig <= 0;
						else
							Eaten_sig <= Eaten_sig;
					end
			endcase
		end
	end
	
	
	always @ (Pixel_x or Pixel_y or Snake_light_sig)
	begin
		if((Pixel_x >= 11'd0) && (Pixel_x < 11'd800) && (Pixel_y >= 11'd0) && (Pixel_y < 11'd600))
		begin
			if(Pixel_x[9:4] == 6'd0 |
				Pixel_y[9:4] == 6'd0 | 
				Pixel_x[9:4] == x_num | 
				Pixel_y[9:4] == y_num)
				
					Object = WALL;
			else if(Pixel_x[9:4] == Body_x[0] && Pixel_y[9:4] == Body_y[0] && Snake_light_sig[0] == 1'd1)//?????????
					
					Object = HEAD;	
			else if(((Pixel_x[9:4] == Body_x[1]) && (Pixel_y[9:4] == Body_y[1]) && (Snake_light_sig[1] == 1'd1)) | 
					 ((Pixel_x[9:4] == Body_x[2]) && (Pixel_y[9:4] == Body_y[2]) && (Snake_light_sig[2] == 1'd1)) | 
					 ((Pixel_x[9:4] == Body_x[3]) && (Pixel_y[9:4] == Body_y[3]) && (Snake_light_sig[3] == 1'd1)) | 
					 ((Pixel_x[9:4] == Body_x[4]) && (Pixel_y[9:4] == Body_y[4]) && (Snake_light_sig[4] == 1'd1)) | 
					 ((Pixel_x[9:4] == Body_x[5]) && (Pixel_y[9:4] == Body_y[5]) && (Snake_light_sig[5] == 1'd1)) | 
					 ((Pixel_x[9:4] == Body_x[6]) && (Pixel_y[9:4] == Body_y[6]) && (Snake_light_sig[6] == 1'd1)) | 
					 ((Pixel_x[9:4] == Body_x[7]) && (Pixel_y[9:4] == Body_y[7]) && (Snake_light_sig[8] == 1'd1)) | 
					 ((Pixel_x[9:4] == Body_x[8]) && (Pixel_y[9:4] == Body_y[8]) && (Snake_light_sig[8] == 1'd1)) | 
					 ((Pixel_x[9:4] == Body_x[9]) && (Pixel_y[9:4] == Body_y[9]) && (Snake_light_sig[9] == 1'd1)) | 
					 ((Pixel_x[9:4] == Body_x[10]) && (Pixel_y[9:4] == Body_y[10]) && (Snake_light_sig[10] == 1'd1)) | 
					 ((Pixel_x[9:4] == Body_x[11]) && (Pixel_y[9:4] == Body_y[11]) && (Snake_light_sig[11] == 1'd1)) | 
					 ((Pixel_x[9:4] == Body_x[12]) && (Pixel_y[9:4] == Body_y[12]) && (Snake_light_sig[12] == 1'd1)) | 
					 ((Pixel_x[9:4] == Body_x[13]) && (Pixel_y[9:4] == Body_y[13]) && (Snake_light_sig[13] == 1'd1)) | 
					 ((Pixel_x[9:4] == Body_x[14]) && (Pixel_y[9:4] == Body_y[14]) && (Snake_light_sig[14] == 1'd1)) | 
					 ((Pixel_x[9:4] == Body_x[15]) && (Pixel_y[9:4] == Body_y[15]) && (Snake_light_sig[15] == 1'd1)))
					
					Object =	BODY;
			else 
					Object = NONE;
		end
		else
			Object = NONE;
	end
	
endmodule

2.4 食物产生模块

在这里插入图片描述

蛇的头部、身体以及食物都是用坐标表示的,所选用的分辨率为640X480,以16X16像素为一格。每个时钟周期Random_num都在改变变,而控制蛇吃下食物的时刻却因走法、按键的时间等有所不同,所以不同时刻吃下食物后下一个食物出现的地方近似随机。

module Apple_generate				
(
	input Clk_50mhz,			
	input Rst_n,				
	
	input [5:0] Head_x,			
	input [5:0] Head_y,			
	
	output reg [5:0] Apple_x,		
	output reg [4:0] Apple_y,		

	output reg Body_add_sig			
);
/*************************************************************/
	reg [31:0] Count1;			
	reg [10:0] Random_num;			
	//Random在头的坐标和食物坐标重合时因为CLK频率较高可近似看作随机
	always@(posedge Clk_50mhz or negedge Rst_n)
	begin
		if(!Rst_n)
	
			Random_num	<= 11'd0;
		else
			Random_num <= Random_num + 11'd921; 
	end												
/********************************************************/	
	always@(posedge Clk_50mhz or negedge Rst_n)
	begin
		if(!Rst_n)
		begin
			Count1 <= 32'd0;	
//设置初始食物坐标
			Apple_x <= 6'd28;	
			Apple_y <= 5'd13;	
			
			Body_add_sig <= 1'd0;	
		end
		else	if(Count1 == 32'd250_000)
				begin
					Count1 <= 32'd0;
					if((Apple_x == Head_x) && (Apple_y == Head_y))
					begin		
						Body_add_sig <= 1'd1;
//判断随机数是否超出屏幕坐标范围将随机数转换为下个食物的X Y坐标
						Apple_x <= (Random_num[10:5]> 6'd38)?	(Random_num[10:5] - 6'd25)	:	((Random_num[10:5] == 6'd0)?	6'd1	:	Random_num[10:5]);
						Apple_y <= (Random_num[4:0] > 5'd28)?	(Random_num[4:0] - 5'd3)	:	((Random_num[4:0] == 5'd0)?	5'd1	:	Random_num[4:0]);
					end   
					else
						Body_add_sig <= 1'd0;
				end
		else
			Count1 <= Count1 + 32'd1;
	end
endmodule

2.5 计分模块

在这里插入图片描述

当头的坐标等于身体坐标值时会产生身体增长信号,当检测到这个信号的上升沿时分数加1,到达到9分时且下一个身体增长信号的上升沿到来则清零,分数的高位加1,并且速度加快,增加游戏难度。代码的其余部分是将所显示字符转化给LCD进行显示。

module score(
	input 		status_flg,
	input		Body_add_sig,
	input		rst_n,
	input		[2:0] game_status,

	output	[127:0] oscore,
	output	reg [31:0] ospeed

);
reg  	[3:0]	iscorel;
reg  	[3:0]	iscoreh;
reg  	[7:0]	scorel;
reg  	[7:0]	scoreh;
reg  	[7:0]	char_S;
reg  	[7:0]	char_c;
reg  	[7:0]	char_o;
reg  	[7:0]	char_r;
reg  	[7:0]	char_e;
reg		[7:0]	maohao;
reg		[31:0]	kongge;
reg	add;

parameter   START = 3'b000;
parameter	speed2=32'd8_000_000;
parameter	speed1=32'd12_500_000;

always @ (posedge status_flg or posedge Body_add_sig  or negedge rst_n)
begin
	if(!rst_n)
		begin
			iscorel = 0;
			iscoreh = 0;
			ospeed = 32'd12_500_000;
		end
	else if(status_flg)	
		begin
			iscorel 	= (game_status == START) ? 4'h0:iscorel;
			iscoreh	= (game_status == START) ? 4'h0:iscoreh;
			ospeed 	= (game_status == START) ? speed1:ospeed;
		end
	else 	if(	iscorel ==  4'd9)
			begin	
				iscorel = 0;
				iscoreh = iscoreh +4'd1;
				ospeed= speed2;
			end
	else 		
				iscorel = iscorel +4'd1;
end

always @(*)
begin
	scorel=8'h30+iscorel;
	scoreh=8'h30+iscoreh;
	char_S=8'h53;
	char_c=8'h63;
	char_o=8'h6f;
	char_r=8'h72;
	char_e=8'h65;
	maohao=8'h3a;
	kongge=32'ha0a0a0a0;	
end
assign oscore={char_S[7:0],char_c[7:0],char_o[7:0],char_r[7:0],char_e[7:0],maohao[7:0],scoreh[7:0],scorel[7:0],kongge[31:0],kongge[31:0]};
endmodule

2.6 LCD显示模块

在这里插入图片描述
三段式状态机的LCD驱动显示模块,通过逐格扫描显示所输入的字符串,这里所显示的格式为score:02(所得分数)。但是这种写法的显示效果不佳,因为每一格都相当于要显示一遍,所以显示所需字符需要扫描整个屏幕,刷新时间久,但是这里字符较少加上人眼的视觉暂留效应整个显示还是可以看清。

module lcd_driver(
	input			clk,
	input 		rst_n,
	input [127:0] 	line_rom1 ,
	input [127:0] 	line_rom2 ,
	output 		lcd_en,	 	// lcd enable
	output reg	lcd_rs, 	// data or cmd cmd_l data_h 
	output 		lcd_rw,		// read or wire read_h wire_l
	output reg [7:0] 	lcd_data	// data
);
/************************************************************/
reg [15:0] 	cnt;
always @ (posedge clk or negedge rst_n)
begin
	if(!rst_n)
		cnt <= 0;
	else
		cnt <= cnt + 1'b1;
end
	
	assign 	lcd_en 		= cnt[15]; 				//lcd enable,keep same time
	assign 	lcd_rw 		= 1'b0;		 			//write only
	wire 	cmd_flag 	= (cnt == 16'h7FFF) ? 1'b1 : 1'b0; 	//when lcd_en is steady,write a cmd
/**********************************************/
// Gray code : 40 states
	parameter IDLE = 8'h00; // IDLE
// lcd init
	parameter DISP_SET 	= 8'h01; 	// /set 16X2,5X7 ,8 bits record
	parameter DISP_OFF 	= 8'h03; 	// display off
	parameter CLR 		= 8'h02; 	// clear the lcd
	parameter CURSOR_SET 	= 8'h06; 	// CURSOR  move 2 right
	parameter DISP_ON 	= 8'h07; 	// display on

// display 1th line
	parameter ROW1_ADDR = 8'h05;
	parameter ROW1_0 = 8'h04;
	parameter ROW1_1 = 8'h0C;
	parameter ROW1_2 = 8'h0D;
	parameter ROW1_3 = 8'h0F;
	parameter ROW1_4 = 8'h0E;
	parameter ROW1_5 = 8'h0A;
	parameter ROW1_6 = 8'h0B;
	parameter ROW1_7 = 8'h09;
	parameter ROW1_8 = 8'h08;
	parameter ROW1_9 = 8'h18;
	parameter ROW1_A = 8'h19;
	parameter ROW1_B = 8'h1B;
	parameter ROW1_C = 8'h1A;
	parameter ROW1_D = 8'h1E;
	parameter ROW1_E = 8'h1F;
	parameter ROW1_F = 8'h1D;
// display 2th line
	parameter ROW2_ADDR = 8'h1C;
	parameter ROW2_0 = 8'h14;
	parameter ROW2_1 = 8'h15;
	parameter ROW2_2 = 8'h17;
	parameter ROW2_3 = 8'h16;
	parameter ROW2_4 = 8'h12;
	parameter ROW2_5 = 8'h13;
	parameter ROW2_6 = 8'h11;
	parameter ROW2_7 = 8'h10;
	parameter ROW2_8 = 8'h30;
	parameter ROW2_9 = 8'h31;
	parameter ROW2_A = 8'h33;
	parameter ROW2_B = 8'h32;
	parameter ROW2_C = 8'h36;
	parameter ROW2_D = 8'h37;
	parameter ROW2_E = 8'h35;
	parameter ROW2_F = 8'h34;
/**************************************************/
// FSM: always1
	reg [5:0] current_state, next_state;
always @ (posedge clk or negedge rst_n)
	begin
	if(!rst_n)
		current_state <= IDLE;
	else if(cmd_flag)
		current_state <= next_state;
	end
/****************************************************/
// FSM: always2
always@*
	begin
	case(current_state)
// lcd init
	IDLE 		: next_state = DISP_SET;
	DISP_SET 	: next_state = DISP_OFF;
	DISP_OFF 	: next_state = CLR;
	CLR 		: next_state = CURSOR_SET;
	CURSOR_SET 	: next_state = DISP_ON;
	DISP_ON 	: next_state = ROW1_ADDR;
// display 1th line
	ROW1_ADDR : next_state = ROW1_0;
	ROW1_0 : next_state = ROW1_1;
	ROW1_1 : next_state = ROW1_2;
	ROW1_2 : next_state = ROW1_3;
	ROW1_3 : next_state = ROW1_4;
	ROW1_4 : next_state = ROW1_5;
	ROW1_5 : next_state = ROW1_6;
	ROW1_6 : next_state = ROW1_7;
	ROW1_7 : next_state = ROW1_8;
	ROW1_8 : next_state = ROW1_9;
	ROW1_9 : next_state = ROW1_A;
	ROW1_A : next_state = ROW1_B;
	ROW1_B : next_state = ROW1_C;
	ROW1_C : next_state = ROW1_D;
	ROW1_D : next_state = ROW1_E;
	ROW1_E : next_state = ROW1_F;
	ROW1_F : next_state = ROW2_ADDR;
// display 2th line
	ROW2_0 : next_state = ROW2_1;
	ROW2_1 : next_state = ROW2_2;
	ROW2_2 : next_state = ROW2_3;
	ROW2_3 : next_state = ROW2_4;
	ROW2_4 : next_state = ROW2_5;
	ROW2_5 : next_state = ROW2_6;
	ROW2_6 : next_state = ROW2_7;
	ROW2_7 : next_state = ROW2_8;
	ROW2_8 : next_state = ROW2_9;
	ROW2_9 : next_state = ROW2_A;
	ROW2_A : next_state = ROW2_B;
	ROW2_B : next_state = ROW2_C;
	ROW2_C : next_state = ROW2_D;
	ROW2_D : next_state = ROW2_E;
	ROW2_E : next_state = ROW2_F;
	ROW2_F : next_state = ROW1_ADDR;
	default : next_state = IDLE ;
	endcase
end
/******************************************************/
// FSM: always3
always @ (posedge clk or negedge rst_n)
	begin
	if(!rst_n)
		begin
		lcd_rs <= 0;
		lcd_data <= 8'hXX;
		end
	else if(cmd_flag)
	begin
// write_cmd
	case(next_state)
	IDLE : lcd_rs <= 0; 		//write_cmd  
//lcd init
	DISP_SET : lcd_rs <= 0; 	
	DISP_OFF : lcd_rs <= 0; 	
	CLR : lcd_rs <= 0; 		
	CURSOR_SET : lcd_rs <= 0; 	
	DISP_ON : lcd_rs <= 0; 		
// display 1th line
	ROW1_ADDR : lcd_rs <= 0; 	
	ROW1_0 : lcd_rs <= 1; 		//record
	ROW1_1 : lcd_rs <= 1; 
	ROW1_2 : lcd_rs <= 1; 
	ROW1_3 : lcd_rs <= 1; 
	ROW1_4 : lcd_rs <= 1; 
	ROW1_5 : lcd_rs <= 1; 
	ROW1_6 : lcd_rs <= 1; 
	ROW1_7 : lcd_rs <= 1; 
	ROW1_8 : lcd_rs <= 1; 
	ROW1_9 : lcd_rs <= 1; 
	ROW1_A : lcd_rs <= 1; 
	ROW1_B : lcd_rs <= 1; 
	ROW1_C : lcd_rs <= 1; 
	ROW1_D : lcd_rs <= 1; 
	ROW1_E : lcd_rs <= 1; 
	ROW1_F : lcd_rs <= 1; 
// display 2th line
	ROW2_ADDR : lcd_rs <= 0; //statement
	ROW2_0 : lcd_rs <= 1; 
	ROW2_1 : lcd_rs <= 1; 
	ROW2_2 : lcd_rs <= 1; 
	ROW2_3 : lcd_rs <= 1; 
	ROW2_4 : lcd_rs <= 1; 
	ROW2_5 : lcd_rs <= 1; 
	ROW2_6 : lcd_rs <= 1; 
	ROW2_7 : lcd_rs <= 1;
	ROW2_8 : lcd_rs <= 1; 
	ROW2_9 : lcd_rs <= 1; 
	ROW2_A : lcd_rs <= 1; 
	ROW2_B : lcd_rs <= 1; 
	ROW2_C : lcd_rs <= 1; 
	ROW2_D : lcd_rs <= 1; 
	ROW2_E : lcd_rs <= 1; 
	ROW2_F : lcd_rs <= 1; 
	endcase
// write lcd_data
	case(next_state)
	IDLE 		: lcd_data <= 8'hxx;
//lcd init
	DISP_SET 	: lcd_data <= 8'h38; 		//set 16X2,5X7 ,8 bits record
	DISP_OFF 	: lcd_data <= 8'h08; 		//off display
	CLR 	 	: lcd_data <= 8'h01; 		//clear lcd
	CURSOR_SET 	: lcd_data <= 8'h06; 		//cursor set
	DISP_ON 	: lcd_data <= 8'h0C; 		//on display
	// display 2th line
	ROW1_ADDR : lcd_data <= 8'h80;
	ROW1_0 : lcd_data <= line_rom1[127:120];
	ROW1_1 : lcd_data <= line_rom1[119:112];
	ROW1_2 : lcd_data <= line_rom1[111:104];
	ROW1_3 : lcd_data <= line_rom1[103: 96];
	ROW1_4 : lcd_data <= line_rom1[ 95: 88];
	ROW1_5 : lcd_data <= line_rom1[ 87: 80];
	ROW1_6 : lcd_data <= line_rom1[ 79: 72];
	ROW1_7 : lcd_data <= line_rom1[ 71: 64];
	ROW1_8 : lcd_data <= line_rom1[ 63: 56];
	ROW1_9 : lcd_data <= line_rom1[ 55: 48];
	ROW1_A : lcd_data <= line_rom1[ 47: 40];
	ROW1_B : lcd_data <= line_rom1[ 39: 32];
	ROW1_C : lcd_data <= line_rom1[ 31: 24];
	ROW1_D : lcd_data <= line_rom1[ 23: 16];
	ROW1_E : lcd_data <= line_rom1[ 15: 8];
	ROW1_F : lcd_data <= line_rom1[ 7: 0];
// display 2th line
	ROW2_ADDR : lcd_data <= 8'hC0;
	ROW2_0 : lcd_data <= line_rom2[127:120];
	ROW2_1 : lcd_data <= line_rom2[119:112];
	ROW2_2 : lcd_data <= line_rom2[111:104];
	ROW2_3 : lcd_data <= line_rom2[103: 96];
	ROW2_4 : lcd_data <= line_rom2[ 95: 88];
	ROW2_5 : lcd_data <= line_rom2[ 87: 80];
	ROW2_6 : lcd_data <= line_rom2[ 79: 72];
	ROW2_7 : lcd_data <= line_rom2[ 71: 64];
	ROW2_8 : lcd_data <= line_rom2[ 63: 56];
	ROW2_9 : lcd_data <= line_rom2[ 55: 48];
	ROW2_A : lcd_data <= line_rom2[ 47: 40];
	ROW2_B : lcd_data <= line_rom2[ 39: 32];
	ROW2_C : lcd_data <= line_rom2[ 31: 24];
	ROW2_D : lcd_data <= line_rom2[ 23: 16];
	ROW2_E : lcd_data <= line_rom2[ 15: 8];
	ROW2_F : lcd_data <= line_rom2[ 7: 0];
	endcase
	end
end
endmodule

2.7 VGA显示模块

对当前坐标所在位置的对象进行判断,输出对应对象的颜色,给VGA驱动模块显示。
在这里插入图片描述

module VGA_dis(
	input			clk_50,
	input			rst_n,
	input	[5:0] 	Apple_x,		
	input 	[4:0] 	Apple_y,
	input 	[10:0] 	pixel_x,
	input 	[10:0] 	pixel_y,
	input 	 [1:0] 	object,			 //00-NONE- 01-HEAD- 10-BODY- 11-WALL-
	output	reg 	[9:0]	VGA_R,
	output	reg 	[9:0]	VGA_G,
	output	reg 	[9:0]	VGA_B
);
	parameter 	NONE = 2’b00;
	parameter	HEAD = 2’b01;
	parameter	BODY = 2’b10;
	parameter	WALL = 2’b11;
	always @(posedge clk_50 or negedge rst_n)
	begin
	if(!rst_n)
		begin
				VGA_R	<= 10'h0;
				VGA_G	<= 10'h0;
				VGA_B	<= 10'h0;
		end
	else if( (Apple_x==pixel_x[9:4]) && (Apple_y==pixel_y[8:4]))
			begin
				VGA_R	<= 10'h3ff;
				VGA_G	<= 10'h0;
				VGA_B	<= 10'h0;
			end
	else

   	case(object)
		NONE:	begin
				VGA_R	<= 10'h0;
				VGA_G	<= 10'h3ff;
				VGA_B	<= 10'h0;
			end
		HEAD:	begin
				VGA_R	<= 10'h3ff;
				VGA_G	<= 10'h3ff;
				VGA_B	<= 10'h3ff;
			end
		BODY:	begin
				VGA_R	<= 10'h0;
				VGA_G	<= 10'h3ff;
				VGA_B	<= 10'h3ff;
			end
		WALL:	begin
				VGA_R	<= 10'h3ff;
				VGA_G	<= 10'h3ff;
				VGA_B	<= 10'h3ff;
			end
	endcase
	end


endmodule

2.8 VGA驱动模块

所选的分辨率为640X480,所以首先对DE2上的50M时钟进行2分频得到25M的时钟。在此时钟下通过水平和垂直同步时间参数的设置(如下表)得到VGA(60Hz),分辨率640X480,60Hz即60帧,在这个帧数下人们就可以看清画面看不出有断续。
在这里插入图片描述
VGA的颜色输入信号是模拟信号,在DE2平台上,由一块高速视频DA转换器ADV7123将输入代表颜色信息的数字信号经过DAC转换后输出给VGA,其中还有消隐以及同步信号控制其消隐和同步。
在这里插入图片描述
在这里插入图片描述
VGA的行时序如图所示,当在c时段为有效显示时段,通过相应的分辨率设置时间参数就可以显示,场时序也类似。并将有效时段的位置作为当前位置坐标输出给其它模块使用。仿真结果如同所示,符合工作时序图。

在这里插入图片描述

module	VGA_Ctrl	(	
						iRed,
						iGreen,
						iBlue,
						oCurrent_X,
						oCurrent_Y,
						oAddress,
						oRequest,
						//	VGA Side
						oVGA_R,
						oVGA_G,
						oVGA_B,
						oVGA_HS,
						oVGA_VS,
						oVGA_SYNC,
						oVGA_BLANK,
						oVGA_CLOCK,
						//	Control Signal
						iCLK,
						iRST_N	);
//	Host Side
input		[9:0]	iRed;
input		[9:0]	iGreen;
input		[9:0]	iBlue;
output		[21:0]	oAddress;
output		[10:0]	oCurrent_X;
output		[10:0]	oCurrent_Y;
output				oRequest;
//	VGA Side
output		[9:0]	oVGA_R;
output		[9:0]	oVGA_G;
output		[9:0]	oVGA_B;
output	reg		oVGA_HS;
output	reg		oVGA_VS;
output			oVGA_SYNC;
output			oVGA_BLANK;
output			oVGA_CLOCK;
//	Control Signal
input			iCLK;
input			iRST_N;	
//	Internal Registers
reg		[10:0]	H_Cont;		
reg		[10:0]	V_Cont;
reg clk_25M;
always@(posedge iCLK or negedge iRST_N)
begin
	if(!iRST_N)
		clk_25M <= 0;
	else
		clk_25M <= ~clk_25M;  	
end	

//	Horizontal	Parameter
parameter	H_FRONT	=	13+3;			//d	front-porch
parameter	H_SYNC		=	96;				//a	sync
parameter	H_BACK		=	45+3;			//b	back-porch
parameter	H_ACT		=	640;			//c	display_valid
parameter	H_BLANK	=	H_FRONT+H_SYNC+H_BACK;
parameter	H_TOTAL	=	H_FRONT+H_SYNC+H_BACK+H_ACT;

//	Vertical Parameter
parameter	V_FRONT	=	9+2;		//d
parameter	V_SYNC		=	2;			//a
parameter	V_BACK		=	32;			//b
parameter	V_ACT		=	480;		//c
parameter	V_BLANK	=	V_FRONT+V_SYNC+V_BACK;
parameter	V_TOTAL	=	V_FRONT+V_SYNC+V_BACK+V_ACT;

assign	oVGA_SYNC	=	1'b1;			//	This pin is unused.
assign	oVGA_BLANK	=	~((H_Cont<H_BLANK)||(V_Cont<V_BLANK));
assign	oVGA_CLOCK	=	~clk_25M;
assign	oVGA_R		=	iRed;
assign	oVGA_G		=	iGreen;
assign	oVGA_B		=	iBlue;
assign	oAddress	=	oCurrent_Y*H_ACT+oCurrent_X;
assign	oRequest	=	((H_Cont>=H_BLANK && H_Cont<H_TOTAL)	&&   (V_Cont>=V_BLANK && V_Cont<V_TOTAL));
assign	oCurrent_X	=	(H_Cont>=H_BLANK)	?	H_Cont-H_BLANK	:	11'h0	;
assign	oCurrent_Y	=	(V_Cont>=V_BLANK)	?	V_Cont-V_BLANK	:	11'h0	;

always@(posedge clk_25M or negedge iRST_N)
begin
	if(!iRST_N)
	begin
		H_Cont		<=	0;
		oVGA_HS		<=	1;		// a line scan done with a low pulse
	end
	else
	begin
		if(H_Cont<H_TOTAL)
			H_Cont	<=	H_Cont+1'b1;
		else
			H_Cont	<=	0;
		//	Horizontal Sync
		if(H_Cont==H_FRONT-1)			//	Front porch end
			oVGA_HS	<=	1'b0;
		if(H_Cont==H_FRONT+H_SYNC-1)		//	Sync pulse end
			oVGA_HS	<=	1'b1;
	end
end
always@(posedge oVGA_HS or negedge iRST_N)
begin
	if(!iRST_N)
	begin
		V_Cont		<=	0;
		oVGA_VS		<=	1;
	end
	else
	begin
		if(V_Cont<V_TOTAL)
			V_Cont	<=	V_Cont+1'b1;
		else
			V_Cont	<=	0;
							//	Vertical Sync
		if(V_Cont==V_FRONT-1)			//	Front porch end  	
			oVGA_VS	<=	1'b0;
		else if(V_Cont==V_FRONT+V_SYNC-1)		//	Sync pulse end		tongbu   reset
			oVGA_VS	<=	1'b1;
	end
end

endmodule

最终设计实现结果

在这里插入图片描述

在这里插入图片描述

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值