FPGA设计—贪吃蛇游戏

news/2024/11/29 21:19:35/

本设计是利用verilog硬件描述语言开发FPGA,在VGA接口显示屏上实现贪吃蛇游戏。

总体设计

设计模块分为时钟分频模块(pll)、按键控制模块(key)、vga显示模块(vga)、苹果产生模块(apple)、数码管显示模块(nixie_tube)、音乐播放模块(play)。

设计的RTL示图:

输入输出接口

         信号名     I/O     位宽                                                           功能
           clk       I       1系统时钟50MHz
         rst_n       I       1系统复位信号
        key_up       I       1蛇身控制键 上
      key_down       I       1蛇身控制键 下
       key_left       I       1蛇身控制键 左
      key_right       I       1蛇身控制键 右
      stop_run       I       1游戏暂停运行键
    key_choose1       I         1模式选择键1
    key_choose2       I       1模式选择键2
          hys      O       1VGA行同步信号
          vys      O       1VGA场同步信号
     rgb_data      O       8RGB数据
     tube_place      O       2数码管为选
     tube_data      O       8数码管段选数据
        been      O       1音频输出  

时钟分频模块

时钟分频模块是将原有的50MHz的时钟频率2分频为25MHz,方便VGA的时序设计。设置FPGA内部的PLL完成分频。

按键控制模块

按键控制模块完成对按键的消抖判断。判断程序如下

//按键消抖module key( clk_25M , rst_n , key_up , key_down , key_left , key_right , stop_run , key_choose1 , key_choose2 ,deal_key_up , deal_key_down , deal_key_left , deal_key_right , deal_stop_run ,deal_key_choose1 , deal_key_choose2);input clk_25M;        //25MHz时钟信号
input rst_n;      //复位信号
input key_up;     //上
input key_down;   //下
input key_left;   //左
input key_right;  //右
input stop_run;    //停止运行
input key_choose1;  //选择1键
input key_choose2; //选择2键output deal_key_up;
output deal_key_down;
output deal_key_left;
output deal_key_right;
output deal_stop_run;
output deal_key_choose1;
output deal_key_choose2;//------------------------------------------------------------reg [6:0] key_rst;     //按键值存放
reg [6:0] key_rst_r;   //存放后一个时钟周期的按键值always @( posedge clk_25M or negedge rst_n ) beginif( !rst_n )key_rst <= 7'b1111111;else key_rst <= { key_choose1 , key_choose2 , stop_run , key_up , key_down , key_left , key_right };   //未消抖的状态
endalways @( posedge clk_25M or negedge rst_n ) beginif( !rst_n )key_rst_r <= 7'b1111111;else key_rst_r <= key_rst;     //过去一个时钟周期的状态
end wire [6:0] key_an;   //第一次状态判断值
assign key_an = key_rst_r[6:0] & (~key_rst[6:0]);  //在那个位置位1,说明这个键被按下,下面进行消抖//------------------------------------------------------------reg [18:0] cnt;always @( posedge clk_25M or negedge rst_n ) beginif( !rst_n )cnt <= 19'd0;else if( key_an )   //第一次判断 有按键按下cnt <= 19'd0;else cnt <= cnt + 1'b1;   //消抖计时
end//---------------------------reg [6:0] low_sw;always @( posedge clk_25M or negedge rst_n ) beginif( !rst_n )low_sw <= 7'b1111111;else if( cnt == 19'h7ffff )  //计满20mslow_sw <= { key_choose1 , key_choose2 , stop_run , key_up , key_down , key_left , key_right };  //消抖后的值
endreg [6:0] low_sw_r;always @( posedge clk_25M or negedge rst_n ) beginif( !rst_n )low_sw_r <= 7'b1111111;else low_sw_r <= low_sw;
end wire [6:0] key;
assign key = low_sw_r[6:0] & (~low_sw[6:0]);//在那个位置位1,说明这个键被按下assign deal_key_choose1 = key[6];
assign deal_key_choose2 = key[5];
assign deal_stop_run = key[4];
assign deal_key_up = key[3];
assign deal_key_down = key[2];
assign deal_key_left = key[1];
assign deal_key_right = key[0];endmodule 

vga显示模块

在VGA模块中包含了VGA的显示以及蛇身的控制。

