基于51单片机和16X16LED点阵屏(74HC138和74HC595驱动)的小游戏《贪吃蛇》

目录

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

系列文章目录


前言

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

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

所用单片机:STC89C52RC

有两个版本:普中开发板矩阵按键版本和最小系统板自制独立按键版本。

本文代码对应的是最小系统板自制独立按键版本。

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

一、效果展示

(1)普中开发板矩阵按键版本
在这里插入图片描述

(2)最小系统板自制独立按键版本
在这里插入图片描述

二、原理分析

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

这里主要说一下显示问题。

此16X16点阵屏模块采用2个74HC138和2个74HC595来驱动显示。74HC138选通某一行,送入2个字节数据到2个74HC595来控制这一行的显示。

芯片MAX7219内含自动扫描电路,驱动的点阵屏不会占用单片机的资源。

此点阵屏模块需要利用单片机的定时器来进行扫描显示,定时的时间太短,会频繁进中断函数,影响主函数的代码的执行,定时的时间太长,点阵屏又会出现闪烁的现象(总共有16行,每个LED灯接通的占空比为1/16)。所以需要找到一个平衡,既不会使点阵屏出现明显的闪烁,又不会影响主函数代码的执行。经过摸索,这个定时的时间大约是1ms多,效果不好的话可以继续调一下。

三、各模块代码

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;	//设置定时初值,定时1msTH0=0xFC;	//设置定时初值,定时1msTF0=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;	//设置定时初值,定时1msTH0=0xFC;	//设置定时初值,定时1msT0Count++;if(T0Count>=1000){T0Count=0;}
}
*/

2、自制八位独立按键

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;	//主程序中获取键码值之后键码值清零,在下一次定时器扫描按键之前再次获取键码值,一定会返回0return 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、点阵屏模块

h文件

#ifndef __MATRIXLED_16X16_74HC138_74HC595_H__
#define __MATRIXLED_16X16_74HC138_74HC595_H__void MatrixLED_Init(void);
void _74HC595_WriteByte(unsigned char Byte);
void _74HC595_StoreData(void);
void _74HC138_PickLine(unsigned char Line);
void MatrixLED_Tick(unsigned char Line,unsigned char State);
void MatrixLED_MoveLeft(unsigned char *Array,unsigned char Offset);
void MatrixLED_MoveUp(unsigned char *Array,unsigned char Offset);#endif

c文件

