系列文章目录
前言
《贪吃蛇》,一款经典的、怀旧的小游戏,单片机入门必写程序。
基于51单片机和8X8LED点阵屏(板载74HC595驱动)的矩阵键盘控制的小游戏《贪吃蛇》【普中A2开发板版本】
基于51单片机和8X8LED点阵屏(MAX7219驱动)的自制独立按键控制的小游戏《贪吃蛇》【散件版本】
之前做了8X8LED点阵屏的贪吃蛇小游戏,现在再做一个独立按键控制的16X16LED点阵屏的。
用到51单片机最小开发板,用八位自制的独立按键控制,单片机芯片为STC89C52RC,晶振@12.0000MHz。16X16LED点阵屏模块通过MAX7219驱动,MAX7219内含自动扫描电路,只需要将要显示的数据送进芯片的寄存器就行了,很好用,建议使用。
效果查看/操作演示:B站搜索“甘腾胜”或“gantengsheng”查看。
源代码下载:B站对应视频的简介有下载链接。
一、效果展示
二、原理分析
游戏的整体框架和8X8点阵屏的差不多。不在赘述,原理不清楚的可以看一下前言的8X8点阵屏版本。
也是用一个字节的高四位(0~15)存储列(1~16列)的数据,低四位(0~15)存储行的数据(1~16行)。
点阵屏的点达到了256个之多,存储蛇身数据至少需要256个字节(Byte),不能像8X8点阵屏的程序那样弄一个中间缓存数组了,需要直接将移动后的数据更新到显示缓存数组DisplayBuffer中。显示缓存数组至少需要32个字节(一个字节有8个位),对应点阵屏的256个点。
8X8点阵屏只能显示英文字母,16X16的可以显示汉字了,所以可以通过取模软件将游戏名称等用汉字显示出来。具体取模怎么设置跟硬件有关,也跟摆放的方向有关,供电口和数据接口在上面的话,按以下图片的设置来取模。
数字取模如果需要16X16的,需要将字宽调为32、字高调为16.
三、各模块代码
1、定时器0
h文件
#ifndef __TIMER0_H__
#define __TIMER0_H__
void Timer0_Init(void);
#endif
c文件
#include <REGX52.H>
/**
* @brief 定时器0初始化,1毫秒@12.000MHz
* @param 无
* @retval 无
*/
void Timer0_Init(void)
{
TMOD&=0xF0; //设置定时器模式(高四位不变,低四位清零)
TMOD|=0x01; //设置定时器模式(通过低四位设为“定时器0工作方式1”的模式)
TL0=0x18; //设置定时初值,定时1ms
TH0=0xFC; //设置定时初值,定时1ms
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=0x18; //设置定时初值,定时1ms
TH0=0xFC; //设置定时初值,定时1ms
T0Count++;
if(T0Count>=1000)
{
T0Count=0;
}
}
*/
2、自制独立按键(8位)
h文件
#ifndef __KEYSCAN_8_H__
#define __KEYSCAN_8_H__
unsigned char Key(void);
void Key_Tick(void);
#endif
c文件
#include <REGX52.H>
sbit Key1=P1^0;
sbit Key2=P1^1;
sbit Key3=P1^2;
sbit Key4=P1^3;
sbit Key5=P1^4;
sbit Key6=P1^5;
sbit Key7=P1^6;
sbit Key8=P1^7;
unsigned char KeyNumber;
/**
* @brief 获取独立按键键码
* @param 无
* @retval 按下按键的键码,范围:0,1~24,0表示无按键按下
*/
unsigned char Key(void)
{
unsigned char KeyTemp=0;
KeyTemp=KeyNumber;
KeyNumber=0; //主程序中获取键码值之后键码值清零,在下一次定时器扫描按键之前再次获取键码值,一定会返回0
return KeyTemp;
}
/**
* @brief 获取当前按下按键的状态,无消抖及松手检测
* @param 无
* @retval 按键值,范围:0~8,无按键按下时返回值为0
*/
unsigned char Key_GetState()
{
unsigned char KeyValue=0;
if(Key1==0){KeyValue=1;}
if(Key2==0){KeyValue=2;}
if(Key3==0){KeyValue=3;}
if(Key4==0){KeyValue=4;}
if(Key5==0){KeyValue=5;}
if(Key6==0){KeyValue=6;}
if(Key7==0){KeyValue=7;}
if(Key8==0){KeyValue=8;}
return KeyValue;
}
/**
* @brief 按键驱动函数,在中断中调用
* @param 无
* @retval 无
*/
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;
default:break;
}
}
//如果上个时间点按键按下,这个时间点按键按下,则是一直按住按键
if(LastState && NowState)
{
if(LastState==1 && NowState==1){KeyNumber=9;}
if(LastState==2 && NowState==2){KeyNumber=10;}
if(LastState==3 && NowState==3){KeyNumber=11;}
if(LastState==4 && NowState==4){KeyNumber=12;}
if(LastState==5 && NowState==5){KeyNumber=13;}
if(LastState==6 && NowState==6){KeyNumber=14;}
if(LastState==7 && NowState==7){KeyNumber=15;}
if(LastState==8 && NowState==8){KeyNumber=16;}
}
//如果上个时间点按键按下,这个时间点按键未按下,则是松手瞬间
if(NowState==0)
{
switch(LastState)
{
case 1:KeyNumber=17;break;
case 2:KeyNumber=18;break;
case 3:KeyNumber=19;break;
case 4:KeyNumber=20;break;
case 5:KeyNumber=21;break;
case 6:KeyNumber=22;break;
case 7:KeyNumber=23;break;
case 8:KeyNumber=24;break;
default:break;
}
}
}
3、16X16LED点阵屏(MAX7219驱动)
h文件
#ifndef __MATRIXLED_16X16_MAX7219_H__
#define __MATRIXLED_16X16_MAX7219_H__
void MAX7219_WriteByte(unsigned char Byte);
void MAX7219_SendData(unsigned char Address,Data);
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);
#endif
c文件
#include <REGX52.H>
sbit MAX7219_DIN=P2^0; //串行数据
sbit MAX7219_CS=P2^1; //片选,上升沿有效
sbit MAX7219_CLK=P2^2; //串行时钟,上升沿有效
unsigned char MAX7219_Buffer[32]; //显示缓存
/**
* @brief 向MAX7219写入字节
* @param Byte 要写入的字节
* @retval 无
*/
void MAX7219_WriteByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
MAX7219_CLK=0; //清零时钟总线
MAX7219_DIN=Byte&(0x80>>i); //从高到低依次读取字节的位
MAX7219_CLK=1; //时钟上升沿,发送数据
}
}
/**
* @brief 向MAX7219发送4次相同的数据
* @param Address 要写入的地址,如果是数码管的编号或点阵列的序号,则范围是:1~8
* @param Data 要写入的数据,只能驱动共阴数码管
* @retval 无
*/
void MAX7219_SendData(unsigned char Address,Data)
{
unsigned char i;
MAX7219_CS=0; //拉低片选线,选中器件
for(i=0;i<4;i++)
{
MAX7219_WriteByte(Address); //写入地址或数码管编号或点阵列的序号
MAX7219_WriteByte(Data); //写入数据
}
MAX7219_CS=1; //上升沿锁存数据
}
/**
* @brief MatrixLED清屏
* @param 无
* @retval 无
*/
void MatrixLED_Clear(void)
{
unsigned char i;
for(i=0;i<8;i++)
{
MAX7219_SendData(i+1,0x00);
}
}
/**
* @brief MatrixLED初始化,设置MAX7219内部的控制寄存器
* @param 无
* @retval 无
*/
void MatrixLED_Init(void)
{
MAX7219_SendData(0x09,0x00); //可按位选用译码器,0x00表示每位都不用译码器,0xFF表示8位都为该模式;
//译码方式:BCD码,例如二进制的0001 0011表示十进制的13
MAX7219_SendData(0x0A,0x03); //亮度,范围:0x00~0x0F
MAX7219_SendData(0x0B,0x07); //扫描界限,范围:0x00~0x07(1~8位数码管)
MAX7219_SendData(0x0C,0x01); //0:停机状态,1:正常工作状态
MAX7219_SendData(0x0F,0x00); //0:按设定模式正常工作;1:测试状态,全部LED将按最大亮度显示
MatrixLED_Clear(); //清屏
}
/**
* @brief 4个8*8的LED点阵屏显示数组数据(要求逐列式取模,高位在下)
* @param Array 传递过来的数组的首地址(数组名对应的就是数组的首地址)
* @param Offset 显示数组数据的偏移量,向左偏移Offset个像素
* @retval 无
*/
void MatrixLED_MoveLeft(unsigned char *Array,unsigned int Offset)
{
unsigned char i;
Array+=Offset*2;
for(i=0;i<8;i++)
{
MAX7219_CS=0; //拉低片选线,选中器件
MAX7219_WriteByte(i+1); //选择要写入数据的寄存器
MAX7219_WriteByte(*(Array+1)); //写入数据
MAX7219_WriteByte(i+1);
MAX7219_WriteByte(*Array);
MAX7219_WriteByte(i+1);
MAX7219_WriteByte(*(Array+17));
MAX7219_WriteByte(i+1);
MAX7219_WriteByte(*(Array+16));
MAX7219_CS=1; //上升沿锁存数据
Array+=2;
}
}
/**
* @brief 4个8*8的LED点阵屏显示数组数据(要求逐列式取模,高位在下)
* @param Array 传递过来的数组的首地址(数组名对应的就是数组的首地址)
* @param Offset 显示数组数据的偏移量,向上偏移Offset个像素
* @retval 无
*/
void MatrixLED_MoveUp(unsigned char *Array,unsigned int Offset)
{
unsigned char i,m,n;
m=Offset/16;
n=Offset%16;
Array+=m*32;
if(n<8)
{
for(i=0;i<16;i++)
{
MAX7219_Buffer[i*2]=(*(Array+i*2)>>n)|(*(Array+i*2+1)<<(8-n)); //将偏移后的数据保存到显示缓存数组中
}
for(i=0;i<16;i++)
{
MAX7219_Buffer[i*2+1]=(*(Array+i*2+1)>>n)|(*(Array+i*2+32)<<(8-n)); //将偏移后的数据保存到显示缓存数组中
}
}
else
{
n=n-8;
for(i=0;i<16;i++)
{
MAX7219_Buffer[i*2]=(*(Array+i*2+1)>>n)|(*(Array+i*2+32)<<(8-n)); //将偏移后的数据保存到显示缓存数组中
}
for(i=0;i<16;i++)
{
MAX7219_Buffer[i*2+1]=(*(Array+i*2+32)>>n)|(*(Array+i*2+33)<<(8-n)); //将偏移后的数据保存到显示缓存数组中
}
}
for(i=0;i<8;i++) //将显示缓存数组MAX7219_Buffer中的数据写入到MAX7219寄存器
{
MAX7219_CS=0;
MAX7219_WriteByte(i+1);
MAX7219_WriteByte(MAX7219_Buffer[i*2+1]);
MAX7219_WriteByte(i+1);
MAX7219_WriteByte(MAX7219_Buffer[i*2]);
MAX7219_WriteByte(i+1);
MAX7219_WriteByte(MAX7219_Buffer[i*2+17]);
MAX7219_WriteByte(i+1);
MAX7219_WriteByte(MAX7219_Buffer[i*2+16]);
MAX7219_CS=1;
}
}
四、主函数
main.c
/*
by甘腾胜@20241206
效果查看/操作演示:可以在B站搜索“甘腾胜”或“gantengsheng”查看
单片机:STC89C52RC
晶振:12T@12.0000MHz
外设:自制的8位独立按键、16X16LED点阵屏(MAX7219驱动)
原理分析:https://blog.csdn.net/gantengsheng/article/details/143581157
注意:16X16LED点阵屏驱动电流较大,如果电脑USB供电不足,就不能正常显示,这时需要独立供电,独立供电电源要跟单片机的电源共地。
操作说明:
(1)自制独立按键版本
K7 K2 上:K7
下:K6
K8 K5 K4 K1 左:K8
右:K5
K6 K3 开始/暂停/继续:K1
返回:K2
(2)普中开发板矩阵按键版本
S1 S2 S3 S4 上:S10
下:S14
S5 S6 S7 S8 左:S13
右:S15
S9 S10 S11 S12 开始/暂停/继续:S16
返回:S12
S13 S14 S15 S16
*/
#include <REGX52.H>
#include <STDLIB.H>
#include "Timer0.h"
#include "KeyScan_8.h"
#include "MatrixLED_16X16_MAX7219.h"
unsigned char KeyNum; //存储获得的键码值
unsigned char Mode; //游戏模式,0:显示游戏名称“《贪吃蛇》”,1:显示汉字“难度选择”,
//2:难度选择界面(数字范围是1~5),3:游戏进行中,4:游戏结束全屏闪烁,
//5:显示汉字“得分”,6:循环滚动显示得分,7:显示作者姓名和编程日期
unsigned char MoveSnakeFlag; //移动蛇身的标志,1:移动,0:不移动
unsigned char NowDirection=1; //蛇头移动的方向,1:向右,2:向上,3:向左,4:向下,游戏开始时默认向右移动
unsigned char LastDirection=1; //蛇头上一次移动的方向,1:向右,2:向上,3:向左,4:向下,游戏开始时默认向右移动
unsigned char Length=2; //蛇的长度,初始值为2
unsigned char Head=1; //保存整条蛇的数据的数组(共256个数据,数据索引为:0~255)中,蛇头对应的数据的索引,蛇的初始长度为2,
//开始时只用了两个数据(数组的第1个数据和第2个数据),蛇头对应的是第2个数据(索引为1),Head的范围:0~255
unsigned char GameOverFlag; //游戏结束的标志,1:游戏结束,0:游戏未结束
unsigned char FlashFlag; //闪烁的标志,1:不显示,0:显示
unsigned int Food; //保存创造出来的食物的位置,高四位(范围:0~15)对应列(1~16列),低四位(范围:0~15)对应行(1~16行)
//从左往右数,分别是1~16列,从上往下数,分别是1~16行
unsigned int Offset1; //偏移量,用来控制汉字或数字向左滚动显示(切换模式后清零)
unsigned int Offset2; //偏移量,用来控制难度对应的数字上下滚动显示(切换模式后不清零)
unsigned char RollFlag; //滚动的标志,1:滚动,0:不滚动
unsigned char RollUpFlag; //难度选择界面,数字向上滚动的标志,1:滚动,0:不滚动
unsigned char RollDownFlag; //难度选择界面,数字向下滚动的标志,1:滚动,0:不滚动
unsigned char RollCount; //上下滚动的计次
unsigned char ExecuteOnceFlag; //各模式中只执行一次的标志,1:执行,0:不执行
unsigned char SnakeMoveSpeed=80; //蛇移动的速度,值越小,速度越快,上电默认0.8s移动一次,定时器计时时间为10ms
unsigned char T0Count0,T0Count1,T0Count2,T0Count3; //定时器计数的变量
unsigned char PauseFlag; //暂停的标志,1:暂停,0:不暂停
unsigned char pdata SnakeBody[256]; //点阵屏是16X16=256像素,需要用256个数据记录蛇身的数据
//用pdata修饰是因为片内RAM不够用,所以保存到片外RAM
unsigned char DisplayBuffer[32]; //显示缓存,一个字节对应8个点,总共256个点,所以需要32个字节
//取模设置:阴码(亮点为1),逐列式取模,高位在下(所有取模都必须按这个格式,否则需要修改函数)
unsigned char code Table1[]={ // “《贪吃蛇》”
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,0x00,0x00,0x80,0x00,0x40,0x01,0x20,0x02,0x90,0x04,
0x48,0x09,0x24,0x12,0x12,0x24,0x09,0x48,0x04,0x10,0x02,0x20,0x00,0x00,0x00,0x00,/*"《",0*/
0x20,0x00,0x20,0x80,0x10,0x80,0x10,0x9F,0x28,0x41,0x24,0x41,0x22,0x21,0x29,0x1D,
0xB2,0x01,0x64,0x21,0x28,0x21,0x10,0x5F,0x10,0x40,0x20,0x80,0x20,0x00,0x00,0x00,/*"贪",1*/
0x00,0x00,0xFC,0x0F,0x04,0x04,0x04,0x04,0xFC,0x0F,0x20,0x00,0x10,0x30,0x4C,0x48,
0x4B,0x44,0x48,0x42,0x48,0x42,0x48,0x41,0xC8,0x40,0x08,0x40,0x08,0x70,0x00,0x00,/*"吃",2*/
0x00,0x20,0xF8,0x63,0x08,0x21,0xFF,0x1F,0x08,0x11,0xF8,0x19,0x20,0x30,0x18,0x00,
0xC8,0x3F,0x08,0x44,0x09,0x42,0x0E,0x41,0x88,0x40,0x28,0x40,0x18,0x78,0x00,0x00,/*"蛇",3*/
0x00,0x00,0x00,0x00,0x02,0x20,0x04,0x10,0x09,0x48,0x12,0x24,0x24,0x12,0x48,0x09,
0x90,0x04,0x20,0x02,0x40,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/*"》",4*/
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,/*" ", */
};
unsigned char code Table2[]={ // “ 难度选择 1”
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,/*" ", */
0x04,0x20,0x24,0x10,0xC4,0x0C,0x04,0x03,0xE4,0x04,0x5C,0x18,0x20,0x00,0xF8,0xFF,
0x4F,0x22,0x48,0x22,0x49,0x22,0xFA,0x3F,0x48,0x22,0x48,0x22,0x08,0x20,0x00,0x00,/*"难",0*/
0x00,0x40,0x00,0x30,0xFC,0x8F,0x24,0x80,0x24,0x84,0x24,0x4C,0xFC,0x55,0x25,0x25,
0x26,0x25,0x24,0x25,0xFC,0x55,0x24,0x4C,0x24,0x80,0x24,0x80,0x04,0x80,0x00,0x00,/*"度",1*/
0x40,0x00,0x40,0x40,0x42,0x20,0xCC,0x1F,0x00,0x20,0x50,0x50,0x4E,0x4C,0xC8,0x43,
0x48,0x40,0x7F,0x40,0xC8,0x4F,0x48,0x50,0x48,0x50,0x40,0x5C,0x00,0x40,0x00,0x00,/*"选",2*/
0x10,0x42,0x10,0x82,0xFF,0x7F,0x10,0x01,0x00,0x00,0x82,0x10,0x86,0x12,0x4A,0x12,
0x52,0x12,0xA2,0xFF,0x52,0x12,0x4A,0x12,0x86,0x12,0x80,0x10,0x80,0x00,0x00,0x00,/*"择",3*/
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,0x00,0x00,0x00,0x20,0x10,0x20,0x10,0x20,0xF0,0x3F,
0xF8,0x3F,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,/*"1 ", */
};
unsigned char code Table3[]={ //难度对应的数字:“1~5”,最后两行数据与前两行数据相同,是为了做成循环显示的效果
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x10,0x20,0x10,0x20,0xF0,0x3F,
0xF8,0x3F,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,/*"1",0*/
0x00,0x00,0x00,0x00,0x30,0x30,0x70,0x30,0x28,0x28,0x08,0x24,0x08,0x24,0x08,0x22,
0x08,0x22,0x08,0x21,0x08,0x21,0xD8,0x20,0xF0,0x30,0x20,0x18,0x00,0x00,0x00,0x00,/*"2",1*/
0x00,0x00,0x00,0x00,0x30,0x18,0x30,0x18,0x28,0x28,0x08,0x20,0x08,0x20,0x08,0x21,
0x08,0x21,0x88,0x21,0x88,0x21,0x70,0x13,0x70,0x1E,0x00,0x0C,0x00,0x00,0x00,0x00,/*"3",2*/
0x00,0x00,0x00,0x04,0x00,0x06,0x00,0x05,0x80,0x04,0x80,0x04,0x40,0x24,0x20,0x24,
0x10,0x24,0xF0,0x3F,0xF8,0x3F,0xF8,0x3F,0x00,0x24,0x00,0x24,0x00,0x24,0x00,0x00,/*"4",3*/
0x00,0x00,0x00,0x00,0x00,0x18,0xF8,0x19,0x08,0x29,0x88,0x20,0x88,0x20,0x88,0x20,
0x88,0x20,0x88,0x20,0x88,0x20,0x88,0x11,0x08,0x1F,0x00,0x0E,0x00,0x00,0x00,0x00,/*"5",4*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x10,0x20,0x10,0x20,0xF0,0x3F,
0xF8,0x3F,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,/*"1",0*/
};
unsigned char code Table5[]={ // “得分”
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,0x02,0x10,0x01,0x88,0x00,0xC4,0xFF,0x33,0x00,0x00,0x02,0xBE,0x0A,0xAA,0x12,
0xAA,0x02,0xAA,0x42,0xAA,0x82,0xAA,0x7F,0xBE,0x02,0x80,0x02,0x00,0x02,0x00,0x00,/*"得",0*/
0x80,0x00,0x40,0x80,0x20,0x40,0x90,0x20,0x88,0x18,0x86,0x07,0x80,0x00,0x80,0x40,
0x80,0x80,0x83,0x40,0x8C,0x3F,0x10,0x00,0x20,0x00,0x40,0x00,0x80,0x00,0x00,0x00,/*"分",1*/
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,/*" ", */
};
unsigned char code Table6[]={ //分数字模:0~9
0x00,0x00,0x00,0x00,0xE0,0x0F,0xF0,0x1F,0x30,0x18,0x08,0x30,0x08,0x20,0x08,0x20,
0x08,0x20,0x08,0x20,0x08,0x20,0x30,0x18,0xF0,0x0F,0xE0,0x07,0x00,0x00,0x00,0x00,/*"0",0*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x10,0x20,0x10,0x20,0xF0,0x3F,
0xF8,0x3F,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,/*"1",1*/
0x00,0x00,0x00,0x00,0x30,0x30,0x70,0x30,0x28,0x28,0x08,0x24,0x08,0x24,0x08,0x22,
0x08,0x22,0x08,0x21,0x08,0x21,0xD8,0x20,0xF0,0x30,0x20,0x18,0x00,0x00,0x00,0x00,/*"2",2*/
0x00,0x00,0x00,0x00,0x30,0x18,0x30,0x18,0x28,0x28,0x08,0x20,0x08,0x20,0x08,0x21,
0x08,0x21,0x88,0x21,0x88,0x21,0x70,0x13,0x70,0x1E,0x00,0x0C,0x00,0x00,0x00,0x00,/*"3",3*/
0x00,0x00,0x00,0x04,0x00,0x06,0x00,0x05,0x80,0x04,0x80,0x04,0x40,0x24,0x20,0x24,
0x10,0x24,0xF0,0x3F,0xF8,0x3F,0xF8,0x3F,0x00,0x24,0x00,0x24,0x00,0x24,0x00,0x00,/*"4",4*/
0x00,0x00,0x00,0x00,0x00,0x18,0xF8,0x19,0x08,0x29,0x88,0x20,0x88,0x20,0x88,0x20,
0x88,0x20,0x88,0x20,0x88,0x20,0x88,0x11,0x08,0x1F,0x00,0x0E,0x00,0x00,0x00,0x00,/*"5",5*/
0x00,0x00,0x00,0x00,0xC0,0x0F,0xE0,0x1F,0x10,0x11,0x88,0x20,0x88,0x20,0x88,0x20,
0x88,0x20,0x88,0x20,0x88,0x20,0x98,0x10,0x10,0x1F,0x00,0x0F,0x00,0x00,0x00,0x00,/*"6",6*/
0x00,0x00,0x00,0x00,0x30,0x00,0x18,0x00,0x08,0x00,0x08,0x00,0x08,0x3C,0x08,0x3E,
0x08,0x13,0x88,0x00,0x48,0x00,0x28,0x00,0x18,0x00,0x08,0x00,0x00,0x00,0x00,0x00,/*"7",7*/
0x00,0x00,0x00,0x0C,0x70,0x1E,0x70,0x12,0xC8,0x21,0x88,0x21,0x88,0x20,0x88,0x21,
0x08,0x21,0x08,0x21,0x88,0x23,0x88,0x22,0x70,0x1E,0x20,0x0C,0x00,0x00,0x00,0x00,/*"8",8*/
0x00,0x00,0x40,0x00,0xF0,0x11,0xF0,0x11,0x08,0x33,0x08,0x22,0x08,0x22,0x08,0x22,
0x08,0x22,0x08,0x22,0x08,0x11,0x90,0x1C,0xF0,0x0F,0xE0,0x07,0x00,0x00,0x00,0x00,/*"9",9*/
};
unsigned char code Table7[]={ // “ by甘腾胜at20241202 ”
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,/*" ", */
0x10,0x00,0xF0,0x3F,0x00,0x11,0x80,0x20,0x80,0x20,0x00,0x11,0x00,0x0E,0x00,0x00,/*"b",0*/
0x80,0x00,0x80,0x81,0x80,0x86,0x00,0x78,0x00,0x18,0x80,0x06,0x80,0x01,0x80,0x00,/*"y",1*/
0x00,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0xFF,0xFF,0x10,0x42,0x10,0x42,0x10,0x42,
0x10,0x42,0x10,0x42,0x10,0x42,0xFF,0xFF,0x10,0x00,0x10,0x00,0x10,0x00,0x00,0x00,/*"甘",2*/
0x00,0x80,0xFE,0x7F,0x22,0x02,0x22,0x82,0xFE,0xFF,0x28,0x01,0xA9,0x20,0x6E,0x2D,
0x28,0x29,0x3F,0x29,0x28,0x29,0x6C,0x4F,0xAB,0x88,0x28,0x79,0x20,0x01,0x00,0x00,/*"腾",3*/
0x00,0x80,0xFE,0x7F,0x22,0x02,0x22,0x82,0xFE,0xFF,0x00,0x00,0x40,0x40,0x3C,0x42,
0x10,0x42,0x10,0x42,0xFF,0x7F,0x10,0x42,0x10,0x42,0x10,0x42,0x00,0x40,0x00,0x00,/*"胜",4*/
0x00,0x00,0x00,0x19,0x80,0x24,0x80,0x24,0x80,0x12,0x00,0x3F,0x00,0x20,0x00,0x00,/*"a",5*/
0x00,0x00,0x80,0x00,0x80,0x00,0xE0,0x1F,0x80,0x20,0x80,0x20,0x00,0x10,0x00,0x00,/*"t",6*/
0x00,0x00,0x70,0x30,0x08,0x28,0x08,0x24,0x08,0x22,0x08,0x21,0xF0,0x30,0x00,0x00,/*"2",7*/
0x00,0x00,0xE0,0x0F,0x10,0x10,0x08,0x20,0x08,0x20,0x10,0x10,0xE0,0x0F,0x00,0x00,/*"0",8*/
0x00,0x00,0x70,0x30,0x08,0x28,0x08,0x24,0x08,0x22,0x08,0x21,0xF0,0x30,0x00,0x00,/*"2",9*/
0x00,0x00,0x00,0x06,0x80,0x05,0x40,0x24,0x30,0x24,0xF8,0x3F,0x00,0x24,0x00,0x24,/*"4",10*/
0x00,0x00,0x00,0x00,0x10,0x20,0x10,0x20,0xF8,0x3F,0x00,0x20,0x00,0x20,0x00,0x00,/*"1",11*/
0x00,0x00,0x70,0x30,0x08,0x28,0x08,0x24,0x08,0x22,0x08,0x21,0xF0,0x30,0x00,0x00,/*"2",12*/
0x00,0x00,0xE0,0x0F,0x10,0x10,0x08,0x20,0x08,0x20,0x10,0x10,0xE0,0x0F,0x00,0x00,/*"0",13*/
0x00,0x00,0x70,0x30,0x08,0x28,0x08,0x24,0x08,0x22,0x08,0x21,0xF0,0x30,0x00,0x00,/*"2",14*/
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,/*" ", */
};
/**
* @brief 创造出随机位置的食物,数据的高四位(范围:0~15)代表食物所在的列(1~16),数据的低四位(范围:0~15)代表食物所在的行(1~16)
* @brief 从左往右数,分别是1~16列,从上往下数,分别是1~16行
* @param 无
* @retval 创造出的食物位置的数据
*/
unsigned char CreateFood(void)
{
unsigned char FoodTemp;
unsigned char i,j,m,n;
m=rand()%16; //产生一个0~15的随机数
n=rand()%16; //产生一个0~15的随机数
for(j=0;j<16;j++) //产生一个随机位置,判断该位置是否是蛇身,如果不是,就返回该位置所对应的数据
{ //如果该位置不是蛇身的位置,则从该点向周围寻找不是蛇身的空位置
for(i=0;i<16;i++)
{
// if( DisplayBuffer[(m+i)%16*2+(n+j)%16/8] & (0x01<<(n+j)%8) == 0 ) //这样会出错
//这两种都可以
if( ( DisplayBuffer[(m+i)%16*2+(n+j)%16/8] & (0x01<<(n+j)%8) ) == 0 )
// if( !(DisplayBuffer[(m+i)%16*2+(n+j)%16/8] & (0x01<<(n+j)%8)) )
{
FoodTemp=(m+i)%16*16+(n+j)%16;
break; //找到了空位置就退出循环
}
}
}
return FoodTemp;
}
/**
* @brief 控制蛇的移动
* @param 无
* @retval 无
*/
void MoveSnake(void)
{
if(NowDirection==1) //如果向右移动
{
if((SnakeBody[Head]/16)==15){GameOverFlag=1;} //移动前判断一下移动后是否撞墙,如果是,则游戏结束,游戏结束的标志置1
else{SnakeBody[(Head+1)%256]=SnakeBody[Head]+16;} //SnakeBody数组中蛇头的下一个数据等于上一个数据加16(即高四位加1),即蛇头移动到了右边这一列
}
if(NowDirection==2) //如果向上移动
{
if((SnakeBody[Head]%16)==0){GameOverFlag=1;}
else{SnakeBody[(Head+1)%256]=SnakeBody[Head]-1;} //SnakeBody数组中蛇头的下一个数据等于上一个数据减1(即低四位减1),即蛇头移动到了上边这一行
}
if(NowDirection==3) //如果向左移动
{
if((SnakeBody[Head]/16)==0){GameOverFlag=1;}
else{SnakeBody[(Head+1)%256]=SnakeBody[Head]-16;} //SnakeBody数组中蛇头的下一个数据等于上一个数据减16(即高四位减1),即蛇头移动到了左边这一列
}
if(NowDirection==4) //如果向下移动
{
if((SnakeBody[Head]%16)==15){GameOverFlag=1;}
else{SnakeBody[(Head+1)%256]=SnakeBody[Head]+1;} //SnakeBody数组中蛇头的下一个数据等于上一个数据加1(即低四位加1),即蛇头移动到了下边这一行
}
if(GameOverFlag==0) //如果没撞墙
{
if(SnakeBody[(Head+1)%256]==Food) //判断蛇头移动后的位置是否是食物所在的位置
{ //如果是
Length++; //蛇身长度加1
DisplayBuffer[Food/16*2+Food%16/8]|=(0x01<<(Food%8)); //防止食物闪烁刚好不显示的时候进入此函数,导致蛇身一个点不显示
Food=CreateFood(); //重新创造一个食物
DisplayBuffer[Food/16*2+Food%16/8]|=(0x01<<(Food%8));
MatrixLED_MoveLeft(DisplayBuffer,0); //更新显示
FlashFlag=0; //创造出新的食物时,食物暂不闪烁
T0Count2=0; //定时器T0Count2重新计数
}
else if( (DisplayBuffer[SnakeBody[(Head+1)%256]/16*2+SnakeBody[(Head+1)%256]%16/8]) & (0x01<<SnakeBody[(Head+1)%256]%8) )
{ //如果蛇头移动后的位置撞在蛇身上,则游戏结束
GameOverFlag=1; //游戏结束的标志置1
}
else //如果蛇头移动后的位置不是食物,也不是撞墙,也不是撞到蛇身的话
{
//显示缓存数组DisplayBuffer中蛇头前进后的新位置对应的位写1
DisplayBuffer[SnakeBody[(Head+1)%256]/16*2+SnakeBody[(Head+1)%256]%16/8] |= (0x01<<SnakeBody[(Head+1)%256]%8);
//显示缓存数组DisplayBuffer中蛇尾的位置对应的位清0(蛇身移动,如果没有吃到食物,相当于蛇尾对应的点跑到了
//蛇头的前一个点,变成了蛇头,原来的蛇头变成蛇身)整条蛇中间的数据不用操作
DisplayBuffer[SnakeBody[(Head+256-Length+1)%256]/16*2+SnakeBody[(Head+256-Length+1)%256]%16/8]
&= ~(0x01<<SnakeBody[(Head+256-Length+1)%256]%8);
//数组SnakeBody中,蛇尾的数据清零,更新数据
//Head+256:+256是因为Head为255后再加1,就会变成了0,防止Head+256-Length+1为负数
//即我们是循环使用SnakeBody数组中的256个数据,蛇头对应数组SnakeBody的第255个数据后,再移动一次,蛇头就来到了数组的第0个数据
SnakeBody[(Head+256-Length+1)%256]=0;
}
}
Head++; //SnakeBody数组中,蛇头对应的数据的序号加1
Head%=256; //蛇头变量Head是无符号字符型数据(范围是0~255,值为255后自增会自动变为0),所以这一行可以省略
//如果Head是整型(int),则需要有这一行
}
void main()
{
unsigned int i; //i的类型必须是int,否则模式2中会陷入死循环
unsigned char Count=0;
Timer0_Init(); //定时器初始化
MatrixLED_Init(); //MAX7219初始化
while(1)
{
KeyNum=Key(); //获取键码值
if(KeyNum) //如果有按键按下
{
srand(TL0); //以定时器0的低八位数据作为随机数的种子,用来产生真随机的数据
if(Mode==7 && KeyNum==18) //如果是显示作者姓名和编程日期的界面,且按下了返回键(K2)(松手瞬间)
{
Mode=2; //返回难度选择界面
ExecuteOnceFlag=1; //各模式只执行一次代码的标志置1
}
if(Mode==6) //如果是循环滚动显示得分的界面
{
if(KeyNum==18) //如果按下了返回键(K2)(松手瞬间)
{
Mode=2; //返回难度选择界面
ExecuteOnceFlag=1; //各模式只执行一次代码的标志置1
}
else if(KeyNum==19) //如果按下了K3(松手瞬间)
{
Mode=7; //切换到显示作者姓名和编程日期的界面
Offset1=0; //滚动显示的偏移量清0
}
}
if(Mode==5 && KeyNum==17) //如果是显示汉字“得分”的界面,且按下开始键(K1)(松手瞬间)
{
Mode=6; //跳过显示汉字,切换到循环滚动显示得分的界面
ExecuteOnceFlag=1;
Offset1=0; //滚动显示的偏移量清0
}
if(Mode==4 && KeyNum==17) //如果游戏结束闪烁时按下K1(松手瞬间)
{
Mode=5; //切换到显示汉字“得分”的界面
ExecuteOnceFlag=1;
Offset1=0;
}
if(Mode==3) //如果是游戏进行模式
{
if(KeyNum==17) //按下K1暂停或继续
{
PauseFlag=!PauseFlag;
}
if(PauseFlag==0) //如果不是暂停
{ //按下瞬间、长按、松手瞬间都进行检测,这样控制方向更有效,防止按键没检测出来导致没能改变方向
if((KeyNum==8 || KeyNum==16 || KeyNum==24) && LastDirection!=1)
{ //如果按了“左”键,且蛇头原来的移动方向不是向右
NowDirection=3; //则方向蛇头方向改为向左
}
if((KeyNum==7 || KeyNum==15 || KeyNum==23) && LastDirection!=4)
{ //如果按了“上”键,且蛇头原来的移动方向不是向下
NowDirection=2; //则方向蛇头方向改为向上
}
if((KeyNum==6 || KeyNum==14 || KeyNum==22) && LastDirection!=2)
{ //如果按了“下”键,且蛇头原来的移动方向不是向上
NowDirection=4; //则方向蛇头方向改为向左
}
if((KeyNum==5 || KeyNum==13 || KeyNum==21) && LastDirection!=3)
{ //如果按了“右”键,且蛇头原来的移动方向不是向左
NowDirection=1; //则方向蛇头方向改为向左
}
}
}
if(Mode==2) //如果是难度选择界面
{
if(KeyNum==23) //如果按了“上”键(松手瞬间)
{
RollUpFlag=1; //数字向上滚动的标志置1
}
if(KeyNum==22) //如果按了“下”键(松手瞬间)
{
RollDownFlag=1; //数字向下滚动的标志置1
}
if(KeyNum==17) //如果按了开始键(K1)(松手瞬间),则开始游戏
{
Mode=3; //切换到游戏模式
ExecuteOnceFlag=1;
}
}
if(KeyNum>=17 && KeyNum<=24) //如果按下任意按键(松手瞬间)
{
//两个if的顺序不能调换,如果调换了,就从模式0直接跳到模式2了
if(Mode==1){Mode=2;Offset1=0;ExecuteOnceFlag=1;} //跳过汉字“难度选择”的显示,切换到难度选择界面
if(Mode==0){Mode=1;Offset1=0;ExecuteOnceFlag=1;} //跳过游戏名“《贪吃蛇》”的显示,切换到汉字“难度选择”的显示界面
}
}
if(Mode==0) //如果是显示游戏名称“《贪吃蛇》”的模式
{
if(RollFlag) //如果滚动的标志RollFlag为1(定时器中每隔50ms将此标志置1)
{
RollFlag=0; //滚动的标志RollFlag清零
MatrixLED_MoveLeft(Table1,Offset1); //向左滚动
Offset1++; //每次向左移动一个像素
Offset1%=96; //越界清零,循环滚动
}
}
if(Mode==1) //如果是显示汉字“难度选择”的模式
{
if(RollFlag && Offset1<=96) //只向左滚动显示一次,不循环滚动显示
{
RollFlag=0;
MatrixLED_MoveLeft(Table2,Offset1);
Offset1++;
}
else if(Offset1>96) //显示数字“1”之后,自动切换到难度选择模式
{
Mode=2;
Offset1=0;
}
}
if(Mode==2) //如果是难度选择模式
{
if(ExecuteOnceFlag) //切换到该模式后,此if中的内容只执行1次
{ //第一次进入游戏,或游戏结束显示得分返回难度选择模式时,所有数据重置
ExecuteOnceFlag=0;
MatrixLED_MoveUp(Table3,Offset2); //显示难度对应的数字,范围:1~5
GameOverFlag=0; //游戏结束标志清零
PauseFlag=0; //游戏暂停标志清零
NowDirection=1; //蛇头默认向右移动
LastDirection=1; //上一次蛇头默认向右移动
Length=2; //蛇的初始长度为2
Head=1; //蛇头对应数组中的第1个数据(注意不是第0个)
for(i=0;i<256;i++) //蛇身数据全部清零,i的类型必须是int,否则会陷入死循环
{ //SnakeBody的数据其实可以不清零
SnakeBody[i]=0;
}
SnakeBody[0]=1*16+1; //蛇身数据全部清零后,写入蛇身初始的两个数据
SnakeBody[1]=2*16+1;
for(i=0;i<32;i++) //显示缓存数据全部清零
{
DisplayBuffer[i]=0;
}
DisplayBuffer[2]=0x02; //显示缓存数据全部清零后,写入蛇身初始的两个数据
DisplayBuffer[4]=0x02;
Food=CreateFood(); //进入游戏前,先创造出一个食物
DisplayBuffer[Food/16*2+Food%16/8]|=(0x01<<(Food%8)); //显示缓存中,食物对应的位置置1
}
if(RollFlag && RollUpFlag) //如果滚动标志为1,且向上滚动的标志也为1
{
RollFlag=0; //定时器中每个50ms将RollFlag标志置1
Offset2++; //向上移动一个像素
Offset2%=80; //越界清零,总共5个数字,每个数字的高度是16,所以是5*16=80
MatrixLED_MoveUp(Table3,Offset2); //更新显示
RollCount++;
if(RollCount==16) //移动了16个像素后停止移动
{
RollCount=0;
RollUpFlag=0;
Offset2=(Offset2/16)*16; //防止移动到一半的时候按下“上”或“下”按键导致数字没有在点阵屏中间
//Offset2的值必须是16的整数倍
switch(Offset2/16)
{
case 0:SnakeMoveSpeed=80;break; //难度1,1s移动1次
case 1:SnakeMoveSpeed=60;break; //难度2,0.75s移动1次
case 2:SnakeMoveSpeed=40;break; //难度3,0.5s移动1次
case 3:SnakeMoveSpeed=20;break; //难度4,0.25s移动1次
case 4:SnakeMoveSpeed=10;break; //难度5,0.12s移动1次
default:break;
}
}
}
if(RollFlag && RollDownFlag) //如果滚动标志为1,且向下滚动的标志也为1
{
RollFlag=0;
if(Offset2==0){Offset2=80;}
Offset2--;
MatrixLED_MoveUp(Table3,Offset2);
RollCount++;
if(RollCount==16)
{
RollCount=0;
RollDownFlag=0;
Offset2=(Offset2/16)*16;
switch(Offset2/16)
{
case 0:SnakeMoveSpeed=80;break;
case 1:SnakeMoveSpeed=60;break;
case 2:SnakeMoveSpeed=40;break;
case 3:SnakeMoveSpeed=20;break;
case 4:SnakeMoveSpeed=10;break;
default:break;
}
}
}
}
if(Mode==3) //如果是游戏进行模式
{
if(ExecuteOnceFlag)
{
ExecuteOnceFlag=0;
MatrixLED_MoveLeft(DisplayBuffer,0); //显示初始的蛇身及食物
MoveSnakeFlag=0; //蛇移动的标志清零
T0Count1=0; //定时器计数变量T0Count1清零,重新计数
}
if(PauseFlag) //如果暂停了
{
DisplayBuffer[Food/16*2+Food%16/8] |= (0x01<<(Food%8)); //食物不闪烁,一直显示
MatrixLED_MoveLeft(DisplayBuffer,0); //更新显示
}
else if(FlashFlag) //如果不暂停,且闪烁标志为1
{
DisplayBuffer[Food/16*2+Food%16/8] &= (~(0x01<<(Food%8))); //不显示食物
MatrixLED_MoveLeft(DisplayBuffer,0);
}
else //如果不暂停,且闪烁标志为0
{
DisplayBuffer[Food/16*2+Food%16/8] |= (0x01<<(Food%8)); //显示食物
MatrixLED_MoveLeft(DisplayBuffer,0);
}
if(MoveSnakeFlag && GameOverFlag==0 && PauseFlag==0)
{ //如果移动的标志为1,且不暂停,且游戏也没结束
LastDirection=NowDirection; //保存上一次移动的方向,用于按键的判断(蛇不能往后移动)
MoveSnakeFlag=0; //移动标志清零
MoveSnake(); //移动一次
MatrixLED_MoveLeft(DisplayBuffer,0); //更新显示
}
if(GameOverFlag==1) //如果游戏结束
{
DisplayBuffer[Food/16*2+Food%16/8] |= (0x01<<(Food%8)); //显示食物
MatrixLED_MoveLeft(DisplayBuffer,0);
Mode=4; //切换到全屏闪烁模式
ExecuteOnceFlag=1;
}
}
if(Mode==4)
{
if(ExecuteOnceFlag)
{
ExecuteOnceFlag=0;
for(i=0;i<160;i++) //蛇身数据部分清零(SnakeBody前160个用来循环显示三位数得分)
{
SnakeBody[i]=0;
}
for(i=0;i<32;i++) //提前将得分(即蛇身的长度)的百位、十位、个位的字模写入数组SnakeBody中
{
SnakeBody[32+i]=Table6[Length/100*32+i];
}
for(i=0;i<32;i++)
{
SnakeBody[64+i]=Table6[Length/10%10*32+i];
}
for(i=0;i<32;i++)
{
SnakeBody[96+i]=Table6[Length%10*32+i];
}
PauseFlag=0;
}
//全屏闪烁
if(FlashFlag){MAX7219_SendData(0x0C,0x00);} //向MAX7219写入指令,让屏幕不显示
else{MAX7219_SendData(0x0C,0x01);} //向MAX7219写入指令,让屏幕显示
}
if(Mode==5)
{
if(ExecuteOnceFlag)
{
ExecuteOnceFlag=0;
//向MAX7219写入指令,屏幕显示,防止在上一个模式中,刚好在写入屏幕不显示的指令后切换到此模式
MAX7219_SendData(0x0C,0x01);
}
if(RollFlag && Offset1<=48) //只显示一次汉字“得分”
{
RollFlag=0;
Count++;
Count%=10;
if(Count==0)
{
MatrixLED_MoveLeft(Table5,Offset1);
Offset1+=16;
}
}
else if(Offset1>48) //显示结束后,自动切换到循环显示得分的模式
{
Mode=6;
Offset1=0; //偏移量清零
}
}
if(Mode==6) //如果是滚动显示得分模式
{
if(RollFlag) //如果滚动的标志为1
{
RollFlag=0;
MatrixLED_MoveLeft(SnakeBody,Offset1);
Offset1++;
Offset1%=64; //循环滚动
}
}
if(Mode==7) //如果是显示作者姓名和编程时间的模式
{
if(RollFlag) //如果滚动的标志为1
{
RollFlag=0;
MatrixLED_MoveLeft(Table7,Offset1);
Offset1++;
Offset1%=160; //循环滚动
}
}
}
}
void Timer0_Routine() interrupt 1 //定时器0中断函数
{
TL0=0xF0; //设置定时初值,定时10ms,晶振@12.0000MHz
TH0=0xD8; //设置定时初值,定时10ms,晶振@12.0000MHz
T0Count0++;
if(PauseFlag==0) //不暂停时,T0Count1和T0Count2才计数
{
T0Count1++;
T0Count2++;
}
T0Count3++;
if(T0Count0>=2) //20ms扫描一次按键
{
T0Count0=0;
Key_Tick();
}
if(T0Count1>=SnakeMoveSpeed) //用来控制蛇移动的速度
{
T0Count1=0;
MoveSnakeFlag=1;
}
if(T0Count2>=25) //0.25s取反闪烁标志FlashFlag的值
{
T0Count2=0;
FlashFlag=!FlashFlag;
}
if(T0Count3>=5) //控制滚动的速度,50ms滚动一次
{
T0Count3=0;
RollFlag=1;
}
}
总结
学会了《贪吃蛇》的游戏原理之后,换一个显示屏,程序框架也差不多,在原来的基础上修改一下就行了。