乒乓球模拟器的组装方法图解(FPGA:乒乓球比赛模拟机的设计)
简介
开发板:EGO1
开发环境:Windows10 + Xilinx Vivado 2020
数字逻辑大作业题目 7: 乒乓球比赛模拟机的设计
乒乓球比赛模拟机用发光二极管(LED)模拟乒乓球运动轨迹 ,是由甲乙双方参赛 ,加上裁判的三人游戏(也可以不用裁判) 。
管脚约束代码:
点击查看代码 set_property IOSTANDARD LVCMOS33 [get_ports CLK] set_property IOSTANDARD LVCMOS33 [get_ports hitA] set_property IOSTANDARD LVCMOS33 [get_ports hitB] set_property PACKAGE_PIN P17 [get_ports CLK] set_property PACKAGE_PIN P5 [get_ports hitA] set_property PACKAGE_PIN R1 [get_ports hitB] set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[5]}] set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[4]}] set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[7]}] set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[6]}] set_property PACKAGE_PIN F6 [get_ports {ballLocation[7]}] set_property PACKAGE_PIN G4 [get_ports {ballLocation[6]}] set_property PACKAGE_PIN G3 [get_ports {ballLocation[5]}] set_property PACKAGE_PIN J4 [get_ports {ballLocation[4]}] set_property PACKAGE_PIN H4 [get_ports {ballLocation[3]}] set_property PACKAGE_PIN J3 [get_ports {ballLocation[2]}] set_property PACKAGE_PIN J2 [get_ports {ballLocation[1]}] set_property PACKAGE_PIN K2 [get_ports {ballLocation[0]}] set_property IOSTANDARD LVCMOS33 [get_ports speedA] set_property PACKAGE_PIN P4 [get_ports speedA] set_property IOSTANDARD LVCMOS33 [get_ports speedB] set_property PACKAGE_PIN N4 [get_ports speedB] set_property IOSTANDARD LVCMOS33 [get_ports {statusOut[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {statusOut[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {statusOut[1]}] set_property PACKAGE_PIN K1 [get_ports {statusOut[3]}] set_property PACKAGE_PIN H6 [get_ports {statusOut[2]}] set_property PACKAGE_PIN M1 [get_ports {statusOut[1]}] set_property PACKAGE_PIN K3 [get_ports {statusOut[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {statusOut[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED1[5]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED0[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED0[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED1[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED0[6]}] set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[4]}] set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[7]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED1[6]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED0[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED1[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED0[4]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED0[7]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED1[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[5]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED1[4]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED0[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED0[5]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED1[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[6]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED1[7]}] set_property PACKAGE_PIN B4 [get_ports {LED0[0]}] set_property PACKAGE_PIN A4 [get_ports {LED0[1]}] set_property PACKAGE_PIN A3 [get_ports {LED0[2]}] set_property PACKAGE_PIN B1 [get_ports {LED0[3]}] set_property PACKAGE_PIN A1 [get_ports {LED0[4]}] set_property PACKAGE_PIN B3 [get_ports {LED0[5]}] set_property PACKAGE_PIN B2 [get_ports {LED0[6]}] set_property PACKAGE_PIN D5 [get_ports {LED0[7]}] set_property PACKAGE_PIN D4 [get_ports {LED1[0]}] set_property PACKAGE_PIN E3 [get_ports {LED1[1]}] set_property PACKAGE_PIN D3 [get_ports {LED1[2]}] set_property PACKAGE_PIN F4 [get_ports {LED1[3]}] set_property PACKAGE_PIN F3 [get_ports {LED1[4]}] set_property PACKAGE_PIN E2 [get_ports {LED1[5]}] set_property PACKAGE_PIN D2 [get_ports {LED1[6]}] set_property PACKAGE_PIN H2 [get_ports {LED1[7]}] set_property PACKAGE_PIN G2 [get_ports {LEDBit[0]}] set_property PACKAGE_PIN C2 [get_ports {LEDBit[1]}] set_property PACKAGE_PIN C1 [get_ports {LEDBit[2]}] set_property PACKAGE_PIN H1 [get_ports {LEDBit[3]}] set_property PACKAGE_PIN G1 [get_ports {LEDBit[4]}] set_property PACKAGE_PIN F1 [get_ports {LEDBit[5]}] set_property PACKAGE_PIN E1 [get_ports {LEDBit[6]}] set_property PACKAGE_PIN G6 [get_ports {LEDBit[7]}] set_property IOSTANDARD LVCMOS33 [get_ports reset] set_property PACKAGE_PIN P2 [get_ports reset]设计要求
主要功能 模拟乒乓球比赛 ,用发光二极管(LED)模拟乒乓球运动轨迹 ,由甲乙双方参赛; 用8个LED灯表示球桌 ,其中点亮的LED来回移动表示乒乓球的运动 ,球速可以调节; 当球移动到最左侧或最右侧时 ,表示一方的击球位置 。如果提前击球 ,或未及时击球 ,则对方得一分; 甲乙得分使用数码管计分 ,一局11球; 用发光二极管表示甲乙的发球权 ,每5分交换发球权 。 附加功能 用发光二极管提示甲乙的接球和发球; 比赛结束后 ,用数码管动态显示胜利的一方 。工作原理
本电路由时钟分频模块,玩家控制器模块 ,分数处理模块 ,游戏控制模块,乒乓球运动控制模块和数码管显示模块组成 。
比赛开始前 ,可以通过reset开关重置比赛; 比赛进行时 ,甲乙两位选手通过扳动开关来实现挥动球拍和控制球速的效果 。当乒乓球到击球位置时 ,若选手未及时击球 ,或提前击球 ,则输掉一球 ,对方加一分 。每打5球 ,就交换一次球权 ,共打11球 ,数码管上会显示当前得分 ,分高者获胜; 比赛结束后 ,数码管会显示箭头来表示一方的获胜; 另外还有4个LED来表示双方的发球和接球 。 系统方框图:各部分模块具体功能及设计思路
游戏控制器模块
模块功能:控制整个模拟器各组件状态; 设计思路:该模块主要是用于控制比赛的进行 。在设计中 ,使用status表示当前的比赛状态 。010表示A发球,001表示B发球 ,110表示玩家A接球 ,101表示玩家B接球 。这样的规定能够有效区分乒乓球不同的运动状态,并判定发/击球的有效性 ,同时显示在LED灯上来提示选手 。另外再用accurateBallLocation [32:0]来表示球的精确位置 ,范围为$1000_{10} - 9000_{10} $ ,这样使球在LED显示的误差范围内 ,可以被击中。 代码: 点击查看代码 `timescale 1ns / 1ps module GameController( //全局状态控制器 input CLK, input reg hitA, //玩家A输入 input [1: 0] speedA, //玩家A速度 input reg hitB, //玩家B输入 input [1: 0] speedB, //玩家B速度 input reg serviceSide, //发球方 input reg reset, //重置 output reg [2: 0] status, //全局状态 output reg [7: 0] ballLocation, //球位置 output reg getScoreA, //A得分 output reg getScoreB //B得分 ); reg hitATrigger; reg hitBTrigger; reg [2: 0] speed; reg [15: 0] accurateBallLocation; reg resetTrigger; // reg serviceSide; initial begin //初始化变量 hitATrigger = b0; hitBTrigger = b0; status = b010; accurateBallLocation = d2000; speed = d2; // serviceSide = b0; getScoreA = b0; getScoreB = b0; resetTrigger = b0; end always @(posedge CLK) begin //根据报告所述转换状态 if(resetTrigger == b0 && reset == b1) begin hitATrigger = b0; hitBTrigger = b0; status = b010; accurateBallLocation = d2000; speed = d2; // serviceSide = b0; getScoreA = b0; getScoreB = b0; end else begin if(status == b010 || status == b001) begin//换发球 status = serviceSide == b0 ? b010 : b001; getScoreA = b0; getScoreB = b0; end if(status == b010) begin //A发球 accurateBallLocation = d2000; if(hitATrigger == b0 && hitA == b1) begin status = b101; if(speedA == d00) speed = d2; else speed = d4; end hitATrigger = hitA; end else if(status == b001) begin //B发球 accurateBallLocation = d10000; if(hitBTrigger == b0 && hitB == b1) begin status = b110; if(speedB == d00) speed = d2; else speed = d4; end hitBTrigger = hitB; end else if(status == b110) begin //A接球 if(hitATrigger == b0 && hitA == b1) begin if(accurateBallLocation >= d1000 && accurateBallLocation <= d3000) begin status = b101; if(speedA == d00) speed = d2; else speed = d4; end end hitATrigger = hitA; if(accurateBallLocation < d500) begin getScoreB = b1; status = serviceSide == b0 ? b010 : b001; end accurateBallLocation -= speed * d3; end else if(status == b101) begin //B接球 if(hitBTrigger == b0 && hitB == b1) begin if(accurateBallLocation >= d9000 && accurateBallLocation <= d11000) begin status = b110; if(speedB == d00) speed = d2; else speed = d4; end end hitBTrigger = hitB; if(accurateBallLocation >d11500) begin getScoreA = b1; status = serviceSide == b0 ? b010 : b001; end accurateBallLocation += speed * d3; end end resetTrigger = reset; if(accurateBallLocation >= d2000 && accurateBallLocation < d3000) ballLocation = b10000000;//球的位置显示 if(accurateBallLocation >= d3000 && accurateBallLocation < d4000) ballLocation = b01000000; if(accurateBallLocation >= d4000 && accurateBallLocation < d5000) ballLocation = b00100000; if(accurateBallLocation >= d5000 && accurateBallLocation < d6000) ballLocation = b00010000; if(accurateBallLocation >= d6000 && accurateBallLocation < d7000) ballLocation = b00001000; if(accurateBallLocation >= d7000 && accurateBallLocation < d8000) ballLocation = b00000100; if(accurateBallLocation >= d8000 && accurateBallLocation < d9000) ballLocation = b00000010; if(accurateBallLocation >= d9000 && accurateBallLocation <= d10000) ballLocation = b00000001; end endmodule玩家控制模块
模块功能:控制玩家输入与接发球操作;
设计思路:在设计电路中规定了使能端EN ,玩家只有在轮到自己发/击球时才有效;并规定了击球的间隔 ,模拟了击空的情况 。除此之外还设计实现了玩家击球速度的选择 。
代码:
点击查看代码 `timescale 1ns / 1ps module Player(CLK, EN, hit, speed, hitOut, speedOut); input CLK, EN, hit, speed; output reg hitOut; output reg [1: 0] speedOut; reg [31: 0] activeInterval = d1000; //一个下降沿到下一个上升沿直接最小时间间隔 reg [31: 0] interval; reg hitTrigger; initial begin interval = d0; hitTrigger = b0; hitOut = b0; speedOut = b1; end always @(posedge CLK) begin if(EN == b1) begin if(hitTrigger ==b0 && hit == b1) begin if(interval >= activeInterval) begin hitOut = hit; end end else if(hitTrigger == b1 && hit == b0) begin interval = d0; hitOut = hit; end hitTrigger = hit; interval += 1; if(speed == b0) begin speedOut = d00; end else begin speedOut = d01; end end end endmodule时钟分频模块
模块功能:对时钟分频;
设计思路:将EG01的100MHZ的时钟分频为1000HZ。
代码:
点击查看代码 `timescale 1ns / 1ps module ClockDivider(originCLK, dividedCLK); input originCLK; output dividedCLK; reg tempDivCLK; reg [31: 0] count; // reg [31: 0] ratio = d2; reg [31: 0] ratio = d100_000; //时钟分频器 ,将P17的100MHz分为1000Hz initial begin tempDivCLK = b0; count = d0; end always @(posedge originCLK) begin count = count + 1; if(count == ratio) count = d0; if(count == d0) tempDivCLK = b0; if(count == ratio / 2) tempDivCLK = b1; end assign dividedCLK = tempDivCLK; endmodule乒乓球控制模块
模块功能:接受信号控制乒乓球从左向右移动 ,或者从右向左移动 ,并且可以根据玩家选择的击球速度去调整;
设计思路:用8个LED模拟 ,点亮的灯表示球的位置 ,然后像流水灯一样来回滚动 ,在发球时暂停 。
代码:这里实际上包括在了游戏控制,下面代码是调用其他的Main 。
点击查看代码 `timescale 1ns / 1ps module Main( input CLK, input hitA, input speedA, input hitB, input speedB, input reset, output reg [3: 0] statusOut, output wire [7: 0] ballLocation, output wire [7:0] LED0, output wire [7:0] LED1, output wire [7:0] LEDBit ); wire [2: 0] status; wire dividedCLK; wire [1: 0] speedOutA; wire [1: 0] speedOutB; wire getScoreA, getScoreB; ClockDivider clockDivider(CLK, dividedCLK); wire serviceSide; reg EnA; reg EnB; initial begin EnA = b1; EnB = b1; end Player player1(dividedCLK, EnA, hitA, speedA, hitOutA, speedOutA); Player player2(dividedCLK, EnB, hitB, speedB, hitOutB, speedOutB); GameController gameController( //调用全局状态控制器 dividedCLK, hitOutA, speedOutA, hitOutB, speedOutB, serviceSide, reset, status, ballLocation, getScoreA, getScoreB ); always @(posedge dividedCLK) begin if(status == b010) begin statusOut = b1000; end else if(status == b001) begin statusOut = b0001; end else if(status == b110) begin statusOut = b0100; end else if(status == b101) begin statusOut = b0010; end end reg [7:0][7:0] dataIn; reg [31:0] count; initial begin count = d0; while(count < 8) begin dataIn[count] = d100; count ++; end count = d0; end DigitalTubeDriver digitalTubeDriver( //调用数码管驱动 dividedCLK, dataIn, LED0, LED1, LEDBit ); wire endGame; wire [1:0] winner; wire [15: 0] scoreA; wire [15: 0] scoreB; ScoreBoard scoreBoard( dividedCLK, getScoreA, getScoreB, reset, serviceSide, endGame, winner, scoreA, scoreB ); reg [7:0] i; reg [7:0] j; reg [31:0] countTemp; reg [31:0] countTemp2; reg resetTrigger; reg [31: 0] flowLightCount; reg endGameTrigger; initial begin resetTrigger = b0; flowLightCount = d0; endGameTrigger = d0; end always @(posedge dividedCLK) begin if(resetTrigger == b0 && reset == b1) begin EnA = b1; EnB = b1; dataIn[2] = d100;//不显示 dataIn[3] = d100; dataIn[4] = d100; dataIn[5] = d100; endGameTrigger = d0; end resetTrigger = reset; i = d0; countTemp = scoreB; while(i < d2) begin dataIn[i] = countTemp % d10; countTemp /= d10; i++; end j = d6; countTemp2 = scoreA; while(j < d8) begin dataIn[j] = countTemp2 % d10; countTemp2 /= d10; j++; end if(endGame == b1) begin //游戏结束时显示箭头指向赢的玩家 if(endGameTrigger == b0) begin EnA = b0; EnB = b0; end if(winner == b10) begin case(flowLightCount) d100: dataIn[2] = d22;//箭头 d200: dataIn[3] = d22; d300: dataIn[4] = d22; d400: dataIn[5] = d22; endcase flowLightCount++; if(flowLightCount == d500) begin flowLightCount = d0; dataIn[2] = d100; dataIn[3] = d100; dataIn[4] = d100; dataIn[5] = d100; end end else begin case(flowLightCount) d100: dataIn[5] = d21;//箭头 d200: dataIn[4] = d21; d300: dataIn[3] = d21; d400: dataIn[2] = d21; endcase flowLightCount++; if(flowLightCount == d500) begin flowLightCount = d0; dataIn[2] = d100; dataIn[3] = d100; dataIn[4] = d100; dataIn[5] = d100; end end end endGameTrigger = endGame; end endmodule分数处理模块
模块功能:计数 。每进行一轮控制分数加1 ,判断是否已打够11球 ,是则判别出获胜方 。
设计思路:在A,B两人分数上升沿时 ,对总分加1 ,然后判断是否已满11球 。若满11球 ,比较判断出胜利的一方 ,随后将其状态传给显示模块用于显示结果 。
代码:
点击查看代码 `timescale 1ns / 1ps module ScoreBoard( input CLK, input getScoreA, input getScoreB, input reset, output reg serviceSide, output reg endGame, output reg [1:0] winner, output reg [15: 0] scoreA, output reg [15: 0] scoreB ); reg getScoreATrigger; reg getScoreBTrigger; reg resetTrigger; initial begin serviceSide = b0; endGame = b0; getScoreATrigger = b0; getScoreBTrigger = b0; scoreA = b0; scoreB = b0; resetTrigger = b0; end always @(posedge CLK) begin if(resetTrigger == b0 && reset == b1) begin serviceSide = b0; endGame = b0; getScoreATrigger = b0; getScoreBTrigger = b0; scoreA = b0; scoreB = b0; end else begin //getScoreA或getScoreB出现上升沿 ,对应玩家得分 if(getScoreATrigger == b0 && getScoreA == b1) scoreA ++; if(getScoreBTrigger == b0 && getScoreB == b1) scoreB ++; getScoreATrigger = getScoreA; getScoreBTrigger = getScoreB; if((scoreA + scoreB) / 5 % 2 == d0) //每5个球换发 serviceSide = b0; else serviceSide = b1; if(scoreA + scoreB == d11) //到达11个球时游戏结束 endGame = b1; if(endGame == 1) begin //游戏结束时判断赢的那方 if(scoreA > scoreB) winner = b10; else if(scoreA < scoreB) winner = b01; else winner = b11; end else begin winner = b00; end end resetTrigger = reset; end endmodule数码管显示模块
模块功能:利用数码管显示比赛数据;
设计思路:使用$ 8 * 8 $的矩阵显示每个数码管的显示情况 ,另外设有对每个数码管表示显示的标志 ,从而动态地去更新 。在有一方获胜后 ,会将不显示分数的数码管动态地闪烁箭头 ,以此来表示获胜的一方 。
代码:
点击查看代码 `timescale 1ns / 1ps //参考EGO1的数码管显示模块 module DigitalTubeDriver( //数码管驱动 input CLK, input reg [7:0][7:0] dataIn, //输入数据 output reg [7:0] LED0, //输出的LED0 ,管理前4位显示 output reg [7:0] LED1, //输出的LED1 ,管理后4位显示 output reg [7:0] LEDBit //LEDBIT ,管理每个亮或不亮 ); reg [3:0] count; wire [7:0] data0; initial begin LEDBit = b00000001; count = d0; end // assign LED1 = LED0; always @(posedge CLK) begin case(dataIn[count]) //检查每种数字或符号对应亮哪些边 d0: LED0 = b00111111; d1: LED0 = b00000110; d2: LED0 = b01011011; d3: LED0 = b01001111; d4: LED0 = b01100110; d5: LED0 = b01101101; d6: LED0 = b01111101; d7: LED0 = b00000111; d8: LED0 = b01111111; d9: LED0 = b01101111; d21: LED0 = b01110000; d22: LED0 = b01000110; default: LED0 = b00000000; endcase if(count == d7) begin count = d0; LEDBit = b00000001; end else if(count == d0) begin LEDBit = b10000000; count = d1; end else begin count++; LEDBit = LEDBit >> 1; end LED1 = LED0; end endmodule参考文献
[1] Vivado环境下多个并行的仿真测试文件如何支持单独仿真 。
https://blog.csdn.net/CDCL19_220327/article/details/125802252?spm=1001.2014.3001.5502
[2] Vivado里程序固化详细教程 。
https://blog.csdn.net/sinat_15674025/article/details/84535754?spm=1001.2014.3001.5502
[3] xilinx vivado 自带仿真工具xsim信号为蓝色Z态的解决办法 。
https://blog.csdn.net/Shawge/article/details/107592471?spm=1001.2014.3001.5502
[4] Vivado环境下多个并行的仿真测试文件如何支持单独仿真?
https://blog.csdn.net/CDCL19_220327/article/details/125802252?spm=1001.2014.3001.5502
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!