建网站的基本流程,施工企业财务管理制度,青岛高端网站设计公司,wordpress下载主题footer目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码1、定时器02、自制八位独立按键3、点阵屏模块 四、主函数总结 系列文章目录 前言
《贪吃蛇》#xff0c;一款经典的、怀旧的小游戏#xff0c;单片机入门必写程序。
以《贪吃蛇》为载体#xff0c;熟悉各种屏… 目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码1、定时器02、自制八位独立按键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);#endifc文件
#include REGX52.H/*** brief 定时器0初始化1毫秒12.000MHz* param 无* retval 无*/
void Timer0_Init(void)
{TMOD0xF0; //设置定时器模式高四位不变低四位清零TMOD|0x01; //设置定时器模式通过低四位设为“定时器0工作方式1”的模式TL00x18; //设置定时初值定时1msTH00xFC; //设置定时初值定时1msTF00; //清除TF0标志TR01; //定时器0开始计时ET01; //打开定时器0中断允许EA1; //打开总中断PT00; //当PT00时定时器0为低优先级当PT01时定时器0为高优先级
}/*定时器中断函数模板
void Timer0_Routine() interrupt 1 //定时器0中断函数
{static unsigned int T0Count; //定义静态变量TL00x18; //设置定时初值定时1msTH00xFC; //设置定时初值定时1msT0Count;if(T0Count1000){T0Count0;}
}
*/2、自制八位独立按键
h文件
#ifndef __KEYSCAN_8_H__
#define __KEYSCAN_8_H__unsigned char Key(void);
void Key_Tick(void);#endifc文件
#include REGX52.Hsbit Key1P1^0;
sbit Key2P1^1;
sbit Key3P1^2;
sbit Key4P1^3;
sbit Key5P1^4;
sbit Key6P1^5;
sbit Key7P1^6;
sbit Key8P1^7;unsigned char KeyNumber;/*** brief 获取独立按键键码* param 无* retval 按下按键的键码范围0,1~24,0表示无按键按下*/
unsigned char Key(void)
{unsigned char KeyTemp0;KeyTempKeyNumber;KeyNumber0; //主程序中获取键码值之后键码值清零在下一次定时器扫描按键之前再次获取键码值一定会返回0return KeyTemp;
}/*** brief 获取当前按下按键的状态无消抖及松手检测* param 无* retval 按键值范围0~8无按键按下时返回值为0*/
unsigned char Key_GetState()
{unsigned char KeyValue0;if(Key10){KeyValue1;}if(Key20){KeyValue2;}if(Key30){KeyValue3;}if(Key40){KeyValue4;}if(Key50){KeyValue5;}if(Key60){KeyValue6;}if(Key70){KeyValue7;}if(Key80){KeyValue8;}return KeyValue;
}/*** brief 按键驱动函数在中断中调用* param 无* retval 无*/
void Key_Tick(void)
{static unsigned char NowState,LastState;LastStateNowState; //按键状态更新NowStateKey_GetState(); //获取当前按键状态//如果上个时间点按键未按下这个时间点按键按下则是按下瞬间if(LastState0){switch(NowState){case 1:KeyNumber1;break;case 2:KeyNumber2;break;case 3:KeyNumber3;break;case 4:KeyNumber4;break;case 5:KeyNumber5;break;case 6:KeyNumber6;break;case 7:KeyNumber7;break;case 8:KeyNumber8;break;default:break;}}//如果上个时间点按键按下这个时间点按键按下则是一直按住按键if(LastState NowState){if(LastState1 NowState1){KeyNumber9;}if(LastState2 NowState2){KeyNumber10;}if(LastState3 NowState3){KeyNumber11;}if(LastState4 NowState4){KeyNumber12;}if(LastState5 NowState5){KeyNumber13;}if(LastState6 NowState6){KeyNumber14;}if(LastState7 NowState7){KeyNumber15;}if(LastState8 NowState8){KeyNumber16;}}//如果上个时间点按键按下这个时间点按键未按下则是松手瞬间if(NowState0){switch(LastState){case 1:KeyNumber17;break;case 2:KeyNumber18;break;case 3:KeyNumber19;break;case 4:KeyNumber20;break;case 5:KeyNumber21;break;case 6:KeyNumber22;break;case 7:KeyNumber23;break;case 8:KeyNumber24;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);#endifc文件
#include REGX52.H//引脚定义
sbit _74HC138_DP2^7; //高位
sbit _74HC138_CP2^6;
sbit _74HC138_BP2^5;
sbit _74HC138_AP2^4; //低位
sbit _74HC138_GP2^3; //使能端低电平有效
sbit _74HC595_DIP2^2; //串行数据输入
sbit _74HC595_CLKP2^1; //移位寄存器时钟输入上升沿有效
sbit _74HC595_LATP2^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_CLK0; //移位寄存器时钟信号初始化_74HC595_LAT0; //储存寄存器时钟信号初始化
}/*** brief 74HC595写入字节* param Byte 需要写入的字节* retval 无*/
void _74HC595_WriteByte(unsigned char Byte)
{unsigned char i;for(i0;i8;i) //循环8次8位移位寄存器{_74HC595_DIByte(0x80i); //先高位后低位先低位再高位也行不过取模设置要更改改成高位在右_74HC595_CLK1; //CLK上升沿时DI口的数据电平写入移位寄存器_74HC595_CLK0;}
}/*** brief 74HC595存储数据* param 无* retval 无*/
void _74HC595_StoreData(void)
{_74HC595_LAT1; //LAT上升沿时数据从移位寄存器转存储存寄存器_74HC595_LAT0;
}/*** brief 74HC138扫描显示MatrixLED* param Line 要显示的行范围0~15对应1~16行* retval 无*/
void _74HC138_PickLine(unsigned char Line)
{switch(Line){case 0:_74HC138_D0;_74HC138_C0;_74HC138_B0;_74HC138_A0;break;case 1:_74HC138_D0;_74HC138_C0;_74HC138_B0;_74HC138_A1;break;case 2:_74HC138_D0;_74HC138_C0;_74HC138_B1;_74HC138_A0;break;case 3:_74HC138_D0;_74HC138_C0;_74HC138_B1;_74HC138_A1;break;case 4:_74HC138_D0;_74HC138_C1;_74HC138_B0;_74HC138_A0;break;case 5:_74HC138_D0;_74HC138_C1;_74HC138_B0;_74HC138_A1;break;case 6:_74HC138_D0;_74HC138_C1;_74HC138_B1;_74HC138_A0;break;case 7:_74HC138_D0;_74HC138_C1;_74HC138_B1;_74HC138_A1;break;case 8:_74HC138_D1;_74HC138_C0;_74HC138_B0;_74HC138_A0;break;case 9:_74HC138_D1;_74HC138_C0;_74HC138_B0;_74HC138_A1;break;case 10:_74HC138_D1;_74HC138_C0;_74HC138_B1;_74HC138_A0;break;case 11:_74HC138_D1;_74HC138_C0;_74HC138_B1;_74HC138_A1;break;case 12:_74HC138_D1;_74HC138_C1;_74HC138_B0;_74HC138_A0;break;case 13:_74HC138_D1;_74HC138_C1;_74HC138_B0;_74HC138_A1;break;case 14:_74HC138_D1;_74HC138_C1;_74HC138_B1;_74HC138_A0;break;case 15:_74HC138_D1;_74HC138_C1;_74HC138_B1;_74HC138_A1;break;default:break;}
}/*** brief 点阵屏驱动函数定时器中断函数中使用* param Line 要显示的行范围0~15对应1~16行* param State 控制屏幕显不显示范围0~10屏幕不显示1屏幕显示* retval 无*/
void MatrixLED_Tick(unsigned char Line,unsigned char State)
{_74HC138_G1;if(State1){_74HC595_WriteByte(MatrixLEDBuffer[16Line]);_74HC595_WriteByte(MatrixLEDBuffer[Line]);}else{_74HC595_WriteByte(0xFF);_74HC595_WriteByte(0xFF);}_74HC595_StoreData();_74HC138_PickLine(Line);_74HC138_G0;
}/*** 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;mOffset/8;nOffset%8;Arraym*16;for(i0;i32;i){MatrixLEDBuffer[i](*Arrayn)|(*(Array16)(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;mOffset/16;nOffset%16;Arraym*32;Arrayn;for(i0;i16;i){if(i16-n){MatrixLEDBuffer[i]*(Array);MatrixLEDBuffer[i16]*(Array16);}else{MatrixLEDBuffer[i]*(Array16);MatrixLEDBuffer[i16]*(Array32);}Array;}
}四、主函数
main.c
/*
by甘腾胜20241219
效果查看/操作演示可以在B站搜索“甘腾胜”或“gantengsheng”查看
单片机STC89C52RC
晶振12T12.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.hunsigned char KeyNum; //存储获得的键码值
unsigned char Mode; //游戏模式0显示游戏名称“《贪吃蛇》”1显示汉字“难度选择”//2难度选择界面数字范围是1~53游戏进行模式4游戏结束全屏闪烁//5显示汉字“得分”6循环滚动显示得分7显示作者姓名和编程日期
unsigned char MoveSnakeFlag; //移动蛇身的标志1移动0不移动
unsigned char NowDirection1; //蛇头移动的方向1向右2向上3向左4向下游戏开始时默认向右移动
unsigned char LastDirection1; //蛇头上一次移动的方向1向右2向上3向左4向下游戏开始时默认向右移动此处可以不赋初值
unsigned char Length2; //蛇的长度初始值为2此处可以不赋初值因为进入游戏进行模式时还会赋值一次
unsigned char Head1; //保存整条蛇的数据的数组共256个数据数据索引为0~255中蛇头对应的数据的索引蛇的初始长度为2//开始时只用了两个数据数组的第1个数据和第2个数据蛇头对应的是第2个数据索引为1Head的范围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 SnakeMoveSpeed1000; //蛇移动的速度值越小速度越快上电默认1.00s移动一次定时器定时1ms
unsigned int T0Count0,T0Count1,T0Count2,T0Count3,T0Count4; //定时器计数的变量
unsigned char PauseFlag; //暂停的标志1暂停0不暂停unsigned char pdata SnakeBody[256]; //点阵屏是16X16256像素需要用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;mrand()%16; //产生一个0~15的随机数nrand()%16; //产生一个0~15的随机数for(j0;j16;j) //产生一个随机位置判断该位置是否是蛇身如果不是就返回该位置所对应的数据{ //如果该位置是蛇身的位置则从该点向周围寻找不是蛇身的空位置for(i0;i16;i){if( (DisplayBuffer[(nj)%16(mi)%16/8*16] (0x01(mi)%8)) ){FoodTemp(mi)%16*16(nj)%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~11点亮0熄灭* retval 无*/
void ConvertData(unsigned char Position,unsigned char State)
{if(State1){DisplayBuffer[Position%16Position/16/8*16] ~(0x01(Position/16%8));}else{DisplayBuffer[Position%16Position/16/8*16] | 0x01(Position/16%8);}
}/*** brief 控制蛇的移动* param 无* retval 无*/
void MoveSnake(void)
{if(NowDirection1) //如果向右移动{//移动前判断一下移动后是否撞墙如果是则游戏结束游戏结束的标志置1if((SnakeBody[Head]/16)15){GameOverFlag1;}//SnakeBody数组中蛇头的下一个数据等于上一个数据加16即高四位加1即蛇头移动到了右边这一列else{SnakeBody[(Head1)%256]SnakeBody[Head]16;}}if(NowDirection2) //如果向上移动{if((SnakeBody[Head]%16)0){GameOverFlag1;}//SnakeBody数组中蛇头的下一个数据等于上一个数据减1即低四位减1即蛇头移动到了上边这一行else{SnakeBody[(Head1)%256]SnakeBody[Head]-1;}}if(NowDirection3) //如果向左移动{if((SnakeBody[Head]/16)0){GameOverFlag1;}//SnakeBody数组中蛇头的下一个数据等于上一个数据减16即高四位减1即蛇头移动到了左边这一列else{SnakeBody[(Head1)%256]SnakeBody[Head]-16;}}if(NowDirection4) //如果向下移动{if((SnakeBody[Head]%16)15){GameOverFlag1;}//SnakeBody数组中蛇头的下一个数据等于上一个数据加1即低四位加1即蛇头移动到了下边这一行else{SnakeBody[(Head1)%256]SnakeBody[Head]1;}}if(GameOverFlag0) //如果没撞墙{if(SnakeBody[(Head1)%256]Food) //判断蛇头移动后的位置是否是食物所在的位置{ //如果是Length; //蛇身长度加1ConvertData(Food,1); //防止食物闪烁刚好不显示的时候进入此函数导致蛇身一个点不显示FoodCreateFood(); //重新创造一个食物ConvertData(Food,1); //先更新显示缓存在主循环中再更新屏幕的显示FlashFlag0; //创造出新的食物时食物暂不闪烁T0Count20; //定时器T0Count2重新计数}else if( !( (DisplayBuffer[SnakeBody[(Head1)%256]%16SnakeBody[(Head1)%256]/16/8*16]) (0x01SnakeBody[(Head1)%256]/16%8) ) ){ //如果蛇头移动后的位置撞在蛇身上则游戏结束GameOverFlag1; //游戏结束的标志置1}else //如果蛇头移动后的位置不是食物也不是撞墙也不是撞到蛇身的话{//显示缓存数组DisplayBuffer中蛇头前进后蛇头的新位置对应的位更新数值ConvertData(SnakeBody[(Head1)%256],1);//显示缓存数组DisplayBuffer中蛇尾的位置对应的位清0蛇身移动如果没有吃到食物相当于蛇尾对应的点跑到了//蛇头的前一个点变成了蛇头原来的蛇头变成蛇身整条蛇中间的数据不用操作ConvertData(SnakeBody[(Head256-Length1)%256],0);//数组SnakeBody中蛇尾的数据清零,//Head256256是因为Head为255后再加1就会变成了0防止Head256-Length1为负数//循环使用SnakeBody数组中的256个数据蛇头对应数组SnakeBody的第255个数据后再移动一次蛇头就来到了数组的第0个数据
// SnakeBody[(Head256-Length1)%256]0; //其实可以不用清零}}Head; //SnakeBody数组中蛇头对应的数据的索引加1Head%256; //蛇头变量Head是无符号字符型数据范围是0~255值为255后自增会自动变为0所以这一行可以省略//如果Head是整型int则需要有这一行
}void main()
{unsigned int i;unsigned char Count0; Timer0_Init(); //定时器初始化MatrixLED_Init(); //点阵屏初始化while(1){KeyNumKey(); //获取键码值if(KeyNum) //如果有按键按下{srand(TL0); //以定时器0的低八位数据作为随机数的种子用来产生真随机的数据if(Mode7 KeyNum18) //如果是显示作者姓名和编程日期的界面且按下了返回键K2松手瞬间{Mode2; //返回难度选择界面ExecuteOnceFlag1; //各模式只执行一次代码的标志置1}if(Mode6) //如果是循环滚动显示得分的界面{if(KeyNum18) //如果按下了返回键K2松手瞬间{Mode2; //返回难度选择界面ExecuteOnceFlag1; //各模式只执行一次代码的标志置1}else if(KeyNum19) //如果按下了K3松手瞬间{Mode7; //切换到显示作者姓名和编程日期的界面Offset10; //滚动显示的偏移量清0}}if(Mode5 KeyNum17) //如果是显示汉字“得分”的界面且按下开始键K1松手瞬间{Mode6; //跳过显示汉字切换到循环滚动显示得分的界面ExecuteOnceFlag1;Offset10; //滚动显示的偏移量清0}if(Mode4 KeyNum17) //如果游戏结束闪烁时按下开始键K1松手瞬间{Mode5; //切换到显示汉字“得分”的界面ExecuteOnceFlag1;Offset10;}if(Mode3) //如果是游戏进行模式{if(KeyNum17) //按下K1暂停或继续松手瞬间{PauseFlag!PauseFlag;}if(PauseFlag0) //如果不是暂停{ //按下瞬间、长按、松手瞬间都进行检测这样控制方向更有效防止按键没检测出来导致没能改变方向if((KeyNum8 || KeyNum16 || KeyNum24) LastDirection!1){ //如果按了“左”键且蛇头原来的移动方向不是向右NowDirection3; //则方向蛇头方向改为向左}if((KeyNum7 || KeyNum15 || KeyNum23) LastDirection!4){ //如果按了“上”键且蛇头原来的移动方向不是向下NowDirection2; //则方向蛇头方向改为向上}if((KeyNum6 || KeyNum14 || KeyNum22) LastDirection!2){ //如果按了“下”键且蛇头原来的移动方向不是向上NowDirection4; //则方向蛇头方向改为向左}if((KeyNum5 || KeyNum13 || KeyNum21) LastDirection!3){ //如果按了“右”键且蛇头原来的移动方向不是向左NowDirection1; //则方向蛇头方向改为向左}}}if(Mode2) //如果是难度选择界面{if(KeyNum23) //如果按了“上”键松手瞬间{RollUpFlag1; //数字向上滚动的标志置1}if(KeyNum22) //如果按了“下”键松手瞬间{RollDownFlag1; //数字向下滚动的标志置1}if(KeyNum17) //如果按了开始键松手瞬间则开始游戏{Mode3; //切换到游戏模式ExecuteOnceFlag1;}}if(KeyNum24 KeyNum17) //如果按下任意按键松手瞬间{//两个if的顺序不能调换如果调换了就从模式0直接跳到模式2了if(Mode1){Mode2;Offset10;ExecuteOnceFlag1;} //跳过汉字“难度选择”的显示切换到难度选择界面if(Mode0){Mode1;Offset10;ExecuteOnceFlag1;} //跳过游戏名“《贪吃蛇》”的显示切换到汉字“难度选择”的显示界面}}if(Mode0) //如果是显示游戏名称“《贪吃蛇》”的模式{if(RollFlag) //如果滚动的标志RollFlag为1定时器中每隔一段时间将此标志置1{RollFlag0; //滚动的标志RollFlag清零MatrixLED_MoveLeft(Table1,Offset1); //向左滚动Offset1; //每次向左移动一个像素Offset1%96; //越界清零循环滚动}}if(Mode1) //如果是显示汉字“难度选择”的模式{if(RollFlag Offset196) //只向左滚动显示一次不循环滚动显示{RollFlag0;MatrixLED_MoveLeft(Table2,Offset1);Offset1;}else if(Offset196) //显示数字“1”之后自动切换到难度选择模式{Mode2;Offset10;}}if(Mode2) //如果是难度选择模式{if(ExecuteOnceFlag) //切换到该模式后此if中的内容只执行1次{ExecuteOnceFlag0;MatrixLED_MoveUp(Table3,Offset2); //显示难度对应的数字范围1~5}if(RollFlag RollUpFlag) //如果滚动标志为1且向上滚动的标志也为1{RollFlag0; //定时器中每个50ms将RollFlag标志置1Offset2; //向上移动一个像素Offset2%80; //越界清零总共5个数字每个数字的高度是16所以是5*1680MatrixLED_MoveUp(Table3,Offset2); //更新显示RollCount;if(RollCount16) //移动了16个像素后停止移动{RollCount0;RollUpFlag0;Offset2(Offset2/16)*16; //防止移动到一半的时候按下“上”或“下”按键导致数字没有在点阵屏中间//Offset2的值必须是16的整数倍switch(Offset2/16){case 0:SnakeMoveSpeed1000;break; //难度1,1.00s移动1次case 1:SnakeMoveSpeed750;break; //难度2,0.75s移动1次case 2:SnakeMoveSpeed500;break; //难度3,0.50s移动1次case 3:SnakeMoveSpeed250;break; //难度4,0.25s移动1次case 4:SnakeMoveSpeed120;break; //难度5,0.12s移动1次default:break;}}}if(RollFlag RollDownFlag) //如果滚动标志为1且向下滚动的标志也为1{RollFlag0;if(Offset20){Offset280;}Offset2--;MatrixLED_MoveUp(Table3,Offset2);RollCount;if(RollCount16){RollCount0;RollDownFlag0;Offset2(Offset2/16)*16;switch(Offset2/16){case 0:SnakeMoveSpeed1000;break; //难度1,1.00s移动1次case 1:SnakeMoveSpeed750;break; //难度2,0.75s移动1次case 2:SnakeMoveSpeed500;break; //难度3,0.50s移动1次case 3:SnakeMoveSpeed250;break; //难度4,0.25s移动1次case 4:SnakeMoveSpeed120;break; //难度5,0.12s移动1次default:break;}}}}if(Mode3) //如果是游戏进行模式{if(ExecuteOnceFlag) //切换到该模式后此if中的内容只执行1次{ //开始游戏后所有数据初始化ExecuteOnceFlag0;GameOverFlag0; //游戏结束标志清零PauseFlag0; //游戏暂停标志清零NowDirection1; //蛇头默认向右移动LastDirection1; //上一次蛇头默认向右移动Length2; //蛇的初始长度为2Head1; //蛇头对应数组中的第2个数据索引为1
// for(i0;i256;i) //蛇身数据全部清零其实可以不用清零
// {
// SnakeBody[i]0;
// }//写入蛇身初始的两个数据SnakeBody[0]1*161; //蛇尾位置第二行第二列SnakeBody[1]2*161; //蛇头位置第二行第三列for(i0;i32;i) //显示缓存数据全部清空阳码亮点为0所以所有字节的每一位置1{DisplayBuffer[i]0xFF;}DisplayBuffer[1]0xF9; //显示缓存数据全部清空后写入蛇身初始的两个数据阳码亮点为0FoodCreateFood(); //进入游戏前先创造出一个食物ConvertData(Food,1); //更新显示缓存MatrixLED_MoveLeft(DisplayBuffer,0); //屏幕显示初始的蛇身及食物MoveSnakeFlag0; //蛇移动的标志清零T0Count10; //定时器计数变量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 GameOverFlag0 PauseFlag0){ //如果移动的标志为1且不暂停且游戏也没结束LastDirectionNowDirection; //保存上一次移动的方向用于按键的判断蛇不能往后移动MoveSnakeFlag0; //移动标志清零MoveSnake(); //移动一次MatrixLED_MoveLeft(DisplayBuffer,0); //MoveSnake函数中更改显示缓存的数据这里用移屏函数更新屏幕显示}if(GameOverFlag1) //如果游戏结束{ConvertData(Food,1);MatrixLED_MoveLeft(DisplayBuffer,0);Mode4; //切换到全屏闪烁模式ExecuteOnceFlag1;}}if(Mode4) //如果是游戏结束全屏闪烁模式{//在中断中处理}if(Mode5) //显示汉字“得分”{if(RollFlag Offset148) //只显示一次汉字“得分”{RollFlag0;Count;Count%10;if(Count0){MatrixLED_MoveLeft(Table5,Offset1);Offset116;}}else if(Offset148) //显示结束后自动切换到循环显示得分的模式{Mode6;Offset10; //偏移量清零ExecuteOnceFlag1;} }if(Mode6) //如果是滚动显示得分模式{if(ExecuteOnceFlag){ExecuteOnceFlag0;for(i0;i112;i) //蛇身数据部分清零SnakeBody前160个用来循环显示三位数得分{SnakeBody[i]0xFF;}//将得分即蛇身的长度的百位、十位、个位的字模写入数组SnakeBody中for(i0;i16;i){SnakeBody[32i]Table6[Length/100*16i];}for(i0;i16;i){SnakeBody[48i]Table6[Length/10%10*16i];}for(i0;i16;i){SnakeBody[64i]Table6[Length%10*16i];}PauseFlag0;}if(RollFlag) //如果滚动的标志为1{RollFlag0;MatrixLED_MoveLeft(SnakeBody,Offset1);Offset1;Offset1%40; //循环滚动}}if(Mode7) //如果是显示作者姓名和编程时间的模式{if(RollFlag) //如果滚动的标志为1{RollFlag0;MatrixLED_MoveLeft(Table7,Offset1);Offset1;Offset1%160; //循环滚动}}}
}void Timer0_Routine() interrupt 1 //定时器0中断函数
{static unsigned char Line;TL00x18; //设置定时初值定时1ms晶振12.0000MHzTH00xFC; //设置定时初值定时1ms晶振12.0000MHzT0Count3;T0Count4;if(T0Count020) //20ms扫描一次按键{T0Count00;Key_Tick();}T0Count0;if(PauseFlag0) //不暂停时T0Count1和T0Count2才计数{T0Count1;T0Count2;}if(T0Count1SnakeMoveSpeed) //用来控制蛇移动的速度{T0Count10;MoveSnakeFlag1;}if(T0Count2250) //0.25s取反闪烁标志FlashFlag的值{T0Count20;FlashFlag!FlashFlag;}if(T0Count350) //控制滚动的速度50ms滚动一次{T0Count30;RollFlag1;}if(T0Count41){T0Count40;if(Mode4 FlashFlag){MatrixLED_Tick(Line,0);} //游戏结束全屏闪烁else{MatrixLED_Tick(Line,1);} Line;if(Line15){Line0;} //总共要扫描16行Line的范围是0~15}
}总结
这个点阵屏模块比较占用单片机的资源如果用速度较快的单片机则影响不大如果是用89系列的单片机会感觉影响比较大建议用MAX7219驱动的点阵屏很好用。