#include <REGX52.H>//引脚定义
sbit _74HC138_D=P2^7;	//高位
sbit _74HC138_C=P2^6;
sbit _74HC138_B=P2^5;
sbit _74HC138_A=P2^4;	//低位
sbit _74HC138_G=P2^3;	//使能端,低电平有效
sbit _74HC595_DI=P2^2;	//串行数据输入
sbit _74HC595_CLK=P2^1;	//移位寄存器时钟输入,上升沿有效
sbit _74HC595_LAT=P2^0;	//储存寄存器时钟输入,上升沿有效//取模设置要求:阳码(亮点为0),行列式取模,高位在右
unsigned char MatrixLEDBuffer[32]={	//MatrixLED缓存
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,	//默认无显示
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};/*** @brief	MatrixLED初始化* @param	无* @retval	无*/
void MatrixLED_Init(void)
{_74HC595_CLK=0;	//移位寄存器时钟信号初始化_74HC595_LAT=0;	//储存寄存器时钟信号初始化
}/*** @brief	74HC595写入字节* @param	Byte 需要写入的字节* @retval	无*/
void _74HC595_WriteByte(unsigned char Byte)
{unsigned char i;for(i=0;i<8;i++)	//循环8次(8位移位寄存器){_74HC595_DI=Byte&(0x80>>i);	//先高位后低位,先低位再高位也行,不过取模设置要更改,改成高位在右_74HC595_CLK=1;	//CLK上升沿时,DI口的数据(电平)写入移位寄存器_74HC595_CLK=0;}
}/*** @brief	74HC595存储数据* @param	无* @retval	无*/
void _74HC595_StoreData(void)
{_74HC595_LAT=1;	//LAT上升沿时,数据从移位寄存器转存储存寄存器_74HC595_LAT=0;
}/*** @brief	74HC138扫描显示MatrixLED* @param	Line 要显示的行,范围:0~15(对应1~16行)* @retval	无*/
void _74HC138_PickLine(unsigned char Line)
{switch(Line){case 0:_74HC138_D=0;_74HC138_C=0;_74HC138_B=0;_74HC138_A=0;break;case 1:_74HC138_D=0;_74HC138_C=0;_74HC138_B=0;_74HC138_A=1;break;case 2:_74HC138_D=0;_74HC138_C=0;_74HC138_B=1;_74HC138_A=0;break;case 3:_74HC138_D=0;_74HC138_C=0;_74HC138_B=1;_74HC138_A=1;break;case 4:_74HC138_D=0;_74HC138_C=1;_74HC138_B=0;_74HC138_A=0;break;case 5:_74HC138_D=0;_74HC138_C=1;_74HC138_B=0;_74HC138_A=1;break;case 6:_74HC138_D=0;_74HC138_C=1;_74HC138_B=1;_74HC138_A=0;break;case 7:_74HC138_D=0;_74HC138_C=1;_74HC138_B=1;_74HC138_A=1;break;case 8:_74HC138_D=1;_74HC138_C=0;_74HC138_B=0;_74HC138_A=0;break;case 9:_74HC138_D=1;_74HC138_C=0;_74HC138_B=0;_74HC138_A=1;break;case 10:_74HC138_D=1;_74HC138_C=0;_74HC138_B=1;_74HC138_A=0;break;case 11:_74HC138_D=1;_74HC138_C=0;_74HC138_B=1;_74HC138_A=1;break;case 12:_74HC138_D=1;_74HC138_C=1;_74HC138_B=0;_74HC138_A=0;break;case 13:_74HC138_D=1;_74HC138_C=1;_74HC138_B=0;_74HC138_A=1;break;case 14:_74HC138_D=1;_74HC138_C=1;_74HC138_B=1;_74HC138_A=0;break;case 15:_74HC138_D=1;_74HC138_C=1;_74HC138_B=1;_74HC138_A=1;break;default:break;}
}/*** @brief  点阵屏驱动函数,定时器中断函数中使用* @param  Line 要显示的行,范围:0~15(对应1~16行)* @param  State 控制屏幕显不显示,范围:0~1,0:屏幕不显示,1:屏幕显示* @retval 无*/
void MatrixLED_Tick(unsigned char Line,unsigned char State)
{_74HC138_G=1;if(State==1){_74HC595_WriteByte(MatrixLEDBuffer[16+Line]);_74HC595_WriteByte(MatrixLEDBuffer[Line]);}else{_74HC595_WriteByte(0xFF);_74HC595_WriteByte(0xFF);}_74HC595_StoreData();_74HC138_PickLine(Line);_74HC138_G=0;
}/*** @brief  更新点阵屏的显示* @param  Array 传递过来的数组的指针(地址),16*16汉字或16*16数字取模要求:阳码(亮点为0),行列式取模,高位在右* @param  Offset 显示数组数据的偏移量,向左偏移Offset个像素* @retval 无*/
void MatrixLED_MoveLeft(unsigned char *Array,unsigned char Offset)
{unsigned char i,m,n;m=Offset/8;n=Offset%8;Array+=m*16;for(i=0;i<32;i++){MatrixLEDBuffer[i]=(*Array>>n)|(*(Array+16)<<(8-n));	//将偏移后的数据保存到点阵屏缓存中(高位在右)Array++;}
}/*** @brief  更新点阵屏的显示* @param  Array 传递过来的数组的指针(地址),16*16汉字或16*16数字取模要求:阳码(亮点为0),行列式取模,高位在右* @param  Offset 显示数组数据的偏移量,向上偏移Offset个像素* @retval 无*/
void MatrixLED_MoveUp(unsigned char *Array,unsigned char Offset)
{unsigned char i,m,n;m=Offset/16;n=Offset%16;Array+=m*32;Array+=n;for(i=0;i<16;i++){if(i<16-n){MatrixLEDBuffer[i]=*(Array);MatrixLEDBuffer[i+16]=*(Array+16);}else{MatrixLEDBuffer[i]=*(Array+16);MatrixLEDBuffer[i+16]=*(Array+32);}Array++;}
}

四、主函数

main.c

/*
by甘腾胜@20241219
效果查看/操作演示:可以在B站搜索“甘腾胜”或“gantengsheng”查看
单片机:STC89C52RC
晶振:12T@12.0000MHz
外设:自制独立按键、16X16LED点阵屏(74HC138和74HC595驱动)
原理分析:https://blog.csdn.net/gantengsheng/article/details/143581157
注意:经过测试,接P0口会导致显示不正常,原因可能是P0口的上拉电阻太大,导致上拉的速度太慢,所以接了P2口操作说明:(1)自制独立按键版本K7				K2				上:K7下:K6K8		K5		K4		K1          左:K8右:K5K6				K3              开始/暂停/继续:K1返回:K2(2)普中开发板矩阵按键版本S1		S2		S3		S4			上:S10				下:S14		S5		S6		S7		S8      	左:S13右:S15S9		S10		S11		S12     	开始/暂停/继续:S16返回:S12S13		S14		S15		S16     */#include <REGX52.H>	//包含寄存器的定义
#include <STDLIB.H>	//包含随机函数的声明
#include "Timer0.h"	//包含工程目录下的头文件,相当于把头文件内容插入此处
#include "KeyScan_8.h"
#include "MatrixLED_16X16_74HC138_74HC595.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 int SnakeMoveSpeed=1000;	//蛇移动的速度,值越小,速度越快,上电默认1.00s移动一次,定时器定时1ms
unsigned int T0Count0,T0Count1,T0Count2,T0Count3,T0Count4;	//定时器计数的变量
unsigned char PauseFlag;	//暂停的标志,1:暂停,0:不暂停unsigned char pdata SnakeBody[256];	//点阵屏是16X16=256像素,需要用256个数据记录蛇身的数据//用pdata修饰是因为片内RAM不够用,所以保存到片外RAM
unsigned char DisplayBuffer[32];	//显示缓存,一个字节对应8个点,总共256个点,所以需要32个字节//阳码(亮点为0),行列式取模,高位在右//取模设置:阳码(亮点为0),行列式取模,高位在右(所有取模都必须按这个格式,否则需要修改函数)
unsigned char code Table1[]={	// “《贪吃蛇》”
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,/*"  ", */
0xFF,0xFF,0xFF,0xFF,0x7F,0xBF,0xDF,0x6F,0xDF,0xBF,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,
0xF7,0xDB,0xED,0xF6,0xFB,0xFD,0xFE,0xFF,0xFE,0xFD,0xFB,0xF6,0xED,0xDB,0xF7,0xFF,/*"《",0*/
0x7F,0xBF,0xDF,0x6F,0xF3,0x0C,0xFF,0xFF,0x07,0xF7,0x77,0x77,0x77,0xBF,0xCF,0xF1,
0xFF,0xFE,0xFD,0xFB,0xE6,0x98,0xFD,0xFE,0xF0,0xF7,0xF7,0xF7,0xF7,0xF9,0xE7,0xDF,/*"贪",1*/
0xFF,0xFF,0x61,0x6D,0xAD,0xCD,0x6D,0xED,0xED,0xED,0xE1,0x6D,0xBF,0xBF,0x7F,0xFF,
0xFE,0xFE,0xFF,0x80,0xFF,0xFF,0xE0,0xEF,0xF7,0xF9,0xFE,0xFF,0xBF,0xBF,0x80,0xFF,/*"吃",2*/
0xF7,0xF7,0xF7,0x41,0x55,0x95,0xD5,0xD5,0xC1,0xF5,0xF7,0xD7,0x87,0xB8,0xFD,0xFF,
0xFB,0xF7,0xF7,0x80,0xBF,0xDF,0xFE,0xEE,0xF6,0xFA,0xFC,0xBE,0xBE,0xBE,0x81,0xFF,/*"蛇",3*/
0xEF,0xDB,0xB7,0x6F,0xDF,0xBF,0x7F,0xFF,0x7F,0xBF,0xDF,0x6F,0xB7,0xDB,0xEF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFE,0xFD,0xFB,0xF6,0xFB,0xFD,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,/*"》",4*/
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,/*"  ", */
};unsigned char code Table2[]={	// “ 难度选择 1”
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,/*"  ", */
0xFF,0xFF,0xC0,0x5F,0x5F,0x2D,0x4B,0x6B,0x77,0x77,0x6B,0x5B,0x5D,0x7E,0x7F,0x7F,
0xFA,0xF6,0xFE,0x80,0xF7,0xF7,0xC0,0xF7,0xF7,0xC0,0xF7,0xF7,0xF7,0x80,0xFF,0xFF,/*"难",0*/
0x7F,0xFF,0x03,0xBB,0xBB,0x03,0xBB,0xBB,0x3B,0xFB,0x0B,0xDB,0xBD,0x7D,0x9E,0xE3,
0xFF,0xFE,0x80,0xFB,0xFB,0xC0,0xFB,0xFB,0xF8,0xFF,0xF0,0xF7,0xFB,0xFC,0xF3,0x8F,/*"度",1*/
0xFF,0xBB,0xB7,0x37,0xDF,0xFF,0x10,0x77,0x77,0x77,0xB7,0xB7,0xD7,0xEB,0x1D,0xFF,
0xFD,0xFD,0xFD,0xE0,0xFD,0xFD,0xC0,0xFB,0xFB,0xFB,0xDB,0xDB,0xC7,0xFF,0x80,0xFF,/*"选",2*/
0xFB,0x1B,0xBB,0x7B,0xF0,0xFB,0x7B,0x9B,0xF3,0x38,0xFB,0xFB,0x1B,0xFB,0xFA,0xFD,
0xFF,0xE0,0xEF,0xF7,0xFA,0xFD,0xF2,0x8D,0xFD,0xE0,0xFD,0xFD,0xC0,0xFD,0xFD,0xFD,/*"择",3*/
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,/*"  ", */
0xFF,0xFF,0xFF,0xFF,0x1F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x0F,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xE0,0xFF,0xFF,/*"1",0*/
};unsigned char code Table3[]={	//难度对应的数字:“1~5”,最后两行数据与前两行数据相同,是为了做成循环显示的效果0xFF,0xFF,0xFF,0xFF,0x1F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x0F,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xE0,0xFF,0xFF,/*"1",0*/
0xFF,0xFF,0xFF,0x0F,0xF3,0xE3,0xF7,0xFF,0xFF,0x7F,0x9F,0xEF,0xF3,0x03,0xFF,0xFF,
0xFF,0xFF,0xFF,0xF0,0xE7,0xCF,0xE7,0xE7,0xF9,0xFE,0xFF,0xDF,0xCF,0xE0,0xFF,0xFF,/*"2",1*/
0xFF,0xFF,0xFF,0x0F,0xF3,0xE3,0xFF,0xFF,0x7F,0xFF,0xFF,0xE3,0xF3,0x0F,0xFF,0xFF,
0xFF,0xFF,0xFF,0xF8,0xE7,0xE7,0xE7,0xF9,0xF0,0xE7,0xCF,0xCF,0xE7,0xF8,0xFF,0xFF,/*"3",2*/
0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xBF,0xCF,0xF7,0xFB,0x01,0xFF,0xFF,0x3F,0xFF,0xFF,
0xFF,0xFF,0xFF,0xF3,0xF0,0xF1,0xF1,0xF1,0xF1,0xF1,0x80,0xF1,0xF1,0x80,0xFF,0xFF,/*"4",3*/
0xFF,0xFF,0xFF,0x07,0xF7,0xF7,0xF7,0x17,0xE7,0xFF,0xFF,0xE3,0xF3,0x0F,0xFF,0xFF,
0xFF,0xFF,0xFF,0xE0,0xFF,0xFF,0xFF,0xF0,0xE7,0xCF,0xCF,0xCF,0xE7,0xF8,0xFF,0xFF,/*"5",4*/
0xFF,0xFF,0xFF,0xFF,0x1F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x0F,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xE0,0xFF,0xFF,/*"1",0*/
};unsigned char code Table5[]={	// “得分”
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,/*"  ", */
0xEF,0x2F,0xB7,0x3B,0xAD,0x2F,0xF7,0x33,0xF5,0x16,0xF7,0xB7,0x77,0xF7,0xF7,0xF7,
0xFF,0xE0,0xEF,0xE0,0xEF,0xE0,0xFF,0xC0,0xF7,0x80,0xF7,0xF7,0xF7,0xF7,0xF5,0xFB,/*"得",0*/
0xFF,0xDF,0xDF,0xEF,0xF7,0xFB,0xFD,0x06,0xDF,0xDF,0xDF,0xEF,0xEF,0xF7,0x7B,0xFD,
0xFD,0xFD,0xFB,0xFB,0xF7,0xEF,0xDF,0xB8,0xFB,0xFB,0xFB,0xFB,0xFB,0xFB,0xFD,0xFE,/*"分",1*/
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,/*"  ", */
};unsigned char code Table6[]={	//分数字模:0~9,大小:宽8*高16
0xFF,0xFF,0xFF,0xE7,0xDB,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xDB,0xE7,0xFF,0xFF,/*"0",0*/
0xFF,0xFF,0xFF,0xEF,0xE3,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0x83,0xFF,0xFF,/*"1",1*/
0xFF,0xFF,0xFF,0xC3,0xBD,0xBD,0xBD,0xBF,0xDF,0xEF,0xF7,0xFB,0xBD,0x81,0xFF,0xFF,/*"2",2*/
0xFF,0xFF,0xFF,0xC3,0xBD,0xBD,0xBF,0xDF,0xE7,0xDF,0xBF,0xBD,0xBD,0xC3,0xFF,0xFF,/*"3",3*/
0xFF,0xFF,0xFF,0xDF,0xCF,0xCF,0xD7,0xDB,0xDB,0xDD,0x01,0xDF,0xDF,0x07,0xFF,0xFF,/*"4",4*/
0xFF,0xFF,0xFF,0x81,0xFD,0xFD,0xFD,0xE1,0xDD,0xBF,0xBF,0xBD,0xDD,0xE3,0xFF,0xFF,/*"5",5*/
0xFF,0xFF,0xFF,0xE7,0xDB,0xFD,0xFD,0xC5,0xB9,0xBD,0xBD,0xBD,0xBB,0xC7,0xFF,0xFF,/*"6",6*/
0xFF,0xFF,0xFF,0x81,0xBD,0xDF,0xDF,0xEF,0xEF,0xF7,0xF7,0xF7,0xF7,0xF7,0xFF,0xFF,/*"7",7*/
0xFF,0xFF,0xFF,0xC3,0xBD,0xBD,0xBD,0xDB,0xE7,0xDB,0xBD,0xBD,0xBD,0xC3,0xFF,0xFF,/*"8",8*/
0xFF,0xFF,0xFF,0xE3,0xDD,0xBD,0xBD,0xBD,0x9D,0xA3,0xBF,0xBF,0xDB,0xE7,0xFF,0xFF,/*"9",9*/
};unsigned char code Table7[]={	// “ by甘腾胜at20241202 ”
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,/*"  ", */
0xFF,0xFF,0xFF,0xFF,0xFC,0xFD,0xFD,0xE5,0xD9,0xBD,0xBD,0xBD,0xD9,0xE5,0xFF,0xFF,/*"b",0*/
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x18,0xBD,0xDB,0xDB,0xE7,0xE7,0xF7,0xF7,0xF9,/*"y",1*/
0xEF,0xEF,0xEF,0xEF,0x01,0xEF,0xEF,0xEF,0xEF,0x0F,0xEF,0xEF,0xEF,0xEF,0x0F,0xEF,
0xF7,0xF7,0xF7,0xF7,0x80,0xF7,0xF7,0xF7,0xF7,0xF0,0xF7,0xF7,0xF7,0xF7,0xF0,0xF7,/*"甘",2*/
0xBF,0x61,0x6D,0x0D,0xED,0x01,0x6D,0xAD,0x4D,0xE1,0x6D,0x6D,0xED,0x2D,0xED,0xE6,
0xED,0xED,0xF5,0xC0,0xFD,0x80,0xF7,0xEF,0x90,0xF7,0xF7,0xC0,0xDF,0xD8,0xD7,0xEF,/*"腾",3*/
0xFF,0xE1,0x6D,0x6D,0x6D,0x61,0xAD,0xED,0xED,0x61,0xED,0xED,0xED,0xED,0x2D,0xE6,
0xFB,0xFB,0xFB,0xFB,0xC0,0xFB,0xFB,0xFB,0xFB,0xC0,0xFB,0xFB,0xFB,0xFB,0x80,0xFF,/*"胜",4*/
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE3,0xDD,0xCF,0xD3,0xDD,0xCD,0x93,0xFF,0xFF,/*"a",5*/
0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xF7,0xC1,0xF7,0xF7,0xF7,0xF7,0xB7,0xCF,0xFF,0xFF,/*"t",6*/
0xFF,0xFF,0xFF,0xC3,0xBD,0xBD,0xBD,0xBF,0xDF,0xEF,0xF7,0xFB,0xBD,0x81,0xFF,0xFF,/*"2",7*/
0xFF,0xFF,0xFF,0xE7,0xDB,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xDB,0xE7,0xFF,0xFF,/*"0",8*/
0xFF,0xFF,0xFF,0xC3,0xBD,0xBD,0xBD,0xBF,0xDF,0xEF,0xF7,0xFB,0xBD,0x81,0xFF,0xFF,/*"2",9*/
0xFF,0xFF,0xFF,0xDF,0xCF,0xCF,0xD7,0xDB,0xDB,0xDD,0x01,0xDF,0xDF,0x07,0xFF,0xFF,/*"4",10*/
0xFF,0xFF,0xFF,0xEF,0xE3,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0x83,0xFF,0xFF,/*"1",11*/
0xFF,0xFF,0xFF,0xC3,0xBD,0xBD,0xBD,0xBF,0xDF,0xEF,0xF7,0xFB,0xBD,0x81,0xFF,0xFF,/*"2",12*/
0xFF,0xFF,0xFF,0xEF,0xE3,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0x83,0xFF,0xFF,/*"1",13*/
0xFF,0xFF,0xFF,0xE3,0xDD,0xBD,0xBD,0xBD,0x9D,0xA3,0xBF,0xBF,0xDB,0xE7,0xFF,0xFF,/*"9",14*/
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,/*"  ", */
};/*** @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[(n+j)%16+(m+i)%16/8*16] & (0x01<<(m+i)%8)) ){FoodTemp=(m+i)%16*16+(n+j)%16;break;	//找到了空位置就退出循环}}}return FoodTemp;
}/*** @brief  更新显示缓存中的数据,例如:想要点亮第一行第十列的点,就需要让数组DisplayBuffer的* @brief  第17个数据(索引为16)的B1位(一个字节从最低位到最高位分别为B0~B7)变为0(阳码,亮点为0)* @brief  注意,更新了数组DisplayBuffer的数据后,还需要配合滚屏函数MatrixLED_MoveLeft更新屏幕的显示* @param  Position,要更新的位置,范围:0~255,高四位(0~15)对应1~16列(从左往右数,分别是1~16列),* @param                                       低四位(0~15)对应1~16行(从上往下数,分别是1~16行)* @param  State,要更新成的状态,范围:0~1,1:点亮,0:熄灭* @retval 无*/
void ConvertData(unsigned char Position,unsigned char State)
{if(State==1){DisplayBuffer[Position%16+Position/16/8*16] &= ~(0x01<<(Position/16%8));}else{DisplayBuffer[Position%16+Position/16/8*16] |= 0x01<<(Position/16%8);}
}/*** @brief  控制蛇的移动* @param  无* @retval 无*/
void MoveSnake(void)
{if(NowDirection==1)	//如果向右移动{//移动前判断一下移动后是否撞墙,如果是,则游戏结束,游戏结束的标志置1if((SnakeBody[Head]/16)==15){GameOverFlag=1;}//SnakeBody数组中蛇头的下一个数据等于上一个数据加16(即高四位加1),即蛇头移动到了右边这一列else{SnakeBody[(Head+1)%256]=SnakeBody[Head]+16;}}if(NowDirection==2)	//如果向上移动{if((SnakeBody[Head]%16)==0){GameOverFlag=1;}//SnakeBody数组中蛇头的下一个数据等于上一个数据减1(即低四位减1),即蛇头移动到了上边这一行else{SnakeBody[(Head+1)%256]=SnakeBody[Head]-1;}}if(NowDirection==3)	//如果向左移动{if((SnakeBody[Head]/16)==0){GameOverFlag=1;}//SnakeBody数组中蛇头的下一个数据等于上一个数据减16(即高四位减1),即蛇头移动到了左边这一列else{SnakeBody[(Head+1)%256]=SnakeBody[Head]-16;}}if(NowDirection==4)	//如果向下移动{if((SnakeBody[Head]%16)==15){GameOverFlag=1;}//SnakeBody数组中蛇头的下一个数据等于上一个数据加1(即低四位加1),即蛇头移动到了下边这一行else{SnakeBody[(Head+1)%256]=SnakeBody[Head]+1;}}if(GameOverFlag==0)	//如果没撞墙{if(SnakeBody[(Head+1)%256]==Food)	//判断蛇头移动后的位置是否是食物所在的位置{	//如果是Length++;	//蛇身长度加1ConvertData(Food,1);	//防止食物闪烁刚好不显示的时候进入此函数,导致蛇身一个点不显示Food=CreateFood();	//重新创造一个食物ConvertData(Food,1);	//先更新显示缓存,在主循环中再更新屏幕的显示FlashFlag=0;	//创造出新的食物时,食物暂不闪烁T0Count2=0;	//定时器T0Count2重新计数}else if( !( (DisplayBuffer[SnakeBody[(Head+1)%256]%16+SnakeBody[(Head+1)%256]/16/8*16]) & (0x01<<SnakeBody[(Head+1)%256]/16%8) ) ){	//如果蛇头移动后的位置撞在蛇身上,则游戏结束GameOverFlag=1;	//游戏结束的标志置1}else	//如果蛇头移动后的位置不是食物,也不是撞墙,也不是撞到蛇身的话{//显示缓存数组DisplayBuffer中蛇头前进后蛇头的新位置对应的位更新数值ConvertData(SnakeBody[(Head+1)%256],1);//显示缓存数组DisplayBuffer中蛇尾的位置对应的位清0(蛇身移动,如果没有吃到食物,相当于蛇尾对应的点跑到了//蛇头的前一个点,变成了蛇头,原来的蛇头变成蛇身)整条蛇中间的数据不用操作ConvertData(SnakeBody[(Head+256-Length+1)%256],0);//数组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数组中,蛇头对应的数据的索引加1Head%=256;	//蛇头变量Head是无符号字符型数据(范围是0~255,值为255后自增会自动变为0),所以这一行可以省略//如果Head是整型(int),则需要有这一行
}void main()
{unsigned int i;unsigned char Count=0;	Timer0_Init();	//定时器初始化MatrixLED_Init();	//点阵屏初始化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)	//如果按了开始键(松手瞬间),则开始游戏{Mode=3;	//切换到游戏模式ExecuteOnceFlag=1;}}if(KeyNum<=24 && KeyNum>=17)	//如果按下任意按键(松手瞬间){//两个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(定时器中每隔一段时间将此标志置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}if(RollFlag && RollUpFlag)	//如果滚动标志为1,且向上滚动的标志也为1{RollFlag=0;	//定时器中每个50ms将RollFlag标志置1Offset2++;	//向上移动一个像素Offset2%=80;	//越界清零,总共5个数字,每个数字的高度是16,所以是5*16=80MatrixLED_MoveUp(Table3,Offset2);	//更新显示RollCount++;if(RollCount==16)	//移动了16个像素后停止移动{RollCount=0;RollUpFlag=0;Offset2=(Offset2/16)*16;	//防止移动到一半的时候按下“上”或“下”按键导致数字没有在点阵屏中间//Offset2的值必须是16的整数倍switch(Offset2/16){case 0:SnakeMoveSpeed=1000;break;	//难度1,1.00s移动1次case 1:SnakeMoveSpeed=750;break;	//难度2,0.75s移动1次case 2:SnakeMoveSpeed=500;break;	//难度3,0.50s移动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=80;}Offset2--;MatrixLED_MoveUp(Table3,Offset2);RollCount++;if(RollCount==16){RollCount=0;RollDownFlag=0;Offset2=(Offset2/16)*16;switch(Offset2/16){case 0:SnakeMoveSpeed=1000;break;	//难度1,1.00s移动1次case 1:SnakeMoveSpeed=750;break;	//难度2,0.75s移动1次case 2:SnakeMoveSpeed=500;break;	//难度3,0.50s移动1次case 3:SnakeMoveSpeed=250;break;	//难度4,0.25s移动1次case 4:SnakeMoveSpeed=120;break;	//难度5,0.12s移动1次default:break;}}}}if(Mode==3)	//如果是游戏进行模式{if(ExecuteOnceFlag)	//切换到该模式后,此if中的内容只执行1次{	//开始游戏后,所有数据初始化ExecuteOnceFlag=0;GameOverFlag=0;	//游戏结束标志清零PauseFlag=0;	//游戏暂停标志清零NowDirection=1;	//蛇头默认向右移动LastDirection=1;	//上一次蛇头默认向右移动Length=2;	//蛇的初始长度为2Head=1;	//蛇头对应数组中的第2个数据(索引为1)
//				for(i=0;i<256;i++)	//蛇身数据全部清零(其实可以不用清零)
//				{
//					SnakeBody[i]=0;
//				}//写入蛇身初始的两个数据SnakeBody[0]=1*16+1;	//蛇尾位置:第二行第二列SnakeBody[1]=2*16+1;	//蛇头位置:第二行第三列for(i=0;i<32;i++)	//显示缓存数据全部清空(阳码:亮点为0,所以所有字节的每一位置1){DisplayBuffer[i]=0xFF;}DisplayBuffer[1]=0xF9;	//显示缓存数据全部清空后,写入蛇身初始的两个数据(阳码:亮点为0)Food=CreateFood();	//进入游戏前,先创造出一个食物ConvertData(Food,1);	//更新显示缓存MatrixLED_MoveLeft(DisplayBuffer,0);	//屏幕显示初始的蛇身及食物MoveSnakeFlag=0;	//蛇移动的标志清零T0Count1=0;	//定时器计数变量T0Count1清零,重新计数}if(PauseFlag)	//如果暂停了{ConvertData(Food,1);	//食物不闪烁,一直显示MatrixLED_MoveLeft(DisplayBuffer,0);	//更新显示}			else if(FlashFlag)	//如果不暂停,且闪烁标志为1{ConvertData(Food,0);	//不显示食物MatrixLED_MoveLeft(DisplayBuffer,0);}else	//如果不暂停,且闪烁标志为0{ConvertData(Food,1);	//显示食物MatrixLED_MoveLeft(DisplayBuffer,0);}if(MoveSnakeFlag && GameOverFlag==0 && PauseFlag==0){	//如果移动的标志为1,且不暂停,且游戏也没结束LastDirection=NowDirection;	//保存上一次移动的方向,用于按键的判断(蛇不能往后移动)MoveSnakeFlag=0;	//移动标志清零MoveSnake();	//移动一次MatrixLED_MoveLeft(DisplayBuffer,0);	//MoveSnake函数中更改显示缓存的数据,这里用移屏函数更新屏幕显示}if(GameOverFlag==1)	//如果游戏结束{ConvertData(Food,1);MatrixLED_MoveLeft(DisplayBuffer,0);Mode=4;	//切换到全屏闪烁模式ExecuteOnceFlag=1;}}if(Mode==4)	//如果是游戏结束全屏闪烁模式{//在中断中处理}if(Mode==5)	//显示汉字“得分”{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;	//偏移量清零ExecuteOnceFlag=1;}	}if(Mode==6)	//如果是滚动显示得分模式{if(ExecuteOnceFlag){ExecuteOnceFlag=0;for(i=0;i<112;i++)	//蛇身数据部分清零(SnakeBody前160个用来循环显示三位数得分){SnakeBody[i]=0xFF;}//将得分(即蛇身的长度)的百位、十位、个位的字模写入数组SnakeBody中for(i=0;i<16;i++){SnakeBody[32+i]=Table6[Length/100*16+i];}for(i=0;i<16;i++){SnakeBody[48+i]=Table6[Length/10%10*16+i];}for(i=0;i<16;i++){SnakeBody[64+i]=Table6[Length%10*16+i];}PauseFlag=0;}if(RollFlag)	//如果滚动的标志为1{RollFlag=0;MatrixLED_MoveLeft(SnakeBody,Offset1);Offset1++;Offset1%=40;	//循环滚动}}if(Mode==7)	//如果是显示作者姓名和编程时间的模式{if(RollFlag)	//如果滚动的标志为1{RollFlag=0;MatrixLED_MoveLeft(Table7,Offset1);Offset1++;Offset1%=160;	//循环滚动}}}
}void Timer0_Routine() interrupt 1	//定时器0中断函数
{static unsigned char Line;TL0=0x18;	//设置定时初值,定时1ms,晶振@12.0000MHzTH0=0xFC;	//设置定时初值,定时1ms,晶振@12.0000MHzT0Count3++;T0Count4++;if(T0Count0>=20)	//20ms扫描一次按键{T0Count0=0;Key_Tick();}T0Count0++;if(PauseFlag==0)	//不暂停时,T0Count1和T0Count2才计数{T0Count1++;T0Count2++;}if(T0Count1>=SnakeMoveSpeed)	//用来控制蛇移动的速度{T0Count1=0;MoveSnakeFlag=1;}if(T0Count2>=250)	//0.25s取反闪烁标志FlashFlag的值{T0Count2=0;FlashFlag=!FlashFlag;}if(T0Count3>=50)	//控制滚动的速度,50ms滚动一次{T0Count3=0;RollFlag=1;}if(T0Count4>=1){T0Count4=0;if(Mode==4 && FlashFlag){MatrixLED_Tick(Line,0);}	//游戏结束,全屏闪烁else{MatrixLED_Tick(Line,1);}		Line++;if(Line>15){Line=0;}	//总共要扫描16行,Line的范围是:0~15}
}

总结

这个点阵屏模块比较占用单片机的资源,如果用速度较快的单片机则影响不大,如果是用89系列的单片机会感觉影响比较大,建议用MAX7219驱动的点阵屏,很好用。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Qt] Qt介绍 | 搭建SDK

