基于51单片机和8X8点阵屏、矩阵按键的小游戏《打地鼠》
小游戏《打地鼠》,普中开发板,STC89C52RC,8X8点阵屏,矩阵按键。
·
系列文章目录
前言
使用的是普中A2开发板。
【单片机】STC89C52RC
【频率】12T@11.0592MHz
【外设】8X8点阵屏、矩阵按键
效果查看/操作演示:B站搜索“甘腾胜”或“gantengsheng”查看。
源代码下载:B站对应视频的简介有工程文件下载链接。
一、效果展示




二、原理分析
1、倒计时和分数的显示
因地鼠的显示只用到第4到第8行,所以可以用第1行显示倒计时,第3行显示分数,都是用二进制来显示,高位在左。
用到一些很常规的操作,与、或、移位、非等等。
2、地鼠的显示和消除
因用到矩阵按键的后面两行,所以袋鼠的显示也用2X4的形式来显示,即两行四列。试过用1个像素和用4个像素来显示一个地鼠,感觉效果不是很好,最终决定用3个像素来显示一个地鼠,即2X2像素去掉右上角那个像素。
显示与消除。如果你能随意控制点阵屏上任意一个LED的亮灭,那肯定也能任意控制三个LED的亮灭。
3、按键的控制
为了使按键控制更加灵敏,按键按下和按住都能打掉地鼠。
4、新地鼠的创造
打掉所有地鼠后,会随机出现1~3个新地鼠,并且新地鼠的位置跟最后被打掉的地鼠的位置不同。
三、各模块代码
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_ShowPoint(unsigned char X,unsigned char Y,unsigned char State);
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
* 参 数:State 亮灭状态,0:灭,非0:亮
* 返 回 值:无
* 说 明:左上角的LED为原点(0,0),向右为X轴正方向,向下为Y轴正方向
*/
void MatrixLED_ShowPoint(unsigned char X,unsigned char Y,unsigned char State)
{
if(State==0)
{
if(X>=0 && X<=7 && Y>=0 && Y<=7){ DisplayBuffer[X] &= ~(0x01<<Y); }
}
else
{
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 __MATRIXKEY_H__
#define __MATRIXKEY_H__
unsigned char MatrixKey(void);
void Key_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,列全为1
i=2;while(i--); //适当延时,延时5us
if(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 Key_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.0592MHz
TH0=0xFC; //设置定时初值,定时1ms,12T@11.0592MHz
TF0=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.0592MHz
TH0=0xFC; //设置定时初值,定时1ms,12T@11.0592MHz
T0Count++;
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.0592MHz
TH1=0xFC; //设置定时初值,定时1ms,12T@11.0592MHz
TF1=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.0592MHz
TH1=0xFC; //设置定时初值,定时1ms,12T@11.0592MHz
T1Count++;
if(T1Count>=1000)
{
T1Count=0;
}
}
*/
四、主函数
main.c
/*by甘腾胜@20250609
【效果查看/操作演示】B站搜索“甘腾胜”或“gantengsheng”查看
【单片机】STC89C52RC
【频率】12T@11.0592MHz
【外设】8X8LED点阵屏、矩阵按键
【简单的原理分析】https://blog.csdn.net/gantengsheng/article/details/143581157
【注意】点阵屏旁边的跳线帽要接三个排针的左边两个
【操作说明】
(1)循环滚动显示游戏英文名的界面按任意按键开始游戏
(2)按S9~S16打地鼠
(3)游戏结束全屏闪烁界面按S1进入滚动显示得分的英文的界面
(5)滚动显示得分的英文的界面可按S1跳过
(6)循环滚动显示得分界面可按S2返回,重新开始游戏
*/
#include <REGX52.H> //51单片机头文件
#include "MatrixLED.h" //8X8点阵屏
#include "MatrixKey.h" //独立按键
#include "Timer0.h" //定时器0
#include "Timer1.h" //定时器1
#include <STDLIB.H> //随机函数
unsigned char KeyNum; //存储获取的键码
unsigned char Mode; //游戏模式,0:循环滚动显示游戏英文名,1:游戏中,2:游戏结束全屏闪烁,3:滚动显示得分的英文,4:循环滚动显示得分
unsigned int Score; //游戏得分,范围:0~65535
unsigned char ScoreLength; //游戏得分的位数,范围:1~5
unsigned char Offset; //偏移量,用来控制字母或数字向左滚动显示
bit OnceFlag; //各模式中切换为其他模式前只执行一次的标志(类似于主函数主循环前的那部分,用于该模式的初始化),1:执行,0:不执行
bit FlashFlag; //闪烁的标志,1:不显示,0:显示
bit RollFlag; //字母或数字滚动一个像素的标志,1:滚动,0:不滚动
unsigned char T0Count_0; //定时器0全局计数变量
unsigned char GameTime; //游戏剩余时间
unsigned char CreateNum; //创造的地鼠的数量,范围:1~3
unsigned char LastMole; //最后一次打的地鼠的序号,范围:0~7(用来控制新创造的地鼠不包含最后打的地鼠)
unsigned char NewMole; //新创造的地鼠的序号,范围:0~7(用来控制新创造的地鼠不包含最后打的地鼠)
//取模要求:阴码(亮点为1),纵向取模,高位在下
//我分享的工程文件夹中有6X8像素的ASCII字符字模
//code:数据保存在flash中
unsigned char code Table1[]={ //游戏名称“打地鼠”的英文:<<WHACK A MOLE>>,宽6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 无显示
0x00,0x08,0x14,0x22,0x49,0x14,0x22,0x41, // << 宽8高8(自定义书名号:两个小于号)
0x00,0x3F,0x40,0x38,0x40,0x3F, // W
0x00,0x7F,0x08,0x08,0x08,0x7F, // H
0x00,0x7C,0x12,0x11,0x12,0x7C, // A
0x00,0x3E,0x41,0x41,0x41,0x22, // C
0x00,0x7F,0x08,0x14,0x22,0x41, // K
0x00,0x00,0x00,0x00,0x00,0x00, //
0x00,0x7C,0x12,0x11,0x12,0x7C, // A
0x00,0x00,0x00,0x00,0x00,0x00, //
0x00,0x7F,0x02,0x0C,0x02,0x7F, // M
0x00,0x3E,0x41,0x41,0x41,0x3E, // O
0x00,0x7F,0x40,0x40,0x40,0x40, // L
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
};
unsigned char xdata ScoreShow[]={ //游戏得分(用于循环滚动显示),xdata:外部扩展RAM
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,0x00,0x00,0x00,0x00, // 得分最多五位数,每一个数字对应6个字节
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 无显示
};
/**
* 函 数:显示或消除地鼠
* 参 数:Position 地鼠位置,范围:0~7
* 参 数:State 显示状态,非0:显示,0:不显示
* 返 回 值:无
* 说 明:每个地鼠用3个像素显示(2X2像素去掉右上方的像素)
* 说 明:点阵屏第5行、第6行对应的地鼠位置值为0~3,点阵屏第7行、第8行对应的地鼠位置值为4~7
*/
void ShowMole(unsigned char Position,unsigned char State)
{
MatrixLED_ShowPoint( Position%4*2, Position/4*2+4, State );
MatrixLED_ShowPoint( Position%4*2, Position/4*2+5, State );
MatrixLED_ShowPoint( Position%4*2+1, Position/4*2+5, State );
}
/**
* 函 数:获取出现的地鼠的数量
* 参 数:无
* 返 回 值:地鼠数量
* 说 明:检测每个地鼠的左下方的LED是否是点亮的
*/
unsigned char GetMoleNumbe(void)
{
unsigned char i;
unsigned char Temp=0;
for(i=0;i<8;i++)
{
if(MatrixLED_GetPoint(i%4*2,i/4*2+5))
{
Temp++;
}
}
return Temp;
}
/**
* 函 数:获取某个地鼠的状态
* 参 数:Position 要判断的位置,范围:0~7
* 返 回 值:地鼠状态,0:被打进洞里了,1:未被打进洞里
*/
unsigned char GetMoleState(unsigned char Position)
{
if(MatrixLED_GetPoint(Position%4*2,Position/4*2+5))
{
return 1;
}
return 0;
}
/**
* 函 数:幂函数/指数函数
* 参 数:X 底
* 参 数:Y 幂
* 返 回 值:X的Y次方
* 说 明:辅助取出Score中的某一位数字
*/
unsigned int Pow(unsigned char X,unsigned char Y)
{
unsigned char i;
unsigned int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* 函 数:主函数(有且仅有一个)
* 参 数:无
* 返 回 值:无
* 说 明:主函数是程序执行的起点,负责执行整个程序的主要逻辑
*/
void main()
{
unsigned char i,j; //For循环用到的临时变量
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; //切换到模式1
OnceFlag=1; //切换模式前只执行一次的标志置1
}
}
else if(Mode==1) //如果是正在游戏的界面
{
if((KeyNum-1)%16/8 && KeyNum<=32) //如果按的是S9~S16,且是按下或长按
{
if(GetMoleState((KeyNum-1)%8)) //如果所按位置对应的地鼠的LED是点亮的
{
ShowMole((KeyNum-1)%8,0); //则熄灭该处位置的地鼠的LED
Score++; //分数加1
LastMole=(KeyNum-1)%8;
}
}
}
else if(Mode==2) //如果是游戏结束全屏闪烁的界面
{
if(KeyNum==33) //如果是松开S1瞬间
{
Mode=3;
OnceFlag=1;
}
}
else if(Mode==3) //如果是滚动显示英文“SCORE”的界面
{
if(KeyNum==33) //如果是松开S1瞬间
{
Mode=4;
OnceFlag=1;
}
}
else if(Mode==4) //如果是循环滚动显示得分的界面
{
if(KeyNum==34) //如果是松开S2瞬间
{
Mode=1; //重新开始游戏
OnceFlag=1;
}
}
}
/*游戏处理*/
if(Mode==0) //循环滚动显示游戏英文名
{
if(OnceFlag) //切换到其他模式前,此if中的代码只执行1次
{
OnceFlag=0; //只执行一次的标志置0
Offset=0; //滚动显示的偏移量清零
}
if(RollFlag) //如果滚动的标志RollFlag为真(非零即真)
{
RollFlag=0; //滚动的标志RollFlag置0
MatrixLED_MoveLeft(Table1,Offset); //更新显示
Offset++; //每次向左移动一个像素
Offset%=96; //越界清零,循环滚动显示
}
}
else if(Mode==1) //游戏进行中
{
if(OnceFlag)
{
OnceFlag=0;
//游戏初始化
MatrixLED_Clear(); //清屏
Score=0; //得分清零
for(i=0;i<46;i++){ScoreShow[i]=0;} //清空分数显示的缓存数组的数据
T0Count_0=0; //定时器0全局计数变量清零
GameTime=60; //游戏时间初始化,总共60s
}
if(GameTime==0) //如果倒计时结束,则游戏结束
{
Mode=2; //切换到下一个模式:全屏闪烁
}
for(i=0;i<8;i++) //点阵屏第一行以二进制显示剩余时间(高位在左)
{
if( GameTime & (0x80>>i) )
{
MatrixLED_ShowPoint(i,0,1);
}
else
{
MatrixLED_ShowPoint(i,0,0);
}
}
for(i=0;i<8;i++) //点阵屏第三行以二进制显示得分的低八位(高位在左)
{
if( Score & (0x80>>i) )
{
MatrixLED_ShowPoint(i,2,1);
}
else
{
MatrixLED_ShowPoint(i,2,0);
}
}
if(GetMoleNumbe()==0) //如果检测到屏幕上的地鼠数量为0
{
CreateNum=rand()%3+1; //产生一个1~3的随机数
while(GetMoleNumbe()<CreateNum)
{ //在随机一个位置产生一个地鼠,直到数量与CreateNum相等
while(1) //新产生的地鼠不在产生地鼠时最后打地鼠的位置
{
NewMole=rand()%8;
if(NewMole!=LastMole){break;} //退出循环
}
ShowMole(NewMole,1);
}
}
}
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;
//判断得分是多少位数
if(Score>=10000){ScoreLength=5;}
else if(Score>=1000){ScoreLength=4;}
else if(Score>=100){ScoreLength=3;}
else if(Score>=10){ScoreLength=2;}
else{ScoreLength=1;}
//将得分的字模写入数组ScoreShow中
for(j=0;j<ScoreLength;j++)
{
for(i=0;i<6;i++)
{
ScoreShow[8+6*j+i]=Table3[(Score/Pow(10,ScoreLength-1-j)%10)*6+i];
}
}
}
if(RollFlag)
{
RollFlag=0;
MatrixLED_MoveLeft(ScoreShow,Offset);
Offset++;
Offset%=8+ScoreLength*6; //循环滚动显示
}
}
}
}
/**
* 函 数:定时器0中断函数
* 参 数:无
* 返 回 值:无
*/
void Timer0_Routine() interrupt 1
{
static unsigned char T0Count0,T0Count1,T0Count2; //定时器计数变量(静态)
TL0=0x00; //设置定时初值,定时10ms,12T@11.0592MHz
TH0=0xDC; //设置定时初值,定时10ms,12T@11.0592MHz
T0Count0++;
T0Count1++;
T0Count2++;
T0Count_0++;
if(T0Count0>=2) //每隔20ms检测一次键码
{
T0Count0=0;
Key_Tick();
}
if(T0Count1>=50) //每隔500ms置反FlashFlag
{
T0Count1=0;
FlashFlag=!FlashFlag;
}
if(T0Count2>=10) //每隔100ms滚动显示一次字母或数字
{
T0Count2=0;
RollFlag=1;
}
if(T0Count_0>=100) //每隔1s游戏时间变量减1
{
T0Count_0=0;
if(GameTime)
{
GameTime--;
}
}
}
/**
* 函 数:定时器1中断函数
* 参 数:无
* 返 回 值:无
* 说 明:专门用定时器1来扫描显示LED点阵屏,定时器1的优先级要比定时器0的高,否则显示会有闪烁现象
*/
void Timer1_Routine() interrupt 3
{
TL1=0x66; //设置定时初值,定时1ms,12T@11.0592MHz
TH1=0xFC; //设置定时初值,定时1ms,12T@11.0592MHz
if(Mode==2 && FlashFlag){P0=0xFF;} //控制游戏结束后的全屏闪烁
else{MatrixLED_Tick();}
}
总结
之前拿TM1638模块也做了一个《打地鼠》,那个简单一些,每次都是出现一个地鼠。单片机通过三根线与TM1638通信,通过读写TM1638的寄存器来完成按键检测、控制LED亮灭、控制数码管的显示。
这次用8X8点阵屏做的《打地鼠》做了一点点变化,就是每次打完屏幕上地鼠后,在屏幕上随机位置随机产生1~3个地鼠。
更多推荐



所有评论(0)