EDA fpga乒乓球实验报告指导老师:杨月 姓名: 孙鹏 吴斌 李文伯
一实验原理:利用两个按键表示两位选手,LED灯表示乒乓球的轨迹,数码管显示击球结果。 本程序由3个部分组成,第一部分为分频程序,将de2自带的50Mhz频率分为1MHZ,1lKHZ,10hz.第二部分为数码管分时扫描程序,扫描速度1khz.第三部分是击球状态机程序。 因手上无示波器,也无必要用示波器,分频仿真如下图所示: 由乒乓球跳动速度来看,乒乓球在接发球时速度可以达到0.4s,乒乓球在运行中速度慢些,可以达到0.8s. 二源代码如下所示: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; time_sum<=1; end if(time_sum==0)begin 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; 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; led_r[qiu]=0; qiu<=qiu+5'b00001; led_r[qiu]=1; if(qiu>=3) 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 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
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
//本程序仅供参考,可能与实际不符 三实验感想:通过一天多来的努力,我们终于把老师交给我们的任务完成了,明白了fpga编程的一些小技巧。开始第一天因为电脑系统原因,无法安装驱动,后来换了电脑驱动就可以安装了。到网上没有找到引脚库文件,所以只好自己一个一个定义引脚。 在编程过程中,发现直接赋值常数语句最好用x’b---比较好,否则程序默认常数为32位,出现警告。在整个模块中,寄存器值只能在某个always语句中被赋值,否则会出现错误。定义的输入输出引脚,寄存器引脚也最好使用,否则也会出现警告,若这些变量不需要使用,可以注释掉,以便不时之需。
|