目录 1. Qt 简介 什么是 Qt&#xff1f; 1.1 引入 1.2 GUI 1.3 Qt 介绍 2. Qt 发展史 3. Qt 支持的平台 4. Qt 版本信息 5. Qt 的优点 6. Qt 应用场景 7. Qt 成功案例 8. Qt 发展前景及就业分析 二. Qt 开发环境搭建 1. 开发工具概述 2.Qt SDK 安装 3.使用 1. …

mysql连接时报错1130-Host ‘hostname‘ is not allowed to connect to this MySQL server

不在mysql服务器上通过ip连接服务提示1130错误怎么回事呢。这个错误是因为在数据库服务器中的mysql数据库中的user的表中没有权限。 解决方案 查询mysql库的user表指定账户的连接方式 SELECT user, host FROM mysql.user;修改指定账户的host连接方式 update mysql.user se…

Elasticsearch: 高级搜索

这里写目录标题 一、match_all匹配所有文档1、介绍&#xff1a; 二、精确匹配1、term单字段精确匹配查询2、terms多字段精确匹配3、range范围查询4、exists是否存在查询5、ids根据一组id查询6、prefix前缀匹配7、wildcard通配符匹配8、fuzzy支持编辑距离的模糊查询9、regexp正则…

把vue项目或者vue组件发布成npm包或者打包成lib库文件本地使用