VGA接口时序使用的是640*480的分辨率,刷新频率为60Hz,时序控制如下:

reg [9:0] cnt_hs;  //行信号clk计数
reg [9:0] cnt_vs;  //场信号clk计数wire add_cnt_hs;   //行信号clk计数器加1条件
wire end_cnt_hs;   //行信号clk计数器结束条件wire add_cnt_vs;   //场信号clk计数器加1条件
wire end_cnt_vs;   //场信号clk计数器结束条件always @( posedge clk_25M or negedge rst_n ) beginif( !rst_n )cnt_hs <= 10'd0;else if( add_cnt_hs ) beginif( end_cnt_hs )cnt_hs <= 10'd0;else cnt_hs <= cnt_hs + 1'b1;end	
endassign add_cnt_hs = 1'b1;
assign end_cnt_hs = add_cnt_hs && ( cnt_hs == 800-1 );  //end_cnt_hs=1 结束计数always @( posedge clk_25M or negedge rst_n ) beginif( !rst_n ) cnt_vs <= 10'd0;else if( add_cnt_vs ) beginif( end_cnt_vs )cnt_vs <= 10'd0;else cnt_vs <= cnt_vs + 1'b1;end	
endassign add_cnt_vs = end_cnt_hs;
assign end_cnt_vs = add_cnt_vs && ( cnt_vs == 525-1 );wire [9:0] vga_x;	//VGA的x坐标
wire [9:0] vga_y;	//VGA的y坐标assign vga_x = cnt_hs - 10'd144;
assign vga_y = cnt_vs - 10'd35;//-------------------------------------------------------------------
//行信号与场信号
always @( posedge clk_25M or negedge rst_n ) beginif( !rst_n )hys <= 1'b1;else if( cnt_hs == 10'd95 )hys <= 1'b1;else if( end_cnt_hs )hys <= 1'b0;
endalways @( posedge clk_25M or negedge rst_n ) beginif( !rst_n )vys <= 1'b1;else if( cnt_vs == 10'd1 ) vys <= 1'b1;else if( end_cnt_vs )vys <= 1'b0;
end

蛇身控制是由按键作为输入,控制蛇的运动方向。程序如下:

//按键控制坐标
//25MHz
always @( posedge clk_25M or posedge system_rst_pulse or negedge rst_n ) beginif( !rst_n || system_rst_pulse ) beginsite_x[0] <= 10'd520;    //蛇头起始坐标site_y[0] <= 10'd230;site_x[1] <= 10'd530;site_y[1] <= 10'd230;site_x[2] <= 10'd540;site_y[2] <= 10'd230;endelse if( end_cnt_s && stop_run && (!die_flag_r) && (!success_flag_r) ) begin   //蛇头坐标赋值给后一个块,以此类推reg [8:0] temp1;  //for循环变量 for( temp1=0 ; temp1<7'd24 ; temp1=temp1+1'b1 ) begin   //循环次数固定site_x[temp1+1'b1] <= site_x[temp1];site_y[temp1+1'b1] <= site_y[temp1];endif( up ) site_y[0] <= site_y[0] - 10'd10;else if( down ) site_y[0] <= site_y[0] + 10'd10;else if( left )      //默认状态site_x[0] <= site_x[0] - 10'd10;else if( right ) site_x[0] <= site_x[0] + 10'd10;end
end

苹果产生模块

苹果产生模块,当蛇吃到苹果后,通过x轴与y轴不同的计数器产生随机的x轴与y轴坐标,在这个坐标上产生苹果。

