二、實驗?zāi)康?/div>
1、初步認識EDA技術(shù)Verilog HDL,熟悉可編輯邏輯器件基礎(chǔ);
2、掌握Verilog HDL語法及要素,能夠熟練運用行為語句,了解常用數(shù)據(jù)單元及設(shè)計風(fēng)格;
3、學(xué)習(xí)典型的FPGA的結(jié)構(gòu)與配置,引腳輸入輸出端口功能;
4、進行狀態(tài)機的設(shè)置,更進一步的深入熟悉一些基礎(chǔ)例程;
5、學(xué)會Quartus Ⅱ的應(yīng)用及一些仿真與測試。
三、實驗題目
乒乓球游戲設(shè)計
比賽規(guī)則約定:七局四勝;11分一局;比賽進行,18個LED排列成行模擬乒乓球臺;點亮的LED模擬乒乓球,受FPGA控制從左到右或從右到左移動;比賽選手通過按鈕輸入模擬擊球信號,實現(xiàn)LED移位方向的控制;若發(fā)亮的LED運動在球臺倒數(shù)第四個至對方終點之間時,對方未能及時按下?lián)羟虬粹o使其向相反方向移動,即失去一分。
四、實驗要求
任務(wù)要求:用按鍵與LED(或者點陣)表示輸入與輸出。
(1)初始時,16個LED最邊上的點亮,按下鍵表示發(fā)球,亮的燈依次向?qū)Ψ揭苿樱灰莆挥嫈?shù)器控制。
(2)當?shù)竭_另一邊倒數(shù)第 4 個燈時表示乒乓球觸到桌面反彈;對方必須在反彈后且靠 近己方的最后一個燈亮起來前按下按鍵表示接球,否則輸球;
(3)接球后燈亮的規(guī)則、對方接球的規(guī)則同發(fā)球;
(4)雙方靠近自己的 4個燈亮的間隔 0.4s,其余燈亮的間隔 0.8s;計數(shù)器來實現(xiàn)時 鐘控制;
(5)輸球或者犯規(guī),即失去一分;自動顯示于數(shù)碼管(共陰);
(6)乒乓球比賽規(guī)則計分,顯示于數(shù)碼管;
這種比較簡單的方式實現(xiàn)
五、實驗設(shè)計
利用兩個按鍵表示兩位選手,LED燈表示乒乓球的軌跡,數(shù)碼管顯示擊球結(jié)果。分析乒乓球跳動速度,乒乓球在接發(fā)球狀態(tài)時時速度可以達到0.4s,乒乓球在運行中速度慢些,可以達到0.8s.
本程序由3個部分組成,第一部分為分頻程序,將de2自帶的50Mhz頻率分為1MHZ,1lKHZ,10hz.第二部分為數(shù)碼管分時掃描程序,掃描速度1khz.第三部分是擊球狀態(tài)機程序。
因手上無示波器,也無必要用示波器,故只用了軟件仿真。
六、源代碼及注釋
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計數(shù)器
reg[9:0] clk_sum1k;//fenpinwei 1khz //定義分頻1khz分頻器
reg[6:0] clk_sum10hz;//ledxianshipinlv //定義分頻10hz分頻器
reg[6:0] hex_buff1; //定義數(shù)碼管緩存寄存器
reg[3:0] hex_buff; //定義數(shù)碼管取值寄存器
reg[3:0] hex_sum; //數(shù)碼管掃描片選信號
reg[3:0] r_sum,l_sum; //定義每局中左邊或者右邊得分數(shù)目默認a_為左,b_為右
reg[3:0] ju_sum; //led燈延時計數(shù)器,每100ms計數(shù)一次
reg[2:0] a_run,b_run; //定義左邊和右邊當前贏得的局數(shù)
reg[2:0] state; //定義狀態(tài)機所處位置
reg[3:0] game_on; //定義按鍵處于何種狀態(tài)
reg[31:0] time_sum; //定義LED從次態(tài)到現(xiàn)態(tài)時間
reg[4:0] qiu; //定義當前LED燈位置
output[6:0] hex0,hex1,hex2,hex3,hex4,hex5,hex6,hex7;//8段數(shù)碼管顯示數(shù)
reg[6:0] hex0,hex1,hex2,hex3,hex4,hex5,hex6,hex7;
output[17:0]led_r;
reg [17:0]led_r; //led燈狀態(tài)
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內(nèi)處理數(shù)碼管和按鍵
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; //第二位 顯示左邊邊當前贏得局數(shù),實際為0
//由于de2開發(fā)板數(shù)碼管位置移位,故該程序中數(shù)碼管非實驗板相應(yīng)位置,需要調(diào)整
7:hex_buff<=b_run; //第一位顯示右邊當前贏得局數(shù)
0:hex_buff<= a_run; //第三位顯示左邊贏得局數(shù)
1:hex_buff<= ju_sum%10;//第四位顯示當前局數(shù)
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燈是否落在左邊,若果是繼續(xù)下去,否則記為錯誤按左鍵
game_on<=4'b0110;
else
game_on<=4'b1110;
end
if(!set_r)
begin
if(state==2||state==3)
//右按鍵按下,判斷LED燈是否落在右邊,若果是繼續(xù)下去,否則記為錯誤按右鍵
game_on<=4'b0101;
else
game_on<=4'b1101;
end
end
always@(negedge clk_1khz) begin
//在clk-1khz的下降沿中處理函數(shù),將數(shù)碼管中緩存值翻譯過來,賦給數(shù)碼管
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進行狀態(tài)機設(shè)置
time_sum<=time_sum+1;
led_r=0; //避免LED全亮,先將LED全滅
if(time_sum==8) //每個LED燈間隔發(fā)光的時間不超過0.8s
time_sum<=0;
case(state)
5: //狀態(tài)5左邊發(fā)球
if(game_on==4'b0110)//如果檢測到左鍵正確按下,執(zhí)行程序
begin
led_r[qiu]=1;
if(time_sum==3)begin //如果LED燈已走了0.4s,則點亮下個LED燈
time_sum<=0;
led_r[qiu]=0; //先將現(xiàn)態(tài)LED燈光滅,在點亮下個LED燈
qiu<=qiu-5'b00001;
led_r[qiu]=1;
if(qiu<=14)
//因為定義了18個LED燈作為實驗乒乓球臺,所以當左邊已發(fā)完球后,應(yīng)立即進入左邊運球給右邊的狀態(tài)4
state<=4;
end
end
else
begin / /如果沒有檢測到左鍵正確按下,則LED【17】常亮
led_r[17]<=~led_r[17];
end
4: //左邊運球到右邊的狀態(tài)
begin
led_r[qiu]=1; //若果球已到右邊接球邊界,進入右邊接球狀態(tài)3
if(qiu<=3)
state<=3;
if(state==4)
begin
if(game_on==4'b1101)begin
l_sum<=l_sum+4'b0001;
state<=2; //若右鍵在運球中錯誤按下,算右邊犯規(guī),左邊加一分
led_r[qiu]=0; //球歸右邊并自動發(fā)球
qiu<=0;
led_r[qiu]=1; //顯示當前LED燈位置
time_sum<=1;
end
if(time_sum==0)begin
//每隔0.8s判斷LED燈是否跳入下個狀態(tài),若是則將當前LED燈光滅,將下個LED燈點亮
led_r[qiu]=0;
qiu<=qiu-5'b00001;
led_r[qiu]=1; //運球沒有失誤,進入右邊接球狀態(tài)3
if(qiu<=3)
state<=3;
end
end
end
3:
begin
if(game_on==4'b0101) //右邊等待接球狀態(tài)
begin
state<=2;
led_r[qiu]=0;
qiu<=qiu+5'b00010; //若已接球,立即進入狀態(tài)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; //關(guān)閉當前LED燈,打開下個LED燈
qiu<=qiu-5'b00001;
led_r[qiu]=1;
if(qiu==0)begin
state<=2;
l_sum<=l_sum+4'b0001; //若接球失敗,進入右邊準備發(fā)球狀態(tài),左邊得一分
end
end
end
end
2: //右邊發(fā)球狀態(tài)
if(game_on==4'b0101)
begin //若有有效鍵按下,則立即發(fā)球否則進入等待狀態(tài)
led_r[qiu]=1;
if(qiu>=3) //若超出界限,立即進入狀態(tài)右邊運球左邊的狀態(tài)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) //若球已超出右邊發(fā)球邊界,進入右邊運球到左邊的狀態(tài)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;
//運球過程中,左邊錯誤按下,右邊得一分,球立即從左邊發(fā)出
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; //球到左邊接球邊界,進入左邊接球狀態(tài)0
if(qiu>=14)
state<=0;
end
end
end
0: //左邊接球狀態(tài)
begin
if(game_on==4'b0110)
begin //左鍵正確按下,進入左邊發(fā)球狀態(tài)
state<=5;
led_r[qiu]=0;
qiu<=qiu-5'b00010;
led_r[qiu]=1;
end
if(qiu==17)begin
//初始狀態(tài)球可能賦值為零,這里強制轉(zhuǎn)換到狀態(tài)5,實際沒有出現(xiàn)這種狀態(tài)
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分為一局,一局結(jié)束后進行下一局,左邊右邊當前局數(shù)歸零,總局數(shù)加一
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狀態(tài)機圖
程序設(shè)計為6個狀態(tài),實際只有4個狀態(tài),可能是由于狀態(tài)0狀態(tài)5優(yōu)化,狀態(tài)2和狀態(tài)3優(yōu)化為一個狀態(tài)。
4、將程序下載進入DE2的開發(fā)板,當前處于等待發(fā)球狀態(tài),顯示結(jié)果為第一局結(jié)束,當前局數(shù)比左邊:右邊=0:1。
5、左邊按下發(fā)球鍵,乒乓球向前移動,右邊進入等待接球狀態(tài);
6、在乒乓球進入右邊第四個燈亮之前右邊接球,此時左邊得一分,并由右邊發(fā)球繼續(xù)進行比賽,如果在最后一個燈亮之前未接球,對方加一分,并有己方發(fā)球繼續(xù)比賽;
7、長按左邊第一個鍵,此時進入時間暫停,小分暫不顯示;
8、按要求左右雙方在倒數(shù)第四個燈亮之后,最后一個燈亮前接球均為好球,這個接球狀態(tài)有1.2秒左右,就可進行乒乓球的游戲;
9、游戲進行,比分交替上升,當有人小分率先到達十一分時,進入下一局,如圖顯示為:第二局結(jié)束,局數(shù)比為1:1,準備進入下一局,同樣規(guī)則繼續(xù),當有一方率先贏下四局便獲勝,游戲結(jié)束。
八、心得體會
通過一天多來的努力,我們終于把老師交給我們的任務(wù)完成了,明白了fpga編程的一些小技巧。開始第一天因為電腦系統(tǒng)原因,無法安裝驅(qū)動,后來換了電腦驅(qū)動就可以安裝了。到網(wǎng)上沒有找到引腳庫文件,所以只好自己一個一個定義引腳。
在編程過程中,發(fā)現(xiàn)直接賦值常數(shù)語句最好用x’b---比較好,否則程序默認常數(shù)為32位,出現(xiàn)警告。在整個模塊中,寄存器值只能在某個always語句中被賦值,否則會出現(xiàn)錯誤。定義的輸入輸出引腳,寄存器引腳也最好使用,否則也會出現(xiàn)警告,若這些變量不需要使用,可以注釋掉,以便不時之需。
歡迎光臨 (http://www.torrancerestoration.com/bbs/) |
Powered by Discuz! X3.1 |