标题: EDA乒乓球 [打印本页]
作者: 51黑ss 时间: 2016-3-30 23:20
标题: EDA乒乓球
无处不在的电子应用需求,对电子技术的发展提出了更高的要求。大规模集成电路的设计、制作技术是电子技术的基础。集成电路电容量、低功耗、小体积的发展方向,使得传统的数字系统设计方法已经远远不能满足设计需要,电子设计自动化EDA技术代替传统的数字系统设计方法和手段已成为必然。
二、实验目的
1、初步认识EDA技术Verilog HDL,熟悉可编辑逻辑器件基础;
2、掌握Verilog HDL语法及要素,能够熟练运用行为语句,了解常用数据单元及设计风格;
3、学习典型的FPGA的结构与配置,引脚输入输出端口功能;
4、进行状态机的设置,更进一步的深入熟悉一些基础例程;
5、学会Quartus Ⅱ的应用及一些仿真与测试。
三、实验题目
乒乓球游戏设计
比赛规则约定:七局四胜;11分一局;比赛进行,18个LED排列成行模拟乒乓球台;点亮的LED模拟乒乓球,受FPGA控制从左到右或从右到左移动;比赛选手通过按钮输入模拟击球信号,实现LED移位方向的控制;若发亮的LED运动在球台倒数第四个至对方终点之间时,对方未能及时按下击球按钮使其向相反方向移动,即失去一分。
四、实验要求
任务要求:用按键与LED(或者点阵)表示输入与输出。
(1)初始时,16个LED最边上的点亮,按下键表示发球,亮的灯依次向对方移动;移位计数器控制。
(2)当到达另一边倒数第 4 个灯时表示乒乓球触到桌面反弹;对方必须在反弹后且靠 近己方的最后一个灯亮起来前按下按键表示接球,否则输球;
(3)接球后灯亮的规则、对方接球的规则同发球;
(4)双方靠近自己的 4个灯亮的间隔 0.4s,其余灯亮的间隔 0.8s;计数器来实现时 钟控制;
(5)输球或者犯规,即失去一分;自动显示于数码管(共阴);
(6)乒乓球比赛规则计分,显示于数码管;
这种比较简单的方式实现
五、实验设计
利用两个按键表示两位选手,LED灯表示乒乓球的轨迹,数码管显示击球结果。分析乒乓球跳动速度,乒乓球在接发球状态时时速度可以达到0.4s,乒乓球在运行中速度慢些,可以达到0.8s.
本程序由3个部分组成,第一部分为分频程序,将de2自带的50Mhz频率分为1MHZ,1lKHZ,10hz.第二部分为数码管分时扫描程序,扫描速度1khz.第三部分是击球状态机程序。
因手上无示波器,也无必要用示波器,故只用了软件仿真。
六、源代码及注释
module pinpang(clk_1MHZ,clk_1khz,clk_10hz,clk_27,clk_50,hex0,hex1,hex2,hex3,hex4,hex5,hex6,hex7,clc,set_r,set_l,led_r);
input clc,set_r,set_l; //定义清除键,在程序中按下不放为暂停
input clk_27,clk_50; //定义输入时钟信号,此程序中只用了clk_50,即50M的频率
output clk_1MHZ,clk_1khz,clk_10hz;//定义分频后信号1MHZ,1KHZ,10HZ
reg clk_1MHZ,clk_1khz,clk_10hz;
reg[5:0] clk_sum;//1MHZfenpin //定义分频1MHZ计数器
reg[9:0] clk_sum1k;//fenpinwei 1khz //定义分频1khz分频器
reg[6:0] clk_sum10hz;//ledxianshipinlv //定义分频10hz分频器
reg[6:0] hex_buff1; //定义数码管缓存寄存器
reg[3:0] hex_buff; //定义数码管取值寄存器
reg[3:0] hex_sum; //数码管扫描片选信号
reg[3:0] r_sum,l_sum; //定义每局中左边或者右边得分数目默认a_为左,b_为右
reg[3:0] ju_sum; //led灯延时计数器,每100ms计数一次
reg[2:0] a_run,b_run; //定义左边和右边当前赢得的局数
reg[2:0] state; //定义状态机所处位置
reg[3:0] game_on; //定义按键处于何种状态
reg[31:0] time_sum; //定义LED从次态到现态时间
reg[4:0] qiu; //定义当前LED灯位置
output[6:0] hex0,hex1,hex2,hex3,hex4,hex5,hex6,hex7;//8段数码管显示数
reg[6:0] hex0,hex1,hex2,hex3,hex4,hex5,hex6,hex7;
output[17:0]led_r;
reg [17:0]led_r; //led灯状态
parameter hexx0=7'b1000000, //0
hexx1=7'b1111001, //1
hexx2=7'b0100100, //2
hexx3=7'b0110000, //3
hexx4=7'b0011001, //4
hexx5=7'b0010010, //5
hexx6=7'b0000010, //6
hexx7=7'b1111000, //7
hexx8=7'b0000000, //8
hexx9=7'b0010000, //9
hexxn=7'b1111111; //null
always@(posedge clk_50)begin //fenpinwei1MHZ
clk_sum<=clk_sum+6'b000001; //分频为1MHZ
if(!clc)
clk_sum<=0;
if(clk_sum==24)begin
clk_sum<=0;
clk_1MHZ<=~ clk_1MHZ;
end
end
always@(posedge clk_1MHZ)begin //FENPINWEI 1KHZ
clk_sum1k<=clk_sum1k+10'b0000000001; //分频为1KHZ
if(!clc)
clk_sum1k<=0;
if(clk_sum1k==499)begin
clk_sum1k<=0;
clk_1khz<=~ clk_1khz;
end
end
always@(posedge clk_1khz)begin //FENPINWEI 10HZ//分频为10hz
clk_sum10hz<=clk_sum10hz+7'b0000001;
if(!clc)
clk_sum10hz<=0;
if(clk_sum10hz==49)begin
clk_sum10hz<=0;
clk_10hz<=~ clk_10hz;
end
end
always@(posedge clk_1khz)begin //FENPINWEI 1kHZ//在1kHz内处理数码管和按键
hex_sum<=hex_sum+4'b0001;
if(!clc)
hex_sum<=0;
if(hex_sum==8)
hex_sum<=0;
case(hex_sum)
6:hex_buff<=a_run; //第二位 显示左边边当前赢得局数,实际为0
//由于de2开发板数码管位置移位,故该程序中数码管非实验板相应位置,需要调整
7:hex_buff<=b_run; //第一位显示右边当前赢得局数
0:hex_buff<= a_run; //第三位显示左边赢得局数
1:hex_buff<= ju_sum%10;//第四位显示当前局数
2:hex_buff<= r_sum%10; //第五位显示右边当前得分个位
3:hex_buff<= r_sum/10; //第六位显示右边当前得分十位
4:hex_buff<= l_sum%10; //第七位显示当前左边赢得得分个位
5:hex_buff<= l_sum/10; //第八位显示当前左边赢得得分十位
default:hex_buff=4'b1111;
endcase
if(!set_l)
begin
if(state==5||state==0)
//左按键按下,判断LED灯是否落在左边,若果是继续下去,否则记为错误按左键
game_on<=4'b0110;
else
game_on<=4'b1110;
end
if(!set_r)
begin
if(state==2||state==3)
//右按键按下,判断LED灯是否落在右边,若果是继续下去,否则记为错误按右键
game_on<=4'b0101;
else
game_on<=4'b1101;
end
end
always@(negedge clk_1khz) begin
//在clk-1khz的下降沿中处理函数,将数码管中缓存值翻译过来,赋给数码管
case(hex_buff)
0:hex_buff1<=hexx0;
1:hex_buff1<=hexx1;
2:hex_buff1<=hexx2;
3:hex_buff1<=hexx3;
4:hex_buff1<=hexx4;
5:hex_buff1<=hexx5;
6:hex_buff1<=hexx6;
7:hex_buff1<=hexx7;
8:hex_buff1<=hexx8;
9:hex_buff1<=hexx9;
default:hex_buff1<=hexx0;
endcase
case(hex_sum)
0: hex0<=hex_buff1;
1: hex1<=hex_buff1;
2: hex2<= hex_buff1;
3: hex3<= hex_buff1;
4: hex4<=hex_buff1;
5: hex5<= hex_buff1;
6: hex6<= hex_buff1;
7: hex7<= hex_buff1;
default:
begin
hex0<=hexxn;
hex1<=hexxn;
hex2<=hexxn;
hex3<=hexxn;
hex4<=hexxn;
hex5<=hexxn;
hex6<=hexxn;
hex7<=hexxn;
end
endcase
end
always@(posedge clk_10hz)begin //每100ms进行状态机设置
time_sum<=time_sum+1;
led_r=0; //避免LED全亮,先将LED全灭
if(time_sum==8) //每个LED灯间隔发光的时间不超过0.8s
time_sum<=0;
case(state)
5: //状态5左边发球
if(game_on==4'b0110)//如果检测到左键正确按下,执行程序
begin
led_r[qiu]=1;
if(time_sum==3)begin //如果LED灯已走了0.4s,则点亮下个LED灯
time_sum<=0;
led_r[qiu]=0; //先将现态LED灯光灭,在点亮下个LED灯
qiu<=qiu-5'b00001;
led_r[qiu]=1;
if(qiu<=14)
//因为定义了18个LED灯作为实验乒乓球台,所以当左边已发完球后,应立即进入左边运球给右边的状态4
state<=4;
end
end
else
begin / /如果没有检测到左键正确按下,则LED【17】常亮
led_r[17]<=~led_r[17];
end
4: //左边运球到右边的状态
begin
led_r[qiu]=1; //若果球已到右边接球边界,进入右边接球状态3
if(qiu<=3)
state<=3;
if(state==4)
begin
if(game_on==4'b1101)begin
l_sum<=l_sum+4'b0001;
state<=2; //若右键在运球中错误按下,算右边犯规,左边加一分
led_r[qiu]=0; //球归右边并自动发球
qiu<=0;
led_r[qiu]=1; //显示当前LED灯位置
time_sum<=1;
end
if(time_sum==0)begin
//每隔0.8s判断LED灯是否跳入下个状态,若是则将当前LED灯光灭,将下个LED灯点亮
led_r[qiu]=0;
qiu<=qiu-5'b00001;
led_r[qiu]=1; //运球没有失误,进入右边接球状态3
if(qiu<=3)
state<=3;
end
end
end
3:
begin
if(game_on==4'b0101) //右边等待接球状态
begin
state<=2;
led_r[qiu]=0;
qiu<=qiu+5'b00010; //若已接球,立即进入状态2或1
led_r[qiu]=1;
End
if(state==3)
begin
led_r[qiu]=1;
if(time_sum==3)begin //接球中LED灯闪动速度为0.4s
time_sum<=0;
led_r[qiu]=0; //关闭当前LED灯,打开下个LED灯
qiu<=qiu-5'b00001;
led_r[qiu]=1;
if(qiu==0)begin
state<=2;
l_sum<=l_sum+4'b0001; //若接球失败,进入右边准备发球状态,左边得一分
end
end
end
end
2: //右边发球状态
if(game_on==4'b0101)
begin //若有有效键按下,则立即发球否则进入等待状态
led_r[qiu]=1;
if(qiu>=3) //若超出界限,立即进入状态右边运球左边的状态1
state<=1;
if(state==2)begin
if(time_sum==3)begin
time_sum<=0; //每隔0.4s球向左边移动一位
led_r[qiu]=0;
qiu<=qiu+5'b00001;
led_r[qiu]=1;
if(qiu>=3) //若球已超出右边发球边界,进入右边运球到左边的状态1
state<=1;
end
end
end
else
begin
led_r[0]<=~led_r[0]; //若当前没有有效右键按下,一直处于等待阶段。
end
1:
begin
led_r[qiu]=1;
if(qiu>=14)
state<=0;
if(state==1)begin
if(game_on==4'b1110)begin
r_sum<=r_sum+4'b0001;
state<=5;
//运球过程中,左边错误按下,右边得一分,球立即从左边发出
led_r[qiu]=0;
qiu<=17;
led_r[qiu]=1;
time_sum<=1;
end
if(time_sum==0)begin
led_r[qiu]=0;
qiu<=qiu+5'b00001;
led_r[qiu]=1; //球到左边接球边界,进入左边接球状态0
if(qiu>=14)
state<=0;
end
end
end
0: //左边接球状态
begin
if(game_on==4'b0110)
begin //左键正确按下,进入左边发球状态
state<=5;
led_r[qiu]=0;
qiu<=qiu-5'b00010;
led_r[qiu]=1;
end
if(qiu==17)begin
//初始状态球可能赋值为零,这里强制转换到状态5,实际没有出现这种状态
state<=5;
r_sum<=r_sum+4'b0001;
end
led_r[qiu]=1;
if(state==0)
begin
if(time_sum==3)begin//每隔0.4s球运动到下一位
time_sum<=0;
led_r[qiu]=0;
qiu<=qiu+5'b00001;
led_r[qiu]=1;
if(qiu==17)begin
//左边球落到最后一个LED上,说明球已过线,左边接球失败,右边加一分
state<=5;
r_sum<=r_sum+4'b0001;
end
end
end
end
endcase
if(r_sum==11||l_sum==11)begin
//11分为一局,一局结束后进行下一局,左边右边当前局数归零,总局数加一
if(l_sum==11)a_run<=a_run+1;
if(r_sum==11)b_run<=b_run+1;
r_sum<=0;
l_sum<=0;
ju_sum<=ju_sum+4'b0001;
end
//若已进行7局,则比赛从新开始
if(ju_sum==7)begin
ju_sum<=0;
a_run<=0;
b_run<=0;
end
end
endmodule
七、实验流程
1、定义引脚图:
2分频仿真:分频为1MHZ,1KHZ(ms)
分频仿真:1MHz,1khz放大图(us)
3状态机图
程序设计为6个状态,实际只有4个状态,可能是由于状态0状态5优化,状态2和状态3优化为一个状态。
4、将程序下载进入DE2的开发板,当前处于等待发球状态,显示结果为第一局结束,当前局数比左边:右边=0:1。
5、左边按下发球键,乒乓球向前移动,右边进入等待接球状态;
6、在乒乓球进入右边第四个灯亮之前右边接球,此时左边得一分,并由右边发球继续进行比赛,如果在最后一个灯亮之前未接球,对方加一分,并有己方发球继续比赛;
7、长按左边第一个键,此时进入时间暂停,小分暂不显示;
8、按要求左右双方在倒数第四个灯亮之后,最后一个灯亮前接球均为好球,这个接球状态有1.2秒左右,就可进行乒乓球的游戏;
9、游戏进行,比分交替上升,当有人小分率先到达十一分时,进入下一局,如图显示为:第二局结束,局数比为1:1,准备进入下一局,同样规则继续,当有一方率先赢下四局便获胜,游戏结束。
八、心得体会
通过一天多来的努力,我们终于把老师交给我们的任务完成了,明白了fpga编程的一些小技巧。开始第一天因为电脑系统原因,无法安装驱动,后来换了电脑驱动就可以安装了。到网上没有找到引脚库文件,所以只好自己一个一个定义引脚。
在编程过程中,发现直接赋值常数语句最好用x’b---比较好,否则程序默认常数为32位,出现警告。在整个模块中,寄存器值只能在某个always语句中被赋值,否则会出现错误。定义的输入输出引脚,寄存器引脚也最好使用,否则也会出现警告,若这些变量不需要使用,可以注释掉,以便不时之需。
欢迎光临 (http://www.51hei.com/bbs/) |
Powered by Discuz! X3.1 |