//苹果输出module apple( clk_25M , rst_n , apple_x , apple_y ,updata_apple );input clk_25M;         //25M时钟信号
input rst_n;           //复位信号
input updata_apple;     //苹果是否被吃标志 产生新的苹果触发
output [9:0] apple_x;  //苹果坐标
output [8:0] apple_y;reg [9:0] apple_x;
reg [8:0] apple_y;//-----------------------------------------------------------------
//计数器reg [14:0] cnt_1ms;
wire add_cnt_1ms;     //加一条件
wire end_cnt_1ms;     //结束条件always @( posedge clk_25M or negedge rst_n ) beginif( !rst_n )cnt_1ms <= 15'd0;else if( add_cnt_1ms ) beginif( end_cnt_1ms )cnt_1ms <= 15'd0;else cnt_1ms <= cnt_1ms + 1'b1;end
endassign add_cnt_1ms = 1;
assign end_cnt_1ms = add_cnt_1ms && ( cnt_1ms == 25000-1 );   //1ms计时reg [3:0] cnt_10ms;
wire add_cnt_10ms;    //加一条件
wire end_cnt_10ms;    //结束条件always @( posedge clk_25M or negedge rst_n ) beginif( !rst_n)cnt_10ms <= 4'd0;else if( add_cnt_10ms ) beginif( end_cnt_10ms )cnt_10ms <= 4'd0;else cnt_10ms <= cnt_10ms + 1'b1;end
endassign add_cnt_10ms = end_cnt_1ms;
assign end_cnt_10ms = add_cnt_10ms && ( cnt_10ms == 10-1 );   //10ms计数//-----------------------------------------------------------------reg [4:0] count_x;
reg [5:0] count_y;always @( posedge clk_25M or negedge rst_n ) beginif( !rst_n ) begincount_x <= 6'd0;count_y <= 6'd0;endelse beginif( end_cnt_1ms ) begincount_x <= count_x + 1'b1;if( count_x == 6'd27 )count_x <= 6'd1;endif( end_cnt_10ms ) begincount_y <= count_y + 1'b1;if( count_y == 6'd35 )count_y <= 6'd1;endend
end//-----------------------------------------------------------------
reg [9:0] apple_x_r;
reg [8:0] apple_y_r;always @( posedge clk_25M or negedge rst_n ) beginif( !rst_n )apple_x_r <= 10'd0;else begincase( count_x )6'd1 : apple_x_r <= 10'd330;6'd2 : apple_x_r <= 10'd340;6'd3 : apple_x_r <= 10'd350;6'd4 : apple_x_r <= 10'd360;6'd5 : apple_x_r <= 10'd370;6'd6 : apple_x_r <= 10'd380;6'd7 : apple_x_r <= 10'd390;6'd8 : apple_x_r <= 10'd400;6'd9 : apple_x_r <= 10'd410;6'd10 : apple_x_r <= 10'd420;6'd11 : apple_x_r <= 10'd430;6'd12 : apple_x_r <= 10'd440;6'd13 : apple_x_r <= 10'd450;6'd14 : apple_x_r <= 10'd460;6'd15 : apple_x_r <= 10'd470;6'd16 : apple_x_r <= 10'd480;6'd17 : apple_x_r <= 10'd490;6'd18 : apple_x_r <= 10'd500;6'd19 : apple_x_r <= 10'd510;6'd20 : apple_x_r <= 10'd520;6'd21 : apple_x_r <= 10'd530;6'd22 : apple_x_r <= 10'd540;6'd23 : apple_x_r <= 10'd550;6'd24 : apple_x_r <= 10'd560;6'd25 : apple_x_r <= 10'd570;6'd26 : apple_x_r <= 10'd580;default : apple_x_r <= 10'd330;endcase end
endalways @( posedge clk_25M or negedge rst_n ) beginif( !rst_n )apple_y_r <= 10'd0;else begincase( count_y )6'd1 : apple_y_r <= 10'd70;6'd2 : apple_y_r <= 10'd80;6'd3 : apple_y_r <= 10'd90;6'd4 : apple_y_r <= 10'd100;6'd5 : apple_y_r <= 10'd110;6'd6 : apple_y_r <= 10'd120;6'd7 : apple_y_r <= 10'd130;6'd8 : apple_y_r <= 10'd140;6'd9 : apple_y_r <= 10'd150;6'd10 : apple_y_r <= 10'd160;6'd11 : apple_y_r <= 10'd170;6'd12 : apple_y_r <= 10'd180;6'd13 : apple_y_r <= 10'd190;6'd14 : apple_y_r <= 10'd200;6'd15 : apple_y_r <= 10'd210;6'd16 : apple_y_r <= 10'd220;6'd17 : apple_y_r <= 10'd230;6'd18 : apple_y_r <= 10'd240;6'd19 : apple_y_r <= 10'd250;6'd20 : apple_y_r <= 10'd260;6'd21 : apple_y_r <= 10'd270;6'd22 : apple_y_r <= 10'd280;6'd23 : apple_y_r <= 10'd290;6'd24 : apple_y_r <= 10'd300;6'd25 : apple_y_r <= 10'd310;6'd26 : apple_y_r <= 10'd320;6'd27 : apple_y_r <= 10'd330;6'd28 : apple_y_r <= 10'd340;6'd29 : apple_y_r <= 10'd350;6'd30 : apple_y_r <= 10'd360;6'd31 : apple_y_r <= 10'd370;6'd32 : apple_y_r <= 10'd380;6'd33 : apple_y_r <= 10'd390;6'd34 : apple_y_r <= 10'd400;default : apple_y_r <= 10'd70;endcaseend
endalways @( posedge updata_apple or negedge rst_n ) begin   //updata_apple上升沿改变(x,y)if( !rst_n ) beginapple_x <= 10'd400;  //第一个苹果apple_y <= 10'd150;endelse beginapple_x <= apple_x_r;apple_y <= apple_y_r;end
endendmodule 

数码管显示模块与音乐播放模块

数码管模块显示当前的分数,音乐播放模块在蛇吃到苹果、死亡时会产生声音。

 


http://www.ppmy.cn/news/154014.html

相关文章

DELL server T630系统安装与配置

DELL server T630系统安装与配置 https://www.lmlphp.com/user/1888/article/item/20136/ 目录 DELL server T630系统安装与配置 IDRAC 安装系统 Error Error1 内存的错误 Error2 System Board Intrusion Chassis is open Error3 其他 总结 有幸可以折腾一下DELL的T63…

dell笔记本耳机怎么设置_对戴尔系统上的耳机/麦克风插孔问题进行故障排除

文章内容 解决方案 本文提供有关如何对戴尔系统上的耳机/麦克风插孔问题进行故障排除的信息。 耳机插入OptiPlex系统的前部音频插孔中后&#xff0c;系统上的后部音频插孔被禁用。 根据设计&#xff0c;如果设备通过前部音频端口连接&#xff0c;系统上的后部音频端口将被禁用。…

传说中的D50对手

很久以来&#xff0c;一直有说法佳能会基于低端胶片机EOS 3000v机身推出一款入门级数码单反EOS 3000D。很显然&#xff0c;是针对尼康D50的市场动作&#xff0c;并以此进一步降低EOS系列的门槛。

5030. A

题目大意 给定长度为1,2,3,4的砖块 a,b,c,d 个&#xff0c;两个人轮流操作。 每次可以选择一个长度大于1的砖头分成两个砖头&#xff0c;或者选择 n 个长度为n的砖头粉碎。 无法操作的人数。询问先手是否必胜。 Data Constraint a,b,c,d≤1010000 题解 打表可以发现&…

戴尔游匣G15 5520成功安装ubuntu2004 intel ax201 驱动(亲测可用)

我们公司批量采购了戴尔游匣G15-5520, 测试安装了最新的2204&#xff0c;所有驱动是可用的。但因为公司的开发环境是ubuntu 2004, 安装完以后发现网卡和蓝牙没有驱动&#xff0c;在网上找烂了各种教程都无法使用。于是各种折腾&#xff0c;终于OK了&#xff0c;特分享下。 开机…

【D3 API 中文手册】

【D3 API 中文手册】 声明&#xff1a;本文仅供学习所用&#xff0c;未经作者允许严禁转载和演绎 《D3 API 中文手册》是D3官方API文档的中文翻译。始于2014-3-23日&#xff0c;基于VisualCrew小组的六次协作任务之上&#xff0c;目前已经大致翻译完毕&#xff0c;将陆续向官网…

D. 505

传送门 分析 这个道题的题意是给你一个n * m的01矩阵&#xff0c;然后可以更改矩阵中的数字&#xff0c;比如把0改成1&#xff0c;或者把1改成0&#xff0c;要求最后矩阵中没有任何一个边长为偶数的子矩阵中含有偶数个1&#xff0c;如果不含偶数边长的子矩阵&#xff0c;则不…

网络篇汇总

路由器&#xff1a;属于网关设备&#xff0c;通过路由器可以将各种局域网、城域网、广域网连接起来&#xff0c;一般工作于网络层。它会根据信号的情况自动选择和设定路由&#xff0c;以最佳路径&#xff0c;按照前后顺序发送信号。路由器可连接多个逻辑上分开的网络&#xff0…