将vue项目发布成npm库文件&#xff0c;第三方通过npm依赖安装使用&#xff1b;使用最近公司接了一个项目&#xff0c;这个项目需要集成到第三方页面&#xff0c;在第三方页面点击项目名称&#xff0c;页面变成我们的项目页面&#xff1b;要求以npm库文件提供给他们&#xff1b;…

实现一个通用的树形结构构建工具

文章目录 1. 前言2. 树结构3. 具体实现逻辑3.1 TreeNode3.2 TreeUtils3.3 例子 4. 小结 1. 前言 树结构的生成在项目中应该都比较常见&#xff0c;比如部门结构树的生成&#xff0c;目录结构树的生成&#xff0c;但是大家有没有想过&#xff0c;如果在一个项目中有多个树结构&…

【新教程】华为昇腾NPU的pytorch环境搭建

1 硬件配置 使用学校的集群&#xff0c;相关配置如下&#xff1a; CPU&#xff1a;鲲鹏920 NPU&#xff1a;昇腾910B 操作系统&#xff1a;openEuler 22.03 2 安装版本 根据昇腾官方gitee上的信息&#xff0c;Pytoch 2.1.0是长期支持版本&#xff0c;因此选择安装这一版本&a…

在Ubuntu 18.04.6 LTS安装OpenFace流程

