乒乓球游戏电路
1.设计的目的:
随着科学技术日益迅速的发展,数字系统己深入到生活的各个方面,它具有技术效果好、经济效益高、技术先进、造价低、可靠性高、维修方便等许多优点。所以我们更应当熟练掌握数字系统的设计,以便将来更好地应用在实践方面。下面通过学过的venlog HDL硬件描述语言,设计一款乒乓球游戏电路,通过给定的一个信号来满足灯的亮、灭与移动的速度,进而来实现迷你型的乒乓球游戏。
2.课程设计题目描述和要求
该游戏电路的实际效果图如下所示。游戏共有两人,分别为甲方和乙方,双方轮流发球,按下按键表示发球。在发球后,发球方最近一位 LED 点亮,亮的灯依次向对方移动(如甲发球,则 LED 灯从 LEDOI 开始向右移动),移动速度自定,当到达对方最后一位时,1s 内对方必须按下按键表示接球(如到达 LED07 时乙必须在 1s 内按键),接球后 LED 灯向对方移动。否则输球。接球时,LED没有亮到最后一位时就按下接球按键为犯规。输球或者犯规,对方加1分,率先加到 11 分者游戏胜出。
游戏效果图
3.设计思想和过程
利用所学的 Verilog 知识,可以分别定义正 af、aj 、bf、bj,分别表示 A 的发球与接球、B 的发球与接球。采用七段数码管分别记录和显示其 A 与 B 的得分情况,当作控制指定的按键来实现接球与发球的游戏功能,并且利用 7位二进制数来控制具体的乒乓球的运动情况和各个得分情况,来实现课题所要求的各种情况。对于时间方面,可以采用分频器来进行具体的时间控制。按照上述思想,整个设计的代码如下:
module pp(shift, seg7, seg8, clk50Mhz, rst, af, aj, bf, bj);
output[4:0] shift;
output[6:0] seg7;
output[6:0] seg8;
input clk50Mhz;
input af;
input aj;
input bf;
input bj;
input rst;
reg[4:0] shift;
reg[6:0] seg7;
reg[6:0] seg8;
reg clk2hz;
reg[3:0] a_score, b_score;
reg[23:0] cnt;
reg a, b;
reg[4:0] shift_1;
always@(posedge clk50Mhz)
begin
if(cnt==24'd12500000)
begin
clk2hz=~clk2hz;
cnt<=0;
end
else
cnt<=cnt+1;
end
always@(posedge clk50Mhz)
begin
if(rst)
begin
a_score<=0;
b_score<=0;
a<=0;
b<=0;
shift_1<=0;
end
else
begin
if(!a&&!b&&af)
begin
a<=1;
shift_1<='b10000;
end
else if(!a&&!b&&bf)
begin
b<=1;
shift_1<='b00001;
end
else if(a&&!b)
begin
if(shift_1>'b00100)
begin
if(bj)
begin
a_score<=a_score+1;
a<=0;
b<=0;
shift_1<='b00000;
end
else
begin
shift_1[4:0]<=shift_1[4:0]>>1;
end
end
else if(shift_1==1'b0)
begin
a_score<=a_score+1;
a<=0;
b<=0;
end
else
begin
if(bj)
begin
a<=0;
b<=1;
end
else
begin
shift_1[4:0]<=shift_1[4:0]>>1;
end
end
end
else if(b&&!a)
begin
if(shift_1<'b00100&&shift_1!='b0)
begin
if(aj)
begin
b_score<=b_score+1;
a<=0;
b<=0;
shift_1<='b00000;
end
else
begin
shift_1[4:0]<=shift_1[4:0]<< 1;
end
end
else if(shift_1=='b0)
begin
b_score<=b_score+1;
a<=0;
b<=0;
end
else
begin
if(aj)
begin
a<=1;
b<=0;
end
else
begin
shift_1[4:0]<=shift_1[4:0]<<1;
end
end
end
end
shift<=shift_1;
if(a_score=='b1011&&!rst)
begin
a_score<=a_score;
b_score<=b_score;
end
if(b_score=='b1011&&!rst)
begin
a_score<=a_score;
b_score<=b_score;
end
end
always@(posedge clk2hz)
begin
case(a_score[3:0])
'b0000: seg7[6:0]=7'b0000001;
'b0001: seg7[6:0]=7'b1001111;
'b0010: seg7[6:0]=7'b0010010;
'b0011: seg7[6:0]=7'b0000110;
'b0100: seg7[6:0]=7'b1001100;
'b0101: seg7[6:0]=7'b0100100;
'b0110: seg7[6:0]=7'b0100000;
'b0111: seg7[6:0]=7'b0001111;
'b1000: seg7[6:0]=7'b0000000;
'b1001: seg7[6:0]=7'b0000100;
default: seg7[6:0]='bx;
endcase
case(b_score[3:0])
'b0000: seg8[6:0]=7'b0000001;
'b0001: seg8[6:0]=7'b1001111;
'b0010: seg8[6:0]=7'b0010010;
'b0011: seg8[6:0]=7'b0000110;
'b0100: seg8[6:0]=7'b1001100;
'b0101: seg8[6:0]=7'b0100100;
'b0110: seg8[6:0]=7'b0100000;
'b0111: seg8[6:0]=7'b0001111;
'b1000: seg8[6:0]=7'b0000000;
'b1001: seg8[6:0]=7'b0000100;
default: seg8[6:0]='bx;
endcase
end
endmodule
该设计模块采用的是 if 语句的方式,根据不同的输入信号来进行条件判断并输出最终结果。由于使用 if 嵌套逻辑上较为烦琐,将LED灯缩减为5个,以减少代码量。
编写测试代码如下:
module tbpp;
reg clk;
reg af;
reg aj;
reg bf;
reg bj;
reg reset;
wire[4:0] shift;
wire[6:0] seg7;
wire[6:0] seg8;
initial
begin
clk=0;
reset=0;
#10 reset=1;
#20 reset=0;
end
always #5 clk=~clk;
initial
begin
af=0;bf=0;
#40 bf=1;
#10 bf=0;
repeat (4) @(posedge clk);
#5 aj=1;
#10 aj=0;
repeat (3) @(posedge clk);
bj=1;
#10 bj=0;
#30;
@(posedge clk);
#5 bf=1;
#10 bf=0;
#100 $stop;
end
pp pp(shift,seg7,seg8,clk,reset,af,aj,bf,bj);
endmodule
运行仿真后可得下图所示的功能仿真波形图,模拟的过程是b先发球,随后a接球成功,b接球失败。然后b发球,a一直没有接球。这样两次得到的a和b的积分均为1。最后按照引脚配置下载到开发板即可完成硬件的验证。
功能仿真波形图
该设计模块可以得到如下所示的电路结构。
由于本设计是一个时序电路,而且明显有不同的状态区分:发球过程、球移动过程和接球过程,所以也可以使用状态机的方式来完成设计,这样得到设计史加具有时序性,而且也便于设计,故给出一个状态机设计的代码如下:
module pingp(clk, reset, push1, push0, led, decode1, decode2, decode3, decode4, clk_out);
input clk, reset;
input push1, push0;
output [6:0] led, decode1, decode2, decode3, decode4;
output clk_out;
fenpin hz(clk, reset, clk_out);
ctl ctll(.clk(clk),.reset(reset),.push1(push1),.push0(push0),.led(led),
.decode1(decode1),.decode2(decode2),.decode3(decode3),.decode4(decode4));
endmodule
module ctl(clk, reset, push1, push0, led, decode1, decode2, decode3, decode4);
input clk, reset;
input push1, push0;
output [6:0] led, decode1, decode2, decode3, decode4;
reg [3:0] M,N;
reg [6:0] led, decode1, decode2, decode3, decode4;
reg [2:0] state;
parameter s0=3'b000,
s1=3'b001,
s2=3'b010,
s3=3'b011,
s4=3'b100;
always@(posedge clk)
begin
if(reset)
begin
led<=7'b0000000;
M<=4'b0000;
N<=4'b0000;
end
else
begin
case(state)
s0: //初始发球
begin
led<=7'b0000000;
if(push0)
begin
state<=s1;
led<=7'b1000000;
end
else if(push1)
begin
state<=s3;
led<=7'b0000001;
end
end
s1: //甲发球或甲接球后,球的移动
begin
if(push1)
begin
state<=s0;
M<=M+4'b0001;
end
else if(led==7'b0000001)
begin
state<=s2;
end
else
begin
state<=s1;
led[6:0]<=led[6:0]>>1;
end
end
s2:if(push1) //乙接球
begin
state<=s3;
led<=7'b0000010;
end
else
begin
state<=s0;
M<=M+4'b0001;
end
s3: //乙发球或接球后,球的移动
begin
if(push1)
begin
state<=s0;
N<=N+4'b0001;
end
else if(led==7'b1000000)
begin
state<=s4;
end
else
begin
state<=s3;
led[6:0]<=led[6:0]<<1;
end
end
s4: //甲接球
if(push0)
begin
state<=s1;
led=7'b0100000;
end
else
begin
state<=s0;
N<=N+4'b0001;
end
default:state<=s0;
endcase
if(M==4'b1011 || N==4'b1011)
begin
M<=4'b0000;
N<=4'b0000;
end
case(M) //显示甲得分
8'b0000: begin
decode1<=7'b1000000;
decode2<=7'b1000000;
end
8'b0001: begin
decode2<=7'b1000000;
decode1<=7'b1111001;
end
8'b0010: begin
decode2<=7'b1000000;
decode1<=7'b0100100;
end
8'b0011: begin
decode2<=7'b1000000;
decode1<=7'b0101111;
end
8'b0100: begin
decode2<=7'b1000000;
decode1<=7'b0011001;
end
8'b0101: begin
decode2<=7'b1000000;
decode1<=7'b0010010;
end
8'b0110: begin
decode2<=7'b1000000;
decode1<=7'b1000010;
end
8'b0111: begin
decode2<=7'b1000000;
decode1<=7'b1111000;
end
8'b1000: begin
decode2<=7'b1000000;
decode1<=7'b0000000;
end
8'b1001: begin
decode2<=7'b1000000;
decode1<=7'b0010000;
end
8'b1010: begin
decode2<=7'b1111001;
decode1<=7'b1000000;
end
8'b1011: begin
decode2<=7'b1111001;
decode1<=7'b1111001;
end
default: begin
decode2<=7'b1000000;
decode1<=7'b1000000;
end
endcase
end
case(N) //显示乙得分
8'b0000: begin
decode4<=7'b1000000;
decode3<=7'b1000000;
end
8'b0001: begin
decode4<=7'b1000000;
decode3<=7'b1111001;
end
8'b0010: begin
decode4<=7'b1000000;
decode3<=7'b0100100;
end
8'b0011: begin
decode4<=7'b1000000;
decode3<=7'b0101111;
end
8'b0100: begin
decode4<=7'b1000000;
decode3<=7'b0011001;
end
8'b0101: begin
decode4<=7'b1000000;
decode3<=7'b0010010;
end
8'b0110: begin
decode4<=7'b1000000;
decode3<=7'b0000010;
end
8'b0111: begin
decode4<=7'b1000000;
decode3<=7'b1111000;
end
8'b1000: begin
decode4<=7'b1000000;
decode3<=7'b0000000;
end
8'b1001: begin
decode4<=7'b1000000;
decode3<=7'b0010000;
end
8'b1010: begin
decode4<=7'b1111001;
decode3<=7'b1000000;
end
8'b1011: begin
decode4<=7'b1111001;
decode3<=7'b1111001;
end
default: begin
decode4<=7'b1000000;
decode3<=7'b1000000;
end
endcase
end
endmodule
module fenpin(reset, clock, clk1hz);
input reset, clock;
output clk1hz;
reg clk1hz;
reg[24:0] count1;
always @(posedge clock or posedge reset)
begin
if(reset)
count1<=0;
else if(count1==25'd25000000)
begin
clk1hz<=~clk1hz;
count1<=0;
end
else
count1<=count1+1;
end
//always @(clk)
//clk_out=clk;
endmodule
可以看到在设计模块中使用了一个状态机来完成不同情况的转换。在初始状态下 LED 全灭,随着游戏者拨动开关,使 push0 和 push1 信号产生变化,进入下属的四个不同状。例如,进入甲发球则进入 s1 状态,此状态的主要功能是完成LED灯从左向右的移动过程,并且判断在此过程中的乙方输入值,如果在 s1 状态中乙方有输入,表示球未到而乙拨动了开关,此时乙输而甲加分。而如果在球移动的过程中乙未接,则在 LED 灯移动到乙处时进入 s2 状态,即乙接球状态。在s2状态中如果乙方有输入,则表示接球成功,进入 s3 状态;如果乙方没有输入,表示接球失败,甲加分。接下来的s3 状态和 s1 状态相似,只是球的运行方向变为由乙向甲,如果甲有输入则甲输,如果甲没有输入则等到Led灯移动甲处进入 s4 状态。s4 状态与 s2 状态相似,如果甲接球成功则进入 s1 状态,完成循环:甲接球失败则算甲输。
编写测试模块如下,与上一个测试模块基本相似,验证所设想功能的正确性。
module tbpingp;
reg clk, reset;
reg push1, push0;
wire[6:0] led, decode1, decode2, decode3, decode4;
wire clk_out;
initial
begin
clk=0;
reset=0;
#10 reset=1;
#20 reset=0;
end
always #5 clk=~clk;
initial
begin
push1=0;push0=0;
#40 push1=1;
#10 push1=0;
repeat (7) @(posedge clk);
push0=1;
#20 push0=0;
repeat (3) @(posedge clk);
push1=1;
#10 push1=0;
#30 ;
@(posedge clk);
#5 push1=1;
#10 push1=0;
#100 $stop;
end
pingp pingpang(clk, reset, push1, push0, led, decode1, decode2, decode3,
decode4, clk_out);
endmodule
运行测试模块得到下图所示的仿真波形图。该波形图中共体现了两种情况;第一种情况出现在 reset 高电平之后,push1 出现高电平,表示乙发球,接下来在 LED 灯移动到甲处时(即 LED 值为 1000000 时)push0 出现高电平,表示甲接起球,然后在未到接球位置时 push1 再次出现高电平,表示乙接球失败,M 计数为 0001。第二种情况是接下来继续 push1 再次变为 1,在移动到甲处时没有甲的输入信号,所以甲输,此时 N 计数变为 0001。这样分别模拟了接到一次发球和没有接到发球两种情况。
功能仿真波形图
由于该游戏电路实际硬件验证结果更加直观,所以可以使用开发板做硬件验证,使用 Quartus 可以得到如图所示的电路结构图、状态转换图。
结构图
**
在代码通过编译和综合之后可以对设计模块进行引脚分配,配置中decode共有四组,decod2 和 decode1 是M的计数显示,decode4 和 decode3 是 N 的计数显示,这两个端口在仿真波形图中没有添加,这是因为数码管的译码输出在仿真波形图中显示并不直观,放在硬件电路中显示更容易验证正误。
4.设计扩展
本设计的基本功能己经无法扩展,如要增加设计的复杂性,可以在外围电路方面添加一些额外的功能。例如,可以在计数到 11 之后添加蜂鸣或闪灯等警示功能,或者增加一个裁判开关,可由裁判来判别增加或减少得分、或者胜负标志等。
5.视频连接: