基于51单片机和8X8点阵屏、矩阵按键的记忆类小游戏
目录
- 系列文章目录
- 前言
- 一、效果展示
- 二、原理分析
- 三、各模块代码
- 1、8X8LED点阵屏
- 2、矩阵按键
- 3、定时器0
- 4、定时器1
- 四、主函数
- 总结
系列文章目录
前言
用的是普中A2开发板,用到板上的8X8点阵屏和矩阵按键。
【单片机】STC89C52RC
【频率】12T@11.0592MHz
效果查看/操作演示:B站搜索“甘腾胜”或“gantengsheng”查看。
源代码下载:B站对应视频的简介有工程文件下载链接。
一、效果展示
二、原理分析
根据另一个案例修改
基于51单片机和8X8点阵屏、矩阵按键的敏捷类小游戏
将超时检测删掉了,定义一个数组按顺序存储生成的随机方向。
定义一个变量“Score”记录分数,即已闯关数,根据已闯关数来生成下一次产生的随机方向数量。
按对了右上角显示一个“√”,按错则显示“×”,且游戏结束。
为了让按键响应更加有效,设置了短按和长按都可以。
三、各模块代码
1、8X8LED点阵屏
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);
void MatrixLED_ShowDirection(unsigned char Direction,unsigned char Quantity);
void MatrixLED_ShowResult(unsigned char Result);#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;}
}/*** 函 数:MatrixLED在指定位置按指定方向显示一个点或两个点* 参 数:Direction 方向,范围:0~3(0:右, 1:上, 2:左,3:下)* 参 数:Quantity 显示的LED的数量,范围:1~2* 返 回 值:无*/
void MatrixLED_ShowDirection(unsigned char Direction,unsigned char Quantity)
{if(Direction==0){if(Quantity==1){DisplayBuffer[3] |= (0x01<<5);DisplayBuffer[4] &= ~(0x01<<5);}else if(Quantity==2){DisplayBuffer[3] |= (0x01<<5);DisplayBuffer[4] |= (0x01<<5);}}else if(Direction==1){if(Quantity==1){DisplayBuffer[2] |= (0x01<<4);DisplayBuffer[2] &= ~(0x01<<3);}else if(Quantity==2){DisplayBuffer[2] |= (0x01<<4);DisplayBuffer[2] |= (0x01<<3);}}else if(Direction==2){if(Quantity==1){DisplayBuffer[1] |= (0x01<<5);DisplayBuffer[0] &= ~(0x01<<5);}else if(Quantity==2){DisplayBuffer[1] |= (0x01<<5);DisplayBuffer[0] |= (0x01<<5);}}else if(Direction==3){if(Quantity==1){DisplayBuffer[2] |= (0x01<<6);DisplayBuffer[2] &= ~(0x01<<7);}else if(Quantity==2){DisplayBuffer[2] |= (0x01<<6);DisplayBuffer[2] |= (0x01<<7);}}
}/*** 函 数:MatrixLED在指定位置显示按键的结果* 参 数:Result 结果,范围:0~2,0:无(一横线),1:正确(勾√),2:错误(叉×)* 返 回 值:无* 说 明:显示区域大小是宽4高3(点阵屏的右上角)*/
void MatrixLED_ShowResult(unsigned char Result)
{unsigned char i;for(i=0;i<4;i++){DisplayBuffer[i+4] &= 0xF8;} //先清空区域的显示if(Result==0){for(i=0;i<4;i++){DisplayBuffer[i+4] |= 0x02;}}else if(Result==1){DisplayBuffer[4] |= 0x02;DisplayBuffer[5] |= 0x04;DisplayBuffer[6] |= 0x02;DisplayBuffer[7] |= 0x01;}else if(Result==2){DisplayBuffer[5] |= 0x05;DisplayBuffer[6] |= 0x02;DisplayBuffer[7] |= 0x05;}
}
2、矩阵按键
h文件
#ifndef __MATRIXKEYSCAN_H__
#define __MATRIXKEYSCAN_H__unsigned char MatrixKey(void);
void MatrixKey_Tick(void);#endif
c文件
#include <REGX52.H>#define Matrix_Port P1 //矩阵按键接口unsigned char KeyNumber;/*** 函 数:获取矩阵按键键码* 参 数:无* 返 回 值:按下按键的键码,范围:0~48,0表示无按键按下* 说 明:在下一次检测按键之前,第二次获取键码一定会返回0*/
unsigned char MatrixKey(void)
{unsigned char KeyTemp=0;KeyTemp=KeyNumber;KeyNumber=0;return KeyTemp;
}/*** 函 数:检测当前按下的按键,无消抖及松手检测* 参 数:无* 返 回 值:按下按键的键值,范围:0~16,无按键按下时返回值为0*/
unsigned char Key_GetState()
{unsigned char KeyValue=0;unsigned char i=0;Matrix_Port=0x0F; //给所有行赋值0,列全为1i=2;while(i--); //适当延时,延时5usif(Matrix_Port!=0x0F){Matrix_Port=0x0F; //测试列i=2;while(i--);switch(Matrix_Port) //所有行拉低,检测哪一列按下{case 0x07:KeyValue=1;break;case 0x0B:KeyValue=2;break;case 0x0D:KeyValue=3;break;case 0x0E:KeyValue=4;break;default:break;}Matrix_Port=0xF0; //测试行i=2;while(i--);switch(Matrix_Port) //所有列拉低,检测哪一行按下{case 0x70:KeyValue=KeyValue;break;case 0xB0:KeyValue=KeyValue+4;break;case 0xD0:KeyValue=KeyValue+8;break;case 0xE0:KeyValue=KeyValue+12;break;default:break;}}else{KeyValue=0;}return KeyValue;
}/*** 函 数:矩阵按键驱动函数,在中断中调用* 参 数:无* 返 回 值:无*/
void MatrixKey_Tick(void)
{static unsigned char NowState,LastState;LastState=NowState; //更新上一次的键值NowState=Key_GetState(); //获取当前的键值//如果上个时间点按键未按下,这个时间点按键按下,则是按下瞬间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;case 5:KeyNumber=5;break;case 6:KeyNumber=6;break;case 7:KeyNumber=7;break;case 8:KeyNumber=8;break;case 9:KeyNumber=9;break;case 10:KeyNumber=10;break;case 11:KeyNumber=11;break;case 12:KeyNumber=12;break;case 13:KeyNumber=13;break;case 14:KeyNumber=14;break;case 15:KeyNumber=15;break;case 16:KeyNumber=16;break;default:break;}}//如果上个时间点按键按下,这个时间点按键还是按下,则是长按if(LastState && NowState){if (LastState== 1 && NowState== 1){KeyNumber=17;}else if(LastState== 2 && NowState== 2){KeyNumber=18;}else if(LastState== 3 && NowState== 3){KeyNumber=19;}else if(LastState== 4 && NowState== 4){KeyNumber=20;}else if(LastState== 5 && NowState== 5){KeyNumber=21;}else if(LastState== 6 && NowState== 6){KeyNumber=22;}else if(LastState== 7 && NowState== 7){KeyNumber=23;}else if(LastState== 8 && NowState== 8){KeyNumber=24;}else if(LastState== 9 && NowState== 9){KeyNumber=25;}else if(LastState==10 && NowState==10){KeyNumber=26;}else if(LastState==11 && NowState==11){KeyNumber=27;}else if(LastState==12 && NowState==12){KeyNumber=28;}else if(LastState==13 && NowState==13){KeyNumber=29;}else if(LastState==14 && NowState==14){KeyNumber=30;}else if(LastState==15 && NowState==15){KeyNumber=31;}else if(LastState==16 && NowState==16){KeyNumber=32;}}//如果上个时间点按键按下,这个时间点按键未按下,则是松手瞬间if(NowState==0){switch(LastState){case 1:KeyNumber=33;break;case 2:KeyNumber=34;break;case 3:KeyNumber=35;break;case 4:KeyNumber=36;break;case 5:KeyNumber=37;break;case 6:KeyNumber=38;break;case 7:KeyNumber=39;break;case 8:KeyNumber=40;break;case 9:KeyNumber=41;break;case 10:KeyNumber=42;break;case 11:KeyNumber=43;break;case 12:KeyNumber=44;break;case 13:KeyNumber=45;break;case 14:KeyNumber=46;break;case 15:KeyNumber=47;break;case 16:KeyNumber=48;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甘腾胜@20250506
【效果查看/操作演示】B站搜索“甘腾胜”或“gantengsheng”查看
【单片机】STC89C52RC
【频率】12T@11.0592MHz
【外设】8X8LED点阵屏、矩阵按键
【简单的原理分析】https://blog.csdn.net/gantengsheng/article/details/143581157
【操作说明】
(1)S10:上,S13:左,S14:下,S15:右,S16:开始,S12:返回
(2)循环滚动显示游戏英文名的界面按矩阵按键的任意按键进入游戏准备界面
(3)哪个方向出现两个点,就需要按哪个方向的按键
(4)游戏结束全屏闪烁界面按S16跳过
(5)显示得分的英文“SCROR”的界面按S16可跳过
(6)循环滚动显示得分的界面按返回键S12,可以重新开始游戏
*/#include <REGX52.H> //51单片机头文件
#include "MatrixLED.h" //8X8点阵屏
#include "MatrixKeyScan.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; //各模式中切换为其他模式前只执行一次的标志(类似于主函数while(1)前面的那部分),用来实现各模式的初始化,1:执行,0:不执行
unsigned char Offset; //偏移量,用来控制英文字母或数字向左滚动显示
bit RollFlag; //字母或数字滚动一个像素的标志,1:滚动,0:不滚动
bit GameOverFlag; //游戏结束的标志,1:游戏结束,0:游戏未结束
bit FlashFlag; //闪烁的标志,1:不显示,0:显示
unsigned char Score; //游戏得分
bit CorrectFlag; //按键按正确的标志,1:正确,0:无意义
bit ShowOrderFlag; //依次按顺序显示随机方向的标志,1:显示,0:不显示
bit PlayerPressFlag; //轮到玩家按按键的标志,1:轮到玩家按按键,0:未轮到玩家按按键
unsigned char PressPointer; //记录玩家按下按键的顺序号
unsigned char idata Order[99]; //按顺序存储产生的随机方向,最多99个数据,idata:变量存储在片内的简介访问区
unsigned char idata ScoreShow[]={ //二位数游戏得分(用于滚动显示)
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字符字模
unsigned char code Table1[]={ //游戏英文名:<<PRESS IN ORDER>>
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 无显示
0x00,0x08,0x14,0x22,0x49,0x14,0x22,0x41, // << 宽8高8(自定义书名号:两个小于号)
0x00,0x7F,0x09,0x09,0x09,0x06, // P
0x00,0x7F,0x09,0x19,0x29,0x46, // R
0x00,0x7F,0x49,0x49,0x49,0x41, // E
0x00,0x46,0x49,0x49,0x49,0x31, // S
0x00,0x46,0x49,0x49,0x49,0x31, // S
0x00,0x00,0x00,0x00,0x00,0x00, //
0x00,0x00,0x41,0x7F,0x41,0x00, // I
0x00,0x7F,0x04,0x08,0x10,0x7F, // N
0x00,0x00,0x00,0x00,0x00,0x00, //
0x00,0x3E,0x41,0x41,0x41,0x3E, // O
0x00,0x7F,0x09,0x19,0x29,0x46, // R
0x00,0x7F,0x41,0x41,0x22,0x1C, // D
0x00,0x7F,0x49,0x49,0x49,0x41, // E
0x00,0x7F,0x09,0x19,0x29,0x46, // R
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
};/*** 函 数:延时函数,延时约xms毫秒* 参 数:xms 延时的时间,范围:0~65535* 返 回 值:无* 说 明:分频系数及晶振频率为:12T@11.0592MHz*/
void Delay(unsigned int xms)
{unsigned char i,j;while(xms--){i=2;j=199;do{while(--j);}while(--i);}
}/*** 函 数:主函数(有且仅有一个)* 参 数:无* 返 回 值:无* 说 明:主函数是程序执行的起点,负责执行整个程序的主要逻辑*/
void main()
{unsigned char i;P2_5=0; //防止开发板上的蜂鸣器发出声音Timer0_Init(); //定时器0初始化Timer1_Init(); //定时器1初始化MatrixLED_Init(); //点阵屏初始化while(1){KeyNum=MatrixKey(); //获取独立按键的键码/*键码处理*/if(KeyNum){srand(TL0); //用定时器0的低8位做种子,从而产生真随机数if(Mode==0) //如果是循环滚动显示游戏英文名的界面{if(KeyNum>=33 && KeyNum<=48) //如果按下任意按键(松手瞬间){Mode=1; //切换到模式1OnceFlag=1; //切换模式前只执行一次的标志置1}}else if(Mode==1) //如果是游戏进行中{if((KeyNum==15 || KeyNum==31) && PlayerPressFlag) //如果短按或长按“右”键(S15){ //如果按键方向和显示的方向相同,则正确的标志CorrectFlag置1if(Order[PressPointer]==0){CorrectFlag=1;PressPointer++;}else{GameOverFlag=1;} //如果不相同,则游戏结束}if((KeyNum==10 || KeyNum==26) && PlayerPressFlag) //如果短按或长按“上”键(S10){if(Order[PressPointer]==1){CorrectFlag=1;PressPointer++;}else{GameOverFlag=1;}}if((KeyNum==13 || KeyNum==29) && PlayerPressFlag) //如果短按或长按“左”键(S13){if(Order[PressPointer]==2){CorrectFlag=1;PressPointer++;}else{GameOverFlag=1;}}if((KeyNum==14 || KeyNum==30) && PlayerPressFlag) //如果短按或长按“下”键(S14){if(Order[PressPointer]==3){CorrectFlag=1;PressPointer++;}else{GameOverFlag=1;}}}else if(Mode==2) //如果是游戏结束全屏闪烁的界面{if(KeyNum==48) //如果按了S16(松手瞬间){Mode=3;OnceFlag=1;}}else if(Mode==3) //如果是滚动显示速度英文“SCORE”的界面{if(KeyNum==48) //如果按了S16(松手瞬间){Mode=4;OnceFlag=1;}}else if(Mode==4) //如果是循环滚动显示得分的界面{if(KeyNum==44) //如果按了S12(松手瞬间){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%=108; //越界清零,循环滚动显示}}else if(Mode==1) //游戏进行中{if(OnceFlag){OnceFlag=0;//游戏初始化MatrixLED_Clear(); //清屏GameOverFlag=0; //游戏结束的标志置0Score=0; //得分清零ShowOrderFlag=1; //按顺序显示随机方向的标志置1PressPointer=0; //按键的序号(记录按到第几个)清零(类似于指针)CorrectFlag=0; //按键按正确的标志置0MatrixLED_ShowResult(0); //显示一条横线for(i=0;i<4;i++){MatrixLED_ShowDirection(i,1);} //四个方向均显示一个点}if(GameOverFlag) //如果游戏结束的标志为真{MatrixLED_ShowResult(2); //显示一个“ × ”Mode=2; //切换到全屏闪烁的模式}if(ShowOrderFlag && GameOverFlag==0) //如果按顺序显示随机方向的标志为真,且游戏未结束{ShowOrderFlag=0; //标志置0PlayerPressFlag=1; //轮到玩家按按键的标志置1for(i=0;i<Score+1;i++) //产生Score+1个随机方向,保存到数组中{Order[i]=rand()%4;}for(i=0;i<Score+1;i++) //按顺序显示所产生的随机方向{Delay(333); //延时333msMatrixLED_ShowDirection(Order[i],2);Delay(333);MatrixLED_ShowDirection(Order[i],1); }}if(CorrectFlag) //如果正确的标志为真{CorrectFlag=0; //正确的标志置0MatrixLED_ShowResult(1); //显示一个“ √ ”Delay(333);MatrixLED_ShowResult(0); //显示横线}if(PressPointer==Score+1){PlayerPressFlag=0;ShowOrderFlag=1;PressPointer=0;Score++; //按顺序全部按对,分数加一if(Score==99) //最多99分{GameOverFlag=1;}}}else if(Mode==2) //游戏结束全屏闪烁{//在定时器1中实现全屏闪烁}else if(Mode==3) //滚动显示得分的英文“SCORE”{if(OnceFlag){OnceFlag=0;Offset=0;}if(RollFlag && Offset<=38) //只滚动显示一次英文“SCORE”{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;for(i=0;i<6;i++) //将得分的十位、个位的字模写入数组ScoreShow中{ScoreShow[ 8+i]=Table3[(Score/10)*6+i]; //十位}for(i=0;i<6;i++){ScoreShow[14+i]=Table3[(Score%10)*6+i]; //个位}}if(RollFlag){RollFlag=0;MatrixLED_MoveLeft(ScoreShow,Offset);Offset++;Offset%=20; //循环滚动显示}}}
}/*** 函 数:定时器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++;if(T0Count1>=2) //每隔20ms检测一次键码{T0Count1=0;MatrixKey_Tick();}if(T0Count2>=50) //每隔500ms置反FlashFlag{T0Count2=0;FlashFlag=!FlashFlag;}if(T0Count3>=10) //每隔100ms滚动显示一次字母或数字{T0Count3=0;RollFlag=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();}
}
总结
比较简单的一个小游戏,难度不大。