本科時候做的一個課程作業,自己搭一個很簡易的電路,比較有意思且易上手,故將之記錄下來。(全套的仿真及代碼,演示視頻,課程報告以及PPT展示上傳在CSDN。)
一. 實驗目的
(1) 通過對C51語言的理解,編寫程序實現對貪吃蛇的有效控制;
(2) 通過對51單片機硬件的學習,學會運用面包板,獨立按鍵、點陣屏等,完成對完整電路的搭建過程;
(3) 通過對Proteus仿真軟件的學習,實現基於STC89C52單片機的8*8點陣貪吃蛇的硬件電路仿真。
二. 實驗介紹
貪吃蛇是一款經典小游戲,其游戲的規則是:玩家通過四個方向鍵來控制蛇的移動,控制其在地圖上吃豆子。吃掉豆子后蛇身相應加長,蛇身速度加快。蛇運動過程中撞到牆壁或蛇身,則立即結束本輪游戲。
三. 實現功能
(1)制作一個8*8點陣的貪吃蛇游戲;
(2)通過LED點陣屏為載體顯示數據;
(3)外接4個獨立按鍵作為輸入端,分別控制蛇的移動方向(上、下、左、右);
(4)蛇頭與牆壁或蛇身相撞,隨即結束游戲並復位。
四. 方案設計
在該系統中,硬件部分包括STC89C52單片機,8*8點陣屏,4個按鍵;軟件部分是在keil環境下用C51語言編寫,設置蛇的初始段數為2點,並設置有障礙牆壁,游戲結束后自動復位。
(1)貪吃蛇的移動
在貪吃蛇的移動過程中,每次需要將蛇頭要到的下一個LED燈點亮,對應蛇尾的LED燈熄滅。在程序中即是先把蛇尾位置的值傳給蛇頭的下一個位置,然后改變蛇尾的值即可。蛇頭下一個位置的確定由蛇頭和偏移量來確定,每次通過操作四個獨立按鍵來控制蛇步進的偏移量。因而只要將蛇頭的位置加上其偏移量的值,即可得到新的蛇頭位置。
(2)食物的出現
在市場上所流行的貪吃蛇游戲中,食物的出現是一種隨機行為,這在程序中需要做一個隨機數來支撐該過程。我們組在實驗過程中也嘗試了該過程,最終選擇讓食物出現在蛇尾的后一步,來執行整個程序。與此同時,食物出現的位置不能與蛇的位置重合,也不能超出牆外,否則需要重置食物。
五. 模塊應用
(1)AT89C52單片機最小系統模塊
本系統是以STC89C52RC為核心,加上復位電路和晶振電路來構成最小系統。該系統中選用11.0592M晶振,使得單片機有較為合理的運行速度;其起振電容對振盪器的頻率高低、穩定性以及快速性影響較合適,復位電路為按鍵高電平復位。

(2)1588BS 8*8共陽點陣屏模塊
本實驗中是采用8*8共陽紅色點陣顯示屏,它共16個引腳,分別與單片機P1口的八位管腳、P2口的八位管腳,按照一定要求(連接規則來源於百度查詢)通過杜邦線一一對應連接,繼而用來顯示貪吃蛇的游戲畫面。

點陣屏各點的點亮原理:
該點陣屏各引腳分別對應各led點(其原理圖詳見下圖),其基本原理是:當第一行接入高電平,第一列接入低電平,且其它列為高電平時,則第一個led燈點亮。同理,其他所有的led燈點亮原理均是如此。

(3)獨立按鍵模塊
本實驗中外接4個獨立按鍵,分別通過控制單片機P3口的P3.1~P3.4,從而控制蛇的游走方向(上、下、左、右)。當按鍵未按下時,控制P3口為低電平;當其中某一按鍵按下后,電流會通過該按鍵,通過P3口中相對應的管腳進入單片機,使單片機變為高電平。當單片機檢測到高電平的時候,會做出相應反應,繼而實現貪吃蛇游戲。

六. 程序流程
本實驗中主程序工作流程如下圖5所示,系統上電后首先對LED進行初始化,接着對定時器初始化,並啟動定時器,之后執行程序主題邏輯部分,程序主題邏輯執行一遍后檢查是否有中斷發生。本實驗中有兩個中斷源:一個是驅動貪吃蛇自動前行的定時中斷,另一個是用戶控制貪吃蛇移動方向的按鍵中斷。任意中斷的到來都將改變貪吃蛇當前狀態。若當前沒有中斷發生,主程序將繼續判斷蛇頭是否碰壁或發生頭尾相撞。若是,則結束游戲,否則返回繼續執行程序主體循環即可。

七. 附 錄
7.1 Proteus電路仿真圖

