目錄
- 系列文章目錄
- 前言
- 一、效果展示
- 二、原理分析
- 三、各模塊代碼
- 1、8X8點陣屏
- 2、獨立按鍵
- 3、定時器0
- 4、定時器1
- 四、主函數
- 總結
系列文章目錄
前言
用的是普中A2開發板。
【單片機】STC89C52RC
【頻率】12T@11.0592MHz
【外設】8X8點陣屏、獨立按鍵
效果查看/操作演示:B站搜索“甘騰勝”或“gantengsheng”查看。
源代碼下載:B站對應視頻的簡介有工程文件下載鏈接。
一、效果展示
二、原理分析
1、8X8點陣屏的顯示
普中開發板上的64個LED通過74HC595和P0口來驅動,通過P0口來選擇導通哪一列(低電平導通),通過74HC595串行移位寄存器來確定每一列顯示的數據。
跟8位數碼管(同樣有64個LED)一樣,用掃描的方法來驅動顯示,每隔1ms切換下一列顯示。本代碼中專門用定時器1來掃描點陣屏,并設置定時器1的優先級比定時器0的高。
2、8X8點陣屏的控制
關鍵是下面這三個函數,通過與、或、移位、取反等操作,控制任意一個LED的亮滅,或者獲取任意一個LED的亮滅狀態。
/*** 函 數: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;}
}
3、玩家控制的點的移動
利用上邊的三個函數中的前兩個,即void MatrixLED_DrawPoint(unsigned char X,unsigned char Y)和void MatrixLED_ClearPoint(unsigned char X,unsigned char Y),移動的時候,先清除原來位置LED的顯示,再點亮新的位置的LED。
4、障礙物的下落
緩存數組的8個字節對應的是8列,對緩存數組的字節進行移位即可實現全屏的LED向下移動一個像素,這樣會讓玩家控制的點熄滅,但是問題不大,移位之后立刻又點亮玩家控制的那個點的LED即可。
5、隨機障礙物的產生
本代碼中產生的障礙物的尺寸都是寬1高2,如何實現呢?通過函數unsigned char MatrixLED_GetPoint(unsigned char X,unsigned char Y)來獲取LED亮滅狀態,緩存數組的8個字節移位之后,檢測每一列,如果這一列的LED的第二行為亮,第三行為滅,則點亮這一列的第一行的那個LED,否則不點亮。
但是要注意,讓障礙物之間有起碼一個像素的空隙,否則容易堵死,降低可玩性。同樣用到函數unsigned char MatrixLED_GetPoint(unsigned char X,unsigned char Y)。緩存數組的8個字節移位之后,先產生一個0~7的隨機數(對應1~8列),檢測該列、前一列(如果不是第一列的話)、后一列(如果不是最后一列的話)的第二行的LED是否都是熄滅狀態,如果是的話,就在此列產生新的障礙物,否則向左或向右尋找滿足條件的列。
6、獨立按鍵的檢測
獨立按鍵的檢測跟點陣屏的驅動類似,利用定時器每隔一段時間檢測一次,然后跟上次的結果對比,從而判斷是按下瞬間、長按還是松手瞬間。
三、各模塊代碼
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__extern unsigned char KeyNumber1; //在main.c里使用
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;
unsigned char KeyNumber1; //在外部的main.c里使用/*** 函 數:獲取獨立按鍵鍵碼* 參 數:無* 返 回 值:按下按鍵的鍵碼,范圍: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;}}KeyNumber1=KeyNumber;
}
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甘騰勝@20250521
【效果查看/操作演示】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:游戲未結束
bit PauseFlag; //暫停的標志,1:暫停,0:繼續
bit RollFlag; //字母或數字滾動一個像素的標志,1:滾動,0:不滾動
bit MoveFlag; //障礙物向下移動一個像素的標志,1:移動,0:不移動
unsigned char Offset; //偏移量,用來控制字母或數字向左滾動顯示
unsigned int Score; //游戲得分
unsigned char T0Count; //定時器0計數全局變量
unsigned int Duration=500; //移動的時間間隔,單位是1ms,初始500ms移動一次
unsigned char Player; //玩家位置,范圍:0~7,對應1~8列
unsigned char RandomColumn; //產生的隨機的列,范圍:0~7
unsigned char FallCount; //下落的像素的計數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[]={ //游戲名稱“滑動或者死亡”的英文:<<SLIDE OR DIE>>,寬6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 無顯示
0x00,0x08,0x14,0x22,0x49,0x14,0x22,0x41, // << 寬8高8(自定義書名號:兩個小于號)
0x00,0x46,0x49,0x49,0x49,0x31, // S
0x00,0x7F,0x40,0x40,0x40,0x40, // L
0x00,0x00,0x41,0x7F,0x41,0x00, // I
0x00,0x7F,0x41,0x41,0x22,0x1C, // D
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==12) //如果按下K4(松手瞬間){PauseFlag=!PauseFlag; //置反暫停的標志MoveFlag=0; //移動的標志置0T0Count=0; //定時器0全局技術變量清零if(PauseFlag==0){MatrixLED_DrawPoint(Player,7);} //由暫停變成繼續,則重新顯示玩家位置(防止顯示不計時)}}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; //得分清零PauseFlag=0; //暫停的標志置0Duration=500; //初始每隔500ms移動一次障礙物Player=rand()%8; //隨機確定玩家的初始位置MatrixLED_DrawPoint(Player,7); //顯示玩家位置MoveFlag=0; //移動的標志置0T0Count=0; //定時器0全局計數變量清零}if(PauseFlag==0) //如果不是暫停狀態{if(MoveFlag) //如果移動的標志為真{MoveFlag=0; //移動的標志置0if(MatrixLED_GetPoint(Player,6)) //如果玩家正上方的點是障礙物{ //則游戲結束Mode=2; //切換到模式2GameOverFlag=1; //游戲結束的標志置1}else //如果游戲未結束{for(i=0;i<8;i++) //整個屏幕的顯示向下平移一個像素{ //會導致玩家控制的點熄滅DisplayBuffer[i]<<=1;}MatrixLED_DrawPoint(Player,7); //重新顯示玩家位置for(i=0;i<8;i++) //每個障礙物都是寬1高2的條狀物{if( MatrixLED_GetPoint(i,1)==1 && MatrixLED_GetPoint(i,2)==0 ){MatrixLED_DrawPoint(i,0);}}RandomColumn=rand()%8; //產生0~7的隨機數(用來投放障礙物)if(rand()%2) //通過隨機數確定向左或向右尋找合適的列投放障礙物{for(i=0;i<8;i++) //向右尋找{if( MatrixLED_GetPoint((RandomColumn+i)%8,1)==0 && MatrixLED_GetPoint((RandomColumn+i+1)%8,1)==0&& MatrixLED_GetPoint((RandomColumn+i+7)%8,1)==0 ) //保證障礙物之間起碼有一個像素的空隙{MatrixLED_DrawPoint((RandomColumn+i)%8,0);break; //找到合適的位置則退出循環}}}else //向左尋找{for(i=8;i>0;i--){if( MatrixLED_GetPoint((RandomColumn+i)%8,1)==0 && MatrixLED_GetPoint((RandomColumn+i+1)%8,1)==0&& MatrixLED_GetPoint((RandomColumn+i+7)%8,1)==0 ){MatrixLED_DrawPoint((RandomColumn+i)%8,0);break;}}}FallCount++; //記錄下落的像素數if(FallCount%8==0) //每下落8個像素,分數加1{FallCount=0; //變量清零Score++;Duration=Duration*9/10; //每增加一分,速度變為10/9,即時間間隔變為9/10if(Duration<100){Duration=100;} //控制時間間隔的最小值為100ms}}}}else //如果是暫停狀態{if(FlashFlag) //如果閃爍的標志為真{MatrixLED_ClearPoint(Player,7); //不顯示}else{MatrixLED_DrawPoint(Player,7); //顯示}}}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(KeyNumber1 && Mode==1) //如果有按鍵按下且處于游戲進行中{if(KeyNumber1==1 && PauseFlag==0) //如果短按K1,且不是暫停狀態{if(Player>0) //如果不是在最左{if(MatrixLED_GetPoint(Player-1,7)) //如果向左移動后,左側為障礙物{ //則游戲結束Mode=2; //切換到全屏閃爍界面GameOverFlag=1; //游戲結束的標志置1}else //如果游戲未結束{MatrixLED_ClearPoint(Player,7); //清除原來位置的顯示Player--; //向左移動一個像素MatrixLED_DrawPoint(Player,7); //顯示移動后的新位置}}}if(KeyNumber1==2 && PauseFlag==0) //如果短按K2,且不是暫停狀態{if(Player<7) //如果不是在最右{if(MatrixLED_GetPoint(Player+1,7)) //如果向右移動后,右側為障礙物{ //則游戲結束Mode=2; //切換到全屏閃爍界面GameOverFlag=1; //游戲結束的標志置1}else{MatrixLED_ClearPoint(Player,7); //清除原來位置的顯示Player++; //向右移動一個像素MatrixLED_DrawPoint(Player,7); //顯示移動后的新位置}}}KeyNumber1=0; //獨立按鍵的鍵碼清零}}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();}
}
總結
難在隨機的障礙物的產生,要保證障礙物之間有空隙,防止全部堵死。