一、修改配置:将gcc8&#xff0c;g8作为默认选项 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 100 sudo update-alternatives --config gcc 选择版本&#xff0c;再查看gcc --version sudo update-alternatives --install /usr/bin/g g /usr/bin/g-…

typescript安装后仍然不能使用tsc,如何解决

1.全局安装 npm i typescript -g 2.发现仍然不行 解决方法&#xff1a; C:\Users\你的用户名\AppData\Roaming\npm解决办法&#xff1a; 1.确定对应的文件下载了 我们发现typescript是下载了的 2.设置环境变量的path 路径为typescript下的npm 3.cmd运行

硬件-射频-PCB-常见天线分类-ESP32实例

文章目录 一&#xff1a;常见天线1.1 PCB天线①蓝牙模块的蛇形走线-天线②倒F天线-IFA&#xff1a;③蛇形倒F天线-MIFA④立体的倒F天线-PIFA 1.2 实例示意图1.21 对数周期天线(LPDA):1.22 2.4GHZ的八木天线&#xff1a;1.23 陶瓷天线&#xff1a;1.24 外接天线&#xff1a; 二&…

PCA降维算法详细推导

关于一个小小的PCA的推导 文章目录 关于一个小小的PCA的推导1 谱分解 (spectral decomposition)2 奇异矩阵(singular matrix)3 酉相似(unitary similarity)4 酉矩阵5 共轭变换6 酉等价7 矩阵的迹的计算以及PCA算法推导8 幂等矩阵(idempotent matrix)9 Von Neumanns 迹不等式 [w…

25.1.3

java数组&#xff1a; dataType[] arrayRefVar //推荐写法 //int[] mylist //或 dataType arrayRefVar[] //int mylist[]创建数组对象&#xff1a; arrayRefVar new dataType[arraySize]; dataType[] arrayRefVar new dataType[arraySize];for-each循环&#xff1a; jav…

音频进阶学习九——离散时间傅里叶变换DTFT

文章目录 前言一、DTFT的解释1.DTFT公式2.DTFT右边释义1&#xff09; 复指数 e − j ω n e^{-j\omega n} e−jωn2&#xff09;序列与复指数相乘 x [ n ] ∗ e − j ω n x[n]*e^{-j\omega n} x[n]∗e−jωn复指数序列复数的共轭正交正交集 3&#xff09;复指数序列求和 3.DTF…

【保姆级】sql注入之堆叠注入

一、堆叠注入的原理 mysql数据库sql语句的默认结束符是以";"号结尾&#xff0c;在执行多条sql语句时就要使用结束符隔 开,而堆叠注入其实就是通过结束符来执行多条sql语句 比如我们在mysql的命令行界面执行一条查询语句,这时语句的结尾必须加上分号结束 select * fr…

我的桌面 1.9.75 | 个性化定制手机桌面,丰富的小组件和主题

我的桌面iScreen是一款万能桌面小组件APP&#xff0c;提供各种高颜值桌面主题与创意小组件自由组合。支持X面板、照片、待办清单、时钟、日历等实用有趣的小组件。拥有超过500种小组件供选择&#xff0c;包括灵动面板、滚动相册等&#xff0c;搭配300多种精美主题和高清壁纸&am…

汽车燃油软件标定测试

油箱测试 确定油箱的参数&#xff1a; 总容积&#xff0c;额定容积&#xff0c;不可用容积等。油泵测试&#xff08;静态&#xff09; 分为加油测试&#xff0c;减油测试&#xff0c;1L或者500ml增减&#xff1b; 分别测试油泵的阻值输出&#xff0c;类似&#xff1a; 油量 阻…

07-ArcGIS For JavaScript--隐藏参数qualitySettings(memory和lod控制)

目录 1、综述2、sceneview.qualitySettings2.1、sceneview.qualitySettings.memoryLimit2.2、lodFactor2.3 additionalCacheMemory 3、结论 1、综述 先上重点&#xff0c;SceneView.qualitySettings为隐藏对象参数&#xff0c;该对象的memoryLimit和lodFactor等值&#xff0c;…

信息科技伦理与道德1:研究方法

1 问题描述 1.1 讨论&#xff1f; 请挑一项信息技术&#xff0c;谈一谈为什么认为他是道德的/不道德的&#xff0c;或者根据使用场景才能判断是否道德。判断的依据是什么&#xff08;自身的道德准则&#xff09;&#xff1f;为什么你觉得你的道德准则是合理的&#xff0c;其他…

交换机关于环路、接口绑定、链路聚合的相关知识

文章目录 1、对交换机SW-1进行配置&#xff0c;仅允许Host-1通过Ethernet0/0/1接口与Host-3和Host-4通信&#xff0c;Host-2无法与其他主机通信。2、关闭生成树协议&#xff0c;验证环路造成的影响3、关闭生成树协议通过链路聚合实现两条链路正常通信并提高链路可靠性。 内容包…

QEMU网络配置简介

本文简单介绍下qemu虚拟机网络的几种配置方式。 通过QEMU的支持&#xff0c;常见的可以实现以下4种网络形式&#xff1a; 基于网桥&#xff08;bridge&#xff09;的虚拟网络。基于NAT&#xff08;Network Addresss Translation&#xff09;的虚拟网络。QEMU内置的用户模式网…

(二)当人工智能是一个函数,函数形式怎么选择?ChatGPT的函数又是什么?

在上一篇文章中&#xff0c;我们通过二次函数的例子&#xff0c;讲解了如何训练人工智能。今天&#xff0c;让我们进一步探讨&#xff1a;面对不同的实际问题&#xff0c;应该如何选择合适的函数形式&#xff1f; 一、广告推荐系统中的函数选择 1. 业务目标 想象一下&#x…