数字系统设计————打地鼠游戏设计
游戏外设如鼠标、键盘的专业设计 #生活乐趣# #游戏乐趣# #游戏硬件#
一、设计题目说明
此设计意在实现一个相对具有娱乐性的打地鼠游戏模块功能,能够产生随机出现的地鼠,并能对虚拟的敲击按键做出灵敏的识别与判断,从而进行必要的计分与计时行为。
二、实验平台
开发软件:Quartus II 9.0sp2 Web Edition
开发板:ALTERA FLEX EPF10K20TI144-4 CAA239743
三、总体设计思路
结构:取八位晶体管的前四位数码管作为随机地鼠出现的显示区域,后四位对半分为前两位的倒计时显示模块和后两位的分数计数模块;八个按键脉冲开关取前四位作为地鼠的敲击按键;以数码管的动态化显示代表是否击中。
功能:在游戏开始前,首先根据产生的伪随机数生成地鼠出现的位置信息,由指定的时钟脉冲信号控制地鼠跳动的频率,最终交由晶体管显示。重置倒计时与分数,待随机数已生成一段时间,拨动开始键,驱动游戏进行。此时倒计时与比较模块开始工作,将地鼠出现的位置信息与敲击按键的位置信息进行比对,如果一致,则分数加一,如不一致,分数保持不变,直到60s倒计时结束,各模块停止工作。按下重置键复位重新开始游戏。
四、详细模块设计
①分频模块:将100,000Hz的时钟信号源分频为所需的1Hz时钟信号,使得时钟信号能够每秒产生一个时钟脉冲。
--分频模块 library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity divclock is port( oldclk: IN std_logic; currclk: buffer std_logic); end; architecture one of divclock is constant useHz:integer:=100000;--旧时钟频率为100000Hz begin process(oldclk) variable count:integer range 0 to useHz-1;--设置计数器,保留旧时钟频率产生上升沿的次数 begin if oldclk'event and oldclk='1' then if count=(useHz-1)/2 then--0.5s时,计数器置0,新时钟翻转 count:=0; currclk<=NOT currclk; else count:=count+1; end if; end if; end process; end one;
1234567891011121314151617181920212223242526 ②随机数模块:取m序列的每四位的后两位,产生一定数量的伪随机数,作为地鼠的位置信息。
--output 2bits random number --利用m序列产生四位随机数,取每四位的后两位作为本次课设所需的随机数 library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_unsigned.all; entity rand is port(Reset: IN std_logic; Clk: IN std_logic; Data_out: OUT std_logic_vector(1 downto 0)); end rand; architecture rtl of rand is signal Shift_Register:std_logic_vector(3 downto 0); begin process(Reset,Clk) begin if(Reset='1') then Shift_Register<="1000"; else if(Clk'event and Clk='1') then Data_Out<=Shift_Register(1 downto 0); Shift_Register(0)<=Shift_Register(1); Shift_Register(1)<=Shift_Register(2); Shift_Register(2)<=Shift_Register(3); Shift_Register(3)<=Shift_Register(3) xor Shift_Register(0); end if; end if; end process; end rtl;
1234567891011121314151617181920212223242526272829303132333435 ③按键模块:通过转码,将按键的位置信息转化为对应位置上地鼠的位置信息。
--按键模块 library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity button_decode is port(a,b,c,d: IN std_logic;--定义4个按键 --clk: IN std_logic; result: OUT std_logic_vector(1 downto 0)); end button_decode; architecture one of button_decode is begin process(a,b,c,d) begin--对按键进行位置信息的转换 --if(clk'event and clk='1') then if a='0' then result<="11"; elsif b='0' then result<="10"; elsif c='0' then result<="01"; elsif d='0' then result<="00"; else result<=NULL; end if; --end if; end process; end;
12345678910111213141516171819202122232425262728293031 ④倒计时模块:利用分频得到1Hz的时钟频率设置倒计时,每当经过一个时钟脉冲,模块中的计时器减一,直至设定的时间结束为0或者通过reset重置。
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity timedown is port(start: IN std_logic; clk: IN std_logic; reset: IN std_logic; S: OUT std_logic_vector(3 downto 0);--十位 F: OUT std_logic_vector(3 downto 0);--个位 accomplish: OUT std_logic); --倒计时状态的输出信号,如果倒计时结束,则输出1 end timedown; architecture one of timedown is begin process(clk,start,reset) variable s2:std_logic_vector(3 downto 0);--十位 variable f2:std_logic_vector(3 downto 0);--个位 begin if(clk'event and clk='1') then if(reset='1') then --重置 s2:="0110";--6 f2:="0000";--0 elsif start='1' then if f2="0000" then --如果个位等于0而十位不等于0,则将个位置为9,十位减一 if s2/="0000" then f2:="1001"; s2:=s2-1; accomplish<='0'; else --如二者都为0,则倒计时保持“00”不变,同时将倒计时状态置为1 accomplish<='1'; f2:=f2; s2:=s2; end if; else --其他情况下,个位减一,倒计时状态置0 f2:=f2-1; accomplish<='0'; end if; end if; S<=s2; F<=f2; end if; end process; end one;
123456789101112131415161718192021222324252627282930313233343536373839404142434445 ⑤比较模块:将按键模块和随机数模块传入的位置信息进行比较,如果相同,则传送信号‘1’给计分模块,否则传送信号‘0’。
--compare library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity compare is port(Data_out,button: IN std_logic_vector(1 downto 0); clk: IN std_logic; accomplish: IN std_logic; true: OUT std_logic; start: IN std_logic); end compare; architecture one of compare is begin process(clk) begin if(start='1') then--如果游戏开始且尚未结束,则进行判断 if(accomplish='0') then if(Data_out=button) then--如果位置信息相同,则置信号为高电平 true<='1'; else true<='0'; end if; end if; end if; end process; end;
1234567891011121314151617181920212223242526272829 ⑥计分模块:判断比较模块传递的信号,如果位置信息相同则分数加一,否则分数保持不变。
--score library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity score is port(clk: IN std_logic; start: IN std_logic; true: IN std_logic; accomplish: IN std_logic; reset: IN std_logic; G: OUT std_logic_vector(7 downto 0));--分数 end score; architecture one of score is begin process(clk,start,true) variable temp1,temp2: std_logic_vector(3 downto 0);--分数的十位与个位 begin if(clk'event and clk='1') then if reset='1' then--重置分数 temp1:="0000";--十位 temp2:="0000";--个位 else if start='1' and true='1' then--如果游戏开始,且命中地鼠 if accomplish='0' then --如果倒计时没有结束 temp2:=temp2+1; --个位+1 if(temp2="1010")then --如果个位等于10,则十位加一,个位置0 temp1:=temp1+1; temp2:="0000"; end if; end if; elsif start='1' and true='0' then--如果游戏开始,地鼠未被命中,则分数保持不变 if accomplish='1' then temp2:=temp2; temp1:=temp1; end if; elsif start='0' then --如果拨下开始键,则分数清零 temp2:="0000"; temp1:="0000"; end if; G(7 downto 4)<=temp1; G(3 downto 0)<=temp2; end if; end if; end process; end one;
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 ⑦显示模块:根据各模块传送的数据,通过动态扫描技术实现相应内容的显示。
--显示模块 library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity transform is--定义多个接收端用来接收其他模块的信息,从而向晶体管输出对应的信息 port(Data_out: IN std_logic_vector(1 downto 0);--地鼠位置信息 S: IN std_logic_vector(3 downto 0); --倒计时十位 F: IN std_logic_vector(3 downto 0); --倒计时个位 G: IN std_logic_vector(7 downto 0); --分数 clk: IN std_logic; --动态扫描时钟信号 true: IN std_logic; --比较模块传来的位置比较结果 sel: OUT std_logic_vector(7 downto 0); --位选信号 led: OUT std_logic_vector(6 downto 0)); --晶体管显示 end transform; architecture one of transform is begin process(clk,true) variable m: integer range 0 to 4:=0;--定义变量m,以区分不同时间片下晶体管的显示区域 begin if(clk'event and clk='1') then if(m=0) then --m=0,以LED形式呈现地鼠(m=others时情况类似) m:=m+1; if(data_out="00") then sel<="11101111"; led<="1111110"; if(true='1') then --如果命中,则地鼠呈现被打扁状 led<="0001000"; end if; elsif(data_out="01") then sel<="11011111"; led<="1111110"; if(true='1') then led<="0001000"; end if; elsif(data_out="10") then sel<="10111111"; led<="1111110"; if(true='1') then led<="0001000"; end if; elsif(data_out="11") then sel<="01111111"; led<="1111110"; if(true='1') then led<="0001000"; end if; end if; elsif(m=1) then --如果 m=1,切换显示倒计时的十位 m:=m+1; sel<="11110111"; case S is when "0000" => led<="0111111";--0 when "0001" => led<="0000110";--1 when "0010" => led<="1011011";--2 when "0011" => led<="1001111";--3 when "0100" => led<="1100110";--4 when "0101" => led<="1101101";--5 when "0110" => led<="1111101";--6 when others => led<="0000000"; end case; elsif(m=2) then --如果 m=2,切换显示倒计时的个位 m:=m+1; sel<="11111011"; case F is when "0000" => led<="0111111";--0 when "0001" => led<="0000110";--1 when "0010" => led<="1011011";--2 when "0011" => led<="1001111";--3 when "0100" => led<="1100110";--4 when "0101" => led<="1101101";--5 when "0110" => led<="1111101";--6 when "0111" => led<="0000111";--7 when "1000" => led<="1111111";--8 when "1001" => led<="1101111";--9 when others => led<="0000000"; end case; elsif(m=3) then --如果 m=3,切换显示分数的十位 m:=m+1; sel<="11111101"; case G(7 downto 4) is when "0000" => led<="0111111";--0 when "0001" => led<="0000110";--1 when "0010" => led<="1011011";--2 when "0011" => led<="1001111";--3 when "0100" => led<="1100110";--4 when "0101" => led<="1101101";--5 when "0110" => led<="1111101";--6 when "0111" => led<="0000111";--7 when "1000" => led<="1111111";--8 when "1001" => led<="1101111";--9 when others => led<="0000000"; end case; elsif(m=4) then --如果 m=4,切换显示分数的个位 m:=0; sel<="11111110"; case G(3 downto 0) is when "0000" => led<="0111111";--0 when "0001" => led<="0000110";--1 when "0010" => led<="1011011";--2 when "0011" => led<="1001111";--3 when "0100" => led<="1100110";--4 when "0101" => led<="1101101";--5 when "0110" => led<="1111101";--6 when "0111" => led<="0000111";--7 when "1000" => led<="1111111";--8 when "1001" => led<="1101111";--9 when others => led<="0000000"; end case; end if; end if; end process; end one;
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116之后将编译好的子模块导入新项目中,将各模块生成各个单独的元器件,创建原理图文件,将各模块元器件导入,连线。
仿真五、板载测试
①通电下载后,随机数生成模块首先开始运作,前四位数码管显示随机出现的虚拟地鼠模型
②拨动reset键重置倒计时,进入游戏准备阶段。
③拨动选择游戏难度。
④拨动start键开始游戏,倒计时开始,比较和按键模块开始运行。
⑤如果击中地鼠,地鼠呈现被打扁状,分数+1,指示灯闪亮。
⑥倒计时未结束拨动reset键使置1,由于游戏期间不允许暂停,故倒计时重置,分数暂留,直至reset键置0,分数清零。
⑦倒计时结束后,分数保持不变,按键和比较模块停止工作,直至start置0,分数清零。
六、不足之处
地鼠一次只能出现一只且器械老化存在一定的干扰(时间紧迫,按键抖动未加)。
七、心得体会
通过这次实验设计,我真真正正地从实践中学到了很多。经历了无数的困难,我被给予了更多的经验与教训,让我有足够的能力去力图改进和应对下一次可能出现的艰难险阻。
在一开始的尝试过程中,我尝试以一个大文件的形式,写出一个极具整体性的VHDL代码,奈何能力不足,在实现上遇到了不少的困难,给自己增加了不少不必要的负担。在经过一段时间的考虑后,我选择将整体拆分成多个不同的小模块逐一实现,最后以原理图的方式连线实现。在新一轮的实践中,我试图将显示模块嵌入在其他各个小模块中,实践中发现这种方法存在诸多不便,最终确立将各模块完全剥离,得到了现阶段的最优的方案。在接下来的实现过程中,常常遇到因为考虑不周而使得部分模块超时执行的不正常现象,也有遇到不熟悉VHDL部分语句而出现的逻辑错误,在查阅资料并多次实验尝试各种不同的方法后,问题渐渐得以解决,最终实现了基本的打地鼠功能。
在板载测试成功后,此时距离提交成果的最后期限还有很多时间,在这段时间里,我开始考虑如何去完善和美化它,在之后,我陆陆续续修正了其中的一些不足之处,完善了部分代码,同时加入了地鼠的第二阶段形态以及游戏模式选择控件,使得游戏整体更加符合现实生活中真实用户的需求,最终得到了现在相对成熟的成果。
在这次的课程设计中,我不仅仅提高了自己的动手能力,更为重要的是理解了EDA融入于我们生活的真谛,我们真正学习技术,不应只会浅薄的纸上谈兵,更应该把技术应用于实践,用“死”的代码赋予现实生活丰富多彩。EDA是,又不是简简单单的一种技术,它是我们链接数字世界与三维世界的一把钥匙,是我们人与机械器件沟通的纽带与桥梁。我们要时时刻刻记住自己身为一名工科生的责任与担当,化静为动,化“死”为生。
源文件下载地址
盘+1234
网址:数字系统设计————打地鼠游戏设计 https://www.yuejiaxmz.com/news/view/611661
相关内容
SPRINGBOOT004旅游路线规划系统(JAVA毕业设计,附数据库和源码)如何更好地设计游戏中的日常任务?
快乐之道:游戏设计的黄金法则
游戏生活场景建模设计,游戏生活场景建模设计方案
家居设计游戏
装饰生活家居设计游戏
艺术留学娱乐设计=游戏设计?别天真了!
js实现打地鼠游戏(快来一起试试吧!!!)
游戏设计快乐之道(第2版)【全本
数字人IP形象设计创意玩法