基于51单片机和8X8点阵屏、独立按键的填充消除类小游戏
目录
- 系列文章目录
- 前言
- 一、效果展示
- 二、原理分析
- 三、各模块代码
- 1、8X8点阵屏
- 2、独立按键
- 3、定时器0
- 4、定时器1
- 四、主函数
- 总结
系列文章目录
前言
使用的是普中A2开发板。
【单片机】STC89C52RC
【频率】12T@11.0592MHz
【外设】8X8点阵屏、独立按键
效果查看/操作演示:B站搜索“甘腾胜”或“gantengsheng”查看。
源代码下载:B站对应视频的简介有工程文件下载链接。
一、效果展示
二、原理分析
基于我的上一个小游戏进行修改
基于51单片机和8X8点阵屏、独立按键的射击消除类小游戏
把“射击”改成“填充”,再进行相应的处理就行了。
即:
射击:从玩家所在的列的第7行往上检测,一直到第1行,检测到的第一个点亮着的LED后,则把这个LED熄灭,然后退出循环。如果检测到第1行了也没有点亮的LED,则不做处理。
填充:填充前先检测玩家所在的列的第7行有没有障碍物,如果有,则游戏结束。如果没有,则从玩家所在的列的第6行往上检测,一直到第1行,检测到的第一个点亮着的LED后,则把这个LED正下方的LED也点亮,然后退出循环。如果检测到第1行了也没有点亮着的LED,则把第1行的这个LED点亮。
通过for循环进行检测,通过break关键字跳出循环。
填充后,从第1行开始检测,如果发现整行的LED都点亮了,则这一行到第7行的LED全部熄灭。
用到三个比较基础的操作一个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;}
}
三、各模块代码
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甘腾胜@20250528
【效果查看/操作演示】B站搜索“甘腾胜”或“gantengsheng”查看
【单片机】STC89C52RC
【频率】12T@11.0592MHz
【外设】8X8LED点阵屏、独立按键
【简单的原理分析】https://blog.csdn.net/gantengsheng/article/details/143581157
【注意】点阵屏旁边的跳线帽要接三个排针的左边两个
【操作说明】
(1)循环滚动显示游戏英文名的界面按任意按键开始游戏
(2)K1、K2两个按键控制左右移动
(3)游戏中按K3进行发射填充
(4)游戏时按K4可以暂停或继续游戏
(5)游戏结束全屏闪烁界面按K4进入滚动显示得分的英文的界面
(6)滚动显示得分的英文的界面可按K4跳过
(7)循环滚动显示得分界面可按K3返回,重新开始游戏
*/#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; //移动的时间间隔,单位是1ms
unsigned int Score; //游戏得分,范围:0~65535
unsigned char ScoreLength; //游戏得分的位数,范围:1~5
bit PauseFlag; //暂停的标志,1:暂停,0:继续
unsigned int T0Count; //定时器0计数全局变量
unsigned char Player; //玩家位置,范围:0~7,对应1~8列
unsigned char NewCreation; //整个屏幕向下平移一个像素后,第一行产生的随机的创造物,范围:1~254(高位在左)
bit FillFlag; //填充的标志,1:填充,0:不填充
unsigned char LineCount; //统计某一行点亮的LED的个数,范围:0~8
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, // 无显示
};//取模要求:阴码(亮点为1),纵向取模,高位在下
//我分享的工程文件夹中有6X8像素的ASCII字符字模
//code:数据保存在flash中
unsigned char code Table1[]={ //游戏名称“填充或者死亡”的英文:<<FILL OR DIE>>,宽6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 无显示
0x00,0x08,0x14,0x22,0x49,0x14,0x22,0x41, // << 宽8高8(自定义书名号:两个小于号)
0x00,0x7F,0x09,0x09,0x09,0x01, // F
0x00,0x00,0x41,0x7F,0x41,0x00, // I
0x00,0x7F,0x40,0x40,0x40,0x40, // L
0x00,0x7F,0x40,0x40,0x40,0x40, // L
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
};/*** 函 数:幂函数/指数函数* 参 数: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=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==3 || KeyNum==7) //如果按下K3(短按或长按){FillFlag=1; //射击的标志置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; //只执行一次的标志置0Offset=0; //滚动显示的偏移量清零}if(RollFlag) //如果滚动的标志RollFlag为真(非零即真){RollFlag=0; //滚动的标志RollFlag置0MatrixLED_MoveLeft(Table1,Offset); //向左滚动Offset++; //每次向左移动一个像素Offset%=96; //越界清零,循环滚动显示}}else if(Mode==1) //游戏进行中{if(OnceFlag){OnceFlag=0;//游戏初始化MatrixLED_Clear(); //清屏GameOverFlag=0; //游戏结束的标志置0Score=0; //得分清零PauseFlag=0; //暂停的标志置0Duration=3000; //初始每隔3s向下移动一次Player=rand()%8; //随机确定玩家的初始位置MatrixLED_DrawPoint(Player,7); //显示玩家(射击)位置for(i=0;i<46;i++){ScoreShow[i]=0;} //清空数组ScoreShow的数据MoveFlag=1; //障碍物移动的标志置1T0Count=0; //定时器0全局计数变量清零}if(PauseFlag==0) //如果不是暂停状态{if(FillFlag) //如果射击的标志为真{FillFlag=0; //射击的标志置0if(MatrixLED_GetPoint(Player,6)) //如果玩家所在的列第7行的LED是点亮的{ //再继续填充的话Mode=2; //则游戏结束GameOverFlag=1; //游戏结束}else //如果不是{for(i=0;i<6;i++) //则从玩家所在列的第6行往上寻找第一个点亮了的LED{if(MatrixLED_GetPoint(Player,5-i)) //如果找到了{MatrixLED_DrawPoint(Player,6-i); //则将正下方的LED也点亮break; //退出循环}if(i==5 && MatrixLED_GetPoint(Player,0)==0) //如果到了第一行,且第一行的LED不亮{MatrixLED_DrawPoint(Player,0); //则点亮第一行的LED}}}for(i=0;i<7;i++) //检测前七行,从第一行开始{LineCount=0;for(j=0;j<8;j++) //看有没有整行都填满(整行的LED都点亮)的情况{if(MatrixLED_GetPoint(j,i)){LineCount++;}}if(LineCount==8) //如果有{Score++; //分数加一if(Score%10==0) //分数每增加10,下落的时间间隔减为9/10{Duration=Duration*9/10;if(Duration<2000){Duration=2000;} //下落的最小时间间隔为2s}for(;i<7;i++) //这一行到第7行的LED全部熄灭{for(j=0;j<8;j++){MatrixLED_ClearPoint(j,i);}}break; //退出循环}}}if(MoveFlag) //如果移动的标志为真{MoveFlag=0; //移动的标志置0for(i=0;i<8;i++) //向下移动前先判断第七行(玩家所在第八行的上一行)是否有未熄灭的LED{if(MatrixLED_GetPoint(i,6)) //如果有,则游戏结束{Mode=2;GameOverFlag=1;}}if(GameOverFlag==0) //如果游戏未结束{for(i=0;i<8;i++) //整个屏幕的显示向下平移一个像素{ //会导致玩家控制的点熄灭DisplayBuffer[i]<<=1;}MatrixLED_DrawPoint(Player,7); //重新显示玩家位置NewCreation=rand()%254+1; //重新生成障碍物,范围:1~254for(i=0;i<8;i++) //将NewCreation按照高位在左的方式放在第一行{if( (0x80>>i) & NewCreation ){MatrixLED_DrawPoint(i,0);}}}}}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;//判断得分是多少位数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 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 || KeyNumber1==5) && PauseFlag==0) //如果短按K1或长按K1,且不是暂停状态{if(Player>0) //如果不是在最左{MatrixLED_ClearPoint(Player,7); //清除原来位置的显示Player--; //向左移动一个像素MatrixLED_DrawPoint(Player,7); //显示移动后的新位置}}if( (KeyNumber1==2 || KeyNumber1==6) && PauseFlag==0) //如果短按K2或长按K2,且不是暂停状态{if(Player<7) //如果不是在最右{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();}
}
总结
做的上一个小游戏是射击类小游戏,在上一个游戏的基础上简单修改就完工了。
相关文章:

基于51单片机和8X8点阵屏、独立按键的填充消除类小游戏
目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码1、8X8点阵屏2、独立按键3、定时器04、定时器1 四、主函数总结 系列文章目录 前言 使用的是普中A2开发板。 【单片机】STC89C52RC 【频率】12T11.0592MHz 【外设】8X8点阵屏、独立按键 效果查看/操作演示&#x…...
将数据库表导出为C#实体对象
数据库方式 use 数据库;declare TableName sysname 表名 declare Result varchar(max) /// <summary> /// TableName /// </summary> public class TableName {select Result Result /// <summary>/// CONVERT(NVARCHAR(500), ISNULL(ColN…...

物联网技术发展与应用研究分析
文章目录 引言一、物联网的基本架构(一)感知层(二)网络层(三)平台层(四)应用层 二、物联网的关键技术(一)传感器技术(二)通信技术&…...

金融系统渗透测试
金融系统渗透测试是保障金融机构网络安全的核心环节,它的核心目标是通过模拟攻击手段主动发现系统漏洞,防范数据泄露、资金盗取等重大风险。 一、金融系统渗透测试的核心框架 合规性驱动 需严格遵循《网络安全法》《数据安全法》及金融行业监管要求&am…...
C++ 信息学奥赛总复习题
第一章 C 基础语法 一、填空题 C 源文件的扩展名通常是______。C 程序的入口函数是______。在 C 中,注释有两种形式,分别是______和______。声明一个整型变量 a 的语句是______。输出语句的关键字是______。 二、判断题 C 区分大小写。( …...

9.进程间通信
1.简介 为啥要有进程间通信? 如果未来进程之间要协同呢?一个进程要把自己的数据交给另一个进程!进程是具有独立性的,所以把一个进程的数据交给另一个进程----基本不可能!必须通信起来,就必须要有另一个人…...
性能剖析:在 ABP 框架中集成 MiniProfiler 实现性能可视化诊断
🚀 性能剖析:在 ABP 框架中集成 MiniProfiler 实现性能可视化诊断 📚 目录 🚀 性能剖析:在 ABP 框架中集成 MiniProfiler 实现性能可视化诊断一、为什么选择 MiniProfiler? 🧐二、集成 MiniProf…...

React 基础入门笔记
一、JSX语法规则 1. 定义虚拟DOM时,不要写引号 2.标签中混入JS表达式时要用 {} (1).JS表达式与JS语句(代码)的区别 (2).使用案例 3.样式的类名指定不要用class,要用className 4.内…...
C++.OpenGL (12/64)光照贴图(Lightmaps)
光照贴图(Lightmaps) 静态光照烘焙技术 #mermaid-svg-1vJKLLr1zSCp1ASH {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-1vJKLLr1zSCp1ASH .error-icon{fill:#552222;}#mermaid-svg-1vJKLLr1zSCp1ASH .error-text…...

压测软件-Jmeter
1 下载和安装 1.1 检查运行环境 Jmeter需要运行在java环境(JRE 或 JDK)中 在window的"命令提示窗"查看安装的java版本: java -version 1.2 下载Jmeter 从Apache官网下载Jmeter安装包 1.3 解压和运行 解压后,进入bin文件夹,双击jmeter.bat即可…...
Linux 常用命令语法总结
Linux 常用命令语法总结 1. 文件和目录操作 1.1 基本文件操作 # 列出文件和目录 ls # 列出当前目录内容 ls -l # 详细列表格式 ls -la # 显示隐藏文件 ls -lh # 人性化显示文件大小 ls...
青少年编程与数学 01-011 系统软件简介 02 UNIX操作系统
青少年编程与数学 01-011 系统软件简介 02 UNIX操作系统 一、UNIX的历史沿革二、UNIX系统架构三、UNIX主要特性四、UNIX分支与变种五、UNIX设计哲学六、UNIX的影响与遗产 **摘要:**UNIX操作系统是现代计算领域最具影响力的操作系统之一,其设计哲学和技术…...

NLP学习路线图(三十):微调策略
在自然语言处理领域,预训练语言模型(如BERT、GPT、T5)已成为基础设施。但如何让这些“通才”模型蜕变为特定任务的“专家”?微调策略正是关键所在。本文将深入剖析七种核心微调技术及其演进逻辑。 一、基础概念:为什么需要微调? 预训练模型在海量语料上学习了通用语言表…...

leetcode刷题日记——1.组合总和
解答: class Solution { public:void dfs(vector<int>& candidates, int target, vector<vector<int>>& ans, vector<int>& combine, int idx) {if(idxcandidates.size()){//遍历完的边界return;}if(target0){//找完了能组成和…...

关于单片机的基础知识(一)
成长路上不孤单😊😊😊😊😊😊 【14后😊///计算机爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于单片机基础知识的相关内容…...
Python训练营打卡Day45
知识点回顾: tensorboard的发展历史和原理tensorboard的常见操作tensorboard在cifar上的实战:MLP和CNN模型 效果展示如下,很适合拿去组会汇报撑页数: 作业:对resnet18在cifar10上采用微调策略下,用tensorbo…...

Xilinx FPGA 重构Multiboot ICAPE2和ICAPE3使用
一、FPGA Multiboot 本文主要介绍基于IPROG命令的FPGA多版本重构,用ICAP原语实现在线多版本切换。需要了解MultiBoot Fallback点击链接。 如下图所示,ICAP原语可实现flash中n1各版本的动态切换,在工作过程中,可以通过IPROG命令切…...

Redis专题-基础篇
题记 本文涵盖了Redis的各种数据结构和命令,Redis的各种常见Java客户端的应用和最佳实践 jedis案例github地址:https://github.com/whltaoin/fedis_java_demo SpringbootDataRedis案例github地址:https://github.com/whltaoin/springbootData…...

springMVC-11 中文乱码处理
前言 本文介绍了springMVC中文乱码的解决方案,同时也贴出了本人遇到过的其他乱码情况,可以根据自身情况选择合适的解决方案。 其他-jdbc、前端、后端、jsp乱码的解决 Tomcat导致的乱码解决 自定义中文乱码过滤器 老方法,通过javaW…...

【iOS安全】iPhone X iOS 16.7.11 (20H360) WinRa1n 越狱教程
前言 越狱iPhone之后,一定记得安装一下用于屏蔽更新的描述文件(可使用爱思助手) 因为即便关闭了自动更新,iPhone仍会在某些时候自动更新系统,导致越狱失效;更为严重的是,更新后的iOS版本可能是…...
MongoDB检查慢查询db.system.profile.find 分析各参数的作用
db.system.profile.find() 是分析 MongoDB 性能的关键工具,其返回的文档包含丰富的性能指标。下面是对各参数的详细解释和优化建议: {"op": "query", // 操作类型(query/update/remove)"ns": "test.users", // 命名…...

智能标志桩图像监测装置如何守护地下电缆安全
在现代城市基础设施建设中,大量电缆、管道被埋设于地下,这虽然美化了城市景观,却也带来了新的安全隐患。施工挖掘时的意外破坏、自然灾害的影响,都可能威胁这些"城市血管"的安全运行。 传统的地下设施标识方式往往只依…...

【网站建设】网站 SEO 中 meta 信息修改全攻略 ✅
在做 SEO 优化时,除了前一篇提过的Title之外,meta 信息(通常指 <meta> 标签)也是最基础、最重要的内容之一,主要包括: <meta name="description"> <meta name="keywords"> 搜索引擎重点参考这些信息,决定你网页的展示效果与排名。…...

计算机视觉处理----OpenCV(从摄像头采集视频、视频处理与视频录制)
一、采集视频 VideoCapture 用于从视频文件、摄像头或其他视频流设备中读取视频帧。它可以捕捉来自 多种源的视频。 cv2.VideoCapture() 打开摄像头或视频文件。 cap cv2.VideoCapture(0) # 0表示默认摄像头,1是第二个摄像头,传递视频文件路径也可以 …...
elasticsearch基本操作笔记
1.通过kibana查看elasticsearch版本信息 a.左上角三道横->Management->Dev Tools b.GET / 执行 c.执行结果 { “name” : “xxxx”, “cluster_name” : “xxxxxxx”, “cluster_uuid” : “vl1UudAoQp-aHWAzyPoMyw”, “version” : { “number” : “7.15.1”, “build…...
LVGL手势识别事件无上报问题处理记录
最近在使用LVGL8.3开源库开源UI界面时,碰到使用FB驱动显示UI时,触摸屏手势识别事件接收不到的情况,通过如下调整可以处理该问题: 1、创建Top Object时,不能使用如下语句: lv_obj_t *page_obj = lv_obj_create(lv_scr_act()); 而要使用如下语句: lv_obj_t *page_obj =…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- 第一篇:MIPI CSI-2基础入门
第一篇:MIPI CSI-2基础入门 1. 为什么需要CSI-2? 痛点场景对比 (用生活案例降低理解门槛) 传统并行接口CSI-2接口30根线传输720P图像仅需5根线(1对CLK4对DATA)线距>5cm时出现重影线缆可长达1…...

变幻莫测:CoreData 中 Transformable 类型面面俱到(一)
概述 各位似秃似不秃小码农们都知道,在苹果众多开发平台中 CoreData 无疑是那个最简洁、拥有“官方认证”且最具兼容性的数据库框架。使用它可以让我们非常方便的搭建出 App 所需要的持久存储体系。 不过,大家是否知道在 CoreData 中还存在一个 Transfo…...

开源技术驱动下的上市公司财务主数据管理实践
开源技术驱动下的上市公司财务主数据管理实践 —— 以人造板制造业为例 引言:财务主数据的战略价值与行业挑战 在资本市场监管日益严格与企业数字化转型的双重驱动下,财务主数据已成为上市公司财务治理的核心基础设施。对于人造板制造业而言࿰…...
婚恋小程序直播系统框架搭建
逻辑分析 直播流管理:需要处理主播端的直播流推送,确保直播流能够稳定、高效地传输到各个观看用户的设备上。这涉及到选择合适的流媒体协议,如 RTMP(Real-Time Messaging Protocol)、HLS(HTTP Live Streami…...