基于51单片机(STC12C5A60S2)和8X8彩色点阵屏(WS2812B驱动)的小游戏《贪吃蛇》(普中开发板矩阵按键控制)

news2025/1/5 22:31:38

目录

  • 系列文章目录
  • 前言
  • 一、效果展示
  • 二、原理分析
  • 三、各模块代码
    • 1、定时器0
    • 2、矩阵按键
    • 3、8X8彩色点阵屏
  • 四、主函数
  • 总结

系列文章目录


前言

《贪吃蛇》,一款经典的、怀旧的小游戏,单片机入门必写程序。

以《贪吃蛇》为载体,熟悉各种屏幕的使用。

所用单片机:STC12C5A60S2。

外设:普中开发板板载矩阵按键、8X8彩色点阵屏(WS2812B驱动)。

效果查看/操作演示:B站搜索“甘腾胜”或“gantengsheng”查看。
源代码下载:B站对应视频的简介有工程文件下载链接。

一、效果展示

在这里插入图片描述
在这里插入图片描述

二、原理分析

游戏原理可以参考以下这篇文章:
基于51单片机和8X8LED点阵屏(板载74HC595驱动)的普中开发板矩阵按键控制的小游戏《贪吃蛇》

这里主要说一下8X8彩色点阵屏(WS2812B)的显示问题。

一个灯(图片中白色正方形)里面有三个LED,分别是红、绿、蓝,白色正方形中其实也集成了WS2812B芯片,一个芯片可以存储3个字节(Byte)的数据,接收到3个字节的数据后,会把后面接收到的数据传给下一个灯的WS2812B芯片。一个字节控制一种颜色的LED,即一个灯需要用24位(Bit)控制,所以一个灯的颜色变化种类有224=16777216‬种。

如何发送0和1呢?类似DS18B20的数据发送,通过高电平的时长来确定发送的是0还是1,“0”的高电平时长较短,“1”的高电平时长较长,具体的时序图可以参考一下其他博主的文章。经过测试,高低电平的时长要求不算严格,但是需要注意的是,如果不注意定时器中断函数打断的影响,屏幕会出现“花屏”的情况,即一个字节才发送一半就被定时器中断函数打断的话,原来要发送的数据就改变了。如果以一个字节为单位,发送一个字节前关闭总中断(EA=0),发送完一个字节后再开启中断(EA=1),这样显示就不会出现问题,这样既不影响显示,也不影响定时器的运行,但是要注意,定时器中断函数代码的执行时间不能太长,太长的话相当于发送了重置(RESET)指令给WS2812B芯片,屏幕会乱闪。

需要注意的是,普通89/90系列的12T/6T单片机无法驱动这个彩色点阵屏了,因为WS2812B芯片的时序是纳秒(ns)级别的,需要用运行速度更快的1T单片机才行。普中开发板的话可以用STC12C5A60S2这款1T单片机,引脚和编程完全与89系列的单片机兼容,直接把单片机换了装上就能用了。

在这里插入图片描述
本文代码对应的彩色点阵屏的数据传输方向如上图所示。

三、各模块代码

1、定时器0

h文件

#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0_Init(void);

#endif

c文件

#include <STC12C5A60S2.H>

/**
  * @brief	定时器0初始化
  * @param  无
  * @retval 无
  */
void Timer0_Init(void)
{
	AUXR|=0x80;	//定时器时钟1T模式	
	TMOD&=0xF0;	//设置定时器模式(16位不自动重载)(高四位不变,低四位清零)
	TMOD|=0x01;	//设置定时器模式(16位不自动重载)(通过低四位设为“定时器0工作方式1”的模式)
	TL0=0xCD;	//设置定时初始值,定时1ms,1T@11.0592MHz
	TH0=0xD4;	//设置定时初始值,定时1ms,1T@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=0xCD;	//设置定时初始值,定时1ms,1T@11.0592MHz
	TH0=0xD4;	//设置定时初始值,定时1ms,1T@11.0592MHz
	T0Count++;
	if(T0Count>=1000)
	{
		T0Count=0;
		
	}
}
*/

2、矩阵按键

h文件

#ifndef __MATRIXKEYSCAN_H__
#define __MATRIXKEYSCAN_H__

unsigned char Key(void);
void Key_Tick(void);

#endif

c文件

#include <STC12C5A60S2.H>

#define Matrix_Port P1	//矩阵按键接口

unsigned char KeyNumber;

/**
  * @brief  获取矩阵按键键码,扫描周期内第二次获取键码值,会返回0
  * @param  无
  * @retval 按下按键的键码,范围:0~48,0表示无按键按下
  */
unsigned char Key(void)
{
	unsigned char KeyTemp=0;
	KeyTemp=KeyNumber;
	KeyNumber=0;
	return KeyTemp;
}

/**
  * @brief  检测当前按下按键的状态,无消抖及松手检测
  * @param  无
  * @retval 按下按键的键值,范围:0~16,无按键按下时返回值为0
  */
