基于ARM9的贪吃蛇游戏
摘要
本课程
设计
领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计
是使用我们学习过的嵌入式系统的有关知识,在ARM9嵌入式硬
-II的软件开发框架下,实现一个还有AD转换,按键,LCD件开发平台和μC/OS
等硬件功能的实时多任务的简单贪吃蛇的设计。文中首先μC/OSII系统和ARM9进行了介绍,然后对设计要求进行分析,给出了系统总体上设计,并对各个功能模块进行了介绍,紧接着给出了 系统软件设计,最后介绍了系统的调试方法和测试结果。
关键词:μC/OS ARM9 嵌入式 贪吃蛇
一 开发环境简介
1、μC /OS-II操作系统
μC/OS-II是一种可移植的,可植入ROM的,可裁剪的,抢占式的,实时多任务操作系统内核。它被广泛应用于微处理器、微控制器和数字信号处理器。uC/OS-II只是一个实时操作系统内核,它仅仅包含了任务调度,任务管理,时间管理,内存管理和任务间的通信和同步等基本功能。没有提供输入输出管理,文件系统,网络等额外的服务。但由于uC/OS-II良好的可扩展性和源码开放,这些非必须的功能完全可以由用户自己根据需要分别实现。 uC/OS-II目标是实现一个基于优先级调度的抢占式的实时内核,并在这个内核之上提供最基本的系统服务,如信号量,邮箱,消息队列,内存管理,中断管理等
μC/OS-II 是专门为计算机的嵌入式应用设计的, 绝大部分代码是用C语言编写的。CPU 硬件相关部分是用汇编语言编写的、总量约200行的汇编语言部分被压缩到最低限度,为的是便于移植到任何一种其它的CPU 上。
μC /OS-II 中最多可以支持64个任务,分别对应优先级0,63,其中0 为最高优先级。63为最低级,系统保留了4个最高优先级的任务和4个最低优先级的任务,所有用户可以使用的任务数有56个,每个任务都有不同的优先级,用户在创建任务的时候定义该任务的优先级。μC/OS操作系统中每个任务可以有5种状态:休眠态、就绪态、运行态、等待或挂起状态、中断态,在任一个时候,任务的状态一定是这5种状态之一。任务在等待消息、邮箱或者信号量等事件的到来的时候会进入挂起状态,当任务接到消息以后,则处于就绪状态。
uC/OS-II的时间管理是通过定时中断来实现的,该定时中断一般为10毫秒或100毫秒发生一次,时间频率取决于用户对硬件系统的定时器编程来实现。中断发生的时间间隔是固定不变的,该中断也成为一个时钟节拍。
uC/OS-II要求用户在定时中断的服务程序中,调用系统提供的与时钟节拍相关的系统函数,例如中断级的任务切换函数,系统时间函数。 在ANSI C中是使用malloc和free两个函数来动态分配和释放内存。但在嵌入式实时系统中,多次这样的操作会导致内存碎片,且由于内存管理算法的原因,malloc和free的执行时间也是不确定。
uC/OS-II中把连续的大块内存按分区管理。每个分区中包含整数个大
小相同的内存块,但不同分区之间的内存块大小可以不同。用户需要动态分配内存时,系统选择一个适当的分区,按块来分配内存。释放内存时将该块放回它以前所属的分区,这样能有效解决碎片问题,同时执行时间也是固定的。
对一个多任务的操作系统来说,任务间的通信和同步是必不可少的。uC/OS-II中提供了4种同步对象,分别是信号量,邮箱,消息队列和事件。所有这些同步对象都有创建,等待,发送,查询的接口用于实现进程间的通信和同步。uC/OS-II 采用的是可剥夺型实时多任务内核。可剥夺型的实时内核在任何时候都运行就绪了的最高优先级的任务。 uC/os-II的任务调度是完全基于任务优先级的抢占式调度,也就是最高优先级的任务一旦处于就绪状态,则立即抢占正在运行的低优先级任务的处理器资源。为了简化系统设计,uC/OS-II
规定
关于下班后关闭电源的规定党章中关于入党时间的规定公务员考核规定下载规定办法文件下载宁波关于闷顶的规定
所有任务的优先级不同,因为任务的优先级也同时唯一标志了该任务本身。
随着信息化技术的发展和数字化产品的普及,以计算机技术、芯片技术和软件技术为核心的嵌入式系统再度成为当前研究和应用的热点。
对功能、可靠性、成本、体积和功耗严格要求的嵌入式系统一般由嵌入式微处理器、外围硬件设备、嵌入式操作系统以及用户的应用程序等四个部分组成,其中嵌入式微处理器和嵌入式操作系统分别是其硬件和软件的核心。
ARM处理器由于其具有小体积、低功耗、低成本、高性能等特点,广泛应用在16/32位嵌入式RISC解决
方案
气瓶 现场处置方案 .pdf气瓶 现场处置方案 .doc见习基地管理方案.doc关于群访事件的化解方案建筑工地扬尘治理专项方案下载
中,几乎占有嵌入式微处理器市场分额的75% ,本文选定三星公司生产的一款基于ARM920T核的高性能低功耗SOC芯片S3C2410作为移植方案的硬件平台。市场上主流的嵌入式实时操作系统有Vxworks、pSos、WinCE、Linux等,基于实时性、成本以及开发难度方面的考虑,我们选择uC/OS II——开放源代码的嵌入式实时操作系统。
采用基于ARM9的S3C2410嵌入式微处理器,可以使系统具备高性能的运算能力的同时便于与各种外设连接扩展,简化了硬件设计,维持小型化的同时降低了系统成本。uC/OS II作为一个源代码公开的操作系统,在具体应用中稳定可靠,并且支持uIP TCP/IP
协议
离婚协议模板下载合伙人协议 下载渠道分销协议免费下载敬业协议下载授课协议下载
栈、ucGUI等,可扩展性强,功能强大。本系统采ARM9+uC/OS II开发设计,具有精度高、运行稳定、实时性好、抗干扰能力强、性价比高的特点,可以在各种工业场合中广泛应用,达到了设计的初衷。
2、试验箱基本硬件信息
1(S3C2410-S Core 小板:采用S3C2410X 处理器,64M NANDFLASH 64RAM。 2(Double 100M EtherNet网卡:均由AX88796 构成,采用现有电路但分配不同地址。
3(4 HOST / 1 DEVICE USB 接口:主USB 口扩展为4个,由AT43301 构成USB HUB,其中电源管理用MIC2525。USB 从口保持处理器本身的1个。
4(3 UART/IrDA:保持2个RS232 串口,增加1个RS485 串口,1个IrDA 收发器,均从处理器的UART2 引出。
5(168Pin EXPORT:有一个168Pin 扩展卡插槽,并去掉已经被主板上各模块占用的资源。网卡部分还在局部总线上,其余电路包括168Pin 扩展槽都在外部总线。
6(LCD:兼容多种LCD,可采用5 寸256 色屏或8 寸16bit 真彩屏,同时预留一个24bit 接口。可以支持板外8bit 或24bit 屏。
7(TouchScr:采用ADS7843,预备了直接用2410 内部ADC 构成的转换电路接口 8(AUDIO:采用UDA1341,具有放音、录音等功能。
9(PS2 KEYPAD:使用ATMEGA8 单片机控制2个PS2 接口和板载17键小键盘。两个PS2 可接PC 键盘和鼠标。
10(LED:使用ZLG7290 只驱动8 只小数码管。同时可作IIC 总线实验。 11(POWER SUPPLY、RESET、RTC 等必须资源。
12(ADC:板载3个电位器和选择跳线,同时在板上设模拟电压输入专用接口。 13(IDE/CF 卡插座:支持2.5 英尺的笔记本硬盘读写和IDE 模式下的CF 卡读写。 14(PCMCIA 和SD 卡插座:由EPM3128A100 CPLD 实现。
15(IC 卡插座。由ATMEGA8 单片机控制。
16(DC/STEP 电机。步进电机采用74HC573 扩展IO,软件形成时序来控制。同时剩余IO 可以控制CAN 等电路,以节省CPU 的GPIO 资源。
17(CAN BUS:设置1个CAN 口,采用MCP2510 和TJA1050。
18(Double DA:设置两个DAC 端口,采用MAX504 接SPI 总线。 19(GPRS/GPS 扩展板不做在主板上,单独设计扩展板。注意GPS 的RS232 需要
芯片来转换为TTL 才能引到168Pin 插座上。 增加MAX3232
3 、ADS1.2集成开发环境
ADS1.2 ADS是ARM公司的集成开发环境,他的功能非常强大。ADS包括了四个模块分别是:SIMULATOR;C 编译器;实时调试器;应用函数库。ADS1.2提供完整的WINDOWS界面开发环境。C编译器效率极高,支持c 以及c++,使工程师可以很方便的使用C语言进行开发。提供软件模拟仿真功能,使没有Emulators的学习者也能够熟悉ARM的指令系统。配合FFT-ICE使用,ADS1.2提供强大的实时调试跟踪功能,片内运行情况尽在掌握。ADS1.2需要硬件支持才能发挥强大功能。目前支持的硬件调试器有Multi-ICE以及兼容Multi-ICE的调试工具如FFT-ICE。
ADS由命令行开发工具,ARM实时库,GUI开发环境(Code Warrior和AXD),适用程序和支持软件组成。有了这些部件,用户就可以为ARM系列的RISC处理器编写和调试自己的开发应用程序了。
二 系统设计要求
贪吃蛇游戏是一款经典的小游戏,它的原型是1976年,Gremlin平台推出了一款经典街机游戏Blockade。真正走红的是该游戏随诺基亚手机走向世界。本课程设计有关贪吃蛇的功能描述:
1、可以通过嵌入式平台的键盘控制游戏,键盘中起作用的是方向键(2,4,6,8)和回车键,方向键控制蛇的上下左右运动,回车键控制游戏的继续和暂停。 2、贪吃蛇由若干连续黄色方块构成,程序中随机出现一些“食物”(用蓝色的方块表示),贪吃蛇通过吃“食物”增加自身的长度并增加相应的分值。 3、游戏分三个关口,第一关没有障碍物,第二关有2个障碍物,第三关有四个障碍物。
4、贪吃蛇撞到矩形边界,障碍物或者自己身体的一部分,游戏即结束,重新开始。
5、为了增加难度,加入AD转换器用于控制蛇的运动速度,这也是本设计的创
新点。
6、贪吃蛇每吃一个食物分数会增加,屏幕上显示得分和游戏所用时间。 三 系统总体设计
本设计需要创建两个任务,系统结构框图如图3-1所示,任务状态切换如图
。具体的任务
流程
快递问题件怎么处理流程河南自建厂房流程下载关于规范招聘需求审批流程制作流程表下载邮件下载流程设计
和任务所要完成的功能如下: 3-2所示
在系统启动后,同时创建两个任务,任务一main_task和任务二snake_task。任务一主要功能是等待键盘消息,有键盘消息的时候判断是什么键盘,并对相应的变量重新赋值。任务二主要功能是控制并在屏幕上显示蛇的移动和速度,并完
系统初始化
任务一 任务二
等控待制键采显蛇盘样 示 移消动 息
图3-1 系统结构框图 LCD
AD
任务一,任务二就绪
是否有键盘消息 任务一挂起
任务二运行 任务二挂起
任务一运行
图3-2 任务切换框图
8 如果蛇现在的方向是左或
等待键盘者右就把方向改成向上
消息 2 如果蛇现在的方向是左或
者右就把方向改成向下
是 4 是否有按识别是哪如果蛇现在的方向是上或
键按下 个按键 者下就把方向改成向左
6 否 如果蛇现在的方向是上或
者下就把方向改成向右
如果游戏是运行的则改为
竞争,静止就改为运行
/n
图3-3 系统主任务流程框图
成对分数,游戏等级和其他相关参数的记录和显示。任务一为主任务,在创建任务的时候,赋给它的优先级别比任务二高,所以任务一优先运行,任务二处于就绪状态,因为任务一主要是等待键盘消息,在无键盘消息的时候,任务一被挂起,这时候任务二进入运行状态。
1、任务一程序流程框图
如图3-3所示, 该任务完成对键盘消息的接收,识别,并改变相应变量 2、任务二流程图:
如图3-4所示,主要功能是控制蛇的速度并在屏幕上显示蛇的移动,并完成对分数、游戏等级、和游戏用时的记录和显示。
四 系统具体功能的实现
1初始化
初始化包括了硬件初始化和软件初始化。硬件初始化主要有AD转换器的初始化,LCD的初始化,串口初始化,ARM目标板的初始化等,软件初始化包括了系统初始化,游戏初始化。系统初始化包括了任务的建立,信号量的创建,变量的创建,系统文件初始化,初始化绘图设备上下文,系统实时时钟的初始化和函数的声明等,游戏的初始化,包括了有关参数属性的初始化化和游戏界面的初始化。
硬件初始化:
#define ADCCON_FLAG (0x1<<15) #define ADCCON_ENABLE_START_BYREAD (0x1<<1)
#define rADCCON (*(volatile unsigned *)0x58000000)
#define rADCDAT0 (*(volatile unsigned *)0x5800000C)
#define PRSCVL (49<<6)
#define ADCCON_ENABLE_START (0x1) #define STDBM (0x0<<2)
开始
初始化
蛇增加长度
计数器=0
长度计数
器减1,
蛇长加1
是否撞到障碍
物或者边框, 游戏结束
重新开始
撞到食物
加分
蛇长计数器
赋值 更新蛇头
和蛇尾
进入下一关
更新LCD显示 下一关初始化
调用等待函数,根据等
级和AD转换 确定等待
时间
图3-4 游戏任务流程图
#define PRSCEN (0x1<<14)
ARMTargetInit();
void init_ADdevice()
{//初始化
rADCCON=(PRSCVL|ADCCON_ENABLE_START|STDBM|PRSCEN); }
OSInitUart()
软件初始化
1)系统初始化
initOSMessage();
initOSDC();
LoadFont();
OSTaskCreate(Main_Task, (void *)0, (OS_STK *)&Main_Stack[STACKSIZE-1], Main_Task_Prio);
OSTaskCreate(Snake_Task, (void *)0, (OS_STK *)&Snake_Stack[STACKSIZE-1], Snake_Task_Prio);
LCD_ChangeMode(DspGraMode);
OSStart();
enum WN_Type{
TP_Space,
TP_Block,
TP_Cake,
TP_SkBody, //Snake Body
TP_SkHead,
TP_SkTail
};
enum Direction {Up=0,Down=1,Left=2,Right=3}; 2)游戏初始化
游戏的初始化将LCD屏幕以等大小的方块进行划分,并定义每个方块的属性,边
框和障碍物的属性相同,还有蛇头的属性,蛇身的属性,蛇尾的属性,食物的属
性,空白方块的属性。并调用绘图上下文设备完成游戏界面的初始化,显示关口,
得分和时间。同时产生随机产生食物。
MoveTo(pdc,20,440);
LineTo(pdc,620,440);
MoveTo(pdc,20,480);
LineTo(pdc,620,480);
MoveTo(pdc,220,440);
LineTo(pdc,220,480);
MoveTo(pdc,420,440);
LineTo(pdc,420,480);
strChar2Unicode(text1,"NO.:1000201036 Name:Chen ZhePing");
TextOut(pdc,20,420,text1,TRUE,FONTSIZE_MIDDLE); //Snake Head
xNode=&WorldArray[W_YSize/2][W_XSize/2-1];
xNode->Type=TP_SkHead;
xNode->isBlock=1;
HeadNode=xNode;
UpdateNextNode(xNode,Left);
xNode->PreDir=Left;
//Snake Body
xNode=&WorldArray[W_YSize/2][W_XSize/2];
xNode->Type=TP_SkBody;
xNode->isBlock=1;
UpdateNextNode(xNode,Left);
>PreDir=Left; xNode-
//Snake Tail
xNode=&WorldArray[W_YSize/2][W_XSize/2+1];
xNode->Type=TP_SkTail;
xNode->isBlock=1;
TailNode=xNode;
UpdateNextNode(xNode,Left);
xNode->PreDir=Left;
RandCake();
2 判断蛇是否吃到食物
判断蛇是否吃到食物的方法比较简单,只要判断蛇头的属性是否和下一个节点的属性一样。吃到食物后改变响应的游戏参数,并要让蛇的节数多三节,这时候要让变量lenadd加3, 并且还要对蛇补画上一节,本设计采用补上蛇头的方法,要注意根据此时蛇的运动方向来确定新蛇头的坐标。,以后食物产生子程序能够判断食物已经被蛇“吃到”了,要重新产生食物。
if(HeadNode->Type==TP_Cake)
{
score++;
if(score==5)
flag1=1;
else
if (score==10)
flag2=1;
LenToAdd+=CakeLength;
RandCake();
}
3 判断游戏是否结束
(1) 蛇头撞到蛇身
判断蛇是否撞到蛇身的方法可以从蛇头开始依次开始判断蛇头的属性是否和下一个方块的属性相同,一样的话即表明蛇撞到蛇身了,此时游戏结束。 (2)蛇头撞到游戏界面边框
判断蛇是否撞到边框的方法,只要比较蛇头的属性是不是同时边框的属性一样,一样的话,即表明蛇撞到边框,游戏结束。
If ( HeadNode -> isBlock )
{
nRet = 1;//Hit a Block
goto SnakeStep_End;
}
4 蛇移动功能
这部分功能是游戏的主体,实现蛇移动的方式是首先擦除蛇尾,擦除的方发是改变蛇尾的属性,通过调用绘图上下文设备,蛇尾就变成白色底色不可见,然后从蛇尾开始,依次改变下一个节点的属性。例如刚开始蛇是5节,这时候依次把第2四节的属性给第1节;把第3节(即蛇头)的属性给第2节;把第4四节的属性给第3节;把第4节(即蛇头)的属性给第3节;把第5节(即蛇头)的属性给第4节。擦除蛇尾和转移坐标这两个顺序不能颠倒。这时候蛇头的属性和第4节的属性是一样的,这时候需要判断蛇的方向变量的值,根据蛇的方向变量的值补画上蛇头。
If ( LenToAdd != 0)
{
LenToAdd --;
}
else
{
TailNode -> Type= TP_Space;
TailNode -> IsBlock = 0;
DrawPoint ( TailNode ); //old tail
TailNode = TailNode -> Next;
TailNode -> Type = TP_SkTail;
DrawPoint ( TailNode ); //new tail
}
HeadNode- > Type = TP_SkBody; //old head
DrawPoint ( HeadNode );
HeadNode -> Next -> PreDir = HeadNode -> Dir; //save predir
HeadNode = HeadNode -> Next; //new head
5 食物的产生
食物的产生主要要注意食物产生之前,我们必须要判断画面上面是已经存在食物,如有已经存在食物了,就不再产生食物,如果食物被蛇吃到了,那就要重新画上食物。本设计是通过在每次吃完食物后调用随机产生食物函数,另外还要保证食物不能超出蛇的活动界面,并要保证食物能被蛇吃到,所以需要指定食物坐标的范围,被通过舍去食物坐标个位的方法保证食物坐标是整十,这样就能被蛇吃到。本设计用一个矩形方框来代表食物,所以通过上面介绍的方法产生食物坐标后,对应在该坐标的位置画上一个矩形方框。
void RandCake (void)
{
//Get a new rand cake position
INT32U x,y;
While (1)
{
X = rand () %W_XSize;
Y = rand () %W_YSize;
if( !WorldArray[y][x].isBlock )
break;
}
CakeNode = &WorldArray[y][x];
CakeNode -> Type = TP_Cake;
DrawPoint ( CakeNode ); }
5、监听键盘动作:
任务通过等待消息而处于挂起状态,当任务接到消息以后,则处于就绪状态,然后开始判断所接受到的这个消息是不是需要处理,如果是执行相应的处理函数,最后,删除所接收到的消息,继续挂起等待下一条消息。
void Main_Task ( void *Id )
{
POSMSG pMsg=0;//定义消息结构
ClearScreen ();//清屏
WorldSem = OSSemCreate (1);
GameRun = OSSemCreate (1);
Pdc = CreateDC ();
SetLCDUpdata ( pdc , FALSE );
//消息循环
For ( ; ; )
{
pMsg = WaitMessage ( 0);//等待消息
switch ( pMsg -> Message)
{
case OSM_KEY://键盘消息
onKey ( pMsg -> WParam , pMsg -> LParam );
break;
}
DeleteMessage ( pMsg );//删除消息
}
DestoryDC(pdc);
}
6 AD控制蛇速度
在每次蛇前进一步后,通过刷新LCD屏幕来显示画面和相关参数,紧接着占用系统信号量,占用的时间是同过当前游戏的关口和调用AD采样函数来确定的额,游戏关口越高,蛇的前进速度越快,AD采样值越小,蛇的前进速度越快。 OSTimeDly ( GetSpeed (gamelevel));
INT32S GetSpeed ( U8 level) { int ADData;
U16 lastData;
ADData = GetADresult(0);
lastData = ADData*3.3*50/1023;
if ( level == 1 ) return 300 + lastData;
else
{
if (level==2) return 200 + lastData;
else return 100 + lastData;
}
}
四 程序下载与调试
1、把程序下载到试验箱中:
(1)执行菜单Project | Make 对工程进行编译连接。在出现的错误/警告窗
警告信息,ADS 会自动打开相应源文件并用箭头指向出错的文口中选择某错误/
本行。如果某个源文件被修改,重新编译时ADS 会自动同步各文件的日期信息。
(2)在ADS 中执行菜单Project | Debug 启动ADS1.2 的调试工具AXD。
3)在AXD 中执行菜单Options | Configure Target 对AXD 进行设置。选(
择ADP 即远程调试,点Configure 按钮进一步设置具体参数。
(4)点Select 按钮选择远程连接为ARM ethernet driver,点Configure 按 钮输入仿真器的IP 地址。如果用户使用的是并行口仿真器,请输入127.0.0.1 即可。
(5)等待程序装载完毕以后,通过Execute | Go 菜单以及Execute | Stop(或者工具栏中
的相应按钮)运行或暂停程序。程序暂停后在窗口中将显示出程序暂停的位置。
(6)通过Execute | Step 菜单(或者工具栏中的相应按钮)可以单步运行程序。也可以
使用 Step In、Step Out 菜单命令进入或者跳出函数的调用。Run To Cursor 命令运行到光标位置。
(7)程序停止后可以通过Processor Views | Sources 菜单查看源文件,并可在适当位置
按F9 设置端点。
(8)使用在Processor View 菜单下的Registers、Variables 和Memory 命令可以查看工作
寄存器或者内存变量。读者可以逐一地尝试,为以后调试程序打下基础。
注意:在进行调试时在ADS 中必须选择当前工程的Debug 版本,如果选择Release 版本则无法正常调试程序。但在调试通过后就必须选择Release 版本进行编译连接并将生产的system.bin 文件复制到开发板的Flash 中。将开发板上的Flash 激活并使Windows 认其为一个
2、运行结果与不足:
本程序能够完成设计目的的所有基本要求;
(1)能够通过方向键来控制蛇的移动方向;
(2)蛇吃到食物后能够重新出现食物;并且能够更新和显示分数,时间; (3)每吃到5个食物后,游戏能够自动进入下一关,并能通过增加蛇的移动速度来增加游戏的难度;
(4)能够通过实验箱上的旋钮控制蛇的运行速度。
未能实现的功能;
本程序设计时预想在游戏开始前能够停留在游戏界别设置画面,并能通过手动进行游戏级别的调整,“+”按键用来增加游戏级别,“-”按键用来减少游戏级别,并能在屏幕上显示游戏灌输,等到有确认信息,游戏才开始。但是此部功能没有实现,实验的时候程序能够进入级别调整画面,并等待用户对游戏等级进行调整,但是“+”、“-”按键和确认按键无效,没办法对游戏级别进行调整。由于调试时间有限,最后删掉次部分功能。
不足之处:
(1)方向按键按下后,要有一定的延迟蛇才能对按键作出反应,这个延迟不是很明显,但是刚开始游戏的时候,总感觉不能刚好控制蛇吃到食物,需要稍微提早一些时间按下按键才能让蛇延目标方向移动。
这个不足的原理是因为每次按键按下,要等到下一次循环,蛇才能响应方向改变,而蛇控制的程序中每次循环中用到了比较多的循环语句,判断,跳转,每次程序循环所需要的时间比较长,所以感觉按键有一点延迟,但是这个延迟非常的小,并不明显。
解决方法:要优化蛇控制程序,简化程序过程。
(2)蛇的移动范围和游戏界面有些不一致,例如蛇会稍稍超出游戏界面的边框。这是因为蛇移动时候所用的坐标原点和画游戏界面所用的坐标原点不一样,在判断蛇是否超出边框时要注意计算在新坐标原点下,边框的位置,以便准确判断出蛇是否超出游戏界面。这个问题仅仅是画面上有点缺陷,不影响贪吃蛇的功能。
解决方法:要准确计算出游戏边框相对坐标原点的距离(像素点)。 五 总结
想要写出好的程序,需要我们有扎实的基础,这样遇到一些基本算法的时候可能就会游刃有余了。在编程是我们要有丰富的想象力。不要拘泥于固定的思维方式,遇到问题的时候要多想几种解决问题的方案,试试别人从没想过的方法。丰富的想象力是建立在丰富的知识的基础上,所以我们要通过多的途径来帮助自己建立较丰富的知识结构。
在编程时我们会碰到了很多的困难,这就需要我们多与别人交流。三人行必有我师,也许在一次和别人不经意的谈话中,就可以迸出灵感的火花。在编程的过程中我们也看到了有良好的编程风格是十分重要的,至少在时间效率上就体现了这一点。养成良好的习惯,代码的缩进编排,变量的命名规则要始终保持一致,这些都是提高我们编程的注意点。
还有在变成中最能体现简单的原则。所以我们要思考讨论简单的程序,这样简单的方法更容易被人理解,更容易实现,也更容易维护。遇到问题时要优先考虑最简单的方案,只有简单方案不能满足要求时再考虑复杂的方案。
附录
程序代码:
/***************************************************************************\
Copyright (c) 2004-2007 threewater@up-tech.com, All rights reserved.
by threewter 2004.5.12
\***************************************************************************/ /***************************************************************************\
#说明: C main 函数,ucos-ii初始化等定义
---------------------------------- Bug
--------------------------------------
---------------------------------- TODO list
--------------------------------------
----------------------------------修正
--------------------------------------
2004-5-12 创建
\***************************************************************************/
#include"../ucos-ii/includes.h" /* uC/OS interface */ #include "../ucos-ii/add/osaddition.h"
#include "../inc/drivers.h"
#include "../ucos-ii/add/Osmessage.h"
#include "../inc/sys/lib.h"
#include "../src/gui/gui.h"
#include "../ucos-ii/add/rtc.h"
#include
#include
#include
#include
#pragma import (__use_no_semihosting_swi ) // ensure no functions that use semihosting
///******************任务定义***************///
OS_STK Main_Stack [STACKSIZE] = {0, }; //Main_Test_Task堆栈
void Main_Task ( void *Id ); //Main_Test_Task #define Main_Task_Prio 12
OS_STK Snake_Stack [STACKSIZE] ={ 0, }; //snake_Test_Task堆栈
void Snake_Task ( void *Id ); //snake_Test_Task
#define Snake_Task_Prio 16
/*************已经定义的OS任务************* #define SYS_Task_Prio 1 // 系统任务 #define Touch_Screen_Task_Prio 9 // 触摸屏任务 #define Main_Task_Prio 12 // 主任务 #define Snake Stack_Prio 16 // 游戏任务 #define Key_Scan_Task_Prio 58 // 按键扫描任务 #define Lcd_Fresh_prio 59 // 液晶屏刷新任务 #define Led_Flash_Prio 60 // 数码管刷新任务
**********************************************/
///*****************事件定义*****************/// OS_EVENT *WorldSem; //定义全局信号量 OS_EVENT *GameRun; //定义游戏运行信号量
#define W_XSize 32 // 游戏屏幕的宽度 #define W_YSize 21 // 游戏屏幕的高度
#define CakeLength 3 // 定义每次吃个食物蛇长增加3
//defs...................... struct Pt //定义坐标变量 {
INT8U x;
INT8U y;
};
enum WN_Type{ //定义枚举变量的方块类型
TP_Space , // 空白类型
TP_Block , // 块状类型
TP_Cake , //食物类型
TP_SkBody , //蛇的躯体
TP_SkHead , // 蛇的头
TP_SkTail // 蛇的尾
};
enum Direction { Up = 0,Down = 1,Left = 2,Right = 3};// 定义方向的枚举型变量
typedef struct WorldNode //定义节点类型结构变量 {
BOOLEAN isBlock; // 布尔型变量
enum WN_Type Type; //方块类型的变量
enum Direction Dir; //方向类型的变量
enum Direction PreDir; // 方向类型的变量
struct Pt pos; //坐标类型的变量
struct WorldNode* Next; // 定义节点类型的指针
} WorldNode;
//functions................. 函数声明
void UpdateNextNode ( struct WorldNode* xNode, enum Direction Dir );//更新下一节
点函数
void DrawPoint ( struct WorldNode* xNode) ; //画方块函数
void RandCake ( void ); //随机产生食物函数
/**** Global vars ****/
WorldNode WorldArray[ W_YSize ][ W_XSize ]; // 定义节点类型的数组大小为游戏界
面的长和款
WorldNode *HeadNode,*TailNode,*CakeNode; // 定义节点类型的蛇头指针,蛇尾指针,食
物指针
INT8U LenToAdd = 0; //需要增加的蛇长计数器 const INT8U ReverseDir[4] = {1,0,3,2}; // 第一反方向的常量
INT8U ERRNO; // 定义时间常量
PDC pdc; // 定义绘图上下文设备 INT32S score = 0; // 得分
U16 gamelevel = 1;//game为关数,目前为三关,每关只改变速度
U16 timeuse; // 游戏用时
BOOLEAN isRunning = 1; // 定义游戏运行标志
U8 flag1=0; //定义 第二关游戏标志
U8 flag2=0; // 定义 第三关游戏标志
///********************AD 定义和初始化************************/// #define ADCCON_FLAG ( 0x1<<15 ) //AD 控制标志
#define ADCCON_ENABLE_START_BYREAD ( 0x1<<1 ) //AD使能读信号
#define rADCCON ( * ( volatile unsigned * ) 0x58000000 )
#define rADCDAT0 ( * ( volatile unsigned * ) 0x5800000C )
#define PRSCVL ( 49 << 6 )
#define ADCCON_ENABLE_START ( 0x1 ) #define STDBM ( 0x0 << 2 )
#define PRSCEN ( 0x1 << 14 )
void init_ADdevice()
{//初始化
rADCCON = ( PRSCVL | ADCCON_ENABLE_START | STDBM | PRSCEN );
}
int GetADresult ( int channel) {
rADCCON = ADCCON_ENABLE_START_BYREAD | ( channel <<3 )| PRSCEN |
PRSCVL; // 驱动转换
hudelay ( 10 ); //延迟10秒钟,转换时间
while ( ! ( rADCCON & ADCCON_FLAG ) );//判断是否转换结束
return ( 0x3ff & rADCDAT0 );//返回采样值
}
///***********蛇的步进速度控制函数*************/// INT32S GetSpeed ( U8 level ) // 关口作为传递参数 { int ADData; // 定义AD转换数据变量
U16 lastData; //定义换算结果变量
ADData = GetADresult(0); //调用转换函数,获得转换数据
lastData = ADData*3.3*50/1023; //换算成实际电压值
if ( level == 1 ) return 300 + lastData;//根据关口返回对应的速度参数
else
{
if ( level == 2 ) return 200 + lastData;
else return 100 + lastData;
}
}
///**************根据关数初始化游戏*****************/// void WorldInit( void )
{
INT8U x,y;
U16 text1 [ 100 ];
WorldNode *xNode;
OSSemPend ( WorldSem,0,&ERRNO ); //等待全局信号量
MoveTo ( pdc,20,440 ); //绘制边框,移动到该点
LineTo ( pdc,620,440 ); // 画线
MoveTo ( pdc,20,480 );
LineTo ( pdc,620,480 );
MoveTo ( pdc,220,440 );
LineTo ( pdc,220,480 );
MoveTo ( pdc,420,440 );
LineTo ( pdc,420,480 );
strChar2Unicode ( text1,"NO.:1000201036 Name:Chen ZhePing" );//转换成UNICODE
TextOut ( pdc,20,420,text1,TRUE,FONTSIZE_MIDDLE );// 显示姓名,学号 文本
For ( y = 0 ;y < W_YSize ;y++ ) // 定义各个点的属性
for( x=0 ;x < W_XSize ;x++)
{
xNode = &WorldArray [ y ][ x ] ;
xNode -> pos.x = x;
xNode -> pos.y = y;
if ( gamelevel == 1 ) //根据不同的关口初始化界面
{
If ( x == 0 || y == 0 || x == W_XSize-1 || y == W_YSize-1 ) //
如果是第一关,则没有障碍物
{
xNode -> Type = TP_Block;
xNod e-> isBlock = 1;
}
else
{
xNode -> Type = TP_Space;
xNode -> isBlock = 0;
}
}
else
{
if ( gamelevel == 2 )
{
If ( ( x == W_XSize/2 & ( y == W_YSize/3 | y==W_YSize*2/3 ) )|( x
== 0 || y == 0 || x == W_XSize-1 || y == W_YSize-1)) //如果是第二关则有两个障碍物
{
xNode -> Type = TP_Block;
xNode -> isBlock = 1;
}
else
{
xNode -> Type = TP_Space;
xNode -> isBlock=0;
}
}
else
{
If ( (( x == W_XSize/3 | x == W_XSize*2/3 ) & ( y == W_YSize/3 |
y == W_YSize*2/3 ))|( x == 0||y== 0||x == W_XSize-1|| y == W_YSize-1 ) ) //如果是第三关则有四个障碍物
{
xNode -> Type = TP_Block;
xNode -> isBlock = 1;
}
else
{
xNode -> Type = TP_Space;
xNode -> isBlock = 0;
}
}
}
}
//Snake Head
xNode = &WorldArray[ W_YSize/2 ][ W_XSize/2-1 ];
xNode -> Type = TP_SkHead;
xNode -> isBlock = 1;
HeadNode = xNode;
UpdateNextNode( xNode,Left );
xNode -> PreDir = Left;
//Snake Body
xNode = &WorldArray[ W_YSize/2 ][ W_XSize/2 ];
xNode -> Type = TP_SkBody;
xNode -> isBlock = 1;
UpdateNextNode ( xNode,Left );
xNode -> PreDir = Left;
//Snake Tail
xNode = &WorldArray[ W_YSize/2 ][ W_XSize/2+1 ];
xNode -> Type = TP_SkTail;
xNode -> isBlock = 1;
TailNode = xNode;
UpdateNextNode ( xNode,Left );
xNode -> PreDir = Left;
RandCake(); //产生食物
OSSemPost( WorldSem ); //释放该全局信号量
}
///************游戏结束********************/// void GameOver ( void )//显示游戏结束界面
{
U16 text2[ 100 ];
strChar2Unicode( text2,"Game over!");//转换成unicode码
ClearScreen();//清屏
pdc=CreateDC(); //创建绘图上下文设备
TextOut ( pdc,230,230,text2,TRUE,FONTSIZE_MIDDLE );//显示游戏结束画面
OSTimeDly ( 500 ); //延迟一定时间
DestoryDC ( pdc ); //删除绘图设备上下文
}
///************利用随机函数rank产生随机方块**********/// void RandCake ( void )
{
//Get a new rand cake position
INT32U x,y;
while(1) //产生在游戏界面内的食物
{
X = rand()%W_XSize;
Y = rand()%W_YSize;
If ( !WorldArray[y][x].isBlock ) //不在界面内 就重新生成
break;
}
CakeNode = &WorldArray[ y ][ x ];
CakeNode -> Type=TP_Cake;
DrawPoint( CakeNode ); //画出食物
}
char pbuf[2000];
///****************画图显示*************/// void ShowWorld ( void )
{
INT8U x,y;
WorldNode *xNode;
For ( y = 0;y < W_YSize;y++ )
{
For ( x = 0;x < W_XSize;x++ )
{
xNode = &WorldArray[ y ][ x ];
DrawPoint ( xNode );
}
}
}
///************调用绘图上下文以小方块为元素画图*****************///
void DrawPoint (WorldNode* xNode) {
Switch ( xNode -> Type )
{
case TP_SkBody:
FillRect( pdc,xNode -> pos.x*20,xNode -> pos.y*20,(xNode ->
pos.x+1)*20-1,(xNode -> pos.y+1)*20-1,GRAPH_MODE_NORMAL,RGB(255, 255, 0));break;
case TP_SkHead:
FillRect(pdc,xNode -> pos.x*20,xNode -> pos.y*20,(xNode -> pos.x+1)*20-1,(xNode -> pos.y+1)*20-1,GRAPH_MODE_NORMAL,RGB(255, 255, 0));break;
case TP_SkTail:
FillRect(pdc,xNode -> pos.x*20,xNode -> pos.y*20,(xNode -> pos.x+1)*20-1,(xNode -> pos.y+1)*20-1,GRAPH_MODE_NORMAL,RGB(255, 255, 0));break;
case TP_Space:
FillRect(pdc,xNode -> pos.x*20,xNode -> pos.y*20,(xNode -> pos.x+1)*20-1,(xNode -> pos.y+1)*20-1,GRAPH_MODE_NORMAL,RGB(255, 255, 255));break;
case TP_Block:
FillRect(pdc,xNode -> pos.x*20,xNode -> pos.y*20,(xNode -> pos.x+1)*20-1,(xNode -> pos.y+1)*20-1,GRAPH_MODE_NORMAL,RGB(0,0, 255));break;
case TP_Cake:
FillRect(pdc,xNode -> pos.x*20,xNode -> pos.y*20,(xNode -> pos.x+1)*20-1,(xNode -> pos.y+1)*20-1,GRAPH_MODE_NORMAL,RGB(0, 0, 0));break;
}
}
///***************更新蛇的各个节点的方向*******************///
void UpdateNextNode (WorldNode* xNode, enum Direction Dir) {
//
struct Pt pos;
if ( xNode -> Type == TP_SkHead)
if( ReverseDir[Dir] == HeadNode -> PreDir )
return;
pos = xNode -> pos;
switch ( Dir )
{
case Up:
pos.y--;break;
case Down:
pos.y++;break;
case Left:
pos.x--;break;
case Right:
pos.x++;break;
}
xNode -> Dir = Dir;
xNode -> Next = &WorldArray[pos.y][pos.x];
}
///************下一关界面***********************///
void Nextlevel( void )//上一关结束,准备进入下一关
{
U16 text3[100];
strChar2Unicode ( text3,"Next level!");// 转换成UNICODE 码
ClearScreen();//清屏
TextOut ( pdc,230,230,text3,TRUE,FONTSIZE_MIDDLE);显示下一关界面
OSTimeDly ( 500 );//延迟一定时间
}
///***************蛇步进驱动函数****************/// INT8U SnakeStep ( void )
{
//Drive the Snake to go a step
INT8U nRet = 0;
OSSemPend ( WorldSem,0,&ERRNO );// 等待信号量
//Something for Tail
if ( LenToAdd != 0) //如果不为零 则不更新擦除蛇尾
{
LenToAdd--;
}
else
{
TailNode -> Type = TP_Space;
TailNode -> isBlock = 0;
DrawPoint ( TailNode ); //old tail
TailNode = TailNode -> Next;
TailNode -> Type = TP_SkTail;
DrawPoint ( TailNode ); //new tail
}
// 更新蛇头
HeadNode -> Type = TP_SkBody; //old head
DrawPoint ( HeadNode );
HeadNode -> Next -> PreDir = HeadNode -> Dir; //save predir
HeadNode = HeadNode -> Next; //new head
if( HeadNode -> isBlock ) //判断是否妆到障碍物 或者边界
{
nRet = 1;//Hit a Block
goto SnakeStep_End;
}
Else // 如果没有 判断是否撞到食物
{
HeadNode -> isBlock = 1;
If ( HeadNode -> Type == TP_Cake)
{
score++; //撞到 加分
if ( score == 5) //判断是否进入第二关
flag1 = 1;
else
if ( score == 10) //判断是否进入第三关
flag2 = 1;
LenToAdd += CakeLength; //蛇长增加计数器加上固定长度
RandCake (); // 重新随机产生食物
}
else
{
flag1 = 0;
flag2 = 0;
}
//Then ( HeadNode -> Type == TP_Space )
HeadNode -> Type = TP_SkHead;
UpdateNextNode( HeadNode,HeadNode -> PreDir );
DrawPoint( HeadNode );
}
SnakeStep_End:OSSemPost ( WorldSem ); //释放信号量
return nRet; //返回参数
}
///************方向键对应的响应函数********************/// void Sk_Key_Up ( void )
{
UpdateNextNode( HeadNode,Up ); }
void Sk_Key_Down ( void )
{
UpdateNextNode( HeadNode,Down ); }
void Sk_Key_Left ( void )
{
UpdateNextNode( HeadNode,Left ); }
void Sk_Key_Right ( void )
{
UpdateNextNode( HeadNode,Right ); }
///**************键盘处理程序****************************///
// 定义符号常量
#define K_UP 56|0x80000000 #define K_DOWN 50|0x80000000 #define K_LEFT 52|0x80000000 #define K_RIGHT 54|0x80000000
U8 onKey( int nkey, int fnkey ) {
OSSemPend( WorldSem,0,&ERRNO );//等待全局信号量
switch(nkey){ //判断按键类型
case '\r'://enter
if ( isRunning ) //如果游戏正在进行
{
OSSemPend ( GameRun,0,&ERRNO );//占有信号量
isRunning = FALSE;
}
else
{
OSSemPost ( GameRun );//否则释放信号量
isRunning = TRUE;
}
break;
case K_UP: //方向键的相应
Sk_Key_Up();
break;
case K_DOWN:
Sk_Key_Down();
break;
case K_LEFT:
Sk_Key_Left();
break;
case K_RIGHT:
Sk_Key_Right();
break;
}
OSSemPost( WorldSem );
return TRUE;
}
///************游戏关数显示******************///
void dspgamelevel ( U16 gamelevel )
{
U16 s16buf[100];
strChar2Unicode ( s16buf ,"Level: ");
TextOut ( pdc,240 ,450 ,s16buf ,TRUE ,FONTSIZE_MIDDLE );
Int2Unicode ( gamelevel ,s16buf );
TextOut ( pdc ,320 ,450 ,s16buf ,TRUE ,FONTSIZE_MIDDLE ); }
///************分数显示******************///
void dspscore ( U16 score )
{
U16 s16buf[100];
strChar2Unicode ( s16buf,"Score: " );
TextOut ( pdc ,20 ,450 ,s16buf ,TRUE ,FONTSIZE_MIDDLE );
Int2Unicode ( score ,s16buf );
TextOut ( pdc ,100 ,450 ,s16buf ,TRUE ,FONTSIZE_MIDDLE ); }
///************用时显示******************///
void dsptimeuse( U16 timeuse )
{
U16 s16buf[100];
U8 shi,fen,miao;
Shi = timeuse/3600; //时间格式的转换
Fen = (timeuse%3600)/60;
Miao = timeuse%60;
strChar2Unicode ( s16buf,"Timeuse: " );
TextOut ( pdc ,440 ,450 ,s16buf ,TRUE ,FONTSIZE_MIDDLE );
Int2Unicode ( shi ,s16buf );
TextOut ( pdc ,520 ,450 ,s16buf ,TRUE,FONTSIZE_MIDDLE );
strChar2Unicode ( s16buf,": " );
TextOut ( pdc ,540 ,450 ,s16buf ,TRUE,FONTSIZE_MIDDLE );
Int2Unicode ( fen,s16buf );
TextOut ( pdc ,545 ,450 ,s16buf ,TRUE,FONTSIZE_MIDDLE );
strChar2Unicode ( s16buf ,": " );
TextOut ( pdc ,565 ,450 ,s16buf ,TRUE ,FONTSIZE_MIDDLE );
Int2Unicode ( miao ,s16buf );
TextOut ( pdc ,570 ,450 ,s16buf ,TRUE ,FONTSIZE_MIDDLE ); }
///********************Main function**********************/// int main ( void)
{
ARMTargetInit(); // do target (uHAL based ARM system) initialisation //
OSInit(); // needed by uC/OS-II //
OSInitUart(); // 初始化串口
initOSFile();
InitRtc();//为时间的访问创建控制权,及初始化时钟
init_ADdevice(); //初始化AD转换器
#if USE_MINIGUI == 0
initOSMessage(); //初始化系统消息
initOSDC(); //初始化系统绘图设备
LoadFont(); //加载字库
#endif
loadsystemParam();
LCD_printf ( "Create task on uCOS-II...\n" );
OSTaskCreate ( Main_Task , (void *)0 , (OS_STK *)&Main_Stack[STACKSIZE-1] ,
Main_Task_Prio );
OSTaskCreate ( Snake_Task , (void *)0 , (OS_STK *)&Snake_Stack[STACKSIZE-1] ,
Snake_Task_Prio );
OSAddTask_Init(1); //系统附加任务初始化
LCD_printf("Starting uCOS-II...\n");
LCD_printf("Entering graph mode...\n");
LCD_ChangeMode(DspGraMode);
OSStart(); // start the OS //
// never reached //
return 0;
}//main
///*******************Main Task***********************///
void Main_Task(void *Id) {
POSMSG pMsg = 0;//定义消息结构
ClearScreen();//清屏
WorldSem = OSSemCreate (1);//创建系统信号量
GameRun = OSSemCreate (1);//创建游戏信号量
Pdc = CreateDC();//创建绘图设备上下文
SetLCDUpdata ( pdc ,FALSE ); //设置LCD更新方式
//消息循环
for(;;)
{
pMsg = WaitMessage(0);//等待消息
switch ( pMsg -> Message )
{
case OSM_KEY://键盘消息
onKey ( pMsg -> WParam,pMsg -> LParam );
break;
}
DeleteMessage( pMsg );//删除消息
}
DestoryDC ( pdc );
}
///************************Snake Task**************************///
void Snake_Task ( void *p_arg ) {
while(1)
{
timeuse = 0;
score = 0;
gamelevel = 1;
ClearScreen ();//清屏
WorldInit ();
ShowWorld ();
While ( !SnakeStep () )
{
if ( flag1 ) //判断游戏关口
{
Gamelevel = 2;
Nextlevel ();
WorldInit ();
ShowWorld ();
}
else
if ( flag2 )
{
Gamelevel = 3;
Nextlevel ();
WorldInit ();
ShowWorld ();
}
OSSemPend ( GameRun ,0 ,&ERRNO );//占有信号量
Dspscore ( score ); //显示得分
Dspgamelevel ( gamelevel ); //显示游戏关口
if(Rtc_IsTimeChange ( RTC_SECOND_CHANGE )) timeuse++; //刷新时间
dsptimeuse ( timeuse ); //显示游戏时间
LCD_Refresh (); //LCD刷新
OSTimeDly(GetSpeed ( gamelevel )); //调用系统延迟
OSSemPost ( GameRun ); //释放信号量
}
GameOver ();
}
}