目錄
- 系列文章目錄
- 前言
- 一、效果展示
- 二、原理分析
- 三、各模塊代碼
- 1、8X8點陣屏
- 2、獨立按鍵
- 3、定時器0
- 4、定時器1
- 四、主函數
- 總結
系列文章目錄
前言
用的是普中A2開發板。
【單片機】STC89C52RC
【頻率】12T@11.0592MHz
【外設】8X8點陣屏、獨立按鍵
效果查看/操作演示:B站搜索“甘騰勝”或“gantengsheng”查看。
源代碼下載:B站對應視頻的簡介有工程文件下載鏈接。
一、效果展示
二、原理分析
1、8X8點陣屏的驅動
用定時器1專門掃描顯示點陣屏,要注意設置定時器1比定時器0的優先級高,否則顯示可能會有閃爍的現象。
很簡單,跟數碼管的掃描的原理是一樣的。
寫幾個專門的函數,通過與、或、非、移位等操作,能隨意控制任意一個LED亮和滅,以及獲取任意一個LED的亮滅狀態,就OK了,這是最基本的。
2、獨立按鍵的檢測
如果通過延時來消抖,會阻塞程序的運行,所以要用定時器來掃描檢測。本代碼設置每隔20ms檢測一次,時間間隔大點小點都問題不大。
為了讓控制不出現問題,代碼設置了短按或長按按鍵都能控制點的移動。
3、分數和加速的次數的顯示
為了充分利用屏幕,第一列用二進制顯示加速的總次數,高位在上。最后一列用二進制顯示分數除以256的余數,同樣是高位在上。
4、障礙物的創造和顯示
通過一個數組保存產生的障礙物,為了產生真隨機的障礙物,每次獲取非零的鍵碼后,都以定時器0的低八位作為隨機數的種子,通過隨機函數rand()來產生隨機數。
5、游戲結束的判定
移動前檢測玩家控制的點如果移動后是否與障礙物的點重合,如果是的話,則不移動,并且判定游戲結束,全屏閃爍。
6、移動的時間間隔的最小值
分數每增加3,都讓移動的時間間隔變為當前的9/10,但是要控制其最小值,否則可玩性會降低。
三、各模塊代碼
1、8X8點陣屏
h文件
#ifndef __MATRIXLED__
#define __MATRIXLED__extern unsigned char DisplayBuffer[];
void MatrixLED_Clear(void);
void MatrixLED_Init(void);
void MatrixLED_MoveLeft(unsigned char *Array,unsigned int Offset);
void MatrixLED_MoveUp(unsigned char *Array,unsigned int Offset);
void MatrixLED_Tick(void);
void MatrixLED_DrawPoint(unsigned char X,unsigned char Y);
void MatrixLED_ClearPoint(unsigned char X,unsigned char Y);
unsigned char MatrixLED_GetPoint(unsigned char X,unsigned char Y);#endif
c文件
#include <REGX52.H>/*引腳定義*/sbit _74HC595_DS=P3^4; //串行數據輸入
sbit _74HC595_STCP=P3^5; //儲存寄存器時鐘輸入,上升沿有效
sbit _74HC595_SHCP=P3^6; //移位寄存器時鐘輸入,上升沿有效/*
每一個B對應一個燈。緩存數組DisplayBuffer的8個字節分別對應這8列,高位在下
B0 B0 B0 B0 B0 B0 B0 B0
B1 B1 B1 B1 B1 B1 B1 B1
B2 B2 B2 B2 B2 B2 B2 B2
B3 B3 B3 B3 B3 B3 B3 B3
B4 B4 B4 B4 B4 B4 B4 B4
B5 B5 B5 B5 B5 B5 B5 B5
B6 B6 B6 B6 B6 B6 B6 B6
B7 B7 B7 B7 B7 B7 B7 B7
*///想要改變顯示內容,改變數組DisplayBuffer的數據就行了,由定時器自動掃描
unsigned char DisplayBuffer[8];/*函數定義*//*** 函 數:LED點陣屏清空顯示* 參 數:無* 返 回 值:無* 說 明:直接更改緩存數組的數據就行了,由定時器自動掃描顯示*/
void MatrixLED_Clear(void)
{unsigned char i;for(i=0;i<8;i++){DisplayBuffer[i]=0;}
}/*** 函 數:MatrixLED初始化(即74HC595初始化)* 參 數:無* 返 回 值:無*/
void MatrixLED_Init(void)
{_74HC595_SHCP=0; //移位寄存器時鐘信號初始化_74HC595_STCP=0; //儲存寄存器時鐘信號初始化MatrixLED_Clear(); //點陣屏清屏
}/*** 函 數:74HC595寫入字節* 參 數:Byte 要寫入的字節* 返 回 值:無*/
void _74HC595_WriteByte(unsigned char Byte)
{unsigned char i;for(i=0;i<8;i++) //循環8次{_74HC595_DS=Byte&(0x01<<i); //低位先發_74HC595_SHCP=1; //SHCP上升沿時,DS的數據寫入移位寄存器_74HC595_SHCP=0;}_74HC595_STCP=1; //STCP上升沿時,數據從移位寄存器轉存到儲存寄存器_74HC595_STCP=0;
}/*** 函 數:8X8LED點陣屏顯示數組內容* 參 數:Array 傳遞過來的數組的首地址(即指針),數組名就是數組的首地址* 返 回 值:Offset 偏移量,向左偏移Offset個像素* 說 明:要求逐列式取模,高位在下*/
void MatrixLED_MoveLeft(unsigned char *Array,unsigned int Offset)
{unsigned char i;Array+=Offset;for(i=0;i<8;i++){DisplayBuffer[i]=*Array;Array++; //地址自增}
}/*** 函 數:8X8LED點陣屏顯示數組內容* 參 數:Array 傳遞過來的數組的首地址(即指針),數組名就是數組的首地址* 返 回 值:Offset 顯示數組數據的偏移量,向上偏移Offset個像素* 說 明:要求逐列式取模,高位在下*/
void MatrixLED_MoveUp(unsigned char *Array,unsigned int Offset)
{unsigned char i,m,n;m=Offset/8;n=Offset%8;Array+=m*8;for(i=0;i<8;i++){DisplayBuffer[i]=(*Array>>n)|(*(Array+8)<<(8-n));Array++;}
}/*** 函 數:LED點陣屏驅動函數,中斷中調用* 參 數:無* 返 回 值:無*/
void MatrixLED_Tick(void)
{static unsigned char i=0; //定義靜態變量P0=0xFF; //消影_74HC595_WriteByte(DisplayBuffer[i]); //將數據寫入到74HC595中鎖存P0=~(0x80>>i); //位選,低電平選中i++; //下次進中斷后掃描下一列i%=8; //顯示完第八列后,又從第一列開始顯示
}/*** 函 數:MatrixLED在指定位置畫一個點* 參 數:X 指定點的橫坐標,范圍:0~7* 參 數:Y 指定點的縱坐標,范圍:0~7* 返 回 值:無* 說 明:左上角的LED為原點(0,0),向右為X軸正方向,向下為Y軸正方向*/
void MatrixLED_DrawPoint(unsigned char X,unsigned char Y)
{if(X>=0 && X<=7 && Y>=0 && Y<=7){ DisplayBuffer[X] |= 0x01<<Y; }
}/*** 函 數:MatrixLED在指定位置清除一個點* 參 數:X 指定點的橫坐標,范圍:0~7* 參 數:Y 指定點的縱坐標,范圍:0~7* 返 回 值:無* 說 明:左上角的LED為原點(0,0),向右為X軸正方向,向下為Y軸正方向*/
void MatrixLED_ClearPoint(unsigned char X,unsigned char Y)
{if(X>=0 && X<=7 && Y>=0 && Y<=7){ DisplayBuffer[X] &= ~(0x01<<Y); }
}/*** 函 數:MatrixLED獲取其中一個LED的狀態* 參 數:X 指定點的橫坐標,范圍:0~7* 參 數:Y 指定點的縱坐標,范圍:0~7* 返 回 值:LED的亮滅狀態,1:亮,0:滅,2:說明超出了屏幕范圍* 說 明:左上角的LED為原點(0,0),向右為X軸正方向,向下為Y軸正方向*/
unsigned char MatrixLED_GetPoint(unsigned char X,unsigned char Y)
{if(X>=0 && X<=7 && Y>=0 && Y<=7){if( DisplayBuffer[X] & (0x01<<Y) ) {return 1;}else {return 0;}}else {return 2;}
}
2、獨立按鍵
h文件
#ifndef __KEYSCAN_H__
#define __KEYSCAN_H__unsigned char Key(void);
void Key_Tick(void);#endif
c文件
#include <REGX52.H>sbit Key1=P3^1;
sbit Key2=P3^0;
sbit Key3=P3^2;
sbit Key4=P3^3;unsigned char KeyNumber;/*** 函 數:獲取獨立按鍵鍵碼* 參 數:無* 返 回 值:按下按鍵的鍵碼,范圍:0~12,0表示無按鍵按下* 說 明:在下一次檢測按鍵之前,第二次獲取鍵碼一定會返回0*/
unsigned char Key(void)
{unsigned char KeyTemp=0;KeyTemp=KeyNumber;KeyNumber=0;return KeyTemp;
}/*** 函 數:按鍵驅動函數,在中斷中調用* 參 數:無* 返 回 值:無*/
void Key_Tick(void)
{static unsigned char NowState,LastState;static unsigned int KeyCount;LastState=NowState; //保存上一次的按鍵狀態NowState=0; //如果沒有按鍵按下,則NowState為0//獲取當前按鍵狀態if(Key1==0){NowState=1;}if(Key2==0){NowState=2;}if(Key3==0){NowState=3;}if(Key4==0){NowState=4;}//如果上個時間點按鍵未按下,這個時間點按鍵按下,則是按下瞬間if(LastState==0){switch(NowState){case 1:KeyNumber=1;break;case 2:KeyNumber=2;break;case 3:KeyNumber=3;break;case 4:KeyNumber=4;break;default:break;}}//如果上個時間點按鍵按下,這個時間點按鍵按下,則是一直按住按鍵if(LastState && NowState){KeyCount++;if(KeyCount%5==0) //定時器中斷函數中每隔20ms檢測一次按鍵{ //長按后每隔100ms返回一次長按的鍵碼if (LastState==1 && NowState==1){KeyNumber=5;}else if(LastState==2 && NowState==2){KeyNumber=6;}else if(LastState==3 && NowState==3){KeyNumber=7;}else if(LastState==4 && NowState==4){KeyNumber=8;}}}else{KeyCount=0;}//如果上個時間點按鍵按下,這個時間點按鍵未按下,則是松手瞬間if(NowState==0){switch(LastState){case 1:KeyNumber=9;break;case 2:KeyNumber=10;break;case 3:KeyNumber=11;break;case 4:KeyNumber=12;break;default:break;}}}
3、定時器0
h文件
#ifndef __TIMER0_H__
#define __TIMER0_H__void Timer0_Init(void);#endif
c文件
#include <REGX52.H>/*** 函 數:定時器0初始化* 參 數:無* 返 回 值:無*/
void Timer0_Init(void)
{ TMOD&=0xF0; //設置定時器模式(高四位不變,低四位清零)TMOD|=0x01; //設置定時器模式(通過低四位設為16位不自動重裝)TL0=0x66; //設置定時初值,定時1ms,12T@11.0592MHzTH0=0xFC; //設置定時初值,定時1ms,12T@11.0592MHzTF0=0; //清除TF0標志TR0=1; //定時器0開始計時ET0=1; //打開定時器0中斷允許EA=1; //打開總中斷PT0=0; //當PT0=0時,定時器0為低優先級,當PT0=1時,定時器0為高優先級
}/*定時器中斷函數模板
void Timer0_Routine() interrupt 1 //定時器0中斷函數
{static unsigned int T0Count; //定義靜態變量TL0=0x66; //設置定時初值,定時1ms,12T@11.0592MHzTH0=0xFC; //設置定時初值,定時1ms,12T@11.0592MHzT0Count++;if(T0Count>=1000){T0Count=0;}
}
*/
4、定時器1
h文件
#ifndef __TIMER1_H__
#define __TIMER1_H__void Timer1_Init(void);#endif
c文件
#include <REGX52.H>/*** 函 數:定時器1初始化* 參 數:無* 返 回 值:無*/
void Timer1_Init(void)
{TMOD&=0x0F; //設置定時器模式(低四位不變,高四位清零)TMOD|=0x10; //設置定時器模式(通過高四位設為16位不自動重裝的模式)TL1=0x66; //設置定時初值,定時1ms,12T@11.0592MHzTH1=0xFC; //設置定時初值,定時1ms,12T@11.0592MHzTF1=0; //清除TF1標志TR1=1; //定時器1開始計時ET1=1; //打開定時器1中斷允許EA=1; //打開總中斷PT1=1; //當PT1=0時,定時器1為低優先級,當PT1=1時,定時器1為高優先級
}/*定時器中斷函數模板
void Timer1_Routine() interrupt 3 //定時器1中斷函數
{static unsigned int T1Count; //定義靜態變量TL1=0x66; //設置定時初值,定時1ms,12T@11.0592MHzTH1=0xFC; //設置定時初值,定時1ms,12T@11.0592MHzT1Count++;if(T1Count>=1000){T1Count=0;}
}
*/
四、主函數
main.c
/*by甘騰勝@20250520
【效果查看/操作演示】B站搜索“甘騰勝”或“gantengsheng”查看
【單片機】STC89C52RC
【頻率】12T@11.0592MHz
【外設】8X8LED點陣屏、獨立按鍵
【簡單的原理分析】https://blog.csdn.net/gantengsheng/article/details/143581157
【注意】點陣屏旁邊的跳線帽要接三個排針的左邊兩個
【操作說明】
(1)循環滾動顯示游戲英文名的界面按任意按鍵開始游戲
(2)K1、K2兩個按鍵控制左右移動
(3)游戲結束全屏閃爍界面按K4進入滾動顯示得分的英文的界面
(4)滾動顯示得分的英文的界面可按K4跳過
(5)循環顯示得分界面可按K3返回,重新開始游戲
(6)游戲時按K4可以暫停或繼續游戲
*/#include <REGX52.H> //51單片機頭文件
#include "MatrixLED.h" //8X8點陣屏
#include "KeyScan.h" //獨立按鍵
#include "Timer0.h" //定時器0
#include "Timer1.h" //定時器1
#include <STDLIB.H> //隨機函數unsigned char KeyNum; //存儲獲取的鍵碼
unsigned char Mode; //游戲模式,0:循環滾動顯示游戲英文名,1:游戲中,2:游戲結束全屏閃爍,3:滾動顯示得分的英文,4:循環滾動顯示得分
bit OnceFlag; //各模式中切換為其他模式前只執行一次的標志(類似于主函數主循環前的那部分,用于該模式的初始化),1:執行,0:不執行
bit FlashFlag; //閃爍的標志,1:不顯示,0:顯示
bit GameOverFlag; //游戲結束的標志,1:游戲結束,0:游戲未結束
unsigned char Offset; //偏移量,用來控制字母或數字向左滾動顯示
bit RollFlag; //字母或數字滾動一個像素的標志,1:滾動,0:不滾動
bit MoveFlag; //障礙物向下移動一個像素的標志,1:移動,0:不移動
unsigned int Duration=500; //移動的時間間隔,單位是1ms,初始500ms移動一次
unsigned char Speed; //用于屏幕顯示的加速的次數
unsigned int Score; //游戲得分
bit PauseFlag; //暫停的標志,1:暫停,0:繼續
unsigned char T0Count; //定時器0計數全局變量
bit NowPosition; //玩家現在位置,0:左邊,1:右邊
bit LastPosition; //玩家上次位置,0:左邊,1:右邊
bit DodgedFlag; //已經移動了的標志,1:已移動,0:未移動
unsigned char idata Obstacles[32]; //保存生成的障礙物,1:障礙物,0:空氣,索引0~15為左列,索引16~31為右列
unsigned char NowPointer; //現在顯示的障礙物對應數組Obstacles中的位置,范圍:0~15,0:對應數組索引0和16,1:對應數組索引1和17,以此類推
unsigned char CreatPointer; //即將要創造的障礙物對應數組Obstacles中的位置,范圍:0~15,0:對應數組索引0和16,1:對應數組索引1和17,以此類推
unsigned char ObstacleLength; //障礙物的長度
unsigned char GapLength; //障礙物與障礙物之間間隙的長度
bit LeftOrRight; //左或右,0:左,1:右
unsigned char OneSideTimes; //障礙物連續在同一側的次數
bit NowOtherSideState; //玩家另外一側是否為障礙物,1:是,0:否
bit LastOtherSideState; //記錄上一次玩家另外一側是否為障礙物,1:是,0:否
unsigned char idata ScoreShow[]={ //二位數游戲得分(用于滾動顯示),idata:變量保存在片內的簡介尋址區
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 無顯示
0x00,0x00,0x00,0x00,0x00,0x00, // 得分的百位
0x00,0x00,0x00,0x00,0x00,0x00, // 得分的十位
0x00,0x00,0x00,0x00,0x00,0x00, // 得分的個位
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 無顯示
};//取模要求:陰碼(亮點為1),縱向取模,高位在下
//我分享的工程文件夾中有6X8像素的ASCII字符字模
//code:數據保存在flash中
unsigned char code Table1[]={ //游戲名稱“躲閃或者死亡”的英文:<<DODGE OR DIE>>,寬6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 無顯示
0x00,0x08,0x14,0x22,0x49,0x14,0x22,0x41, // << 寬8高8(自定義書名號:兩個小于號)
0x00,0x7F,0x41,0x41,0x22,0x1C, // D
0x00,0x3E,0x41,0x41,0x41,0x3E, // O
0x00,0x7F,0x41,0x41,0x22,0x1C, // D
0x00,0x3E,0x41,0x49,0x49,0x7A, // G
0x00,0x7F,0x49,0x49,0x49,0x41, // E
0x00,0x00,0x00,0x00,0x00,0x00, //
0x00,0x3E,0x41,0x41,0x41,0x3E, // O
0x00,0x7F,0x09,0x19,0x29,0x46, // R
0x00,0x00,0x00,0x00,0x00,0x00, //
0x00,0x7F,0x41,0x41,0x22,0x1C, // D
0x00,0x00,0x41,0x7F,0x41,0x00, // I
0x00,0x7F,0x49,0x49,0x49,0x41, // E
0x00,0x41,0x22,0x14,0x49,0x22,0x14,0x08, // >>
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 無顯示
};
unsigned char code Table2[]={ //“得分”的英文:“SCORE”,寬6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //無顯示
0x00,0x46,0x49,0x49,0x49,0x31, // S
0x00,0x3E,0x41,0x41,0x41,0x22, // C
0x00,0x3E,0x41,0x41,0x41,0x3E, // O
0x00,0x7F,0x09,0x19,0x29,0x46, // R
0x00,0x7F,0x49,0x49,0x49,0x41, // E
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //無顯示
};
unsigned char code Table3[]={ //游戲得分的字模數據,寬6高8
0x00,0x3E,0x51,0x49,0x45,0x3E, // 0
0x00,0x00,0x42,0x7F,0x40,0x00, // 1
0x00,0x42,0x61,0x51,0x49,0x46, // 2
0x00,0x21,0x41,0x45,0x4B,0x31, // 3
0x00,0x18,0x14,0x12,0x7F,0x10, // 4
0x00,0x27,0x45,0x45,0x45,0x39, // 5
0x00,0x3C,0x4A,0x49,0x49,0x30, // 6
0x00,0x01,0x71,0x09,0x05,0x03, // 7
0x00,0x36,0x49,0x49,0x49,0x36, // 8
0x00,0x06,0x49,0x49,0x29,0x1E, // 9
};/*** 函 數:主函數(有且僅有一個)* 參 數:無* 返 回 值:無* 說 明:主函數是程序執行的起點,負責執行整個程序的主要邏輯?*/
void main()
{unsigned char i;P2_5=0; //防止開發板上的蜂鳴器發出聲音Timer0_Init(); //定時器0初始化Timer1_Init(); //定時器1初始化MatrixLED_Init(); //點陣屏初始化while(1){KeyNum=Key(); //獲取鍵碼/*鍵碼處理*/if(KeyNum){srand(TL0); //每次獲取非零鍵碼都用定時器0的低8位做種子,從而產生真隨機數if(Mode==0) //如果是循環滾動顯示游戲英文名的界面{if(KeyNum>=9 && KeyNum<=12) //如果按下任意按鍵(松手瞬間){Mode=1; //切換到模式1OnceFlag=1; //切換模式前只執行一次的標志置1}}else if(Mode==1) //如果是正在游戲的界面{if( (KeyNum==1 || KeyNum==5) && PauseFlag==0 ) //如果是游戲進行中并且不是暫停{ //短按K1或長按K1,玩家移動到左邊NowPosition=0;if(NowPosition!=LastPosition) //由右邊移到左邊時,躲閃了的標志置1{ //如果已經在左邊了,再按K1,躲閃了的標志不置1,防止另一側玩家正對的點顯示不正常DodgedFlag=1; //用來更新玩家的位置}LastPosition=NowPosition; //更新變量的值}if( (KeyNum==2 || KeyNum==6) && PauseFlag==0 ) //如果是游戲進行中并且不是暫停{ //短按K2或長按K2,玩家移動到右邊NowPosition=1;if(NowPosition!=LastPosition) //由左邊移到右邊時,躲閃了的標志置1{ //如果已經在右邊了,再按K2,躲閃了的標志不置1,防止另一側玩家正對的點顯示不正常DodgedFlag=1; //用來更新玩家的位置}LastPosition=NowPosition; //更新變量的值}if(KeyNum==12) //如果按下K4(松手瞬間){PauseFlag=!PauseFlag; //置反暫停的標志MoveFlag=0; //障礙物向下移動的標志置0T0Count=0; //定時器0的全局計數變量清零}}else if(Mode==2) //如果是游戲結束全屏閃爍的界面{if(KeyNum==12) //如果按下K4(松手瞬間){Mode=3;OnceFlag=1;}}else if(Mode==3) //如果是滾動顯示英文“SCORE”的界面{if(KeyNum==12) //如果按下K4(松手瞬間){Mode=4;OnceFlag=1;}}else if(Mode==4) //如果是循環滾動顯示得分的界面{if(KeyNum==11) //如果按下K3(松手瞬間){Mode=1; //重新開始游戲OnceFlag=1;}}}/*游戲處理*/if(Mode==0) //循環滾動顯示游戲英文名{if(OnceFlag) //切換到其他模式前,此if中的代碼只執行1次{OnceFlag=0; //只執行一次的標志清零Offset=0; //滾動顯示的偏移量清零}if(RollFlag) //如果滾動的標志RollFlag為真(非零即真){RollFlag=0; //滾動的標志RollFlag清零MatrixLED_MoveLeft(Table1,Offset); //向左滾動Offset++; //每次向左移動一個像素Offset%=96; //越界清零,循環滾動顯示}}else if(Mode==1) //游戲進行中{if(OnceFlag){OnceFlag=0;//游戲初始化MatrixLED_Clear(); //清屏GameOverFlag=0; //游戲結束的標志置0Score=0; //得分清零NowPosition=rand()%2; //確定游戲開始時玩家的隨機位置LastPosition=NowPosition; //確定游戲開始時玩家的隨機位置PauseFlag=0; //暫停的標志置0NowPointer=0; //屏幕正在顯示的內容對應緩存數組中的數據的位置CreatPointer=8; //即將創造的障礙物對應緩存數組中的數據的位置LeftOrRight=rand()%2; //用來隨機確定第一個障礙物的左右位置OneSideTimes=0; //連續在同一側的次數清零ObstacleLength=0; //障礙物的長度清零GapLength=0; //障礙物之間的間隙長度清零Duration=500; //初始每隔500ms移動一次障礙物NowOtherSideState=0; //玩家另一側障礙物狀態置0LastOtherSideState=0; //玩家另一側障礙物狀態置0Speed=0; //用于屏幕顯示的加速的次數清零for(i=0;i<8;i++) //顯示第3列和第6列,作為邊界{MatrixLED_DrawPoint(2,i);MatrixLED_DrawPoint(5,i);}if(NowPosition==0){MatrixLED_DrawPoint(3,6);} //顯示玩家位置else{MatrixLED_DrawPoint(4,6);}for(i=0;i<32;i++){Obstacles[i]=0;} //清空數組的數據MoveFlag=0; //障礙物移動的標志置0T0Count=0; //定時器0全局計數變量清零}while(( (CreatPointer>NowPointer) && ((NowPointer+16-CreatPointer)>(ObstacleLength+GapLength)) ) || ( (CreatPointer<NowPointer) && ((NowPointer-CreatPointer)>(ObstacleLength+GapLength)) ) ){ //如果緩存數組中未創造障礙物的區域足夠多while(ObstacleLength) //障礙物{ //一直創造障礙物,并保存到數組中,直到障礙物長度減為0if(LeftOrRight==0){Obstacles[CreatPointer]=1;Obstacles[CreatPointer+16]=0;}else{Obstacles[CreatPointer]=0;Obstacles[CreatPointer+16]=1;}CreatPointer++;CreatPointer%=16;ObstacleLength--; //障礙物長度ObstacleLength自減}while(GapLength) //障礙物之間的間隙{ //一直創造障礙物之間的間隙,并保存到數組中,直到間隙長度減為0Obstacles[CreatPointer]=0;Obstacles[CreatPointer+16]=0;CreatPointer++;CreatPointer%=16;GapLength--; //障礙物之間的間隙長度GapLength自減}ObstacleLength=rand()%2+1; //障礙物長度范圍:1~2GapLength=rand()%2+2; //障礙物之間的間隙長度:2~3if(OneSideTimes==0){OneSideTimes=rand()%3+1; //障礙物連續在同一側的次數UpOrDownLength的范圍:1~3LeftOrRight=!LeftOrRight;}OneSideTimes--;}if(PauseFlag==0) //如果不是暫停狀態{if(DodgedFlag) //如果移動了的標志為1,則更新玩家的位置{DodgedFlag=0; //躲閃了的標志置0if( (NowPosition==0 && Obstacles[(1+NowPointer)%16]) || (NowPosition==1 && Obstacles[(1+NowPointer)%16+16]) ){ //如果碰撞到了障礙物Mode=2; //切換到游戲結束全屏閃爍模式GameOverFlag=1; //游戲結束標志置1}else //如果沒碰撞到障礙物{if(NowPosition==0) //如果在左邊{MatrixLED_ClearPoint(4,6);MatrixLED_DrawPoint(3,6);}else //如果在右邊{MatrixLED_ClearPoint(3,6);MatrixLED_DrawPoint(4,6);}}}if(MoveFlag && GameOverFlag==0) //如果移動的標志為真,且游戲還未結束{MoveFlag=0;NowPointer++;NowPointer%=16;if( (NowPosition==0 && Obstacles[(1+NowPointer)%16]) || (NowPosition==1 && Obstacles[(1+NowPointer)%16+16]) ){ //如果碰撞到了障礙物Mode=2; //切換到游戲結束全屏閃爍模式GameOverFlag=1; //游戲結束標志置1}else{if(NowPosition==0){NowOtherSideState=Obstacles[(1+NowPointer)%16+16];} //獲取玩家對側的狀態,看是否為障礙物else{NowOtherSideState=Obstacles[(1+NowPointer)%16];}if(LastOtherSideState==1 && NowOtherSideState==0) //如果玩家的另一側由障礙物變成空隙{Score++; //則分數加1if(Score%3==0) //每經過三次障礙物,速度增加一次,變為原來的10/9{Duration=Duration*9/10; //移動的時間間隔變為原來的9/10if(Duration<90) //控制Duration的最小值為90{Duration=90;}else{Speed++; //加速的次數自增}}for(i=0;i<8;i++) //最后一列以二進制的方式顯示分數(高位在上){if(Score & (0x80>>i)){MatrixLED_DrawPoint(7,i);}else{MatrixLED_ClearPoint(7,i);}}for(i=0;i<8;i++) //第一列以二進制的方式顯示已加速的次數(高位在上){if(Speed & (0x80>>i)){MatrixLED_DrawPoint(0,i);}else{MatrixLED_ClearPoint(0,i);}}}LastOtherSideState=NowOtherSideState; //更新變量for(i=0;i<8;i++) //更新障礙物和玩家的顯示{if(i==1 && NowPosition==0) //玩家位置{MatrixLED_DrawPoint(3,6);}else //障礙物和間隙{if(Obstacles[(NowPointer+i)%16]){MatrixLED_DrawPoint(3,7-i);}else{MatrixLED_ClearPoint(3,7-i);}}if(i==1 && NowPosition==1) //玩家位置{MatrixLED_DrawPoint(4,6);}else //障礙物和間隙{if(Obstacles[(NowPointer+i)%16+16]){MatrixLED_DrawPoint(4,7-i);}else{MatrixLED_ClearPoint(4,7-i);}}}}}}else //如果是暫停狀態,則閃爍玩家控制的“點”{if(FlashFlag) //如果閃爍的標志為真{ //則不顯示玩家控制的點if(NowPosition==0){MatrixLED_ClearPoint(3,6);}else{MatrixLED_ClearPoint(4,6);}}else //否則顯示{if(NowPosition==0){MatrixLED_DrawPoint(3,6);}else{MatrixLED_DrawPoint(4,6);}}}}else if(Mode==2) //游戲結束全屏閃爍{//在定時器1中實現全屏閃爍}else if(Mode==3) //滾動顯示得分的英文“SCORE”{if(OnceFlag){OnceFlag=0;Offset=0;}if(RollFlag && Offset<=38) //只滾動顯示一次英文{RollFlag=0;MatrixLED_MoveLeft(Table2,Offset);Offset++;}else if(Offset>38) //滾動結束后,自動切換到循環滾動顯示得分的模式{Mode=4;OnceFlag=1;} }else if(Mode==4) //循環滾動顯示得分{if(OnceFlag){OnceFlag=0;Offset=0;//將得分的十位、個位的字模寫入數組ScoreShow中for(i=0;i<6;i++){ScoreShow[ 8+i]=Table3[(Score/100%10)*6+i]; //百位}for(i=0;i<6;i++){ScoreShow[14+i]=Table3[(Score/10%10)*6+i]; //十位}for(i=0;i<6;i++){ScoreShow[20+i]=Table3[(Score%10)*6+i]; //個位}}if(RollFlag){RollFlag=0;MatrixLED_MoveLeft(ScoreShow,Offset);Offset++;Offset%=26; //循環滾動顯示}}}
}/*** 函 數:定時器0中斷函數* 參 數:無* 返 回 值:無*/
void Timer0_Routine() interrupt 1
{static unsigned char T0Count1,T0Count2,T0Count3; //定時器計數變量TL0=0x00; //設置定時初值,定時10ms,12T@11.0592MHzTH0=0xDC; //設置定時初值,定時10ms,12T@11.0592MHzT0Count1++;T0Count2++;T0Count3++;T0Count++;if(T0Count1>=2) //每隔20ms檢測一次鍵碼{T0Count1=0;Key_Tick();}if(T0Count2>=50) //每隔500ms置反FlashFlag{T0Count2=0;FlashFlag=!FlashFlag;}if(T0Count3>=10) //每隔100ms滾動顯示一次字母或數字{T0Count3=0;RollFlag=1;}if(T0Count>=Duration/10) //每隔Duration ms移動一次障礙物{T0Count=0;MoveFlag=1;}
}/*** 函 數:定時器1中斷函數* 參 數:無* 返 回 值:無* 說 明:專門用定時器1來掃描顯示LED點陣屏,定時器1的優先級要比定時器0的高,否則顯示會有閃爍現象*/
void Timer1_Routine() interrupt 3
{TL1=0x66; //設置定時初值,定時1ms,12T@11.0592MHzTH1=0xFC; //設置定時初值,定時1ms,12T@11.0592MHzif(Mode==2 && FlashFlag){P0=0xFF;} //控制游戲結束后的全屏閃爍else{MatrixLED_Tick();}
}
總結
之前做過一個LCD1602版本的躲閃類游戲,是按照之前的思路將代碼移植過來的。
感覺用點陣屏玩這個游戲效果更好,LCD1602版本的,由于硬件問題,速度較快時會有重影。