unsigned char Key_GetState()
{
	unsigned char KeyValue=0;

	Matrix_Port=0x0F;	//给所有行赋值0,列全为1
	if(Matrix_Port!=0x0F)
	{
		Matrix_Port=0x0F;	//测试列
		switch(Matrix_Port)	//保存行为0时,按键按下后的列值
		{
			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;	//测试行
		switch(Matrix_Port)	//保存列为0时,按键按下后的键值
		{
			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;
}

/**
  * @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;	//因开发板的晶振不能换,只能用11.0592MHz的晶振,速度较慢
//			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;}
//		if(LastState==2 && NowState==2){KeyNumber=18;}
//		if(LastState==3 && NowState==3){KeyNumber=19;}
//		if(LastState==4 && NowState==4){KeyNumber=20;}
//		if(LastState==5 && NowState==5){KeyNumber=21;}
//		if(LastState==6 && NowState==6){KeyNumber=22;}
//		if(LastState==7 && NowState==7){KeyNumber=23;}
//		if(LastState==8 && NowState==8){KeyNumber=24;}
		if(LastState==9 && NowState==9){KeyNumber=25;}
		if(LastState==10 && NowState==10){KeyNumber=26;}
		if(LastState==11 && NowState==11){KeyNumber=27;}
		if(LastState==12 && NowState==12){KeyNumber=28;}
		if(LastState==13 && NowState==13){KeyNumber=29;}
		if(LastState==14 && NowState==14){KeyNumber=30;}
		if(LastState==15 && NowState==15){KeyNumber=31;}
		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、8X8彩色点阵屏

h文件

#ifndef __WS2812B_H__
#define __WS2812B_H__

extern unsigned char ColorStyle;
extern unsigned char xdata WS2812B_Buffer[];
extern unsigned char code MyColor[];
void WS2812B_Init(void);
void WS2812B_SetBuf(unsigned char Position,unsigned char R,unsigned char G,unsigned char B);
void WS2812B_WriteByte(unsigned char Byte);
void WS2812B_Clear(void);
void WS2812B_UpdateDisplay(void);
void WS2812B_MoveLeft(unsigned char *Array,unsigned char R,unsigned char G,unsigned char B,unsigned int Offset);
void WS2812B_MoveUp(unsigned char *Array,unsigned char R,unsigned char G,unsigned char B,unsigned int Offset);

#endif

c文件

#include <STC12C5A60S2.H>
#include <INTRINS.H>	//需要用空操作_nop_();来延时

sbit WS2812B_DI=P2^7;

unsigned char ColorStyle;	//颜色类型

//用64*3=192个字节作为WS2812B彩色点阵屏的缓存,共有64个灯,每个灯需要写入24Bit(3个字节)控制显示的颜色
//每三个字节为一组,每一组分别对应彩色灯的G(绿)、R(红)、B(蓝)三原色
unsigned char xdata WS2812B_Buffer[192];

//预设颜色(数值设小一些是为了防止太刺眼)
unsigned char code MyColor[]={
16,0,0,		//红色
0,16,0,		//绿色
0,0,16,		//蓝色
8,8,0,		//黄色
8,0,8,		//紫色
0,8,8,		//青色
};

/**
  * @brief  WS2812B彩色点阵屏私有延时函数,1T@24.000MHz调用可延时约1ms
  * @param  无
  * @retval 无
  */
void WS2812B_Delay100Us(void)
{
	unsigned char i, j;
	i=2;
	j=15;
	do
	{
		while(--j);
	} while(--i);
}

/**
  * @brief  WS2812B彩色点阵屏设置一个点的缓存
  * @param  Position 要设置的位置,高四位(0~7)对应1~8列(从左到右),低四位(0~7)对应1~8行(从上到下)
  * @param  R 红(Red),范围:0~255
  * @param  G 绿(Green),范围:0~255
  * @param  B 蓝(Blue),范围:0~255
  * @retval 无
  */
/*
本函数适用于以下数据传输顺序的8X8彩色点阵屏,如果不是的话,需要修改函数
数据从第一列的B7开始,往上到B0,然后再到第二列的B7,再往上到第二列的B0,以此类推
每一个B对应一个灯。缓存数组SnakeBuffer的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
*/
void WS2812B_SetBuf(unsigned char Position,unsigned char R,unsigned char G,unsigned char B)
{
	WS2812B_Buffer[Position/16*24+(21-Position%16*3)]=G;
	WS2812B_Buffer[Position/16*24+(22-Position%16*3)]=R;
	WS2812B_Buffer[Position/16*24+(23-Position%16*3)]=B;
}

/**
  * @brief  WS2812B彩色点阵屏写入一个字节
  * @brief  要求:1T@24.000MHz,频率如果不一样,需要调“_nop_();”的数量
  * @param  Byte 要写入的字节
  * @retval 无
  */
void WS2812B_WriteByte(unsigned char Byte)
{
	unsigned char i;
	
	EA=0;	//关闭总中断(时序要求严格,不能被打断),要求中断函数执行的时间不能太长,否则会“花屏”
	
	for(i=0;i<8;i++)
	{
		if(Byte&(0x80>>i))	//高位先发
		{	//写1
			WS2812B_DI=1;	//根据高电平的时长确定发送的是1还是0,跟DS18B20类似
			_nop_();_nop_();	//用空操作进行延时,单片机使用不同的频率,“_nop_();”的数量就不一样
			WS2812B_DI=0;	//经测试,数据线拉低后可以不用延时
		}
		else
		{	//写0
			WS2812B_DI=1;
			_nop_();
			WS2812B_DI=0;
		}
	}
	
	EA=1;	//开启总中断
}

/**
  * @brief  WS2812B彩色点阵屏清空缓存
  * @param  无
  * @retval 无
  */
void WS2812B_Clear(void)
{
	unsigned char i;
	for(i=0;i<192;i++){WS2812B_Buffer[i]=0;}
}

/**
  * @brief  WS2812B彩色点阵屏更新屏幕显示,将缓存数组WS2812B_Buffer的192个字节写入64个灯的芯片内
  * @param  无
  * @retval 无
  */
void WS2812B_UpdateDisplay(void)
{
	unsigned char i;
	
	for(i=0;i<192;i++){WS2812B_WriteByte(WS2812B_Buffer[i]);}	//连续写入192个字节
	
	WS2812B_Delay100Us();	//Reset(重置)信号
}

/**
  * @brief  WS2812B彩色点阵屏向左滚动显示
  * @param  Array 传递过来的数组的指针(地址),数组名就是数组的首地址
  * @param  R 红(Red),范围:0~255
  * @param  G 绿(Green),范围:0~255
  * @param  B 蓝(Blue),范围:0~255
  * @param  Offset 偏移量,向左平移Offset个像素
  * @retval 无
  */
void WS2812B_MoveLeft(unsigned char *Array,unsigned char R,unsigned char G,unsigned char B,unsigned int Offset)
{
	unsigned char i,j;
	
	Array+=Offset;
	
	for(i=0;i<8;i++)	//每个亮点都写入相同的颜色
	{
		for(j=0;j<8;j++)
		{
			if(*(Array+i)&(0x80>>j))
			{
				WS2812B_Buffer[3*8*i+3*j]=G;	//调了一下顺序,数组WS2812B_Buffer中按
				WS2812B_Buffer[3*8*i+3*j+1]=R;	//GRB的顺序放置,因WS2812B芯片就是要求按
				WS2812B_Buffer[3*8*i+3*j+2]=B;	//GRB的顺序发送数据的,方便数据发送给芯片
			}
			else
			{
				WS2812B_Buffer[3*8*i+3*j]=0;
				WS2812B_Buffer[3*8*i+3*j+1]=0;
				WS2812B_Buffer[3*8*i+3*j+2]=0;
			}
		}
	}
}

/**
  * @brief  WS2812B彩色点阵屏向上滚动显示
  * @param  Array 传递过来的数组的指针(地址),数组名就是数组的首地址
  * @param  R 红(Red),范围:0~255
  * @param  G 绿(Green),范围:0~255
  * @param  B 蓝(Blue),范围:0~255
  * @param  Offset 偏移量,向上平移Offset个像素
  * @retval 无
  */
void WS2812B_MoveUp(unsigned char *Array,unsigned char R,unsigned char G,unsigned char B,unsigned int Offset)
{
	unsigned char i,j;
	unsigned char m,n;
	unsigned char Temp[8];
	
	m=Offset/8;
	n=Offset%8;
	Array+=8*m;
	
	for(i=0;i<8;i++)	//将偏移后的数据保存到缓存数组Temp中,一个Bit对应一个灯
	{
		Temp[i]=(*Array>>n) | (*(Array+8)<<(8-n));
		Array++;
	}
	
	for(i=0;i<8;i++)	//每个亮点都写入相同的颜色
	{
		for(j=0;j<8;j++)
		{
			if(Temp[i]&(0x80>>j))
			{
				WS2812B_Buffer[3*8*i+3*j]=G;
				WS2812B_Buffer[3*8*i+3*j+1]=R;
				WS2812B_Buffer[3*8*i+3*j+2]=B;
			}
			else
			{
				WS2812B_Buffer[3*8*i+3*j]=0;
				WS2812B_Buffer[3*8*i+3*j+1]=0;
				WS2812B_Buffer[3*8*i+3*j+2]=0;
			}
		}
	}
}

四、主函数

main.c

/*
by甘腾胜@20241225
效果查看/操作演示:可以在B站搜索“甘腾胜”或“gantengsheng”查看
开发环境:Keil C51
单片机:STC12C5A60S2
分频系数及频率:1T@11.0592MHz
外设:普中开发板板载矩阵按键、8X8LED彩色点阵屏(WS2812B驱动)
原理分析:https://blog.csdn.net/gantengsheng/article/details/143581157
注意:驱动WS2812B彩色点阵屏需要用1T的单片机,传统的12T单片机无法驱动

操作说明:

(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     


本代码适用于以下数据传输顺序的8X8彩色点阵屏,如果不是的话,需要修改函数
数据从第一列的B7(点阵屏左下角)开始,往上到B0,然后再到第二列的B7,再往上到第二列的B0,以此类推
每一个B对应一个灯。缓存数组SnakeBuffer的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

*/

#include <STC12C5A60S2.H>	//包含寄存器的定义
#include <STDLIB.H>	//包含随机函数的声明
#include "WS2812B.h"	//包含工程目录下的头文件,相当于把头文件内容插入此处
#include "MatrixKeyScan.h"
#include "Timer0.h"

unsigned char KeyNum;	//存储获得的键码值
unsigned char Mode;	//游戏模式,0:显示流水灯及呼吸灯,1:显示游戏名“<<SNAKE>>”,2:显示难度的英文“DIFFICULTY”,
					//3:难度选择界面,难度范围是1~5,4:游戏模式,5:游戏结束全屏闪烁,6:显示英文“SCORE”,
					//7:循环滚动显示二位数得分,8:循环滚动显示作者和编程日期
unsigned char Mode0;	//Mode=0下的子模式,0:拖尾流水灯显示数据传输顺序,1:六种颜色的从左到右的拖尾流水灯,2:六种颜色的呼吸灯
unsigned char MoveFlag;	//移动蛇身的标志,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;	//保存整条蛇的数据的数组SnakeBody(共64个数据,数据索引为:0~63)中,蛇头对应的数据的索引,蛇的初始长度为2,
						//开始时只用了两个数据(数组的第1个数据和第2个数据),蛇头对应的是第2个数据(索引为1),Head的范围:0~63
unsigned char GameOverFlag;	//游戏结束的标志,1:游戏结束,0:游戏未结束
unsigned char FlashFlag;	//闪烁的标志,1:不显示,0:显示
unsigned char Food;	//保存创造出来的食物的位置,高四位(范围:0~7)对应列(1~8列),低四位(范围:0~7)对应行(1~8行)
					//从左往右数,分别是1~8列,从上往下数,分别是1~8行
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 int SnakeMoveSpeed=1000;	//控制蛇移动的速度,值越小,速度越快
unsigned int T0Count0,T0Count1,T0Count2,T0Count3,T0Count4,T0Count5;	//定时器计数的变量
unsigned char PauseFlag;	//暂停的标志,1:暂停,0:不暂停
unsigned char xdata SnakeBody[64];	//点阵屏是8*8=64个像素,需要用64个数据记录蛇身的数据
unsigned char SnakeBuffer[8];	//显示缓存,一个字节对应8个点,屏幕总共64个点,所以需要8个字节
								//阴码(亮点为1),逐列式取模,高位在下
								//此数组用来保存点阵屏哪些点亮,哪些点不亮
unsigned char Breath=1;	//用来实现呼吸灯的效果
unsigned char BreathInOrOut=1;	//用来控制呼吸灯变亮还是变暗
unsigned char BreathFlag;	//呼吸灯改变亮度的标志,定时器中每隔一段时间将此标志置1
unsigned char BreathCount;	//用来使呼吸灯熄灭后的时间长一点
unsigned char BreathColor;	//控制呼吸灯显示的颜色
unsigned char ExecuteOnceFlag0;	//子模式中只执行一次的标志,1:执行,0:不执行

unsigned char xdata Score[]={	//用来滚动显示得分
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
0x00,0x00,0x00,0x00,0x00,0x00,	//游戏结束后将蛇身长度Length的十位的数字字模写入到这一行
0x00,0x00,0x00,0x00,0x00,0x00,	//游戏结束后将蛇身长度Length的个位的数字字模写入到这一行
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
};

unsigned char code FlowingWaterLight1[]={	//流水灯1(拖尾)
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

0,255,0,0,191,0,0,127,0,0,95,0,0,63,0,0,47,0,0,31,0,0,23,0,	//红
0,15,0,0,11,0,0,7,0,0,5,0,0,3,0,0,2,0,0,1,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

0,0,255,0,0,191,0,0,127,0,0,95,0,0,63,0,0,47,0,0,31,0,0,23,	//绿
0,0,15,0,0,11,0,0,7,0,0,5,0,0,3,0,0,2,0,0,1,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

255,0,0,191,0,0,127,0,0,95,0,0,63,0,0,47,0,0,31,0,0,23,0,0,	//蓝
15,0,0,11,0,0,7,0,0,5,0,0,3,0,0,2,0,0,1,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,


0,255,255,0,191,191,0,127,127,0,95,95,0,63,63,0,47,47,0,31,31,0,23,23,	//黄
0,15,15,0,11,11,0,7,7,0,5,5,0,3,3,0,2,2,0,1,1,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

255,255,0,191,191,0,127,127,0,95,95,0,63,63,0,47,47,0,31,31,0,23,23,0,	//紫
15,15,0,11,11,0,7,7,0,5,5,0,3,3,0,2,2,0,1,1,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

255,0,255,191,0,191,127,0,127,95,0,95,63,0,63,47,0,47,31,0,31,23,0,23,	//青
15,0,15,11,0,11,7,0,7,5,0,5,3,0,3,2,0,2,1,0,1,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};

unsigned char code FlowingWaterLight2[]={	//流水灯2(拖尾)
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,	//红
0,127,0,0,127,0,0,127,0,0,127,0,0,127,0,0,127,0,0,127,0,0,127,0,
0,63,0,0,63,0,0,63,0,0,63,0,0,63,0,0,63,0,0,63,0,0,63,0,
0,31,0,0,31,0,0,31,0,0,31,0,0,31,0,0,31,0,0,31,0,0,31,0,
0,15,0,0,15,0,0,15,0,0,15,0,0,15,0,0,15,0,0,15,0,0,15,0,
0,7,0,0,7,0,0,7,0,0,7,0,0,7,0,0,7,0,0,7,0,0,7,0,
0,3,0,0,3,0,0,3,0,0,3,0,0,3,0,0,3,0,0,3,0,0,3,0,
0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,	//绿
0,0,127,0,0,127,0,0,127,0,0,127,0,0,127,0,0,127,0,0,127,0,0,127,
0,0,63,0,0,63,0,0,63,0,0,63,0,0,63,0,0,63,0,0,63,0,0,63,
0,0,31,0,0,31,0,0,31,0,0,31,0,0,31,0,0,31,0,0,31,0,0,31,
0,0,15,0,0,15,0,0,15,0,0,15,0,0,15,0,0,15,0,0,15,0,0,15,
0,0,7,0,0,7,0,0,7,0,0,7,0,0,7,0,0,7,0,0,7,0,0,7,
0,0,3,0,0,3,0,0,3,0,0,3,0,0,3,0,0,3,0,0,3,0,0,3,
0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,	//蓝
127,0,0,127,0,0,127,0,0,127,0,0,127,0,0,127,0,0,127,0,0,127,0,0,
63,0,0,63,0,0,63,0,0,63,0,0,63,0,0,63,0,0,63,0,0,63,0,0,
31,0,0,31,0,0,31,0,0,31,0,0,31,0,0,31,0,0,31,0,0,31,0,0,
15,0,0,15,0,0,15,0,0,15,0,0,15,0,0,15,0,0,15,0,0,15,0,0,
7,0,0,7,0,0,7,0,0,7,0,0,7,0,0,7,0,0,7,0,0,7,0,0,
3,0,0,3,0,0,3,0,0,3,0,0,3,0,0,3,0,0,3,0,0,3,0,0,
1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

0,255,255,0,255,255,0,255,255,0,255,255,0,255,255,0,255,255,0,255,255,0,255,255,	//黄
0,127,127,0,127,127,0,127,127,0,127,127,0,127,127,0,127,127,0,127,127,0,127,127,
0,63,63,0,63,63,0,63,63,0,63,63,0,63,63,0,63,63,0,63,63,0,63,63,
0,31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,31,31,
0,15,15,0,15,15,0,15,15,0,15,15,0,15,15,0,15,15,0,15,15,0,15,15,
0,7,7,0,7,7,0,7,7,0,7,7,0,7,7,0,7,7,0,7,7,0,7,7,
0,3,3,0,3,3,0,3,3,0,3,3,0,3,3,0,3,3,0,3,3,0,3,3,
0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

255,255,0,255,255,0,255,255,0,255,255,0,255,255,0,255,255,0,255,255,0,255,255,0,	//紫
127,127,0,127,127,0,127,127,0,127,127,0,127,127,0,127,127,0,127,127,0,127,127,0,
63,63,0,63,63,0,63,63,0,63,63,0,63,63,0,63,63,0,63,63,0,63,63,0,
31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,
15,15,0,15,15,0,15,15,0,15,15,0,15,15,0,15,15,0,15,15,0,15,15,0,
7,7,0,7,7,0,7,7,0,7,7,0,7,7,0,7,7,0,7,7,0,7,7,0,
3,3,0,3,3,0,3,3,0,3,3,0,3,3,0,3,3,0,3,3,0,3,3,0,
1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

255,0,255,255,0,255,255,0,255,255,0,255,255,0,255,255,0,255,255,0,255,255,0,255,	//青
127,0,127,127,0,127,127,0,127,127,0,127,127,0,127,127,0,127,127,0,127,127,0,127,
63,0,63,63,0,63,63,0,63,63,0,63,63,0,63,63,0,63,63,0,63,63,0,63,
31,0,31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,31,
15,0,15,15,0,15,15,0,15,15,0,15,15,0,15,15,0,15,15,0,15,15,0,15,
7,0,7,7,0,7,7,0,7,7,0,7,7,0,7,7,0,7,7,0,7,7,0,7,
3,0,3,3,0,3,3,0,3,3,0,3,3,0,3,3,0,3,3,0,3,3,0,3,
1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};

//取模要求:阴码(亮点为1),逐列式取模,高位在下
//点阵屏摆放方向不同,取模要求也不同,很多函数也需要修改
unsigned char code Table1[]={	//“<<SNAKE>>”
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 51	宽6高8
0x00,0x7F,0x04,0x08,0x10,0x7F,	// N 46
0x00,0x7C,0x12,0x11,0x12,0x7C,	// A 33
0x00,0x7F,0x08,0x14,0x22,0x41,	// K 43
0x00,0x7F,0x49,0x49,0x49,0x41,	// E 37
0x00,0x41,0x22,0x14,0x49,0x22,0x14,0x08,	// >>
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
};
unsigned char code Table2[]={	//“DIFFICULTY”,宽6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
0x00,0x7F,0x41,0x41,0x22,0x1C,	// D 36
0x00,0x00,0x41,0x7F,0x41,0x00,	// I 41
0x00,0x7F,0x09,0x09,0x09,0x01,	// F 38
0x00,0x7F,0x09,0x09,0x09,0x01,	// F 38
0x00,0x00,0x41,0x7F,0x41,0x00,	// I 41
0x00,0x3E,0x41,0x41,0x41,0x22,	// C 35
0x00,0x3F,0x40,0x40,0x40,0x3F,	// U 53
0x00,0x7F,0x40,0x40,0x40,0x40,	// L 44
0x00,0x01,0x01,0x7F,0x01,0x01,	// T 52
0x00,0x07,0x08,0x70,0x08,0x07,	// Y 57
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
0x00,0x00,0x00,0x42,0x7F,0x40,0x00,0x00,	// 1 17 如果不按按键跳过,则在显示“1”后自动切换到下一个模式
};
unsigned char code Table3[]={	//“123451”,宽8高8
0x00,0x00,0x00,0x42,0x7F,0x40,0x00,0x00,	// 1 17
0x00,0x00,0x42,0x61,0x51,0x49,0x46,0x00,	// 2 18
0x00,0x00,0x21,0x41,0x45,0x4B,0x31,0x00,	// 3 19
0x00,0x00,0x18,0x14,0x12,0x7F,0x10,0x00,	// 4 20
0x00,0x00,0x27,0x45,0x45,0x45,0x39,0x00,	// 5 21
0x00,0x00,0x00,0x42,0x7F,0x40,0x00,0x00,	// 1 17
};
unsigned char code Table4[]={	//“SCORE”,宽6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
0x00,0x46,0x49,0x49,0x49,0x31,	// S 51
0x00,0x3E,0x41,0x41,0x41,0x22,	// C 35
0x00,0x3E,0x41,0x41,0x41,0x3E,	// O 47
0x00,0x7F,0x09,0x19,0x29,0x46,	// R 50
0x00,0x7F,0x49,0x49,0x49,0x41,	// E 37
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
};
unsigned char code Table5[]={	//“0~9”,宽6高8
0x00,0x3E,0x51,0x49,0x45,0x3E,	// 0 16
0x00,0x00,0x42,0x7F,0x40,0x00,	// 1 17
0x00,0x42,0x61,0x51,0x49,0x46,	// 2 18
0x00,0x21,0x41,0x45,0x4B,0x31,	// 3 19
0x00,0x18,0x14,0x12,0x7F,0x10,	// 4 20
0x00,0x27,0x45,0x45,0x45,0x39,	// 5 21
0x00,0x3C,0x4A,0x49,0x49,0x30,	// 6 22
0x00,0x01,0x71,0x09,0x05,0x03,	// 7 23
0x00,0x36,0x49,0x49,0x49,0x36,	// 8 24
0x00,0x06,0x49,0x49,0x29,0x1E,	// 9 25
};
unsigned char code Table6[]={	//“by gantengsheng at 20241225”,宽6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
0x00,0x7F,0x48,0x44,0x44,0x38,	// b 66
0x00,0x1C,0xA0,0xA0,0xA0,0x7C,	// y 89
0x00,0x00,0x00,0x00,0x00,0x00,	//   0
0x00,0x18,0xA4,0xA4,0xA4,0x7C,	// g 71
0x00,0x20,0x54,0x54,0x54,0x78,	// a 65
0x00,0x7C,0x08,0x04,0x04,0x78,	// n 78
0x00,0x04,0x3F,0x44,0x40,0x20,	// t 84
0x00,0x38,0x54,0x54,0x54,0x18,	// e 69
0x00,0x7C,0x08,0x04,0x04,0x78,	// n 78
0x00,0x18,0xA4,0xA4,0xA4,0x7C,	// g 71
0x00,0x48,0x54,0x54,0x54,0x20,	// s 83
0x00,0x7F,0x08,0x04,0x04,0x78,	// h 72
0x00,0x38,0x54,0x54,0x54,0x18,	// e 69
0x00,0x7C,0x08,0x04,0x04,0x78,	// n 78
0x00,0x18,0xA4,0xA4,0xA4,0x7C,	// g 71
0x00,0x00,0x00,0x00,0x00,0x00,	//   0
0x00,0x20,0x54,0x54,0x54,0x78,	// a 65
0x00,0x04,0x3F,0x44,0x40,0x20,	// t 84
0x00,0x00,0x00,0x00,0x00,0x00,	//   0
0x00,0x42,0x61,0x51,0x49,0x46,	// 2 18
0x00,0x3E,0x51,0x49,0x45,0x3E,	// 0 16
0x00,0x42,0x61,0x51,0x49,0x46,	// 2 18
0x00,0x18,0x14,0x12,0x7F,0x10,	// 4 20
0x00,0x00,0x42,0x7F,0x40,0x00,	// 1 17
0x00,0x42,0x61,0x51,0x49,0x46,	// 2 18
0x00,0x42,0x61,0x51,0x49,0x46,	// 2 18
0x00,0x27,0x45,0x45,0x45,0x39,	// 5 21
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
};

/**
  * @brief  创造出一个随机位置的食物
  * @param  无
  * @retval 创造出的食物位置的数据,高四位(0~7)对应1~8列(从左到右),低四位(0~7)对应1~8行(从上到下)
  */
unsigned char CreateFood(void)
{
	unsigned char FoodTemp;
	unsigned char i,j,m,n;
	m=rand()%8;	//产生一个0~7的随机数
	n=rand()%8;	//产生一个0~7的随机数
	for(i=0;i<8;i++)	//产生一个随机位置,判断该位置是否是蛇身
	{	//如果不是,就返回该位置所对应的数据,如果是蛇身的位置,则从该点向周围寻找不是蛇身的空位置
		for(j=0;j<8;j++)
		{
			if( ( SnakeBuffer[(m+i)%8] & (0x01<<((n+j)%8)) ) == 0 )
			{
				FoodTemp=(m+i)%8*16+(n+j)%8;
				break;	//找到了空位置就退出循环
			}
		}
	}
	return FoodTemp;
}

/**
  * @brief  改变缓存数组SnakeBuffer的数据
  * @param  Position 要设置的位置,高四位(0~7)对应1~8列(从左到右),低四位(0~7)对应1~8行(从上到下)
  * @param  State 要修改成的状态,范围:0~1,0:对应的那个Bit置0,1:对应的那个Bit置1
  * @retval 无
  */
void ChangeSnakeBuffer(unsigned char Position,unsigned char State)
{
	if(State)
	{
		SnakeBuffer[Position/16] |= (0x01<<(Position%16));
	}
	else
	{
		SnakeBuffer[Position/16] &= ~(0x01<<(Position%16));
	}
}

/**
  * @brief  控制蛇的移动
  * @param  无
  * @retval 无
  */
void MoveSnake(void)
{
	if(NowDirection==1)	//如果向右移动
	{
		//移动前判断一下移动后是否撞墙,如果是,则游戏结束,游戏结束的标志置1
		if(SnakeBody[Head]/16==7){GameOverFlag=1;}
		
		//(Head+1)%64,取余的目的是为了防止越界,SnakeBody数组的索引范围是:0~63,索引为63后再加1,就越界了
		//SnakeBody数组中索引为(Head+1)%64的数据(准蛇头)等于索引为Head的数据(现蛇头)加16(高四位加1),即蛇头移动到了右边这一列
		else{SnakeBody[(Head+1)%64]=SnakeBody[Head]+16;}
	}
	if(NowDirection==2)	//如果向上移动
	{
		if(SnakeBody[Head]%16==0){GameOverFlag=1;}
		
		//SnakeBody数组中索引为(Head+1)%64的数据(准蛇头)等于索引为Head的数据(现蛇头)减1(低四位减1),即蛇头移动到了上边这一行
		else{SnakeBody[(Head+1)%64]=SnakeBody[Head]-1;}
	}
	if(NowDirection==3)	//如果向左移动
	{
		if(SnakeBody[Head]/16==0){GameOverFlag=1;}
		
		//SnakeBody数组中索引为(Head+1)%64的数据(准蛇头)等于索引为Head的数据(现蛇头)减16(高四位减1),即蛇头移动到了左边这一列
		else{SnakeBody[(Head+1)%64]=SnakeBody[Head]-16;}
	}
	if(NowDirection==4)	//如果向下移动
	{
		if(SnakeBody[Head]%16==7){GameOverFlag=1;}
		
		//SnakeBody数组中索引为(Head+1)%64的数据(准蛇头)等于索引为Head的数据(现蛇头)加1(低四位减1),即蛇头移动到了下边这一行
		else{SnakeBody[(Head+1)%64]=SnakeBody[Head]+1;}
	}
	
	Head++;	//SnakeBody数组中,蛇头对应的数据的索引加1
	Head%=64;	//蛇头变量Head的范围是0~63
	
	if(GameOverFlag==0)	//如果没撞墙
	{
		if(SnakeBody[Head]==Food)	//判断蛇头移动后的位置是否是食物所在的位置
		{	//如果是
			Length++;	//蛇身长度加1
			ColorStyle++;	//蛇身和食物均切换为下一种颜色
			ColorStyle%=6;	//预设了6种颜色:红、绿、蓝、黄、紫、青
			ChangeSnakeBuffer(Food,1);	//更新缓存数组SnakeBody的数据
			if(Length<64)	//如果蛇身长度没有达到最大值64
			{
				Food=CreateFood();	//重新创造一个食物
			}
			else	//如果蛇身长度达到了最大值64
			{
				GameOverFlag=1;	//游戏结束
			}
			FlashFlag=0;	//创造出新的食物时,食物暂不闪烁
			T0Count4=0;	//定时器T0Count4重新计数
		}
		else if( SnakeBuffer[SnakeBody[Head]/16] & (0x01<<(SnakeBody[Head]%16)) )
		{	//如果蛇头移动后的位置不是食物,且撞在蛇身上,则游戏结束
			GameOverFlag=1;	//游戏结束的标志置1
		}
		else	//如果蛇头移动后的位置不是食物,也不是撞墙,也不是撞到蛇身的话
		{
			ChangeSnakeBuffer(SnakeBody[Head],1);
			ChangeSnakeBuffer(SnakeBody[(Head+64-Length)%64],0);
		}
	}

}

/**
  * @brief  IO口初始化,全部设置为上拉模式
  * @param  无
  * @retval 无
  */
void GPIO_Init(void)
{
	P0M1=0;P0M0=0;
	P1M1=0;P1M0=0;
	P2M1=0;P2M0=0;
	P3M1=0;P3M0=0;
	P4M1=0;P4M0=0;
}

void main()
{
	unsigned char i;
	GPIO_Init();	//IO口初始化
	Timer0_Init();	//定时器初始化
	ExecuteOnceFlag=1;

	while(1)
	{
		KeyNum=Key();	//获取键码值

		if(KeyNum)	//如果有按键按下
		{
			srand(TL0);	//以定时器0的低八位数据作为随机数的种子,用来产生真随机的数据

			if(Mode==8)	//如果是显示作者和编程日期的界面
			{
				if(KeyNum==44)	//如果按下K12(松手瞬间)
				{
					Mode=3;	//返回难度选择界面
					ExecuteOnceFlag=1;	//各模式只执行一次代码的标志置1
				}
			}

			if(Mode==7)	//如果是滚动显示得分的界面
			{
				if(KeyNum==41)	//如果按下K9(松手瞬间)
				{
					Mode=8;	//切换到显示作者和编程日期的界面
					ExecuteOnceFlag=1;
				}
				if(KeyNum==44)	//如果按下K12(松手瞬间)
				{
					Mode=3;	//返回难度选择界面
					ExecuteOnceFlag=1;
				}
			}

			if(Mode==6)	//如果是滚动显示英文“SCORE”的界面
			{
				if(KeyNum!=9 && KeyNum!=25 && KeyNum!=41 && KeyNum!=12 && KeyNum!=28 && KeyNum!=44)
				{	//如果按下K9和K12以外的按键
					Mode=7;	//跳过英文显示,切换到滚动显示得分界面
					ExecuteOnceFlag=1;
				}
			}

			if(Mode==5)	//如果是游戏结束全屏闪烁界面
			{
				if(KeyNum==48)	//如果按下K16(松手瞬间)
				{
					Mode=6;	//切换到滚动显示英文“SCORE”的界面
					ExecuteOnceFlag=1;
				}
			}

			if(Mode==4)	//如果是游戏进行中
			{
				if(KeyNum==48)	//按下K16暂停或继续(松手瞬间)
				{
					PauseFlag=!PauseFlag;
					if(PauseFlag==0){T0Count3=0;}
				}
				if(PauseFlag==0)	//如果不是暂停
				{	//按下瞬间、长按、松手瞬间都进行检测,这样控制方向更有效,防止按键没检测出来导致没能改变方向
					if((KeyNum==13 || KeyNum==29 || KeyNum==45) && LastDirection!=1)
					{	//如果按了“左”键,且蛇头原来的移动方向不是向右
						NowDirection=3;	//则方向蛇头方向改为向左
					}
					if((KeyNum==10 || KeyNum==26 || KeyNum==42) && LastDirection!=4)
					{	//如果按了“上”键,且蛇头原来的移动方向不是向下
						NowDirection=2;	//则方向蛇头方向改为向上
					}
					if((KeyNum==14 || KeyNum==30 || KeyNum==46) && LastDirection!=2)
					{	//如果按了“下”键,且蛇头原来的移动方向不是向上
						NowDirection=4;	//则方向蛇头方向改为向左
					}
					if((KeyNum==15 || KeyNum==31 || KeyNum==47) && LastDirection!=3)
					{	//如果按了“右”键,且蛇头原来的移动方向不是向左
						NowDirection=1;	//则方向蛇头方向改为向左
					}
				}
			}

			if(Mode==3)	//如果是难度选择界面
			{
				if(KeyNum==42)	//如果按了“上”键K10(松手瞬间)
				{
					RollUpFlag=1;	//数字向上滚动的标志置1
				}
				if(KeyNum==46)	//如果按了“下”键K14(松手瞬间)
				{
					RollDownFlag=1;	//数字向下滚动的标志置1
				}
				if(KeyNum==48)	//如果按了开始键K16(松手瞬间)
				{
					Mode=4;	//切换到游戏模式
					ExecuteOnceFlag=1;
				}
			}

			if(Mode==2)	//如果是显示英文“DIFFICULTY”的界面
			{
				if(KeyNum>=33 && KeyNum<=48)	//如果按下任意按键(松手瞬间)
				{
					Mode=3;	//切换到难度选择界面
					ExecuteOnceFlag=1;
				}
			}

			if(Mode==1)	//如果是显示游戏名“<<SNAKE>>”的界面
			{
				if(KeyNum>=33 && KeyNum<=48)	//如果按下任意按键(松手瞬间)
				{
					Mode=2;	//切换到英文“DIFFICULTY”的显示界面
					ExecuteOnceFlag=1;
				}
			}

			if(Mode==0)	//如果是显示流水灯界面
			{
				if(KeyNum==42)	//如果按了“上”键K10(松手瞬间)
				{
					Mode0++;	//切换子模式
					Mode0%=3;	//共有三个子模式
					ExecuteOnceFlag0=1;
				}
				if(KeyNum==46)	//如果按了“下”键K14(松手瞬间)
				{
					if(Mode0==0){Mode0=2;}
					else{Mode0--;}
					ExecuteOnceFlag0=1;
				}
				if(KeyNum>=33 && KeyNum<=48 && KeyNum!=42 && KeyNum!=46)
				{	//如果按了K10和K14以外的按键(松手瞬间),切换为显示游戏名“<<SNAKE>>”的模式
					Mode=1;	//切换到游戏模式
					ExecuteOnceFlag=1;
				}
			}

		}

		if(Mode==0)	//如果是显示彩色流水灯的界面
		{
			if(ExecuteOnceFlag)	//切换模式前,此if的内容只执行一次
			{
				ExecuteOnceFlag=0;
				Offset1=0;	//偏移量清零
				for(i=0;i<8;i++)	//呼吸灯需要用到数组SnakeBuffer
				{
					SnakeBuffer[i]=0xFF;
				}
			}
			if(Mode0==0)	//拖尾流水灯(六种颜色:红、绿、蓝、黄、紫、青)显示数据传输顺序
			{
				if(ExecuteOnceFlag0)	//切换到Mode0==0后,切换为其他模式前,此if的代码只执行一次
				{
					ExecuteOnceFlag0=0;
					Offset1=0;	//偏移量清零
				}
				if(RollFlag)
				{
					RollFlag=0;
					for(i=0;i<192;i++)	//通过查表的方法显示流水灯
					{
						WS2812B_Buffer[i]=FlowingWaterLight1[191-i+3*Offset1];
					}
					WS2812B_UpdateDisplay();	//改变缓存WS2812B_Buffer后,更新到屏幕的显示
					Offset1++;
					Offset1%=400;
				}
			}
			if(Mode0==1)	//显示从左到右的六种颜色(红、绿、蓝、黄、紫、青)的拖尾流水灯
			{
				if(ExecuteOnceFlag0)
				{
					ExecuteOnceFlag0=0;
					Offset1=0;
				}				
				if(RollFlag)
				{
					RollFlag=0;
					for(i=0;i<192;i++)	//通过查表的方法显示流水灯
					{
						WS2812B_Buffer[i]=FlowingWaterLight2[191-i+24*Offset1];
					}
					WS2812B_UpdateDisplay();
					Offset1++;
					Offset1%=96;
				}
			}
			if(Mode0==2)	//六种颜色(红、绿、蓝、黄、紫、青)的呼吸灯
			{
				if(ExecuteOnceFlag0)
				{
					ExecuteOnceFlag0=0;
					Breath=1;
					BreathInOrOut=1;
					BreathColor=0;
				}
				if(BreathFlag)
				{
					BreathFlag=0;
					if(Breath==0 && BreathCount<15){BreathCount++;PauseFlag=1;}	//完全熄灭后延时一小段时间
					else{BreathCount=0;PauseFlag=0;}
					if(PauseFlag==0)
					{
						if(Breath==0){BreathInOrOut=1;BreathColor++;BreathColor%=6;}
						if(Breath==255){BreathInOrOut=0;}	//Breath的值决定亮度
						if(BreathInOrOut){Breath++;}	//变亮
						else{Breath--;}	//变暗
						switch(BreathColor)
						{
							case 0:WS2812B_MoveLeft(SnakeBuffer,Breath,0,0,0);break;	//红
							case 1:WS2812B_MoveLeft(SnakeBuffer,0,Breath,0,0);break;	//绿
							case 2:WS2812B_MoveLeft(SnakeBuffer,0,0,Breath,0);break;	//蓝
							case 3:WS2812B_MoveLeft(SnakeBuffer,(unsigned char)(Breath/2),(unsigned char)(Breath/2),0,0);break;	//黄
							case 4:WS2812B_MoveLeft(SnakeBuffer,(unsigned char)(Breath/2),0,(unsigned char)(Breath/2),0);break;	//紫
							case 5:WS2812B_MoveLeft(SnakeBuffer,0,(unsigned char)(Breath/2),(unsigned char)(Breath/2),0);break;	//青
							default:break;
						}
						WS2812B_UpdateDisplay();
					}
				}
			}
		}

		if(Mode==1)	//如果是显示游戏名称“<<SNAKE>>”的界面
		{
			if(ExecuteOnceFlag)
			{
				ExecuteOnceFlag=0;
				Offset1=0;
			}
			if(RollFlag)
			{
				RollFlag=0;
				WS2812B_MoveLeft(Table1,16,0,0,Offset1);	//红色
				WS2812B_UpdateDisplay();
				Offset1++;
				Offset1%=54;	//循环滚动显示
			}
		}

		if(Mode==2)	//如果是显示英文“DIFFICULTY”的界面
		{
			if(ExecuteOnceFlag)
			{
				ExecuteOnceFlag=0;
				Offset1=0;
			}
			if(RollFlag)	//只向左滚动显示一次,不循环滚动显示
			{
				if(Offset1<=68)	//只向左滚动显示一次,不循环滚动显示
				{
					RollFlag=0;
					WS2812B_MoveLeft(Table2,0,16,0,Offset1);	//绿色
					WS2812B_UpdateDisplay();
					Offset1++;
				}
				else if(Offset1<=76)	//只向左滚动显示一次,不循环滚动显示
				{
					RollFlag=0;
					WS2812B_MoveLeft(Table2,0,0,16,Offset1);	//蓝色
					WS2812B_UpdateDisplay();
					Offset1++;
				}
				else	//显示数字“1”之后,自动切换到难度选择界面
				{
					Mode=3;
					ExecuteOnceFlag=1;
				}
			}
		}

		if(Mode==3)	//如果是难度选择界面
		{
			if(ExecuteOnceFlag)
			{
				ExecuteOnceFlag=0;
				WS2812B_MoveUp(Table3,0,0,16,Offset2);	//蓝色
				WS2812B_UpdateDisplay();
			}
			if(RollFlag && RollUpFlag)	//如果滚动标志为1,且向上滚动的标志也为1
			{
				RollFlag=0;
				Offset2++;	//向上移动一个像素
				Offset2%=40;	//越界清零,总共5个数字,每个数字的高度是8,所以是5*8=40
				WS2812B_MoveUp(Table3,0,0,16,Offset2);
				WS2812B_UpdateDisplay();
				RollCount++;
				if(RollCount==8)	//移动了8个像素后停止移动
				{
					RollCount=0;
					RollUpFlag=0;
					Offset2=(Offset2/8)*8;	//防止移动到一半的时候按下“上”或“下”按键导致数字没有在点阵屏中间
											//Offset2的值必须是8的整数倍
					switch(Offset2/8)
					{
						case 0:SnakeMoveSpeed=1000;break;	//难度1,1s移动1次
						case 1:SnakeMoveSpeed=750;break;	//难度2,0.75s移动1次
						case 2:SnakeMoveSpeed=500;break;	//难度3,0.5s移动1次
						case 3:SnakeMoveSpeed=250;break;	//难度4,0.25s移动1次
						case 4:SnakeMoveSpeed=120;break;	//难度5,0.12s移动1次
						default:break;
					}
				}
			}
			if(RollFlag && RollDownFlag)	//如果滚动标志为1,且向下滚动的标志也为1
			{
				RollFlag=0;
				if(Offset2==0){Offset2=40;}
				Offset2--;
				WS2812B_MoveUp(Table3,0,0,16,Offset2);
				WS2812B_UpdateDisplay();
				RollCount++;
				if(RollCount==8)
				{
					RollCount=0;
					RollDownFlag=0;
					Offset2=(Offset2/8)*8;
					switch(Offset2/8)
					{
						case 0:SnakeMoveSpeed=1000;break;
						case 1:SnakeMoveSpeed=750;break;
						case 2:SnakeMoveSpeed=500;break;
						case 3:SnakeMoveSpeed=250;break;
						case 4:SnakeMoveSpeed=120;break;
						default:break;
					}
				}
			}
		}

		if(Mode==4)	//如果是游戏进行模式
		{
			if(ExecuteOnceFlag)	//游戏初始化
			{
				ExecuteOnceFlag=0;
				WS2812B_Clear();	//清除WS2812B显示缓存
				ColorStyle=rand()%6;	//从预设的六种颜色中随机选一种作为蛇的起始颜色
				GameOverFlag=0;	//游戏结束标志清零
				PauseFlag=0;	//游戏暂停标志清零
				NowDirection=1;	//蛇头默认向右移动
				LastDirection=1;	//上一次蛇头默认向右移动
				Length=2;	//蛇的初始长度为2
				Head=1;	//蛇头对应数组中的第2个数据(索引为1)
				for(i=0;i<8;i++){SnakeBuffer[i]=0;}	//SnakeBuffer显示缓存全部清零
				SnakeBody[0]=1*16+1;	//蛇尾,2列2行
				SnakeBody[1]=2*16+1;	//蛇头,3列2行
				ChangeSnakeBuffer(SnakeBody[0],1);
				ChangeSnakeBuffer(SnakeBody[1],1);
				WS2812B_SetBuf(SnakeBody[0],MyColor[ColorStyle%6*3],MyColor[ColorStyle%6*3+1],MyColor[ColorStyle%6*3+2]);
				WS2812B_SetBuf(SnakeBody[1],MyColor[ColorStyle%6*3],MyColor[ColorStyle%6*3+1],MyColor[ColorStyle%6*3+2]);
				Food=CreateFood();	//开始游戏前,先创造出一个食物
				WS2812B_SetBuf(Food,MyColor[(ColorStyle+1)%6*3],MyColor[(ColorStyle+1)%6*3+1],MyColor[(ColorStyle+1)%6*3+2]);
				WS2812B_UpdateDisplay();	//更改缓存后,更新屏幕的显示
				MoveFlag=0;	//蛇移动的标志清零
				T0Count3=0;	//定时器计数变量T0Count3清零,重新计数
			}
			if(GameOverFlag==0)	//如果游戏没结束
			{
				if(MoveFlag && PauseFlag==0)
				{
					MoveFlag=0;	//移动标志清零
					MoveSnake();	//移动一次
					LastDirection=NowDirection;	//保存上一次移动的方向,用于按键的判断(蛇不能往后移动)
					WS2812B_MoveLeft(SnakeBuffer,MyColor[ColorStyle%6*3],MyColor[ColorStyle%6*3+1],MyColor[ColorStyle%6*3+2],0);
				}
				if(PauseFlag)	//如果暂停了
				{	//(ColorStyle+1)%6:食物的颜色是预设模式中,蛇的颜色的下一种颜色,预设颜色:红绿蓝黄紫青
					WS2812B_SetBuf(Food,MyColor[(ColorStyle+1)%6*3],MyColor[(ColorStyle+1)%6*3+1],MyColor[(ColorStyle+1)%6*3+2]);	//食物不闪烁,一直显示
				}			
				else if(FlashFlag)	//如果不暂停,且闪烁标志为1
				{
					WS2812B_SetBuf(Food,0,0,0);	//不显示食物
				}
				else	//如果不暂停,且闪烁标志为0
				{
					WS2812B_SetBuf(Food,MyColor[(ColorStyle+1)%6*3],MyColor[(ColorStyle+1)%6*3+1],MyColor[(ColorStyle+1)%6*3+2]);	//显示食物
				}
				WS2812B_UpdateDisplay();
			}
			else	//如果游戏结束
			{
				Mode=5;	//切换到全屏闪烁模式
				ExecuteOnceFlag=1;			
			}				
		}
		
		if(Mode==5)	//全屏闪烁
		{
			if(FlashFlag)	//不显示
			{
				WS2812B_Clear();
				WS2812B_UpdateDisplay();
			}
			else	//显示
			{
				WS2812B_MoveLeft(SnakeBuffer,MyColor[ColorStyle%6*3],MyColor[ColorStyle%6*3+1],MyColor[ColorStyle%6*3+2],0);
				if(Length<64)	//防止长度为64时有一个点的颜色(食物的颜色)跟其它的不一样
				{
					WS2812B_SetBuf(Food,MyColor[(ColorStyle+1)%6*3],MyColor[(ColorStyle+1)%6*3+1],MyColor[(ColorStyle+1)%6*3+2]);
				}
				WS2812B_UpdateDisplay();				
			}
		}

		if(Mode==6)	//如果是显示英文“SCROE”的界面
		{
			if(ExecuteOnceFlag)
			{
				ExecuteOnceFlag=0;
				Offset1=0;
			}
			if(RollFlag)
			{
				if(Offset1<=38)	//只滚动显示一次英文“SCORE”
				{
					RollFlag=0;
					WS2812B_MoveLeft(Table4,8,8,0,Offset1);	//黄色
					WS2812B_UpdateDisplay();
					Offset1++;
				}
				else //滚动结束后,自动切换到循环显示得分的模式
				{
					Mode=7;
					ExecuteOnceFlag=1;
				}	
			}
		}

		if(Mode==7)	//如果是滚动显示得分界面
		{
			if(ExecuteOnceFlag)
			{
				ExecuteOnceFlag=0;
				for(i=0;i<6;i++)	//将得分(即蛇身的长度)的十位和个位的字模写入数组ScoreShow中
				{
					Score[8+i]=Table5[(Length/10)*6+i];
				}
				for(i=0;i<6;i++)
				{
					Score[14+i]=Table5[(Length%10)*6+i];
				}
				Offset1=0;
			}
			if(RollFlag)	//如果滚动的标志为1
			{
				RollFlag=0;
				WS2812B_MoveLeft(Score,8,0,8,Offset1);	//紫色
				WS2812B_UpdateDisplay();
				Offset1++;
				Offset1%=20;	//循环滚动
			}
		}

		if(Mode==8)	//如果是显示作者和编程日期的界面
		{
			if(ExecuteOnceFlag)
			{
				ExecuteOnceFlag=0;
				Offset1=0;
			}
			if(RollFlag)	//如果滚动的标志为1
			{
				RollFlag=0;
				WS2812B_MoveLeft(Table6,0,8,8,Offset1);	//青色
				WS2812B_UpdateDisplay();
				Offset1++;
				Offset1%=170;	//循环滚动
			}
		}

	}
}

void Timer0_Routine() interrupt 1	//定时器0中断函数
{
	TL0=0x9A;	//设置定时初始值,定时2ms,1T@11.0592MHz
	TH0=0xA9;	//设置定时初始值,定时2ms,1T@11.0592MHz
	T0Count1++;
	T0Count2++;
	if(PauseFlag==0){T0Count3++;}	//不暂停时,T0Count3才计数
	else{T0Count3=0;}	//暂停后继续游戏时,T0Count3重新计数
	T0Count4++;
	T0Count5++;
	if(T0Count1>=10)	//每隔20ms检测一次按键
	{
		T0Count1=0;
		Key_Tick();
	}
	if(T0Count2>=50)	//每隔100ms滚动一次
	{
		T0Count2=0;
		RollFlag=1;
	}
	if(T0Count3>=SnakeMoveSpeed/2)	//控制蛇的移动速度
	{
		T0Count3=0;
		MoveFlag=1;
	}
	if(T0Count4>=125)	//每隔250ms改变FlashFlag的值
	{
		T0Count4=0;
		FlashFlag=!FlashFlag;
	}
	if(T0Count5>=1)	//每隔2ms呼吸灯的亮度改变一次
	{
		T0Count5=0;
		BreathFlag=1;
	}
}

总结

开始测试时,非游戏时都能正常显示,游戏时出现“花屏”的现象,后来才找到原因,是定时器中断函数执行时间太长了,影响了向点阵屏发送的数据,优化了一下才能正常显示。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2270583.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

爆肝1个月:DDR4 的信号完整性(万字长文SI)

前言&#xff1a; 大学里面&#xff0c;总有很多课程&#xff0c;很浪费时间&#xff0c;学了没点用处&#xff0c;问过老师&#xff0c;为什么信号完整性&#xff0c;示波器使用等课程不开呢&#xff0c;这种是对工作真实有帮助的&#xff1f; 老师&#xff1a;因为老师…

使用Xjar给SpringBoot项目jar包加密

1. 新建一个SpringBoot项目 2. 在pom文件添加依赖&#xff0c;github地址&#xff1a;https://github.com/core-lib/xjar <dependencies><!-- 添加 XJar 依赖 --><dependency><groupId>com.github.core-lib</groupId><artifactId>xjar&l…

UNI-APP_i18n国际化引入

官方文档&#xff1a;https://uniapp.dcloud.net.cn/tutorial/i18n.html vue2中使用 1. 新建文件 locale/index.js import en from ./en.json import zhHans from ./zh-Hans.json import zhHant from ./zh-Hant.json const messages {en,zh-Hans: zhHans,zh-Hant: zhHant }…

AI大模型系列之七:Transformer架构讲解

目录 Transformer网络是什么&#xff1f; 输入模块结构&#xff1a; 编码器模块结构&#xff1a; 解码器模块: 输出模块结构&#xff1a; Transformer 具体是如何工作的&#xff1f; Transformer核心思想是什么&#xff1f; Transformer的代码架构 自注意力机制是什么…

【Linux】:多线程(读写锁 自旋锁)

✨ 倘若南方知我意&#xff0c;莫将晚霞落黄昏 &#x1f30f; &#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;Linux—登神长阶 ⛺️ 欢迎关注&#xff1a;&#x1f44d;点赞 &#…

SELECT的使用

目录 1、SQL的查询命令 1.1 SELECT语句 1.2 投影查询 1.3 选择查询 1.4 聚合函数查询 1.5 简单分组查询(GROUP BY) 1.6 内连接查询 1.7 外连接查询 1.8 子查询 1. 无关子查询 2. 相关子查询 带exists的相关子查询&#xff1a; 1.9 集合查询 1. UNION(并) 2. INT…

Vue项目结构推荐(复杂国际化项目与一般项目结构)

Vue项目结构推荐 一、一般项目结构二、复杂国际化项目结构总结/建议 下面结构是基于Vue和TypeScript开发的项目结构下src包下的结构&#xff0c;若只用到vue与js。则去掉typescript部分的包即可。 一、一般项目结构 assets&#xff1a;存放静态资源&#xff0c;如图片、字体、样…

BOC调制信号matlab性能仿真分析,对比功率谱,自相关性以及抗干扰性

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2022a 3.部分核心程序 &#xff08;完整版代码包含详细中文注释和操作步骤视频&#xff09…

电影院售票 - 策略模式(Strategy Pattern)

策略模式&#xff08;Strategy Pattern&#xff09; 策略模式&#xff08;Strategy Pattern&#xff09;策略模式概述策略模式结构图策略模式主要包含的角色 talk is cheap&#xff0c; show you my code总结 策略模式&#xff08;Strategy Pattern&#xff09; 策略模式&…

重学 Android 自定义 View 系列(十):带指针的渐变环形进度条

前言 该篇文章根据前面 重学 Android 自定义 View 系列(六)&#xff1a;环形进度条 拓展而来。 最终效果如下&#xff1a; 1. 扩展功能 支持进度顺时针或逆时针显示在进度条末尾添加自定义指针图片使用线性渐变为进度条添加颜色效果 2. 关键技术点解析 2.1 进度方向控制的…

【北京迅为】iTOP-4412全能版使用手册-第七十章 Linux内核移植

iTOP-4412全能版采用四核Cortex-A9&#xff0c;主频为1.4GHz-1.6GHz&#xff0c;配备S5M8767 电源管理&#xff0c;集成USB HUB,选用高品质板对板连接器稳定可靠&#xff0c;大厂生产&#xff0c;做工精良。接口一应俱全&#xff0c;开发更简单,搭载全网通4G、支持WIFI、蓝牙、…

CG顶会论文阅读|《科技论文写作》硕士课程报告

文章目录 一、基本信息1.1 论文基本信息1.2 课程基本信息1.3 博文基本信息 二、论文评述&#xff08;中英双语&#xff09;2.1 研究问题&#xff08;Research Problem&#xff09;2.2 创新点&#xff08;Innovation/Contribution&#xff09;2.3 优点&#xff08;Why this pape…

.NET周刊【12月第4期 2024-12-22】

国内文章 dotnet 简单使用 ICU 库进行分词和分行 https://www.cnblogs.com/lindexi/p/18622917 本文将和大家介绍如何使用 ICU 库进行文本的分词和分行。 dotnet 简单聊聊 Skia 里的 SKFontMetrics 的各项属性作用 https://www.cnblogs.com/lindexi/p/18621674 本文将和大…

git 问题解决记录

在用git上传文件到仓库中出现了2个问题 第一个问题&#xff1a; 需要修改git的代理端口与电脑自己的代理服务器设置中的端口和VPN的端口保持一致&#xff0c; 比如我的端口是7897&#xff0c;就设置 git config --global http.proxy http://127.0.0.1:7897 git config --glo…

XML结构快捷转JSON结构API集成指南

XML结构快捷转JSON结构API集成指南 引言 在当今的软件开发世界中&#xff0c;数据交换格式的选择对于系统的互操作性和效率至关重要。JSON&#xff08;JavaScript Object Notation&#xff09;和XML&#xff08;eXtensible Markup Language&#xff09;是两种广泛使用的数据表…

Oracle 创建本地用户,授予权限,创建表并插入数据

目录 一. 用户的种类二. 切换session为PDB三. 创建用户并授予权限四. 创建表空间五. 为用户分配默认表空间并指定表空间配额六. 通过创建的用户进行登录七. 创建脚本&#xff0c;简化登录八. 查看用户信息九. 创建表&#xff0c;并插入数据9.1 查看当前用户的schema9.2 插入数据…

系统设计——大文件传输方案设计

摘要 大文件传输是指通过网络将体积较大的文件从一个位置发送到另一个位置的过程。这些文件可能包括高清视频、大型数据库、复杂的软件安装包等&#xff0c;它们的大小通常超过几百兆字节&#xff08;MB&#xff09;甚至达到几个吉字节&#xff08;GB&#xff09;或更大。大文…

【老白学 Java】简单位移动画

简单位移动画 文章来源&#xff1a;《Head First Java》修炼感悟。 上一篇文章中&#xff0c;老白利用内部类的特性完成了多个事件的处理&#xff0c;感觉还不错。 为了更深入理解内部类&#xff0c;本篇文章继续使用内部类创建一个画板&#xff0c;完成简单的位移动画&#x…

彻底解决 Selenium ChromeDriver 不匹配问题:Selenium ChromeDriver 最新版本下载安装教程

在 Python 的 Selenium 自动化测试中&#xff0c;ChromeDriver 是不可或缺的工具。它作为代码与 Chrome 浏览器的桥梁&#xff0c;但如果版本不匹配&#xff0c;就会导致各种报错&#xff0c;尤其是以下常见问题&#xff1a; selenium.common.exceptions.SessionNotCreatedExc…

[CTF/网络安全] 攻防世界 warmup 解题详析

查看页面源代码&#xff0c;发现source.php 得到一串代码&#xff0c;进行代码审计&#xff1a; <?phpclass emmm{public static function checkFile(&$page){$whitelist ["source">"source.php","hint">"hint.php"];…