基于Xilinx的FPGA的 健身游戏机设计
院 系: 电子与信息工程
专 业: 电信
班 级:
姓 名:
学 号:
指导教师:
●设计任务名称:基于Xilinx的FPGA的健身游戏机设计
●设计内容及要求:
1、基本功能:
用8个灯作目标,与之对应有8个按键进行控制。每一次8个灯中随机出现一个灯处于“亮”的状态,在灯亮的时间内要求“踩”到对应的按键,若踩到则加1分,且灯熄灭;否则失败扣一分。
设定初始命数为9,连续成功五次加1条命,分数不清0,连续失败5次扣一条命,命数为0时游戏结束。
游戏分四个难度级别,每个级别灯闪亮的速度不同,级别越高,速度越快,灯亮的时间越短。连续加两条命则速度加1.
设有暂停/继续和开始/停止功能,能记录和更新历史最高分数。控制液晶显示器进行相应的显示。命数最多显示为99条,分数最多显示为999分。
2、扩展功能:
每个难度级别对应产生不同的音乐,与灯闪的频率节奏相当。
●实验条件说明
Spartan-3E入门实验板:
自己设计外围电路:
提供芯片有:译码器74LS138,编码器74LS148,其余有,LED灯8个,按键8个,1k电阻8个,510Ω电阻1个,排线两根,导线一组,排针一组,设计好的外围电路如下:
●系统设计
⏹系统框图与说明
音乐模块
外接LED灯
外部按键输入
说明:
外部按键输入:通过74LS148编码器实现8线—3线编码,实现对应按键输入,因为编码器特性,全部为高时输出为1111(其中最高位为状态为)而在其他状态下区间为(0000----0111),可以方便实现与随机数的比较从而实现分数计算。具体电路图及逻辑见外设部分。
分数计算模块:实现外部按键与内部随机数的匹配比较,若匹配则分数加1,同时为命数模块提供一个win脉冲用来计数;若不匹配则分数减1,同时为命数模块提供一个lose脉冲用来计数。要求保留最高分数。这里要求对按键与随机数的比较次数进行识别,一个随机数只能比较一次,详见详细设计部分。
命数计算:通过分数计算模块说提供的win和lose信号进行计数,累计到5,则实现命数加减1,要求当命数连续加2后输出一个speed信号,此信号给音乐模块和随机数模块,进行音乐的变化和速度的升级。同时在命数减为0后输出一个over信号,告诉系统游戏结束。
随机数生成模块:此模块主要实现伪随机数输出,并且根据speed信号进行生成速度的转换,同时要求在pause信号和over信号为1时停止随机数输出。随机数外接到外设部分,通过译码器控制LED灯的亮灭。
外接LED 灯:根据随机数,通过74LS138译码器控制对应LED灯的随机闪烁。
LCD显示模块:根据分数计算模块,命数模块所输入BCD码以及内部设定在LCD上显示控制信息和生命分数信息。
音乐模块:通过speed信号的输入,控制音乐乐谱的变化,同时当over信号为高时停止音乐输出。
⏹状态转换图与说明
本实验涉及的状态转换较多,这里只详细介绍我所设计的模块的状态转换:
1:分数计算模块:
状态说明:这里S0状态下,将随机数random赋给一个中间变量random-temp,
然后比较按键信号和随机信号,若按键信号和随机信号相等,转入S1,否则转入S2;在S1状态下,若按键和随机数相等且随机信号和中间变量random-temp相等,意味着随机数没变化,则在S1处循环,直到随机数与中间变量不相等,意味着随机数发生变化,则分数加1;另外若在S1状态下,按键和随机数不等,则意味着按键已经抬起,但此时属于正确情况,则在随机数变化后,分数加1,状态转到S0;在S2状态下,若按键信号为“1111”,则意味着没有任何按键按下,则在随机信号变化之前,处于在S2状态循环,若随机信号变化,仍为“1111”,意味着没来的及按下,分数减1,转到S0,若在S2状态下,按键信号等于随机信号,意味着按键正确,则转入S1,若在S2状态下,按键信号不等于随机信号,同时也不等于“1111”,则判断按错,扣一分,转入S3;在S3状态下,一直处于循环,直到随机数变化转到S0。
完成完整的循环,上述状态变化,实现了判断按键与随机数的具体逻辑。后文有具体代码。
2:音乐模块:
Res
状态说明:音乐模块比较简单,主要是在乐谱里有状态的转换,状态S0为第一个曲谱,以Res信号为准,直接进入此状态,在S0状态下,遇到speed脉冲信号,则转换为S1为第二个曲谱,以此类推,没遇到一个speed转换一个曲谱,知道S3状态为最后的曲谱。
⏹输入输出设计(按键,数码管,发光管,蜂鸣器)与说明
分数计算模块:
res:输入为复位键,设置分数初始值为0,但不影响最大分数
clk:时钟输入,时钟触发,这里为了方便音乐模块选择1Mhz频率。
random:随机数输入,为4位总线。
button:按键输入,为4位总线。
lose:扣分输出一个正脉冲。
win:得分输出一个正脉冲。
六个4位总线输出,代表着分数和最大分数的BCD码。
命数模块:
res:复位信号,设置初始命数为9。
clk:时钟信号。
win:信号输入,控制是否加命。
lose:信号输入,控制是否减命。
speed:信号输出,连续加两命一个正脉冲。
Over:信号输出,命数为0则为1.
四个4位总线输出,分别代表命数和最大命数的BCD码。
随机数生成模块:
res复位信号,设置初始状态。
clk1m信号:时钟信号,做触发源。
clka:最低频率时钟信号,0.5Hz。
clkb:次低频时钟信号,2/3Hz。
clkc:较高频时钟信号,1Hz。
clkd:最高频时钟信号,2Hz。
Speed:信号输入,改变速度。
Pause:暂停控制信号。
Over:输出信号,生命为0。
Random:随机数输出。
音乐模块:
Res复位信号,设置初始状态。
Clk1m信号:时钟信号,触发源,分频后控制音节频率。
Clk8:时钟信号,用来控制音节节拍。
Over信号:命数为0时为高电平输入,此时音乐停止。
Pause信号:为高时音乐停止。
Mus信号:就是speed信号,控制曲谱的变化。
M_output:输出至喇叭,通过频率的变化出现音乐。
LCD模块:
res复位信号,设置初始状态。
最大生命;最大分数;当前分数;当前命数的BCD码输入。
Clk信号:时钟信号,这里是500HZ
Pause信号:为高时显示PAUSE
Over信号:为高时显示GAMEOVER
输出为液晶控制信号,有rs,rw,en和数据4位输出。
外设部分:
电路原理图如下
上部的芯片是译码器74LS138,实现3位随机数对应随机的8个LED灯。
下面的芯片是编码器74LS148,实现8个按键对应四位码值输出。
对于74LS138比较熟悉,在我们的数电和电子设计的书上都有对其用法的介绍,这里就不详细介绍,只重点介绍74LS148编码器,首先看下面的真值表:
可以看出,当没有任何按键输入时,恒定输出为1111,符合分数计算要求的逻辑,不过这里有一个地方要说明,验收时老师问我如果都按下会是什么效果,我说以第一个按下的键位为准,这个说法现在看来是错误的,按照真值表可知,这个按键是有优先级的,都按下时,输出为0000。
画出的PCB版图如下所示:
⏹详细设计关键代码及说明
1.分数计算模块:
端口设定代码如下
entity maxscore is
Port ( clk1m : in STD_LOGIC;
res : in STD_LOGIC;
random : in STD_LOGIC_VECTOR (3 downto 0);
button : in STD_LOGIC_VECTOR (3 DOWNTO 0);
win : out STD_LOGIC;
lose : out STD_LOGIC;
max100 : out STD_LOGIC_VECTOR (3 downto 0);
score100 : out STD_LOGIC_VECTOR (3 downto 0);
max10 : out STD_LOGIC_VECTOR (3 downto 0);
score10 : out STD_LOGIC_VECTOR (3 downto 0);
max1 : out STD_LOGIC_VECTOR (3 downto 0);
score1 : out STD_LOGIC_VECTOR (3 downto 0));
end maxscore;
分数计算代码段如下:
(对应于上面所描述的此模块的状态图)
signal random1:std_logic_vector(3 downto 0); --定义中间信号变量,用于寄存随机变量
signal s0,s1,s2,s3:std_logic;
begin
process(clk50m)
begin
if rising_edge(clk50m) then
if res='1' then
s0<='1';
s1<='0';
s2<='0';
s3<='0';
win<='0';
lose<='0';--res复位信号,状态转到S0,win信号和lose信号为0
elsif s0='1' then
random1<=random;--S0状态下,将随机数赋给中间变量
if button = random then
s1<='1';
s2<='0';
s0<='0';
s3<='0';
win<='0';
lose<='0';
--S0状态下,在按键与随机数相匹配时转到S1状态
else
s1<='0';
s2<='1';
s0<='0';
s3<='0';
win<='0';
lose<='0';--S0状态下,在按键与随机数不匹配时转到S2状态
end if;
elsif s1='1' then
if button = random then
if random1=random then
s1<='1';
s2<='0';
s0<='0';
s3<='0';
win<='0';
lose<='0';--S1状态下,若按键和随机数相等
且随机数没有变化,则在S1状态下循环
else
s1<='1';
s2<='0';
s0<='0';
s3<='0';
random1<=random;
win<='1';
lose<='0'; --S1状态下,若按键与随机数相等,
随机数又发生变化,证明已经是第
二次的随机数,则win信号给一个正脉冲
end if;
elsif random1=random then
s1<='1';
s2<='0';
s0<='0';
s3<='0';
win<='0';
lose<='0';--若在S1状态下按键与随机数不等
证明按键抬起,此时若随机数未
变,则仍在S1状态循环。
else
s0<='1';
s1<='0';
s2<='0';
s3<='0';
win<='1';
lose<='0';--若S1下,按键与随机数不等,且
随机数变化,根据上述分析,win
给一个正脉冲。回到S0
end if;
elsif s2='1' then
if button = "1111" then
if random1=random then
s1<='0';
s2<='1';
s0<='0';
s3<='0';
win<='0';
lose<='0';--在S2状态下,若按键为“1111”,
且随机数未变,则在S2循环
else
s0<='1';
s1<='0';
s2<='0';
s3<='0';
win<='0';
lose<='1';--若S2下,按键为“1111”。随机数
变化,则回到状态S0,给lose一个
正脉冲
end if;
elsif button = random then
s1<='1';
s2<='0';
s0<='0';
s3<='0';
win<='0';
lose<='0';--若S2下,按键符合随机数,转到
状态S1。
else
s1<='0';
s2<='0';
s0<='0';
s3<='1';
win<='0';
lose<='1';--若S2下,按键不符合随机数,且
不是“1111”,则给lose一个正脉
转到S3。
end if;
elsif s3='1' then
if random1=random then
s1<='0';
s2<='0';
s0<='0';
s3<='1';
win<='0';
lose<='0';--S3下,若随机数不变,则在S3循环。
else
s1<='0';
s2<='0';
s0<='1';
s3<='0';
win<='0';
lose<='0';--S3下,若随机数变化,则转到S0。
end if;
end if;
end if;
end process;
最大值判断模块:
process(clk50m)
begin
if rising_edge(clk50m) then
if res='1' then
scoret1<="0000";
scoret10<="0000";
scoret100<="0000";
scorem1<="0000";
scorem10<="0000";
scorem100<="0000";--复位信号,给最大值和分数赋0初值。
elsif winx='1' then
if scoret100 = "1001" and scoret10 = "1001" and scoret1 = "1001" then
scoret100 <= "1001";
scoret10 <= "1001";
scoret1 <= "1001"; --保证分数不超过999。
elsif scoret1="1001" then
scoret1<="0000" ;
scoret10<=scoret10+1;
elsif scoret10="1001"+1 then
scoret10<="0000";
scoret100<=scoret100+1;
else
scoret1<=scoret1+1; --实现分数在win信号下累加。
end if;
elsif losex='1' then
if scoret100 = "0000" and scoret10 = "0000" and scoret1 = "0000" then
scoret100 <= "0000";
scoret10 <= "0000";
scoret1 <= "0000";--保证分数不小于000
elsif scoret1="0000" then
scoret1<="1001" ;
scoret10<=scoret10-1;
elsif scoret10="0000"-1 then
scoret10<="1001" ;
scoret100<=scoret100-1;
else
scoret1<=scoret1-1;--实现分数在lose信号下递减。
end if;
end if;
if scorem100 scorem10<=scoret10; scorem1<=scoret1;--若最高位当前分数大于最大分数,则对最大值进行刷新 elsif scorem100=scoret100 and scorem10 scorem10<=scoret10; scorem1<=scoret1;--若最高为相等,十位当前分数大于最大分数,对最大值刷新。 elsif scorem100=scoret100 and scorem10=scoret10 and scorem1 scorem10<=scoret10; scorem1<=scoret1; --若最高位和十位都相等,个位的当前分数大于最大值,对最大值进行刷新。 else scorem100<=scorem100; scorem10<=scorem10; scorem1<=scorem1; --其余情况下最大值不变 end if; end if; end process; 2.音乐模块 首先对音乐模块进行简单介绍:乐曲都是有音符组成的,而音符由频率节拍数组成,音符的频率由pinpu模块实现,这里通过网络查到,各个音节的频率,在fenpin模块进行1mhz时钟输入,然后调用各个音节对应的初值,进行分频,由于直接分频得到的脉冲信号脉宽极窄,所以需另加一个D触发器以均衡其占空比,但得到的频率将变为原来的1/2。 在qupu模块中,存储该乐曲的音符的时长【即节拍数】。TONETABS的CLK输入的是8HZ的脉冲,即0.125S相当于7音符的持续时间为1S。 Pinpu模块 entity pinpu is Port ( INDEX : IN INTEGER RANGE 0 TO 20; TONE: OUT INTEGER RANGE 0 TO 16#7FF#); end pinpu; architecture Behavioral of pinpu is begin PROCESS( INDEX ) BEGIN CASE INDEX IS WHEN 0 =>TONE<=2047; --CODE <=0; HIGH<='0'; WHEN 1 =>TONE<=139; -- CODE <=1; HIGH<= -1; WHEN 2 =>TONE<=348; --CODE <=2; HIGH<= -1; WHEN 3 =>TONE<=532; --CODE <=3; HIGH<= -1; WHEN 4 =>TONE<=614; -- CODE <=4; HIGH<= -1; WHEN 5 =>TONE<=771; --CODE <=5; HIGH<= -1; WHEN 6 =>TONE<=911; --CODE <=6; HIGH<= -1; WHEN 7 =>TONE<=1035; -- CODE <=7; HIGH<= -1; WHEN 8 =>TONE<=1091; -- CODE <=1; HIGH<= '0'; WHEN 9 =>TONE<=1182; --CODE <=2; HIGH<= '0'; WHEN 10 =>TONE<=1288; --CODE <=3; HIGH<= '0'; WHEN 11 =>TONE<=1324; --CODE <=4; HIGH<= '0'; WHEN 12 =>TONE<=1409; -- CODE <=5; HIGH<= '0'; WHEN 13 =>TONE<=1479; --CODE <=6; HIGH<= '0'; WHEN 14 =>TONE<=1541; --CODE <=7; HIGH<= '0'; WHEN 15 =>TONE<=1569; --CODE <=1; HIGH<= '1'; WHEN 16 =>TONE<=1622; --CODE <=2; HIGH<= '1'; WHEN 17 =>TONE<=1668; --CODE <=3; HIGH<= '1'; WHEN 18 =>TONE<=16; --CODE <=4; HIGH<= '1'; WHEN 19 =>TONE<=1728; --CODE <=5; HIGH<= '1'; WHEN 20 =>TONE<=1763; --CODE <=6; HIGH<= '1'; WHEN OTHERS=>NULL; END CASE; END PROCESS; 用2047减去每个音节的初值,就是实现此音节的分频数。 Fenpin模块 entity fenpin is Port ( res :in std_logic; over :in std_logic; pause:in std_logic; CLK1M : in STD_LOGIC; TONE: IN INTEGER RANGE 0 TO 16#7FF# ; SPKS : out STD_LOGIC); end fenpin; architecture Behavioral of fenpin is SIGNAL FULLSPKS: STD_LOGIC; SIGNAL COUNT11: INTEGER RANGE 0 TO 16#7FF#; SIGNAL COUNT2: STD_LOGIC:='0'; begin PROCESS( CLK1M, TONE) BEGIN IF CLK1M'EVENT AND CLK1M='1' THEN IF res = '1' or over='1' or pause ='1' then COUNT11<=16#7FF#; --在复位,结束,暂停模式下, 无音频信号输出 elsif COUNT11=16#7FF# THEN COUNT11<= TONE; FULLSPKS<='1'; --每个音节对应自己的分频数, 进行分频 ELSE COUNT11<=COUNT11+1; FULLSPKS <='0'; END IF; END IF; END PROCESS; PROCESS( FULLSPKS ) BEGIN IF FULLSPKS'EVENT AND FULLSPKS='1' THEN COUNT2<=NOT COUNT2; IF COUNT2='1' THEN SPKS <='1'; ELSE SPKS <='0'; END IF; --D触发器,对每个音节已经分的频率再次2分频。 END IF; END PROCESS; qupu模块 Signal COUNTER : INTEGER RANGE 0 TO 215; signal s0,s1,s2,s3: std_logic:='0'; begin process(clk1m) begin if rising_edge(clk1m) then if res='1' then s0<='1'; s1<='0'; s2<='0'; s3<='0'; --复位时设置状态为S0 elsif s0='1' then if music='1' then s0<='0'; s1<='1'; s2<='0'; s3<='0'; --S0状态时music有脉冲,则转到S1 否则S0循环 else s0<='1'; s1<='0'; s2<='0'; s3<='0'; end if; elsif s1='1' then if music='1' then s0<='0'; s1<='0'; s2<='1'; s3<='0'; else s0<='0'; s1<='1'; s2<='0'; s3<='0'; --S1状态时music有脉冲,则转到S2 否则S1循环 end if; elsif s2='1' then if music='1' then s0<='0'; s1<='0'; s2<='0'; s3<='1'; else s0<='0'; s1<='0'; s2<='1'; s3<='0'; --S2状态时music有脉冲,则转到S3 否则S2循环 end if; end if; end if; end process; process(CLK8,music) Begin If COUNTER=213 or music='1' then COUNTER<=0; Elsif(CLK8'EVENT AND CLK8='1')then COUNTER<=COUNTER+1; End if; --对曲谱的自动递增循环 End process; PROCESS(COUNTER) begin if s0='1' then CASE COUNTER IS WHEN 0 =>TONEINDEX <=3; WHEN 1 =>TONEINDEX <=3; WHEN 2 =>TONEINDEX <=3; WHEN 3 =>TONEINDEX <=3; …………………………………………………………… 以下是对不同状态下不同曲谱的描述,原理简单,这里不赘述。 6. LCD 模块 entity lcd is Port ( res : in STD_LOGIC; clk500 : in STD_LOGIC; mlife10 : in STD_LOGIC_VECTOR (3 downto 0); mscore100 : in STD_LOGIC_VECTOR (3 downto 0); life10 : in STD_LOGIC_VECTOR (3 downto 0); score100 : in STD_LOGIC_VECTOR (3 downto 0); mlife1 : in STD_LOGIC_VECTOR (3 downto 0); mscore10 : in STD_LOGIC_VECTOR (3 downto 0); life1 : in STD_LOGIC_VECTOR (3 downto 0); score10 : in STD_LOGIC_VECTOR (3 downto 0); mscore1 : in STD_LOGIC_VECTOR (3 downto 0); score1 : in STD_LOGIC_VECTOR (3 downto 0); pause : in STD_LOGIC; over:in STD_LOGIC; output : out STD_LOGIC_VECTOR (3 downto 0); lcd_en : out STD_LOGIC; lcd_rs : out STD_LOGIC; lcd_rw : out STD_LOGIC); end lcd; architecture Behavioral of lcd is type my_state is(A,B,C,D,E,G,H,I,J,K,L,M,O,ra,rc,rd,re,rf,rg,rh,rj,rk,rm,rn,ro,rp,rs,rx,ry, sa,sc,sd,se,sf,sg,sh,sj,sk,sm,sn,so,sp,sx,sy); signal state,next_state : my_state; signal count: integer range 0 to 4:=0; begin lcd_en <= clk; --对en使能的设置,使其等于500hz的时钟 lcd_rw<='0'; --设置rw为0 process(clk,res) begin if res='1' then state<=A; --初始状态为A elsif rising_edge (clk) then state <= next_state; end if; end process; process(state,next_state) begin lcd_rs<='0'; case (state) is when A=> output<="0011"; next_state<=B; when B=> output<="0011"; next_state<=C; when C=> output<="0011"; next_state<=D; when D=> output<="0010"; next_state<=E; when E=> output<="0010"; next_state<=G; when G=> output<="1000"; next_state<=H; when H=> output<="0000"; next_state<=I; when I=> output<="0110"; next_state<=J; when J=> output<="0000"; next_state<=K; when K=> output<="1100"; next_state<=L; when L=> output<="0000"; next_state<=M; when M=> output<="0001"; next_state<=O; --上面是对液晶的初始设置 when O=> output<="1000"; next_state<=ra; when ra=> output<="0000"; next_state<=rc; --液晶显示的地址 when rc=> lcd_rs<='1'; output<="0101"; next_state<=ps1; when ps1=> lcd_rs<='1'; if pause = '1' then output<="0000"; next_state<=ps2; --若PAUSE信号为高则显示P,否则显示S else output<="0011"; next_state<=ps2; end if; when ps2=> lcd_rs<='1'; if pause = '1' then output<="0100"; next_state<=ps3; else output<="0101"; next_state<=ps3; end if; when ps3=> lcd_rs<='1'; if pause = '1' then output<="0001"; next_state<=ps4; else output<="0100"; next_state<=ps4; --若PAUSE信号为高则显示A,否则显示T end if; when ps4=> lcd_rs<='1'; if pause = '1' then output<="0101"; next_state<=ps5; else output<="0100"; next_state<=ps5; end if; when ps5=> lcd_rs<='1'; if pause = '1' then output<="0101"; next_state<=ps6; else output<="0001"; next_state<=ps6; --若PAUSE信号为高则显示U,否则显示A end if; when ps6=> lcd_rs<='1'; if pause = '1' then output<="0101"; next_state<=ps7; else output<="0101"; next_state<=ps7; end if; when ps7=> lcd_rs<='1'; if pause = '1' then output<="0011"; next_state<=ps8; else output<="0010"; next_state<=ps8; --若PAUSE信号为高则显示S,否则显示R end if; when ps8=> lcd_rs<='1'; if pause = '1' then output<="0100"; next_state<=ps9; else output<="0101"; next_state<=ps9; end if; when ps9=> lcd_rs<='1'; if pause = '1' then output<="0101"; next_state<=rd; else output<="0100"; next_state<=rd; --若PAUSE信号为高则显示E,否则显示T end if; when rd=> output<="1000"; next_state<=rd1; when rd1=> output<="0111"; next_state<=mh1; --液晶显示地址 when mh1=> lcd_rs<='1'; output<="0100"; next_state<=mh2; when mh2=> lcd_rs<='1'; output<="1101"; next_state<=mh3; --显示M when mh3=> lcd_rs<='1'; output<="0101"; next_state<=mh4; when mh4=> lcd_rs<='1'; output<="0011"; next_state<=mh5; --显示S when mh5=> lcd_rs<='1'; output<="0011"; next_state<=mh6; when mh6=> lcd_rs<='1'; output<=mscore100; next_state<=mh7; when mh7=> lcd_rs<='1'; output<="0011"; next_state<=mh8; when mh8=> lcd_rs<='1'; output<=mscore10; next_state<=mh9; when mh9=> lcd_rs<='1'; output<="0011"; next_state<=mh10; when mh10=> lcd_rs<='1'; output<=mscore1; next_state<=mh11; --以上显示最大分数 when mh11=> lcd_rs<='1'; output<="0100"; next_state<=mh12; when mh12=> lcd_rs<='1'; output<="1101"; next_state<=mh13; --显示M when mh13=> lcd_rs<='1'; output<="0100"; next_state<=mh14; when mh14=> lcd_rs<='1'; output<="1100"; next_state<=mh15; --显示L when mh15=> lcd_rs<='1'; output<="0011"; next_state<=mh16; when mh16=> lcd_rs<='1'; output<=mlife10; next_state<=mh17; when mh17=> lcd_rs<='1'; output<="0011"; next_state<=mh18; when mh18=> lcd_rs<='1'; output<=mlife1; next_state<=rj; --以上显示最大生命值 when rj=> output<="1100"; next_state<=rk; when rk=> output<="0000"; next_state<=rx; --选择液晶显示地址 when rx=> lcd_rs<='1'; if over='1' then output<="0100"; else output<="0101"; end if; next_state<=ry; when ry=> lcd_rs<='1'; if over='1' then output<="0111"; else output<="0011"; end if; next_state<=rm; --若OVER为1,显示G,否则显示S when rm=> lcd_rs<='1'; if over='1' then output<="0100"; else output<="0011"; end if; next_state<=rn; when rn=> lcd_rs<='1'; if over='1' then output<="0001"; else output<=score100; end if; next_state<=rn1; --若OVER为1,显示A,否则显示分数的百位 when rn1=> lcd_rs<='1'; if over='1' then output<="0100"; else output<="0011"; end if; next_state<=ro; when ro=> lcd_rs<='1'; if over='1' then output<="1101"; else output<=score10; end if; next_state<=rp; --若OVER为1,显示M,否则显示分数的十位 when rp=> lcd_rs<='1'; if over='1' then output<="0100"; else output<="0011"; end if; next_state<=rs; when rs=> lcd_rs<='1'; if over='1' then output<="0101"; else output<=score1; end if; next_state<=sa; --若OVER为1,显示E,否则显示分数的个位 when sa=> lcd_rs<='1'; if over='1' then output<="0100"; else output<="0010"; end if; next_state<=sc; when sc=> lcd_rs<='1'; if over='1' then output<="1111"; else output<="0000"; end if; next_state<=sd; --若OVER为1,显示O,否则显示空格 when sd=> lcd_rs<='1'; if over='1' then output<="0101"; else output<="0100"; end if; next_state<=se; when se=> lcd_rs<='1'; if over='1' then output<="0110"; else output<="1100"; end if; next_state<=sf; --若OVER为1,显示V,否则显示L when sf=> lcd_rs<='1'; if over='1' then output<="0100"; else output<="0011"; end if; next_state<=sg; when sg=> lcd_rs<='1'; if over='1' then output<="0101"; else output<=life10; end if; next_state<=sh; --若OVER为1,显示E,否则显示生命的十位 when sh=> lcd_rs<='1'; if over='1' then output<="0101"; else output<="0011"; end if; next_state<=go1; when go1=> lcd_rs<='1'; if over='1' then output<="0010"; else output<=life1; --若OVER为1,显示R,否则显示生命的个位 end if; next_state<=O; --回到O状态循环检测数值的变化 end case; ●系统仿真 1.分数计算模块 包括了几乎所有可能的按键取值,通过仿真可以看出各种情况都满足要求,同时最分数和最大生命数满足要求。不随复位键更改。 2.其余模块 我做的另外两个模块都无法通过仿真验证,所以是在板子上验证的。 其中LCD模块验证结果如下 ●实验总结: 这次的实验在难度上要比上次所作的交通灯要大很多,不过可能是因为在交通灯中学到了不少经验,对于整个流程有了一个比较正确的方向性,所以在软件这一块感觉很轻松,其中比较有难度的一个是分数与按键的匹配判定,还有一个就是音乐的生成,通过画状态图,还有上网查资料,一切都还顺利。 最有难度,也是花了最多时间的反而是硬件,本来觉得这个外接电路并不复杂,PCB应该不难设计,可是真正设计来才发现它的难度,多次的返工几乎让我们放弃,可是当时的感觉就是已经花了这么多的时间,放弃实在是不忍心,骑虎难下啊!终于把PCB设计完,在制作过程中仍旧是一波多折,因为对流程的不熟悉,还有操作的不灵活,导致印制的板子失败了三次,整整两天,终于把板子做好,以为可以放心的焊接上电,新的问题接踵而来,因为设计的失误,导致铜线过细,焊接上一个不小心就造成短路,焊完后才发现这个问题,此时近百的焊点一个一个排查,着实是个大工程,可怜我和潘峰,一个拿万用表,一个拿着电路板,一个姿势就是几个小时,问题出现就要解决,解决短路的问题又引出新的问题,因为铜线过细,一个不小心,铜线就断掉了,用焊锡接,又一个不小心,短路了……如此重复,等到最终作完,才发现,除去前期的思考和学习,真正的制作过程有6天,其中4天竟然都是在做硬件…… 虽然困难重重,但却深刻的感觉到学到了很多东西,前路慢慢,仍需努力啊! 最后很感谢杨老师给我的帮助,没有您的指导,我必然要走更多的弯路,没有上过您的FPGA的课,我也不可能完成的这么轻松! 谢谢您!! ●附录(管脚分配) NET "button<0>" LOC = "D7" ; NET "button<1>" LOC = "C7" ; NET "button<2>" LOC = "F8" ; NET "button<3>" LOC = "E8" ; NET "clk50m" LOC = "C9" ; NET "lcd_en" LOC = "M18" ; NET "lcd_rs" LOC = "L18" ; NET "lcd_rw" LOC = "L17" ; NET "music_out" LOC = "A6" ; NET "output<0>" LOC = "R15" ; NET "output<1>" LOC = "R16" ; NET "output<2>" LOC = "P17" ; NET "output<3>" LOC = "M15" ; NET "pause" LOC = "L13" ; NET "random<0>" LOC = "B4" ; NET "random<1>" LOC = "A4" ; NET "random<2>" LOC = "D5" ; NET "random<3>" LOC = "C5" ; NET "res" LOC = "L14" ;