7.2 代碼
1 #include <reg52.h> 2 #define uchar unsigned char 3 #define SNAKE 22 //最大長度 4 #define TIME 40 //顯示延時時間 5 #define SPEED 88 //速度控制 6 #define keyenable 1 7 8 sbit led = P0^0; 9 sbit up=P3^2; 10 sbit down=P3^4; 11 sbit right=P3^3; 12 sbit left=P3^1; 13 14 uchar x[SNAKE+1]; 15 uchar y[SNAKE+1]; 16 uchar time,n,i,e; //延時時間,當前蛇長,通用循環變量,當前速度 17 char fx,fy; //位移偏移量 18 19 /*************************** 20 延時程序 21 ****************************/ 22 void delay(char MS) 23 { 24 char us,usn; 25 while(MS!=0) 26 { 27 usn = 0; 28 while(usn!=0) 29 { 30 us=0xff; 31 while (us!=0) 32 {us--;}; 33 usn--; 34 } 35 MS--; 36 } 37 } 38 /**************************** 39 判斷碰撞 40 *****************************/ 41 bit knock() 42 { 43 bit k; 44 k=0; 45 if(x[1]>7||y[1]>7) 46 k=1; //撞牆 47 for(i=2;i<n;i++) 48 if((x[1]==x[i])&(y[1]==y[i])) 49 k=1; //撞自己 50 return k; 51 } 52 /***************************** 53 上下左右鍵位處理 54 ******************************/ 55 void turnkey() 56 { 57 if(keyenable) 58 { 59 if(left) 60 { 61 fy=0; 62 if(fx!=1) 63 fx=-1; 64 else fx=1; 65 } 66 if(right) 67 { 68 fy=0; 69 if(fx!=-1) 70 fx=1; 71 else fx=-1; 72 } 73 if(up) 74 { 75 fx=0; 76 if(fy!=-1) 77 fy=1; 78 else fy=-1; 79 } 80 if(down) 81 { 82 fx=0; 83 if(fy!=1) 84 fy=-1; 85 else fy=1; 86 } 87 } 88 } 89 /******************************* 90 乘方程序 91 ********************************/ 92 uchar mux(uchar temp) 93 { 94 if(temp==7) return 128; 95 if(temp==6) return 64; 96 if(temp==5) return 32; 97 if(temp==4) return 16; 98 if(temp==3) return 8; 99 if(temp==2) return 4; 100 if(temp==1) return 2; 101 if(temp==0) return 1; 102 return 0; 103 } 104 /******************************* 105 顯示時鍾 顯示程序 106 *******************************/ 107 void timer0(uchar k) 108 { 109 while(k--) 110 { 111 for(i=0;i<SNAKE+1;i++) 112 { 113 P2=mux(x[i]); 114 P1=255-mux(y[i]); 115 turnkey(); //上下左右鍵位處理 116 delay(TIME); //顯示延遲 117 P2=0x00; 118 P1=0xff; 119 } 120 } 121 } 122 /******************************* 123 主程序 124 *******************************/ 125 void main(void) 126 { 127 e=SPEED; 128 P0=0x00; 129 P1=0xff; 130 P2=0x00; 131 P3=0x00; 132 while(1) 133 { 134 for(i=3;i<SNAKE+1;i++) 135 x[i]=100; 136 for(i=3;i<SNAKE+1;i++) 137 y[i]=100; //初始化 138 x[0]=4; 139 y[0]=4; //設置食物 140 n=3; //貪吃蛇長 141 x[1]=1;y[1]=0; //貪吃蛇頭 142 x[2]=0;y[2]=0; //貪吃蛇尾 143 fx=0; 144 fy=0; //位移偏移 145 while(1) 146 { 147 if(keyenable) 148 break; 149 timer0(1); 150 } 151 while(1) 152 { 153 timer0(e); 154 if(knock()) 155 { 156 e=SPEED; 157 break; 158 } //判斷碰撞 159 if((x[0]==x[1]+fx)&(y[0]==y[1]+fy)) //是否吃東西 160 { 161 n++; 162 if(n==SNAKE+1) 163 { 164 n=3; 165 e=e+10; 166 for(i=3;i<SNAKE+1;i++) 167 x[i]=100; 168 for(i=3;i<SNAKE+1;i++) 169 y[i]=100; 170 } 171 x[0]=x[n-2]; 172 y[0]=y[n-2]; 173 } 174 for(i=n-1;i>1;i--) 175 { 176 x[i]=x[i-1]; 177 y[i]=y[i-1]; 178 } 179 x[1]=x[2]+fx; 180 y[1]=y[2]+fy; //移動 181 } 182 } 183 }
