绪论
1.1 研究背景与意义
随着社会的发展,人们生活的步调日益加快,越来越多的人加入了全球化的世界。
人们不再拘泥于--,J,块天地,加班,出差成了现代人不可避免的公务。而此时一款可以
随时随地娱乐的游戏成了必需品。贪吃蛇这一游戏简单易行,操作方便,娱乐性较强,
吸引了不少人。这一款游戏紧紧地抓住了人们的心理,虽然简单,却其乐无穷,在人们
不断追求更多的欲望下,该游戏给人们带来了追逐的快感,以及成功后的满足感,对于
一直处于高压下的现代人是很好的放松工具。
当前科学技术飞速发展,特别是微电子技术,计算机软件与应用技术的发展,使得
人们的日常生活丰富多彩。单片微型计算机(简称单片机)作为微型计算机家族的一员,
以其独特的结构,良好的稳定性,便宜的价格在嵌入式领域广泛应用。与传统的PC上
设计的贪吃蛇游戏不同,本次作者利用Proteus硬件仿真软件,采用单片机、液晶显示
屏、扬声器、按键等搭建硬件平台,C语言编程,实现便携地贪吃蛇游戏。
传统的贪吃蛇游戏只有单纯的吃豆子,得分,升级以后蛇运动的速度加快等功能。
本次作者对贪吃蛇游戏进行了升级,出上述基本功能外,针对现有硬件条件,加入地图
选择,游戏中途暂停与退出,各种背景音的播放(包括吃豆子背景音、错误提示背景音、
升级背景音、游戏结束背景音、游戏通关背景音等),背景音静音与否的选择等,使玩
家的游戏体验更上一层楼。
1.2单片机发展状况
单片微型计算机(Single.Chip Microcomputer)简称单片机(MCU)。它是在一块
芯片上集成了中央处理单元(CPU)、振荡器电路、只读存储器(ROM)、随机存取存
储器(RAM)、并行/串行I/O接口、可编程定时器/计数器等,有的甚至包含了A/D转
换器。总之,这么一块小小的单片机芯片,就相当于一台微型计算机,它具有体积小、
重量轻、单一电源、低功耗、功能强、价格低廉、运算速度快、抗干扰能力强、可靠性
高等特点。1974年,美国仙童(Fairchild)公司生产出世界上第一块单片机,短短几十
年的时间,单片机如雨后春笋一般,大量涌现出来。目前,已经出现了4位、8位和16
位单片机,甚至32位超大规模集成电路单片机也已问世,性能也在不断地提高。
国内从80年代起开始了单片机的热潮,二十多年过去了,单片机从研究所走出来,
成为与日常生活中的一个不可缺少的部件。早些时候单片机种类稀少,开发工具奇缺。
8035、8048、Z80等在现在主流市场上基本已没有踪影,用汇编语言开发产品的艰苦工
基于51单片机的贪吃蛇游戏
作也逐步被C语言取代。硬件方面日趋多样化,4位、8位、16位、32位等型号共同并
存,在不同的领域存在,如家电、玩具、工业设备、仪器、通讯。价格也从几元到几百
元不等。每一种单片都有它所擅长的领域,如PIC系列较多用于电话机、玩具,51系
列较多用于设备控制和仪器,DSP较多用于DVD、通讯等。软件方面发展主要为汇编
语言、C语言、嵌入式操作系统。速度、稳定性特别要求的场合较多采用汇编语言和C
语言,如电机控制,UPS控制、信号处理等。功能复杂、内容较多的系统多采用嵌入式
操作系统,如PDA、电子词典、游戏机等。以后的发展中,各类型号的单片机种类会进
一步增加,而开发工具和过程会逐步趋向于统一,软件和硬件差别会更加难以区分IlJ。
1.3 LCD发展状况
液晶显示器TFT LCD,全称为薄膜晶体管液晶显示器(Thin Film Transistor Liquid
Crystal Displayer),一般简称LCD(Liquid Crystal Display)。超薄体形、低功耗、低
辐射、无闪烁、完全物理平面、低反光、清晰的字符显示等等,都是大家非常熟悉的液
晶显示器LCD优点。最简单的液体晶体管就是我们常见的小型计算器以及电子手表上
面的液晶字符屏幕。他是把有机液晶原料夹在两片透明的玻璃或者有机玻璃中。没有电
流通过的时候,长棒状的原料晶体分子是无规则排列的,光线无法随意透过玻璃,外表
看上去就是黑色。通电的时候,液晶原料排列顺序随电流极向改变,光线在规则排列的
晶体分子中可以透过,液晶管由原来的非透明状态变成透明状态。通过把液晶材料进行
不同的排列,组成不同的字符形状,就能通过电流控制其开关显示,以显示出我们说需
要的字符。液晶技术发展的早期,由于液晶管的稳定性以及生产技术,还不能大量大规
模的生产,直到了英国的科学家发明了用“联苯(Biphenyl)一作为液晶管的原料,这
个问题才得以解决。1970年,弗格森制造了第一台能够工作的LCD,而在此之前的所
谓LCD都是耗电量大而且对比度极低的昂贵设备。到了1971年,这种新的液晶显示器
开始普遍地为人们接受。当然,那时候的LCD还是单色产品,但是已经不是简单的字
符型液晶屏幕了。LCD技术是把液晶灌入两片偏振玻璃之间。所谓偏振玻璃,就是光线
通过这样的玻璃之后,就会从球面波或者高斯球面波,变成只在一个平面上振动的波,
称为偏振光。偏振光只能通过相应方向的偏振玻璃,如果偏振玻璃的偏振方向和偏振光
线的有一定的夹角,就会减弱偏振光强度,甚至偏振光无法通过。如果大家对这方面有
兴趣,可以参阅有关的大学物理书籍。夹住液晶的两片偏振玻璃,假设为a、b,他们的
偏振方向会设置为90度夹角。光线通过第一片偏振玻璃a后,假设这X方向偏振,通
过液晶后,液晶通电流之后,在电场极化作用下,呈规则排列,X偏振光不会有任何改
一2一
大连理工大学专业学位硕士学位论文
变,投射到b玻璃上。而b玻璃的偏振方向为Y,就是X+90度,X偏振的光线无法通
过,在b玻璃外面看上去就是黑色了。而如果液晶没有电场作用,就是没有通电流,通
过无规则排列的液晶,X偏振光的偏振方向会发生改变,旋转90度,旋转后X偏振光
的偏振方向刚好和b偏振玻璃的偏振方向一样,就是X+90=Y,光线就能通过b玻璃了
【2】
o
1.4作者的主要工作
贪吃蛇游戏是一款经典的小游戏,前人根据不同的需求,使用不同的编程语言和算
法实现过该游戏。本次作者基于51单片机这一常用的硬件平台,充分发挥其性能,在
嵌入式平台利用C语言编程实现这款经典游戏。
由于嵌入式平台对于硬件资源有着相对于PC机开发的应用软件更为苛刻的要求,
特别是在RAM/ROM的存储空间大小上,所以本次作者将重点放在如何处理蛇的运动
轨迹方面,采用了一个无符号char型一维数组来存放蛇头的运动轨迹,并考虑到蛇身的
最大长度,将该数组定义为54,充分节省了有限的存储空间,通过处理按键,完成了对
蛇运动的控制以及游戏控制。显示游戏信息和游戏运行画面的LCDl602和LCDl2864
使用广泛,技术相对成熟,故LCD底层驱动采用前人成果,并在此基础上设计了游戏
的运行界面。单片机背景音的产生于播放也参考了许多他人的思想。
1.5本文结构
本文按照整个系统的开发流程,首先进行游戏的需求分析,然后介绍系统的架构设
计,包括论文使用的硬件结构设计,软件的结构设计与模块划分,最后是各模块的软件
详细设计与实现。
基丁51单片机的贪吃蛇游戏
2贪吃蛇游戏需求分析
贪吃蛇足一款经典小游戏,游戏的规则是:玩家通过方向键(上,下,左,右)来
控制蛇移动,在地图上吃豆子。吃掉豆子后蛇身加长,并且会增加相应分数,达到一定
分数以后升级,升级后,蛀身运动速度加快。蛇运动时撞到墙壁(屏幕框),障碍物或
者蛇身结束游戏。
奉次贪吃蛇游戏主要功能如下:选择游戏地图(4幅地图);蛇控制(移动、吃豆
子等):游戏信息(游戏时间、级别、分数、背景音开关等):游戏界面(游戏运行、
暂停、通芙等界面):游戏背景音(吃豆子、升级、通关,失败等背景音)。
2 1 游戏信息显示界面与分数等级计算规则
游戏时恻显示游戏已经运行时间,以分钟:秒形式显示(00:00)。
游戏分数显不是根据等级、蛇吃豆子的多少增加分数、显示分数。蛇吃一个豆子,
分数加lo。分数从0开始,通关后分数为2100。
游戏等级显示是根据蛇吃豆子增加分数到某个特定值增加等级数。本次设计默认等
级从1开始,吃10个豆子升到第二级,在此基础吃20个豆子升级到第三级,以此类推。
升级到第六级吃60个豆子即可通关(共吃210个豆子)。
坩广选择背景音,则屏幕右上方有喇叭图标处于打开状态显示;用户选择静音模式,
则屏幕右l一方喇叭图标处于静音状态显示。霍蚓2 1游戒信息显示界面
Fig 2】Game Information Display Interface
2 2游戏界面状态显示
上电开机后,LCDl2864界面全屏显示游戏的欢迎信息”Welcometo Snake World!”
蛇的图像,以及点击”start”键的提示信息。通过单击开始键可以进入游戏地图选择界面
丌机界面如图所示:
大连理[火学专业学位硕十学伉论文
圈2 2开机界面
FiE 2.2 Bcot Interface
游戏地图选择界面以图形的方式显示,一共4个地图。用键盘按键选择地图,单击
开始键进入游戏界面。游戏地图选择界面如图所示:
酗2 3游戏地蚓选择界面
Fig 2.3 Game Map Selection interface
游戏界面划分成游戏区与信息区。游戏开始时,游戏区中央显示有一个长为3个单
位的蛇,豆子以及选择的地图。信息区显示一个蛇图片。游戏准各运行界面如图所示:
幽2 4游戏准蔷远行界面
Fig 2 4 Game Read3+Interface
基于5l单片机的贪吃蛇游戏
游戏时游戏区由键盘控制蛇的运动方向吃豆子。信息区显示”COME ON!”及蛇图片。
游戏运行界面如图所示:
图2 5游戏运行界面
Fig 2.5 Game Running Interface
游戏暂停时,游戏区蛇的运动停止。信息区显示“EXITYESNO”。可以选择退出
游戏暂停界面如图所示:
图2 6游戏暂停界面
Fig 2.6 GmaePauseInterface
游戏通关时,蛇的运动停止,全屏显示“Congratulation!”及礼花背景。游戏通关界面
如图所示;
图2 7游戏通关界面
Fig 2.7 GameClearInterface
人连理1.大学专业学忙碗1‘学何论文
游戏结束时,全屏显不”GAME OVER!”。游戏结束界面如图所示
目2 8游戏结柬界面
Fig 2.8 GameOverlmerface
2 3游戏处理
上电以后,初始化游戏。根据玩家选择的游戏地图,柳始化地图,并且在游戏医域
崮定位置出现蛇,蛇的长度为三个单位。在随机位置出现第一个豆子。
蛇的移动:使蛇按照方向键的方向移动。
开始游戏时,蛇头和蛇尾已经固定,按丌始键游戏开始,蛇只能向左或者向右转,
不能向后。
刷新豆子:再子被吃掉后在随机位置出现豆子。豆子不能出现在和蛇身或者障碍物
重含,否则重新刷新豆子。
吃豆子:蛇头吃到豆了,蛇身变K格(在蛇头加)。
分数、等级和蛇身运动速度:游戏等级是高是六缴,默认等级从l丌始,吃1个豆
子增加10分。第一级吃10个豆子爿到第二级,第一级吃20个豆了引绒到第三级,咀
此类推,第六级吃60个豆子,Bll,,d-通关。随着等级的增加,蛇运动速度电逐渐加快
判定死亡:当蛇头碰到屏幕边缘,碰到障碍物,或者碰到蛇自己的身体时,蛇死亡,
游戏结束。
2 4背景音处理块
游戏通芙,播放游戏通关背景音。
游戏结束,播放游戏结束背景音。
游戏州绂,播放游戏升级背景音。
吃到豆子.播放吃再子背景音。
错误提示,播放无效按键背摄音。
基丁51单片机的贪吃蛇游戏
2.5键盘控制块
启动/暂停控制:点击开始键进入地图选择界面,再点开始键进入游戏界面;游戏中,
使用该键,实现暂停/开始控制。
方向控制:用上、下、左、右键选择地图。用上、下、左、右键控制蛇头运动方向。
游戏中暂停时,上、下键选择”EXIT YES NO”,左右键无效。
声音控制:用静音键控制背景音是否打开。
大连理工大学专业学位硕士学位论文
3系统架构设计
3.1软件开发环境
在本次开发中,采用了专门用于MCS.51系列单片机软件开发的C51语言,这种语
言与普通C语言相同,并提供了针对单片机的常量定义、库函数等等。C是一种源于编
写UNIX操作系统的语言,它是一种结构化语言,可产生紧凑代码。由于汇编语言程序
的可读性和可移植性都较差,用汇编语言编写单片机应用系统程序的周期长,且调试和
排错也比较困难。而一般效率高的高级语言难以实现汇编语言对于计算机硬件直接进行
操作(如对内存地址的操作移位操作等的功能)而C语言既具有一般高级语言的特点,
又能直接对计算机的硬件进行操作,并且采用C语言编写的程序能够很容易地在不同类
型的计算机之间进行移植【3】’因此许多以前只能采用汇编语言来解决的问题现在可以改
用C语言来解决。
开发环境选择了Keil uVision2编译调试,硬件仿真软件Proteus 7.1仿真运行。这样
做的目的是节省成本和缩短开发调试时间。Keil软件是目前最流行开发MCS.51系列单
片机的软件,提供了包括C编译器、宏汇编、连接器、库管理和一个功能强大的仿真调
试器等在内的完整开发方案,通过一个集成开发环境(uVision)将这些部份组合在一起,
全Windows界面。另外重要的一点,只要看一下编译后生成的汇编代码,就能体会到
Keil C51生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解14J。
在开发大型软件时更能体现高级语言的优势。Proteus软件是英国Labcenter公司开发的
电路分析与实物仿真软件。它运行于Windows操作系统上,可以绘制电路原理图,仿
真、分析各种模拟器件和集成电路,支持主流单片机系统和多种外围芯片的仿真,提供
软件调试功能,支持第三方的软件编译和调试环境,如Keil C51 uVision2pJ。
3.2硬件结构设计
针对第二章的需求分析,系统采用的硬件设备主要包括51系列单片机,LCDl602,
LCDl2864,按键,扬声器。
51系列单片机采用了飞利浦(PHILIPS)公司生产的型号为P87C51RD2的低功耗
高性能CMOS型8位单片机。选择该型号单片机的原因是,它内置了64K bytes的OTP
只读程序存储器(ROM)和1K bytes的随机存取数据存储器(RAM),能满足游戏对存储空
间的要求。另外配备了32个可编程的I/O端口,3个16位定时器/计数器,一个7中断
源4优先级嵌套中断结构,一个全双工串行通信口,片内震荡器及时钟电路【6l。系统利
用51单片机作为总的控制驱动单元,两个LCD分别显示游戏信息和游戏界面。LCDl602
基丁jl单片机的贪吃蛇游戒
显示游戏相关信息,包括游戏运行时间,游戏等级,游戏得分,静音图标:LCDl2864
显示游戏运行界面。按键分为六个.包括控制上下左右蛇运动方向的四键,游戏开始、
暂停键,游戏静音键。扬声器用于发出背景音。依据上述设计的总体硬件电路幽如下陶
所示。
剀31总体硬件电路幽
Fig 3 I Total Hardware Circuit Diagram
其主要运行原理是使用单片机的]/O口驱动LCD,向LCD的数据口写数据或指令,
使LCD硅示相应的游戏信息和游戏界面。单片机采用查询方式扫描键盘,肖有键按卜
的时候,单片机读取键值,按照按键的功能进入不同的游戏状态。背景音利用定时器0
和定时器1的中断,查询方式产生。蛇的运动和游戏时钟则是采用定时器2中断产生的,
当满足一定条件时通过单片机I/O F3送到LCD上进行显示I”。洋细实现将在软件详细设
大连理工大学专业学位硕士学位论文
计中介绍。
3.3软件结构设计
针对第二章的需求分析,将贪吃蛇游戏按照功能划分成如下几个模块:
(1)主函数模块
主函数是整个程序运行的一个缩影,是一个无限循环的程序。完成的操作包括初始
化一系列硬件软件,利用定时中断完成对游戏时钟的控制,调用其他模块完成按键处理,
蛇运动与游戏的处理。
(2)按键模块
按键模块分为按键检测模块和按键处理模块两部分。
按键检测模块初始化按键对应的管脚,利用查询方式检测具体是哪个按键被按下,
将该按键对应的变量值改变。.
按键处理模块针对不同状态下,对按键做不同的处理,具体划分成七个状态,对应
七个函数,分别是游戏欢迎状态,游戏选择地图状态,游戏等待开始状态,游戏中状态,
游戏中暂停状态,游戏通关状态,游戏结束状态。
(3)LCDl602显示模块
该模块通过获取相关变量的当前值,负责将当前的游戏运行时间,游戏得分,游戏
等级,静音图标显示在LCDl602显示屏上。
(4)LCDl2864显示模块
该模块负责显示游戏界面。针对七种状态(游戏欢迎状态,游戏选择地图状态,游
戏等待开始状态,游戏中状态,游戏中暂停状态,游戏通关状态,游戏结束状态),在
LCDl2864上时时刷新显示不同的画面。
(5)蛇运动控制模块
该模块负责对蛇的动作与状态的控制,包括游戏开始时对蛇的初始化,蛇移动的处
理,对存储地图的数组中的一位做置1,置0或查询操作,蛇吃食物后的处理,根据当
前蛇头坐标及蛇头运动方向,更新下一步蛇头的坐标。
(6)游戏控制模块
该模块负责游戏的控制,具体包括在屏幕上随机生成新豆子;吃到豆子以后分数增
加,等级增加:对游戏时钟的控制。
(7)背景音模块
该模块定义不同的背景音,在不同游戏状态发出对应背景音。背景音包括无效按键
背景音,吃豆子背景音,升级背景音,游戏通关背景音,游戏结束背景音。
基于51单片机的贪修e_ge游戏
4软件详细设计与实现
4.1游戏设计思想
按照需求分析,游戏设计重点要解决游戏界面的显示和蛇身运动处理的问题。
游戏采用两个LCD屏幕显示游戏界面。LCDl602分两行显示游戏信息。第一行显
示游戏运行时间,静音图标;第二行显示游戏等级,游戏得分;LCDl2864显示游戏运
行界面。设定LCDl2864以4x4大小为一个单位点阵,定义贪吃蛇的每一节蛇身大小为
一个单位点阵,豆子的大小,游戏地图中障碍物的大小和一节蛇身大小相同,也是一个
单位点阵。LCDl2864要显示各种游戏状态对应的界面,所以定义数组来存储各种界面
对应的位图数据。定义函数实现在LCDl2864的某一坐标位置增加一个单位点阵或删除
一个单位点阵的操作,这样通过调用该函数实现增加蛇头,消除蛇尾,实现在游戏界面
上蛇运动。
关于蛇身运动处理,游戏采用定时器2的中断方式定时,当1 s的定时时间到的时
候,蛇向前运动一个单位点阵。蛇在运动的过程中,主要需要处理如下几个问题:
(1)运动处理。根据用户按键的键值蛇身进行柔体传动。所谓柔体传动,指贪吃
蛇运动的时候并不是整条蛇向一个方向运动的,而是在每个时钟到来时,由蛇头带动每
个点阵的方向都向下一个点阵传播,然后自己向新的方向运动一步。运动后,下一个点
阵由于得到了上一个点阵的方向,就按照此方向同样地运动一步。所以,它会马上填补
上一个点阵的位置,如此类推。实际上在设计贪吃蛇的时候,只需要把蛇尾的那个点阵
去掉,然后在蛇头的新方向上放一个点阵就可以了。因此定义一个函数用于更新点的坐
标,只要知道蛇头或蛇尾的坐标和运动方向,就可以调用该函数更新蛇头或蛇尾的坐标,
同时调用相关显示函数,实现蛇运动处理。
蛇头的方向可以通过按键捕获实时获得。而蛇尾的方向获得要困难得多。为节省空
间,定义一个无符号char型一维数组aucSnake[54]按位存储蛇头方向信息。蛇身运动期
间需要在该数组中记录下每次蛇身移动一步,蛇头的运动方向。定义两个变量分别代表
蛇头和蛇尾,每次存储蛇头运动方向后,代表蛇头的变量加一,以便在下一位置继续存
储蛇头运动方向;代表蛇尾的变量则指向数组的开始位置,通过该变量读取数组中蛇尾
的运动方向(因为存在该数组中蛇头的运动方向,随着蛇头坐标的移动,存在数组开始
位置的运动方向即是蛇尾的运动方向),通过读取的蛇尾运动方向便可以更新蛇尾坐标。
(2)吃到豆子的处理。蛇头坐标与豆子坐标相等,则吃到豆子。如果吃到豆子,
在蛇头位置增加一个单位点阵,并更新豆子的坐标。
大连理工大学专业学位硕士学位论文
(3)放置新的豆子。通过更新豆子的坐标实现放置新的豆子。放置豆子的过程中,
还需要判断新豆子的坐标是否与蛇身或障碍物坐标重叠,如果重叠,则需要重新放置和
判断,直到新的豆子不与蛇身或障碍物坐标重叠为止。
(4)死亡处理。蛇运动的过程中,若蛇头碰到墙,障碍物或自己的身体,则游戏
结束。在游戏中,任何时候按下开始/暂停键,则游戏暂停,可以进一步选择是否退出游
戏。其中游戏暂停的处理是通过关闭定时器2实现的【8—01。
4.2贪吃蛇游戏中的各种状态
贪吃蛇操作过程中有多种状态,采用宏定义,使各种状态一目了然,思路清晰。同
时在编写代码时,采用匈牙利命名规则命名宏,变量,函数名,方便阅读修改Il¨。
贪吃蛇游戏中的各种状态如下:
按键状态:检测具体按到哪个按键,将每个键对应定义一个常量,具体定义如下:
#define KEY NULL(rNTSU)0 //无按键状态
#define KEY..UP (INTSU)I //按上键
#defme KEY—DOWN(INTSU)4 //按下键
#define KEY--LEFT(INTSU)2 //按左键
#define KEY—RIGHT(INT8U)3 //按右键
#define KEY START(rNT8U)5 //按开始键
#define KEY SOUND(1NTSU)6 //按静音键
蛇头的运动状态除了上下左右运动外,还有一个反方向运动的处理。比如当前蛇头
运动状态向右,这时点击左键,蛇头不会向相反方向调转。此时定义的状态就是蛇头反
方向运动。
#define UP fINT8U)0 //蛇头向上
#define DOWN(INT8U)3 //蛇头向下
#define LEFT (rNTSU)I //蛇头向左
#define RIGHT fINT8U)2 臌头向右
#define OPPOSITE(INTSU)4 //蛇头反方向运动
游戏的七种状态:游戏欢迎状态,游戏选择地图状态,游戏等待开始状态,游戏中
状态,游戏中暂停状态,游戏通关状态,游戏结束状态。
#define GAME—STAT—WELCOME (INTSU)0 //七种游戏状态
#define GAME STAT CHANGEMAP(NTSU)I
基于51单片机的贪吃蛇游戏
#define GAME STAT WAJT
#define GAME STAT DONE
#define GAME STAT DEAD
#define GAME STAT GAMING
#define GA~正STAT PAUSE
(INTSU)2
(INTSU)3
(INTgU)4
(INTSU)5
(INT8U)6
选择四种地图,声音静音与否的状态,五种游戏背景音:
#define MAP——0(INTSU)0
#define MAP——1(INTSU)I
#define MAP一2 (INTSU)2
#define MAP一3 (INTgU)3
#define MAP——UNCHOOSE(INTSU)4
∥四个地图
∥未选择地图
#define SOUND—ON(INTSU)I //声音开关
#define SOUND—.OFF(INTSU)0
#define SOUND.—UP——LEVEL(INT8U)I
#define SOUND——EAT(INTSU)2
#define SOUND—ERR (INTSU)3
#define SOUND.—DONE(INTSU)4
#define SOUND..DEAD(INTSU)5
//游戏升级背景音
∥蛇吃豆子背景音
/腊戏错误背景音
//游戏通关背景音
//游戏结束背景音
4.3主函数模块详细设计与实现
主函数模块主要包括两个函数:void main(void)和void T2__ISR(void)
main()函数是整个程序的入口地址,程序从此函数开始执行,将各个模块串联起来,
实现游戏的各种功能。首先完成软件硬件的初始化任务,然后进入一个无限循环,反复
检测有无按键、处理按键、处理蛇运动、处理时钟节拍这四件事情[71。
代码如下:
void main(void)
{
Gamelnit0;
T2Init0;
while(1)
∥硬件和全局交量初始化
∥定时器2初始化
大连理工大学专业学位硕十学位论文
if(GS_ucKeyValue===KEY NULL)
{
ISO_CLOSE0;
Key_Get();
ISO_OPEN0;
)
if(GS_ucKeyValue!=KEY_NULL)
{
KeyManagej’ask();
//关闭中断
∥获取按键
|船开中断
}
if(ucSnakeFlag—GO&&GS—ucGameState—GAME_STAT_GAMING)
{
SnakeControl——SnakeTask0;
ucSnakeFlag
2 STOP;
)
if(ucTimeFlag—GO&&GS—ucGameState—GAME_STAT_GAMING)
{
GameControl_TimeTask0;
ucTimeFlag=STOP;
)
)
)
T2 ISR()函数是一个中断服务子程序,利用T2定时器工作在方式1状态,设置
TL2,TH2初值为十进制数15536,单片机的晶振频率是12MHz,因此T2定时器每记满
50000个数进入中断子程序需要50ms,定时ls只要进入20次中断子程序即可,游戏运
行时间的时钟节拍就是利用T2定时器计算出来的。其中有两个标志位初始状态都是
STOP,ucSnakeFlag是蛇移动速度的标志位,ucTimeFlag是时钟节拍标志位。定义了一
个变量uiT2Cou计数,uiT2Cou与20模除为0,修改时钟节拍标志位ucTimeFlag为GO,
屏幕时间显示加1;同理,蛇根据等级不同,每秒移动速度不同,修改蛇运动标志位
ucSnakeFlag为GO。只有在游戏运行中状态且这两个标志位修改为GO时,才能进行蛇
运动的控制和游戏时钟的控制。蛇运动的快慢则取决于数组auiSpeed里面的值。
代码如下:
void T2_ISR(void)interrupt 5 using 2 . //T/C2中断服务程序入口
基于5l单片机的贪吃蛇游戏
{
ISQCLOSE0;
TF2=0;
++uiT2Cou;
if(uiT2Cou%(auiSpeed[ucGameLevel-1】)一0)
{
ucSnakeFlag=GO;
)
if(uiT2Cou%20—0)
{
ucTimeFlag=GO;
)
if(uiT2Cou>=60480)
{
uiT2Cou=0;
>
ISQOPEN0;
)
4.4按键模块详细设计与实现
该模块包括两部分:按键检测和按键处理。
以下是按键相关的全局变量:
INT8U data GS_ucKeyValue;
INT8U data GSucGameState;
INT8U data GS_ueMapNumber;
1NT8U data GS ucSoundSwitch;
INT8U data GS_ucDirection;
INT8U data ueCheck=CONT;
4.4.1按键检测模块
//关闭中断
//T2定时器的中断标志位置0
∥计数器+1
//根据等级调整蛇的速度
∥时间每1秒走一次
//计数器置0
.“瓠开邙甑
腑放当前按键
//游戏的状态标志
//游戏用到的地图号
∥声音开关标志
∥蛇头当前的方向
∥记录玩家选择继续(CONT)或者退出(EXIT)
按键检测模块提供接口函数Key_Init()和Key_Get()。六个按键对应单片机的六个管
脚,Key_Init()用来初始化各管脚,将各管脚置1,即各管脚为高电平。Key_Get()将检
大连理工火学专业学位硕士学位论文
测这些管脚的电平值,当某一管脚置0,即该管脚为低电平时,将管脚对应的宏定义的
值赋给全局变量GS_ucKeyValue。模块流程图如下图所示。
图4.1按键检测模块流程图
Fig.4.1 Key Detection Module Flow Chart
4.4.2按键处理模块
按键处理模块,主要实现对按键的处理,游戏有六个按键,分别是“方向上键”,“方
向下键”,“方向左键”,“方向右键”,“声音开关键”,“确定键(开始/暂停键)”,在游戏
的不同阶段,按键有不同的功能,比如方向键,在游戏过程中,起到控制蛇运动方向的
作用,而在游戏开始前,选择地图的时候,它又用来选择地图,所以要根据游戏状态的
不同,对按键做不同的处理。游戏分为七种状态:
(1)欢迎游戏状态(GAME STAT WELCOME)
基于51单片机的贪吃蛇游戏
点击确定键进入选择地图画面,点击声音开关键选择是否静音,其他按键无效。
(2) 选择地图状态(G伽STAT C}L州GEMAP)
点击方向键选择游戏地图,点击确定键确定,点击声音开关键选择是否静音。
(3) 游戏等待状态(G伽STAT WAJT)
点击确定键进入游戏状态,点击声音开关键选择是否静音,其他按键无效。
(4)游戏进行状态(Q心伍STAT GAMING)
点击方向键控制蛇运动,点击确定键开始/暂停游戏,点击声音开关键选择是否静音。
(5)游戏暂停状态(GAME STAT PAUSE)
点击方向上下键选择,点击确定键决定退出还是返回游戏,点击声音开关键选择是
否静音,其他按键无效。
(6)游戏通关状态(GAME STAT DONE)
点击确定键返回欢迎画面,点击声音开关键选择是否静音,其他按键无效。
(7) 游戏结束状态(G伽Sn盯DEAD)
第六种和第七种状态的按键处理是一样的,另外,在各种状态下都可以进行的对声
音开关键的处理,因此将这个模块的功能分为七个函数,分别对他们进行处理。函数原
型声明如下:
void KeyManage Welcome(voidl //欢迎界面时的按键处理
void KeyManage ) //选择地图状态时的按键处理.ChangeMap(void
void KeyManage //游戏等待状态时的按键处理.Wait(void)
void KeyManage 1 ∥游戏进行时的按键处理.Gaming(void
void KeyManage ) 游戏暂停状态时的按键处理.Pause(void //
void KeyManage 脯戏通关时的按键处理.Done(void)
void KeyManage
另外定义了函数void KeyManage Task(void)来调用上述定义的七个函数进行按键
的处理。
下面四个函数是在上面七种游戏状态中都要调用的。
void KeyManage SoundManage(voidl //游戏声音开关键的处理
void KeyManage ) 修改游戏的状态_ChangeGameStat(INT8U stat //
void KeyManage //将按键标志置0_PutKeyValue(void)
void GameEnd(iNTSU statl N游戏通关或结束处理
GameEnd()函数根据传入的参数判断当前的游戏状态,分两种状态:游戏结束时在
大连理工大学专业学位硕士学位论文
大屏上显示游戏结束界面,播放游戏结束的背景音,并修改游戏状态的全局变量;游戏
通关时在大屏上显示游戏通关界面,播放游戏通关的背景音,并修改游戏状态的全局变
量。为了更好的说明该模块功能,下面列出了按键处理模块的流程图和状态迁移图。
图4。2按键处理模块流程图
Fig.4.2 KeyProcessing Module Flow Chart
基y-51单片机的贪吃蛇游戏
状者Ⅱ蕃圈
图4 3按键处理模块状态迁移图
Fig 4.3 KeyProcessingModule StateTransitionDiagram
圆一霸。目一四~口
大连理工大学专业学位硕士学位论文
4.5 LGDl 602显示模块详细设计与实现
LCDl602(以后简称小屏)为两行显示,每行16个字符,5伏供电,通过8条数据
线和3条控制线与微控制器相连,通过送入的数据和指令进行工作。LCD控制器内包含
有显示数据RAM(DDRAM),字符发生器ROM(CGROM),字符发生器RAM(CGRAM)。
DDRAM是用来寄存待显示的代码;CGROM用来提供用户所需字符库;CGRAM可以
存储8个用户自定义的字符图形RAM。字符显示位置,第一行从0x00到0x0F;第二
行从Ox40到0x4F。不能显示汉字,内置含128个字符的ASCII字符集字库,只有并行
接口,无串行接口[12J。LCDl602有14个引脚,其定义如下:
1脚VSS:电源+5V
2脚VDD:地
3脚VEE:液晶显示对比度调节端
4脚RS:寄存器选择。RS=o时,选择指令寄存器;RS=I时,选择数据寄存器
5脚RfW=读写信号线。RJW=I时,读操作;R/W=0时,写操作
6脚E:显示板控制使能端
7~14脚DO.D7:双向三态I/O线。其中D7用于读/写忙检测113】
根据上述管脚功能介绍,硬件管脚相关定义如下:
sbit P3—6=P3^6;
sbit P3—5=P3^5;
sbit P3—4=P3^4;
sbit P1—7=P1^7;
#define
#define
#define
#define
LCDl602 RS
LCDl602 RW
LCDl602 EN
LCDl602 BUSY
#define FIRST LINE 0x80
#defme SECOND LINE 0xC0
P3 6
P3 5
P3 4
P1 7
∥第一行第一个点阵位置的十六进制表示
∥第二行第一个点阵位置的十六进制表示
LCDl602显示模块在进行游戏初始化时初始化LCDl602,并显示静态游戏信息。
具体功能如下:
基r 5】单片机的贪吃蛇游戏
刷新游戏运行时间
刷新游戏当前等级
刷新游戏所得分数
刷新游戏音效玎关小喇叭图形
游戏仞始化以后,游戏时间,游戏等级,游戏分数均为0,,背景音图标显示为打开
具体格式如图所示。
LCDl
LM们6L
蚓4.4初始化显示的游戏信息
Fig 4.4 Display Initialization Game Information
在游戏运行过程中,根据需要刷新游戏运行时问、游戏当前等级、游戏分数和背景
音静音信息。游戏初始化时初始化小屏并显示静态游戏信息。
这些游戏信息的字符都可以通过调用CGROM中提供给用户的字符库显示出来,所
以可以直接定义字符数组。只有背景音提示图标需要用户自定义。LCDl602是按照5x7
点阵,横向取模,字节『F序的方式显示个字符。定义两个字模数组Close[8]和Open[Sl,
将数据写入CGRAM中的相应位置,之后便可以像调用CGROM中奉身提供的用户的
字符库一样调用这两个用户自定义的字符了。以下列出了对应的字符数组定义。
staticINT8U codeTime[10]_{一T,⋯i.’ml-㈦e.’:..一0,一0.。:’.一0.⋯0};
staticINT8U codeLevel[7]={’L—e,V.ue.⋯1,。:.-一0}:
staticINT8U code Sco[8]={一S,’c1.~O,’:1,㈣0,一0川0,一0};
static INT8U code Close[8]={0x07,0x0E0x1F,OxlF,0x1EOxOLOx07,0x00.
staticINTSU codeOpen[8]=f 0x0C.0x10,0x00,0xlC,0x00.0x10.0x0C,0x00
具体功能函数如下:
大连理工大学专业学位硕七学位论文
LCDl602显示模块的内部接口函数如下所示,为底层的驱动函数,对LCDl602进
行基本操作,外部函数通过调用下面的内部函数实现相应功能pJ。
void lnitLCD(void) 彻始化LCD屏幕
void WriteCmd(INT8U command,INTSU flag)//通过内部指令向LCD写入指令
void WriteData(INT8U data,INT8U flag) //通过内部指令向LCD写入数据
void SetXY(INT8U X,INT8U y) //定位到屏上由坐标X、Y指定的位置
void CheckBusy(void) //检查LCD屏是否状态为忙,忙则等待
void ShowDefaultlnfo(void) //显示静态游戏信息,即游戏运行时不变的信息
void ShowSound(void) //显示表示背景音的喇叭图形
这里重点说明ShowSound()函数的实现。
void ShowSound(void)
{
INT8U data i=0:
WriteCmd(0x40,1);
for(;i<8;i++)
{
WriteData(Close[i],O);
}
for(i=0;i<8;i++)
{
WriteData(Open[i],O);
)
WriteCmd(0xS0,1);
SetXY(1 3,0);
WriteData(0,0);
SetXY(14,0);
WriteData(1,0);
代码如下:
//发出指令向CGI乙蝴写入数据
∥以上向CG洲写入16字节数据
//向DDI№M写入数据
∥将写入CGRAM的数据显示在LCD上
)
该函数首先调用WriteCrnd()设置向CGRAM写数据,从地址0的位置开始。然后
利用两个for循环将静音标志的字符写入到CORAM中地址0和1的位置。继续调用
一23—
基于51单片机的贪吃蛇游戏
WriteCmd()函数设置为相DDRAM写数据,定位要显示的位置写入数据,完成了用户自
定义字符的显示过程。
外部接口函数如下:
∥初始化游戏信息,提供给GameInit模块调用
INTSU LCD/nfo__InitGamelnfo(void)
函数首先调用函数InitLCD()初始化与LCDl602硬件相关的各管脚信息,然后通过
WriteCmd()函数向屏幕写入指令,设置屏幕属性;设置16x2显示,5x7点阵,8位数据
接口;不显示光标,光标不闪烁;清屏;当读写一个字符后地址指针加1,光标加1。
然后调用ShowDefaultlnfo()显示静态游戏信息,包括时间,分数,游戏等级。
/刷新LCDl602的相应位置,显示要显示的时间
INTSU LCDInfo._TimeDisplay(consl INTgU minute,const INT8U second)
该函数用于显示要显示的参数。函数传入的参数是分钟和秒。参数合法就显示当前
时间。首先在小屏上调用函数SetXY()确定位置,然后通过除以10和模除lO计算出分
钟数和秒数的个位十位,进行写数据,显示到小屏上。
糯4新LCDl602的相应位置,显示要显示的等级
INT8U LCDInfo_LevelDisplay(eonst INT8U levell
该函数用于显示要显示的等级。函数传入的参数是等级。参数合法就显示当前等级
首先在小屏上调用函数SetXY()确定位置,然后进行写数据,显示到小屏上。
旆J新LCDl602的相应位置,显示要显示的分数
INT8U LCDInfo ScoresDisplay(const INTl 6U scores)
该函数用于显示要显示的分数。函数传入的参数是游戏分数。参数合法就显示当前
游戏分数(游戏分数大于等于0,小于等于2100即为合法)。首先在小屏上调用函数
SetXY()确定位置,然后通过除和模除计算出游戏分数的千位、百位、十位、个位。进
行写数据,显示到小屏上。
堋0新LCDl602的相应位置,显示表示背景音开或关的喇叭图形
INTgU LCDInfo_SoundSwitch(eonst INTgU SoundSwitch)
该函数用于显示要显示的背景音状态图标。函数传入参数SoundSwitch,判断该变
大连理工大学专业学位硕士学位论文
量状态,然后在小屏上调用函数SetXY()确定位置,进行写数据,显示到小屏上背景音
开或关的喇叭图形。函数流程图如下图所示。
图4.5 LCDlnfo SoundSwitch函数流程图
Fig.4.5 LCDInfo—SoundSwitch Function Flow Chart
4.6 LCDI 2864显示模块详细设计与实现
LCDl2864(以后简称大屏)功能强大,5V电压驱动,带背光,内置8192个16x]6
点阵、显示容量为128x64,内部由A,B两屏左右分布组成。LCDl2864有18个引脚,
其定义如下:
1脚CSl:片选A屏,低电平有效
基丁5l单片机的贪吃蛇游戏
2脚CS2:片选B屏,低电平有效
3脚GND:地
4脚VCC:电源
5脚VO:液晶显示对比度调节端
6脚Rs:寄存器选择。RS=0时,选择指令寄存器;RS-1时,选择数据寄存器
7脚R/w:读写信号线。RJW=I时,读操作:RAre=0时,写操作
8脚E:显示板控制使能端
9~16脚DB0-DB7:双向三念I/O线
17脚RST:复位端,低电平有效
l 8脚Vout:对比度调节供电
七eB}———————————————一AMPIRE]2%4 r—1
-目目目盟目目口目目础跚跚一
圈4 6 LCDl2864管脚图⋯
F19 4.6 LCDl2864 Pin Diagramll4
LCDl2864并行方式时,数据线DB0~DB7用来传送数据和命令。CSI和CS2为泄
晶显示器的左右半屏的选择端口。被晶的亮度可以使用改变VO的输入电压来改变【l⋯。
LCDl2864显示的点阵方式是128x64。横向O一127共128个点阵,分为左右两屏.
各占0.63共64个点阵;纵向O—63共64个点阵,每8个纵向点阵(8个点阵可以看成
8位.8位为I字节)构成一页,将屏幕分成8页,并按照纵向取模,字节倒序的方式
显示一字节内容II⋯。由于将4x4点阵定义为单位点阵的大小,并定义单位点阵为豆子的
大连理工大学专业学位硕士学位论文
大小,因此将屏幕按4x4比例从128x64缩小为32x16(即将4x4点阵看成LCDl2864
上的一个最小点阵)。将游戏界面划分成游戏区与信息区,其中游戏区为25x16(其中
去掉上下边框,游戏区的实际范围是25x14),信息区为7x16。本模块提供了LCDl2864
的初始化,显示游戏界面的函数,显示选框的函数、显示游戏状态的函数(游戏运行或
暂停)和显示图形的函数。具体功能如下:
(1)对LCDl2864的初始化
(2)在LCDl2864上显示图形(三角或正方形)
(3)在LCDl2864上显示选框
(4)在LCDl2864上显示各种界面
根据上述管脚功能介绍,硬件管脚相关定义如下:
sbit P2 2=P2^2:
sbit P2 1=P2^1:
sbit P2 0=P2^O:
sbit P2 4=P2^4;
sbit P2 3=P2^3:
#define
#defme
#define
#define
#define
MAINLCD RS
MA仆iLCD RW
MAINLCD EN
MAINLCD CSA
MAINLCD CSB
P2 2; //硬件对应的管脚
P2 1:
P2 0:
P2 4:
P2 3:
利用PO口对应的八个管脚与LCDl2864的八个数据管脚相连接。利用此八个管脚
输入不同的高低电平,进行对LCDl2864的各种控制,包括打开关闭屏幕,设置页,设
置行等。具体定义如下:
#define
#define
#define
#define
#define
#define
MAINLCD D敝K
SET 0N
SET OFF
SET PAGE
SET COL
SINGLE SCREEN COL
PO
Ox3F
0x3E
0xb8
0x40
64
—27~
/甩CDl2864打开
/几CDl2864关闭
//设置页数
//设置行数
∥单屏幕最大列数
基于5I单片机的贪吃蛇游戏
MAP NUMBER为游戏中全屏显示的界面个数,一共五个,具体是:游戏欢迎界面,
选择游戏地图,游戏运行界面,游戏结束界面,游戏通关界面。GAMESEG START—COL 和G枷SEG START ROW设定了游戏区的起始列与起始行。FIGURE MMBER为
存储图形(三角,空心正方形和实心正方形)位图数组的行下标,OPTION NUMBER
为存储选择Yes和No箭头在大屏上位置的数组的下标。SCREEN SIZE为存储全屏显
示游戏五幅位图数组的列下标。具体定义如下:
#define MAP NUMBER 5 //游戏相关信息
#define GAMESEG——START——COL 6
#define GAMESEG—START—ROW 1
#define FIGURE—NUMBER 3
#define OPTl0NJⅢMBER 2
#define SCREEN SIZE 1024 //屏幕的大小(字节)
OPT MAP NUMBER为存储游戏信息区三角形箭头显示位置的数组的下标,同时
又代表游戏运行时的游戏信息区对应的两种状态:游戏运行与游戏暂停的信息区的显
示。其他宏定义包括对信息区的起始页,起始列,信息区的列大小,页大小的定义。其
中信息区起始页被定义为4,因为在游戏运行或暂停时信息区范围内的o__3页显示静态
的蛇图像,只有信息区范围内的争.7页会因为游戏的运行或暂停而显示不同的图像,
所以只要修改信息区范围内4-7页的显示内容即可。具体定义如下:
∥可修改的信息区的信息
#define
#defme
#define
#define
#define
OPTⅣL气P NUMBER
MODIFY START X
MODIFY START Y
MODIFY SIZE X
MODIFY SIZE Y
2
1
4
19
4
//信息区的起始列
∥信息区的起始页
//信息区的列大小
信息区的页大小
选择游戏地图时,具体选择哪个地图的选框的长度与宽度。具体定义如下:
#define CH00SE LENTH 4 //选框的长度
#define CHOOSE WIDE 59 //选框的宽度
按照4x4的点阵大小定义的基本图形。三角形用作“选择是否退出游戏’’的箭头标
记;空心正方形用来表示蛇身和豆子;实心正方形表示障碍物(组成地图的基本点)。
大连理工大学专业学位硕士学位论文
具体定义如下:
#define FIGURE——STRIANGLE(INT8U)0
#define FIGURE_SQUARE(INTSU)I
#define FIGURE—BALK (INT8U)2
∥三角形
//空心正方形
∥实心正方形
另外定义了一些数组,用于存储显示各种图形的位图。具体说明如下:
const INTl6U code GS_auiMap[4][MAX_X】
以4x4大小为单位点阵,按此比例缩小(即将4x4点阵看成LCDl2864上的一个最
小点阵),游戏区变为25x16(此处不忽略边框,因为边框被看做地图的一部分)。该
数组按照25x16的比例存储四幅游戏地图的位图。其中MAX—X的值是25。
static const INT8U code aucFigure[FIGURE_NUMBER]【4】
存储用于显示三角形,空心正方形和实心正方形的位图。利用三角形可以表示“选
择是否退出游戏’’的箭头标记;空心正方形用来表示蛇身和豆子;实心正方形表示障碍
物(组成地图的基本点)。
static const Point code astOptPos[OPTION_NUMBER]
存储在信息区指向YES和NO的坐标地址。
static const Point code astMapOpt[4]
存储在选择游戏地图界面,四幅地图在大屏上的起始地址。
INT8U code aucFullScreen[MAP_NUMBER][SCREEN_SIZE]
存储全屏显示五种游戏状态的位图。
INT8U auclnfoMes[OPT_MAP_NUMBER][MODIFY SlZE X幸MODIFY二SIZE-.Y】
存储游戏运行和游戏暂停时,信息区显示信息的位图。游戏运行时显示”COME
ON!”;游戏暂停时显示”EXIT YES NO”。
static INT8U★pAllMap[MAP娜MBER+OPT MAP NUMBER]
是一个指针数组,存储五个游戏状态对应的全屏图片位图的指针和游戏运行,暂停
时信息区位图的指针。
LCDl2864显示模块的内部接口函数如下所示,为底层驱动函数,对LCDl2864进
行基本操作,外部函数通过调用下面的内部函数实现相应功能。
基于51单片机的贪吃蛇游戏
void MWriteCmd(INTSU ucCmd); //向大屏进行写命令
void MWriteData(INTSU ucDam); //向大屏进行写数据
void ReadB”e(INT8U uePage,INTSU ucC0l,INT8U·ueData);//根据传入的前两个参
数ucPage和ucCol,读出大屏对应页,对应列上一个字节的信息,并存储到指针ucData
所指向的变量里。
INT8U DisplayByte(INT8U uePage,INT8U uc Col,INTSU ucDa:ca);//显示一个字节的数
据,即纵向八个点阵。
INT8U DiSpIayFigu鹏(Point plPos,INTSU ucType,INTSU ucMode);//在指定坐标位置,
显示或清除一个4x4的图形(显示或清除的图形包括三角形,空心正方形和实心正方形)。
INlSU DisplayGsMap(void); //:在游戏区显示选择的对应地图
外部接口函数如下:
∥初始化LCD
void MainLCD Init(void)
该函数初始化大屏,设置全局变量Gs_ucMapN岫ber为未选择地图,并在大屏上
显示游戏欢迎界面,初始化指针数组pXllMap[7]的值为五个游戏状态对应的全屏图片位
图的指针和游戏运行,暂停时信息区位图的指针。。
//在屏幕上全屏显示游戏的界面
INTSU MainLCD_DisplayFullSereen(rNTsU ucPi∞
通过游戏界面的编号ucPic,全屏显示游戏某种状态对应的界面。通过调用Display
B”e()函数读取aucFullScreen【5][1024】数组里存储的位图信息完成界面显示。如果玩家
选择了游戏地图,同时显示当前的游戏地图。
∥在信息区显示游戏的选择界面和加油界面
INT8U MainLCD ModifyInfoSeetion(INT8U uePie)
通过游戏界面的编号uePie,在信息区显示游戏是否退出的选择界面或加油界面。
通过调用DisplayByte()函数读取指针数组v?alMap[7]完成显示。
∥在信息区退出的界面,询问是否退出
INT8U MainLCD_.ChooseOpt(INTSU ucDirect)
根据按键的方向ucDirect调用DiSplayFigure()函数,在信息区相应的新坐标位置显
一30一
大连理工大学专业学位硕士学位论文
示三角形箭头选项,旧坐标位置清除三角形箭头选项。流程图如下所示。
图4.7 MainLCD ChooseOpt函数流程图
Fig.4.7 MainLCD ChooseOpt Function Flow Chart
//在选择的游戏地图上画边框,代表选中此地图
1NT8U MainLCD_ChooseMap(INT8U ucOld,INTSU ucNew)
ucOld是要删除的地图编号,ucNew是要选择的地图编号。通过掩码数组进行逻辑
运算,删除和显示上下两行和左右两行的边框。流程图如下所示。
基于51单片机的贪吃蛇游戏
I参熬:
一ueOId旧田的蝙弓
卜“。w新圈的编号
图4.8 MainLCD ChooseMap函数流程图
Fig.4.8 MainLCD_ChooseMap Function Flow Chart
一32—
大连理工大学专业学位硕士学位论文
4.7蛇运动控制模块详细设计与实现
本模块主要进行蛇运动的控制,触发各种事件的响应。
主要开发内容包括蛇的方向、蛇的速度、蛇的位置的控制,吃食物、撞边界、撞自
己判断并调用其它模块进行相应操作。根据不同的等级,使贪吃蛇以不同的速度前进。
具体功能如下:
(1)贪吃蛇按一定方向移动
(2)贪吃蛇根据按键改变方向
(3)贪吃蛇判断吃食物
(4)贪吃蛇判断撞边界
(5)贪吃蛇判断撞自己
(6)修改游戏地图标记表
相关变量的定义:
const INT8U code ucEns=Ox03; //掩码
const INT1 6U code uiEns=0x8000;
INTl 6U data uiOameScores; //游戏分数
INT8U data ucGameLevel; //游戏等级
#define MAX X 25 //地图的极限坐标
#define MAX—.Y 1 4
#define SN舢江LENGTH 216 //蛇的极限长度为213(为被4整除取216)
#defme ARR LENGTH (SNAKE 蛇数组的大小_LENGTH/4) //
typedef struct{
INT8U x:
INT8U y;
)Point;
Point data stHead;
Poim data stEnd;
Point data stFood;
INTl 6U data uiT:
INTl 6U data uiE:
INT1 6U data uiTime;
INT1 6U auiMap[MAX_X];
//定义点坐标的结构体
//蛇头坐标
//蛇尾坐标
∥豆子坐标
抛(蛇头)运动轨迹数组的下标
∥蛇(蛇尾)运动轨迹数组的下标
∥游戏时间
//地图数组
一33—
基于51单片机的贪吃蛇游戏
INT8U aueSnake[ARR_LENGTH]; ∥蛇运动轨迹数组
//初始化与蛇有关的信息
void SnakeControl_Init(void)
负责蛇的初始化。具体包括初始化豆子的位置,蛇头蛇尾坐标,游戏分数,游戏时
间初始化为O,游戏等级初始化为1。根据四副地图中具体选择了哪副地图,初始化地
图数组,同时初始化蛇运动轨迹数组,并在大屏上初始化蛇【15】【161。
图4.9蛇控制模块流程图
Fig.4.9 Snake Control Module Flow Chart
大连理工大学专业学位硕士学位论文
//记录游戏地图中蛇、障碍的位置,蛇移动时查询、修改具体点的位值
INT1 6U SnakeControl—OameMapSign(Point stP,INT8U ucMode)
给地图数组的一位做置l,置O,查询的操作。该函数根据参数不同,可以实现对
地图数组不同操作。如果对地图数组的一位做增加操作,则利用掩码查询地图数组相应
位未被使用,并将其置1;如果对地图数组的一位做删除操作,则利用掩码查询地图数
组相应位已经被使用,并将其置O;如果对地图数组的一位做查询操作,则利用掩码查
询地图数组相应位,将其坐标值返回。流程图如下图所示。
图4.1 0 SnakeControl_GameMapSign函数流程图
Fig.4.1 0 SnakeControl_GameMapSign Function Flow Chart
∥根据运动方向,更新坐标
void SnakeControl—GetP(Point·stP,INT8U ucDirection)
通过给定点坐标和一个方向,更新坐标。传递给函数的两个参数,一个是蛇头(或
蛇尾)当前坐标,一个是蛇头(或蛇尾)当前运动方向。通过判断当前蛇头(或蛇尾)
基于51单片机的贪吃蛇游戏
运动方向(上,下,左,右),增加X坐标或Y坐标,如果当前蛇头(或蛇尾)运动方
向不是上述四种,则游戏结束。
/r 、
I 开始J
\ /
工
SnakeControl_GameMapSign
(stEnd,DEL)
将旧蛇尾在地图上的位置写
。0’,表示是空
上
MainLCD_DisplaySquare
(stEnd。DEL)
删除蛇尾
J
Point SnakeControI_GetP
(&stEnd,ucDirection)
计算新蛇尾坐标并更新蛇尾坐标
◆
SnakeControl—.GameMapSign
(stHead,ADD)
将新蛇头在地图数组上的位置写
‘l’,表示该点是蛇身体
上
MainLCD_DisplaySquare
(stHead,ADD)
在新的位置显示蛇头
J
/r 、
I 结束J
\ /
图4.1 1 SnakeControl Move函数流程图
Fig.4.1 1 SnakeControl—Move Function Flow Chart
//蛇移动的处理
INT8U SnakeControl-Move(void)
蛇移动的处理。蛇移动一步,就调用SnakeControl_GameMapSign()函数将蛇尾坐标
大连理工大学专业学位硕士学位论文
在地图数组上删除;调用MainLCD
通过蛇(蛇尾)运动轨迹数组的下标uiE和掩码ucEns,从蛇运动轨迹数组aucSnake[54】
计算出蛇尾方向。首先左移蛇尾下标uiE若干位,通过掩码筛出蛇尾方向是哪两位,通
过与操作从aucSnake[54]数组里提出蛇尾的方向,再右移与刚才左移位数相同的次数,
就完成了蛇尾方向的计算。代码如下:
ucTempDirection=( aucSnake[uiE>>2]&(ucEns<<(2士(uiE%4))))>>(2幸(uiE%4));
根据蛇尾方向调用函数SnakeControl GetP()更新蛇尾坐标,将新蛇头位置在地图数组上
置1,并在大屏上显示新蛇头,流程图如上图所示。
//蛇吃食物后的处理
INT8U SnakeControl_Eat(void)
蛇吃食物后的处理。蛇吃食物以后,调用GameControl—Score()函数增加游戏分数,
当游戏分数增加到2100(即吃了210个豆子以后),游戏通关,退出该函数。否则,调
用SnakeControl
数Sound play()播放吃豆子背景音,调用GameCon仃ol SetFood()随机生成新豆子。
//处理蛇的各种状态
void SnakeControl__SnakeTask(void)
该函数针对在游戏运行时蛇的各种状态,调用各种函数处理这些状态。
首先详细讲解在游戏运行时如何将蛇头的方向存在一个一维数组aucSnake[54】中。
代码如下所示:
∥通过方向写蛇运动数组
aucSnake[(uiT%SNAKE_LENGTH)>>2】&=~(ucEns<<(2木(ui”甜)));
aucSnake[(uiT%SNAKE__LENGTH)>>2】15(GS_ucDireetion<<(2木(uiT%4)));
uiT=(uiT+1)%SNAKE_LENGTH;‘忉
定义了一个数组INT8U aucSnake[ARR LENGTH],用来存储蛇的运动轨迹。蛇头
的运动轨迹和蛇身蛇尾的运动轨迹一致,所以该数组只存储蛇头的当前运动轨迹即可。
蛇的运动轨迹包括上、下、左、右四种状态,程序中定义了四个宏:
#define U(INTSU)0 //存在蛇数组中表示蛇头和蛇尾的方向
#define D(INT8U)3
#define L(1NT8U)I
#define R(INTSU)2
O,3、1、2这四个数对应四个方向,即上、下、左、右四种状态,转换成16进制
基于51单片机的贪e-e_舵游戏
数位为Ox00、Oxl l、Ox01、0x10。为节省空间,程序设计成按位存储,即一个数组元素
为一个字节8位,2位存储一个蛇的运动轨迹,一个数组元素可以存储四个蛇的运动轨
迹。这样算来,定义该数组大小为54,就可以同时存储216蛇头运动轨迹。
代码第一句的作用是将数组下标元素的对应两位清零,然后记录当前蛇头的方向到
这两位,将uiT加l。这样就完成了将蛇头的方向存在一维数组aucSnake[54]fih。
接下来调用函数KeyManage SnakeControl
GetP()函数更新蛇头坐标。如果蛇头坐标与豆子坐标相等,则调用SnakeControl Eat()
函数处理吃到豆子以后的操作。如果蛇头超出大屏边界,游戏结束;否则查看是否撞到
障碍或蛇身,撞到同样游戏结束,否则调用SnakeControl Move()函数处理蛇移动【18。201。
4.8游戏控制模块详细设计与实现
游戏控制模块的主要功能是控制游戏运行时间、游戏分数、游戏级别等与小屏相关
的信息,以及控制豆子在大屏的显示。具体功能如下:
(1)在大屏空位置上随即放置豆子
(2)更新游戏分数,并在小屏显示当前游戏分数
(3.)更新游戏等级,并在小屏显示当前游戏等级
(4)更新游戏时间,并在小屏显示游戏的运行时间
(5) 当音效开启的时候,吃豆子和升级要产生相应的声音
具体函数讲解如下:
∥随机生成豆子并显示
INT8U GameControl—SetFood(void)
在大屏空位置上,随机放置豆子,调用MainLCD_DisplaySquare()显示新豆子,函
数返回新豆子的坐标。这里详细讲解随机放置豆子的原理。LCDl2864的点阵是128x64,
当前我们定义4x4点阵为单位大小(即一个豆子的大小),这样屏幕的坐标被等比缩小
为25x16,如前所述,去掉上下边框,实际游戏区的大小是25x14,都在就是这个范围
内随机生成。我们利用srand(TL2)和rand()函数可以生成从TL2到32767之间的随机数,
而TL2是定时器2的低八位,一直变化,这样更体现了随机的可信度。
之后利用i=rand()%25随机出X的坐标,把当前的地图数组i位置的元素同0xFFFF
比较,判断豆子的X坐标值为i时,Y是否有空位。如果有空位,则利用0xOOFF!=
(auiMap[i]>>8)语句判断豆子的X坐标值为i时,高8为是否有空位。有空位则在0到7
(无空位在8到13之间生成随机数)之间随机生成Y坐标值j,判断i,j相交位置是否
大连理工大学专业学位硕士学位论文
有空位,有空位则在此位置生成豆子,反之重新随机生成Y坐标值j。
图4.12游戏控制模块流程图
Fig.4.12 Game Control Module Flow Chart
//更新游戏分数
INTSU GameControl—Score(void)
更新小屏的游戏分数。每次蛇头坐标与豆子坐标重合,就更新一次调用一次该函数。
函数调用一次,游戏分数uiGameScores加10,并调用LCDInfoScoresDisplay()函数在
小屏上刷新分数。当游戏分数uiGameScores为100,300,600,1000,1500时,GameControl
基于51单片机的贪吃蛇游戏
UpLevel()函数被调用,使ucGameLevel加1。
∥更新游戏等级
INTSU GameContro¨pLevel(void)
更新小屏的游戏等级,并产生升级的声音。每次调用该函数,将修改游戏等级
ucGameLevel加1,调用LCDInfo ScoresDisplay()函数在小屏上刷新分数,并调用
Sound_Play()函数发出游戏升级背景音。
∥更新游戏时间
void GameControl__TimeTask(void)
小屏时间每秒加1。只要游戏处于运行,uiTime每秒加1(通过T2定时器实现准确
定时),并将uiTime以uiTime/60:uiTime%60的格式,更新小屏的游戏时间。
4.9背景音模块详细设计与实现
4。9。1 单片机实现播放音乐的原理
单片机演奏音乐都是单音频率,因此单片机奏乐只需弄清楚两个概念即可,也就是
音调和节拍。音调表示一个音符唱多高的频率,节拍表示一个音符唱多长的时间。
在音乐中所谓“音调’’,其实就是我们常说的“音高"。在音乐中常把中央C上方
的A音定为标准音高,其频率f--440Hz。当两个声音信号的频率相差一倍时,也即f2=2fl
时,则称也比f1高一个倍频程,在音乐中1(do)与l,2(来)与2⋯⋯正好相差一个
倍频程,在音乐学中称它相差一个八度音。在一个八度音内,有12个半音。以11八
音区为例, 12个半音是:1一#1、#1_2、2M#2、#2---3、3---4、4一#4,#4—5、
5一#5、#51、6-#6、#卜7、71。这12个音阶的分度基本上是以对数关系来
划分的。如果我们只要知道了这十二个音符的音高,也就是其基本音调的频率,我们就
可根据倍频程的关系得到其他音符基本音调的频率。
知道了一个音符的频率后,要产生音频脉冲,只要算出某一音频的脉冲(1/频率),
然后将此周期除以2,即为半周期的时间,利用定时器计时这个半周期的时间,每当计
时到后就将输出脉冲的I/0反相,然后重复计时此半周期的时间再对I/0反相,就可以
在I/O脚上得到此频率的脉冲。
对于音乐的节拍,在单片机上控制一个音符唱多长可采用循环延时的方法来实现。
首先,就需要确定一个基本时长的延时程序,比如说以十六分音符的时长为基本延时时
大连理工大学专业学位硕士学位论文
间,那么,对于一个音符,如果它为十六分音符,则只需调用一次延时程序,如果它为
八分音符,则只需调用二次延时程序,如果它为四分音符,则只需调用四次延时程序,
依次类推。通过上面关于一个音符音调和节拍的确定方法,我们就可以在单片机上实现
演奏音乐了忙¨。
4.9.2功能实现
该模块实现播放背景音的功能。在蛇吃到豆子、游戏升级、游戏通关、游戏失败、
用户无效按键时分别提示不同的音乐。基于单片机播放音乐的理论基础,利用定时器0
作为音符定时器,工作于方式l,中断操作,16位定时器;利用定时器1作为音长定时
器,工作于方式l,查询操作,16位定时器。具体的实现方法为:将乐谱中的每个音符
的音调及节拍变换成相应的音调参数和节拍参数,按位存储,存放在数组中,通过程序
取出一个音符的相关参数,播放该音符,该音符唱完后,接着取出一个节拍的相关参数,
以此类推。乐曲结束用节拍参数为00H来表示瞄J。该模块对应流程图如下:
图4.13背景音模块流程图
Fig.4.1 3 Baekgroud Sound Module Flow Chart
针
志
基于51单片机的贪吃蛇游戏
下面列出了背景音模块用到的相关定义:
#define SOUND—ON(INTSU)I ∥背景音开关
#define SOUND——OFF(1NT8U)0
#define SOUND—UP—LEVEL(INTSU)I //5种背景音
#define SOUND—.EAT(INT8U)2
#define SOUND——ERR(INT8U)3
#define SOUND——DONE(INT8U)4
#define SOUND—DEAD (INTSU)5
对应五种背景音,分别定义五个一维数组存放背景音的音调与音长。
static INT8U code aucMusicErr[]; //无效按键背景音
static 1NT8U code aucMusicEat[]; //吃豆子背景音
static INT8U code aucMusicLevel[];∥游戏升级背景音
static INT8U code aucMusicDone[]; //游戏通关背景音
static INT8U code aucMusicDead[]; //游戏结束背景音
static INTl6U code auiFreTab[12]; //原始频率表
static INT8U code aucSignTab[7】={0,2,4,5,7,9,11};//1~7在频率表中的位置
static INT8U code aucLengthTab[7]={1,2,4,8,1 6,32,64); //音符节拍
static INT8U data ucSound__TH0.ucSound_TLO; //音符定时器初值暂存
static INT8U data ucSound_THl.ucSound TLl; //音长定时器初值暂存
sbit sBeep=P3^7; //定义输出管脚,输出周期不同的方波
具体定义如下四个函数:
//背景音模块初始化
INT8U Sound_Irfit(void)
初始化时,将音效设置为打开状态,设置定时器状态与初值等。
//中断函数,将对应管脚周期性置反
void BeepTimer0(void)interrupt 1 using 2
采用定时器0工作于方式1状态,将对应管脚周期性置反,并重新对定时器赋初值。
//根据游戏状态的不同,调用内部函数播放不同的乐曲
INT8U Sound_PIay(INT8U const ucSoundState)
函数根据传入的参数不同,调用MusicPlay()函数播放不同的背景音。流程图如下:
I参数:
;ueSoundState标志调用音乐时的游戏状态
图4.14 Sound_Play函数流程图
Fig。4.14 Sound._Play Function Flow Chart
//按照指定音调、音长播放背景音
INT8U MusicPlay(INT8U const·pucSound,INT8U const ucTune,INT8U e.幻nst
ucOctachord,INTl 6U COP_St mSpeed)
函数根据传入的参数,将音调节拍等分解进行处理,转化成音乐播放。流程图如下:
I参数:
lpucSound指向背景音符数组的指针
IucTune背景音调号0—1 l
IucOctachord背景音升降8度标志1~3
L.当speed背景音演奏速度l~12000
图4.15 MusicPlay函数流程图
Fig.4.1 5 MusicPlay Function Flow Chart
大连理工大学专业学位硕士学位论文
结论
基于51单片机设计的贪吃蛇游戏,可以充分发挥单片机的性能,体现嵌入式系统
功耗低,节能,便携性好的特点,给人们的日常生活带来轻松快乐。本次论文按照设计
要求完成了以下工作:
(1)根据实际要求进行了系统硬件电路的设计。以P87C51单片机为核心,扩展
了外围电路,加入LCD,扬声器,按键等构成了贪吃蛇游戏的硬件系统。
(2)软件方面整个游戏采用C语言编写,大大加快了软件开发速度,缩短了开发
周期,而且C语言编程可以方便地在各种型号的单片机上移植。
(3)游戏设计上考虑到嵌入式系统存储容量有限的特点,利用一个一维数组存储
蛇头的运动轨迹,大大节省了存储器空间的使用。代码编写上参考了许多良好的编程规
范,代码可读性增强。
(4)游戏除了拥有传统贪吃蛇游戏的功能外,又加入了地图选择的功能,给用户
带来更多的挑战。丰富的背景音在游戏上给予用户更多的提示,并根据个人需要可以随
时选择打开或者关闭背景音。
当然,由于条件所限,这里还有很多不足。例如不能进行全程持续背景音操作;游
戏的界面应该更加绚丽多彩等,随着学习的深入以后可以在此基础上继续拓展,丰富功
能。
基于5l单片机的贪吃蛇游戏
参考文献
[1]郭天祥.新概念51单片机C语言教程:入门、提高、开发、拓展全攻略[M].北京:电子工业出版
社,2009.
[2]龙脉工作室.51单片机C语言应用开发技术大全[M].北京:人民邮电出版社,2008.
[3]普拉塔.C Primer Plus:第5版[M].北京:人民邮电出版社,2005.
[4]Keil Software-Cx51编译器用户手册[M/oL].2001.
[5]周润景,张丽娜,刘印群.PROTEUS入门使用教程[M].北京:机械工业出版社,2007.
[6]P87C51RA2/RB2/RC2/RD2 DATA S脏ET[M/OL].2003.
[7]樊永显,许勇,张向文等.基于STC89C54RC/RD+单片机的游戏机系统设计[J].湖南工业大学学
报,2007,21(5):66—69.
[8]金春霞,白秋产.基于J21CE技术手机游戏开发与实现[J].计算机与数字工程,2008,36(4):177—
179.
[9]王宏宇.VF游戏设计一贪吃蛇[J].中国科技信息,2007(7):91—92.
[10]李德建,姚远程,周东杰.基于SOPC架构的贪吃蛇游戏研究与设计实现[J].科技创新导报,
2008(31):26-27.
[11]林锐,韩永泉.高质量程序设计指南:C++/C语言[M].北京:电子工业出版社,2007.
[12]Specification for LCD Module TSl620一l[M/oL].SHENZHEN TECHSTAR ELECTRONICS CO.,LTD.
n3]SMCl602A LCM使用说明书[M/oL].长沙太阳人电子有限公司,2001.
[14]Specification for LCD Module AGl2864C[M/0L].晶采光电科技股份有限公司,2003.
[15]李振军,成良玉.基于MIDP的Java手机游戏开发方法的分析与实现[J].计算机应用,2004,24
(3):237-241.
[16]唐天兵,严毅,陈纬文.基于J2ME的手机游戏开发与实现[J].广西大学学报,2005,30(7):51—53.
[17]拉伯罗斯.嵌入式实时操作系统uC/0s—II EM].北京:北京航空航天大学出社,2003.
[18]WolfW.Hardware—Software Co—Design of Embedded Systems Proceedings[J].the
IEEE,2004,82(7):123—126.
[19]KuoKai Shyu.A Newly Robust Controllet Design for the Position Control of
Permanent-Magnet Synchronous Motor[J].IEEE Trans.Ind.Electron,2002,49(3):558—564.
[20]Karl J Astrom.The Application of Keil and Proteus in MCU Game Desisn[J].the
IEEE,2001,80(5):23—27.
[21]谢少伟.基于MCS-51单片机功能完善的音乐播放程序设计[J].电子技术,2007,36(II):36-39.
[22]王健,林原珩.带音效的手机游戏的设计与实现[J].长春师范学院学报,2008,27(5):53—55.
—-46—·
大连理工大学专业学位硕士学位论文
致谢
经过几个月的努力,学位论文终于画上了~个圆满的句号。本次项目涉及到软件与