游蛇吃豆嵌入式课程设计

更新时间:2024-05-23 09:12:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

目录

一、 绪论 .............................................. 2

1. 开发背景 ........................................ 2 2. 开发平台 ........................................ 2 3. 小组成员 ........................................ 3 二、 功能描述 .......................................... 3 三、 基本原理 .......................................... 3 四、 系统总体设计 ...................................... 4

1. 相关初始化 ...................................... 4 2. 任务设计 ........................................ 5 五、 硬件设计 .......................................... 7 六、 软件设计 .......................................... 8

1. 移动功能的实现 .................................. 8 2. 判断吃否吃到豆子 ............................... 10 3. 画豆子 ......................................... 12 4. 声音的播放 ..................................... 13 5. 数码管的点亮 ................................... 14 6. 判断游戏进程 ................................... 15 7. 加速,计分,计时功能 ........................... 16 8. 监听键盘 ....................................... 17 七、 小结 ............................................. 17 八、 参考文献 ......................................... 18

一、绪论

1. 开发背景

贪吃蛇是一款常见的小游戏,简单有趣,深受人们的喜爱,本项目作为学习软件文档写作和简单游戏编程而提出。希望通过《贪吃蛇》游戏软件设计开发,了解软件文档的相关标准和编写原则,训练并掌握软件各类文档写作的技巧,同时提高嵌入式设计的能力。

2. 开发平台

1) 知识储备 嵌入式实时操作系统μC/OS-II简介

μC/OS-II是一个抢占式实时多任务内核。它是用ANSI的C语言编写的,包含一小部分汇编语言代码,使之可以提供给不同架构的微处理器使用。至今,从8位到64位,μC/OS-II已经在40多种不同架构的微处理器上使用。使用μC/OS的领域包括:照相机行业、航空业、医疗器械、网络设备、自动提款机以及工业机器人等。 μC/OS-II全部以源代码的方式提供,大约有5500行。CPU相关的部分使用的是针对Intel80x86微处理器的代码。μC/OS-II可以很容易地移植到不同架构的嵌入式微处理器上。

μC/OS-II的特点: ? 源代码 ? 可移植 ? 可固化 ? 可裁减 ? 可抢占性 ? 支持多任务 ? 可确定性 ? 任务栈 ? 系统服务 ? 中断管理 ? 稳定性

2) ARM简介

采用RISC架构的ARM微处理器一般具有如下特点:

? 体积小、低功耗、低成本、高性能;

? 支持Thumb(16位)/ARM(32位)双指令集,能很好的兼容8位/16位器件;

? 大量使用寄存器,指令执行速度更快; ? 大多数数据操作都在寄存器中完成;

? ? ? ? ?

寻址方式灵活简单,执行效率高; 指令长度固定;

ARM处理器共有37个寄存器,被分为若干个组(BANK),这些寄存器包括: 31个通用寄存器,包括程序计数器(PC指针),均为32位的寄存器。 6个状态寄存器,用以标识CPU的工作状态及程序的运行状态,均为32位,目前只使用了其中的一部分。

? ARM处理器又有7种不同的处理器模式,在每一种处理器模式下均有一组相应的寄存器与之对应。即在任意一种处理器模式下,可访问的寄存器包括15个通用寄存器(R0~R14)、一至二个状态寄存器和程序计数器。在所有的寄存器中,有些是在7种处理器模式下共用的同一个物理寄存器,而有些寄存器则是在不同的处理器模式下有不同的物理寄存器。 ? ARM微处理器的在较新的体系结构中支持两种指令集:ARM指令集和Thumb指令集。其中,ARM指令为32位的长度,Thumb指令为16位长度。Thumb指令集为ARM指令集的功能子集,但与等价的ARM代码相比较,可节省30%~40%以上的存储空间。

3. 小组成员

二、功能描述

基本功能:开始蛇向右方移动。按键盘上定义的上下左右键,蛇改变游动的方向,可以上下左右游动。蛇不能碰到图中的灰色栅栏,如碰到游戏结束。若蛇碰到图中一粒豆子,则豆子被蛇吃掉,图中的豆子消失,蛇身变长。 最终所有的豆子都被吃掉,游戏结束。 增强要求:

(1) 必须改进游戏的界面,增加显示相关的统计信息。左边的显示区保持

不变,右边动态显示积分和总时间统计信息,其中积分栏目显示当前已经吃下的豆子数目,总时间显示本局游戏从开始到现在经过的时间。

(2) 优化主程序,注意CPU和内存的使用效率。

(3) 考虑一个合理的得分算法,得分值应该取决于吃下去的豆子和游戏持

续的时间。

(4) 得分可以在发光二极管上显示出来。

三、基本原理

游戏开始后进入游戏界面.首先初始化蛇的坐标,食物的坐标.线程基本流程:判断是否是暂停阶段,是否有有退出按键,游戏是否有结束,如果都没有就执行,

如果游戏结束了就重新游戏或者退出。开启键盘功能,实现通过方向键来控制蛇的移动方向; 开启数码管功能,实现蛇吃到食物后能够更新和显示分数; 利用变量,实现吃了6个食物后,游戏能够结束,并能通过增加蛇的移动速度来增加游戏的难度。

贪吃蛇游戏设计最主要在蛇移动的控制,在设计中用数组来存放蛇身的坐标,用数组boolbean00[3]、boolbean01[1]、boolbean10[1]、boolbean11[1]存放豆子,变量mscore记录得分,并利用数码管显示技术实时显示分数,考虑一个合理的得分算法,得分值应该取决于吃下去的豆子和游戏持续的时间。

四、系统总体设计

1. 相关初始化

1) 堆栈大小,任务优先级定义

OS_STK Main_Stack[STACKSIZE*8]={0, }; //Main_Test_Task堆栈 void Main_Task(void *Id); //Main_Test_Task #define Main_Task_Prio 12

OS_STK Led_Flash_Stack[STACKSIZE]= {0, }; //LED闪烁任务堆栈 void Led_Flash_Task(void *Id); //LED闪烁任务 #define Led_Flash_Prio 60

OS_STK Transmit_Task_Stack[STACKSIZE*8]={0, }; //Transmit_Task堆栈 void Transmit_Task(void *Id); //Transmit_Task #define Transmit_Task_Prio 20

2) 任务的创建

OSTaskCreate(Main_Task,(void *)0, (OS_STK *)&Main_Stack[STACKSIZE*8-1], Main_Task_Prio);

OSTaskCreate(Led_Flash_Task,(void*)0,(OS_STK*)&Led_Flash_Stack[STACKSIZE-1], Led_Flash_Prio );

OSTaskCreate(Transmit_Task,(void*)0,(OS_STK*)&Transmit_Task_Stack[STACKSIZE-1], Transmit_Task_Prio);

3) 初始化蛇身

U8 HeadPos[2]; U8 EndPos[2];

4) 初始化豆子

u8 bean00[3][2] = { {2,11}, {18,13}, {15,20} }; u8 bean01[1][2] = { {10,27} }; u8 bean10[1][2] = { {38,3} }; u8 bean11[1][2] = { {27,38} };

BOOLEAN boolbean00[3] = {TRUE, TRUE, TRUE}; BOOLEAN boolbean01[1] = { TRUE }; BOOLEAN boolbean10[1] = { TRUE }; BOOLEAN boolbean11[1] = { TRUE };

2. 任务设计

1) 任务设计要求

本设计需要创建三个任务,,任务一Main_Task和任务二Transmit_Task,任务三Led_Flash_Task,任务三为Led显示任务,这里不做详细解释。只对任务一跟任务二详细说明。

在系统启动后,同时创建两个任务,任务一和任务二.任务一主要功能是等待键盘消 息,有键盘消息的时候判断是什么键盘,并对相应的变量重新赋值.任务二主要功能是控制 并在屏幕上显示蛇的移动,并完成对分数和其他相关参数的记录和显示.任务 一为主任务,在创建任务的时候,赋给它的优先级别比任务二高,所以任务一优先运行,任 务二处于就绪状态, 因为任务一主要是等待键盘消息, 在无键盘消息的时候, 任务一被挂起, 这时候任务二进入运行状态.

2) 流程图

图1程序流程图(不包括任务三)

图2任务一(主任务)流程图

图3任务二流程图

五、硬件设计

由于本次课程设计在平台上做开发,所以几乎不会涉及到具体的硬件概要设计、详细设计、制作。但对于所用到的驱动,应有所了解。

硬件不同,平台就不同,程序的效果也就不同。

六、软件设计

1. 移动功能的实现

移动功能的实现:通过ChangePointCount记录改变次数,ChangePointPos[10][2]记录每次的坐标,没有移动改变的时候,用绘图函数画原先的图,有移动的时候,先画头部,再利用循环画中间,最后画尾部。 实现代码:

oldendpos[0] = EndPos[0];//记录蛇的位置 oldendpos[1] = EndPos[1]; //判断snake头部的变化 switch (HeadDirect)

//根据所按键蛇头向上向下向左向右移动,由于使用switch语句,横纵坐标只能有一个发生变化,且一次只能向一个方向,所以蛇头向上向下向左向右移动。 { case 1: HeadPos[0] = HeadPos[0] + m;//蛇头横坐标移动m break; case 2: HeadPos[0] = HeadPos[0] - m; break; case 3: HeadPos[1] = HeadPos[1] - m; //蛇头纵坐标移动m break; case 4: HeadPos[1] = HeadPos[1] + m; break; } //判断snake尾部的变化 if (ChangePointCount > 0) { if

( ( abs(ChangePointPos[ChangePointCount-1][0]-EndPos[0])+abs(ChangePointPos[ChangePointCount-1][1]-EndPos[1])) == 0)

{ DelChangePoint(); } } switch(EndDirect) { case 1: EndPos[0] = EndPos[0] + m; //蛇尾横坐标移动m break; case 2: EndPos[0] = EndPos[0] - m; break; case 3: EndPos[1] = EndPos[1] - m; //蛇尾纵坐标移动m break; case 4: EndPos[1] = EndPos[1] + m; break; } //绘制snake //ClearScreen(); //Draw3DRect2(pdc, psnakeRect, RGB(0, 0, 0), RGB(0, 0, 0)); FillRect2(pdc, pbarRect1, GRAPH_MODE_NORMAL, RGB(0, 255, 255)); FillRect2(pdc, pbarRect2, GRAPH_MODE_NORMAL, RGB(0, 255, 255)); if (ChangePointCount==0) { draw_rect(HeadPos[0],HeadPos[1], EndPos[0], EndPos[1]); }

else //不断调用可以动态显示移动过程 {

draw_rect(HeadPos[0],HeadPos[1],ChangePointPos[0][0], ChangePointPos[0][1]);

for(i=1; i

draw_rect(ChangePointPos[i-1][0],ChangePointPos[i-1][1],

} }

ChangePointPos[i][0],ChangePointPos[i][1]; }

draw_rect(ChangePointPos[i-1][0],ChangePointPos[i-1][1], EndPos[0],EndPos[1]); }

OSTimeDly(200);

2. 判断吃否吃到豆子

判断蛇是否吃到食物的方法比较简单,只要判断蛇头的 X,Y 坐标是否同时和食物的坐 标的 X,Y 一样.吃到食物后改变响应的游戏参数,这时候要让变 量addcount 加 1,要注意根据此时蛇的运动方向来确定新蛇头的坐标.另外,要注意,蛇吃完食物后,要把变量boolbean00[i], bean01[0], bean10[0], bean11[0] 赋值 FALSE, 食物产生子程序能够判断食物已经被蛇\吃到\了,要重新产生食物.

代码:

u8 bean00[3][2] = { {2,11}, {18,13}, {15,20} }; u8 bean01[1][2] = { {10,27} }; u8 bean10[1][2] = { {38,3} }; u8 bean11[1][2] = { {27,38} };

if (HeadPos[0] <= 20) { if (HeadPos[1] <=20) { for (i=0;i<3;i++) if (boolbean00[i] == TRUE) { if ( (bean00[i][0] == HeadPos[0]) && (bean00[i][1] == HeadPos[1]) ) { catch = TRUE; boolbean00[i] = FALSE; addcount = 2; number++; score(number);

mscore=number*2-n*0.01+1;

scoresco(mscore);

key=mscore;//得到按键值

Delay(1);

ZLG7289_ENABLE();//使zlg7289占有同步串口 WriteSDIO(ZLG7289_CMD_DATA0|0);//数码管以方式0译码,第一个数码管亮 WriteSDIO(key);//显示个位 Delay(1);//延时 if(key>9) {//键值大于9显示十位 WriteSDIO(ZLG7289_CMD_DATA0|1);//发送十位数据 WriteSDIO((unsigned char)(key/10)); Delay(1); WriteSDIO(ZLG7289_CMD_HIDE);//使一、二两位数码管显示 WriteSDIO(3); } else {//键值小于10不显示十位 WriteSDIO(ZLG7289_CMD_HIDE);//使个位数码管显示 WriteSDIO(1); } ZLG7289_DISABLE();//zlg7289放弃同步串口控制权 // shine(); return; } } } else { if (boolbean01[0] == TRUE) { if ( (bean01[0][0] == HeadPos[0]) && (bean01[0][1] == HeadPos[1]) ) { catch = TRUE; boolbean01[0] = FALSE; addcount = 2; number++; score(number); } } } } else {

if (HeadPos[1] <=20) { if (boolbean10[0] == TRUE) { if ( (bean10[0][0] == HeadPos[0]) && (bean10[0][1] == HeadPos[1]) ) { catch = TRUE; boolbean10[0] = FALSE; addcount = 2; number++;

score(number); } } } else { if (boolbean11[0] == TRUE) { if ( (bean11[0][0] == HeadPos[0]) && (bean11[0][1] == HeadPos[1]) ) { catch = TRUE; boolbean11[0] = FALSE; addcount = 2; number++;

score(number); } } } }

3. 画豆子

画豆子时,通过判断豆子有没有被吃掉决定要不要画,如果已经被蛇吃掉,就不画,没有吃掉就画,通过数组的值是TRUE还是FALSE来判断。数组的值为TRUE则画圆表示豆子,数组的值为FALSE则不画。

FillRect2(pdc, psnakeRect, GRAPH_MODE_NORMAL, RGB(100, 255, 0));//清除主窗口的显示

2);

for(i=0;i<3;i++) {

if (boolbean00[i] == TRUE) {

Circle(pdc,20+5*bean00[i][0]-2, 20+5*bean00[i][1]-2, } }

if (boolbean01[0] == TRUE) {

Circle(pdc,20+5*bean01[0][0]-2, 20+5*bean01[0][1]-2, 2); }

if (boolbean10[0] == TRUE) {

Circle(pdc,20+5*bean10[0][0]-2, 20+5*bean10[0][1]-2, 2); }

if (boolbean11[0] == TRUE) {

Circle(pdc,20+5*bean11[0][0]-2, 20+5*bean11[0][1]-2, 2); }

4. 声音的播放

void Song() { INT8U err; U32 nbyte; FILE *pfile; char filename[]=\声音的源文件 rIISCON=0; //disable; rIISMOD=IISMOD_TX|IISMOD_16BIT|IISMOD_32FS|IISMOD_MCLK_384FS; rIISFCON=IISFCON_TXDMA|IISFCON_TXFIFO; rIISPSR=0x11; rIISCON=IISCON_PRESCALE|IISCON_ENABLE; Init_UDA1341(); pfile=OpenOSFile(filename, FILEMODE_READ); if(!pfile) return ; ReadOSFile(pfile, (U8*) buffer, 0x16*2);//读取头文件信息

nbyte=ReadOSFile(pfile, (U8*) buffer, sizeof(buffer));//读取波形数据 for(;;) { /****** BDMA0 Initialize ******/ //for Source rBDISRC0=(1<<30)+(1<<28)+(int)buffer; //Half word,inc,Buf //for des rBDIDES0=(1<<30)+(3<<28)+((int)0x1d18010); //M2IO,fix,IISFIF //Size //iis,reserve,done_int,not auto-reload/start,DMA enable,COUNT rBDICNT0=(1<<30)+(1<<26)+(3<<22)+(0<<21)+(0<<20)+(sizeof(buffer)&(~0x3)); rBDICNT0 |= (1<<20);//开启 //Enable DMA rBDCON0 = 0x0<<2; //Tx DMA rIISCON|=IISCON_TXDMA; } }

5. 数码管的点亮

key=mscore;//得到按键值

Delay(1);

ZLG7289_ENABLE();//使zlg7289占有同步串口 WriteSDIO(ZLG7289_CMD_DATA0|0);//数码管以方式0译码,第一个数码管亮 WriteSDIO(key);//显示个位 Delay(1);//延时 if(key>9) {//键值大于9显示十位 WriteSDIO(ZLG7289_CMD_DATA0|1);//发送十位数据 WriteSDIO((unsigned char)(key/10)); Delay(1); WriteSDIO(ZLG7289_CMD_HIDE);//使一、二两位数码管显示 WriteSDIO(3); } else

{//键值小于10不显示十位

WriteSDIO(ZLG7289_CMD_HIDE);//使个位数码管显示 WriteSDIO(1); }

ZLG7289_DISABLE();//zlg7289放弃同步串口控制权

6. 判断游戏进程

判断蛇是否撞到边框的方法,只要比较蛇头的 X,Y 坐标是不是同时和边框的 X,Y 坐标一样,一样的话,即表明蛇撞到边框,游戏结束

int i = 0; //if( (HeadPos[0]>xmax)||(HeadPos[0] < xmin )||(HeadPos[1] > ymax )||(HeadPos[1] < ymin ) ) //{ //gameover = TRUE; //return; //} __asm { cmp HeadPos[0], xmax bgt gameend cmp HeadPos[0], xmin blt gameend cmp HeadPos[1], ymax bgt gameend cmp HeadPos[1], ymin bge gamecontinue gameend: mov gameover,#1 gamecontinue: } time(n); n++; if (gameover == TRUE) return; if (IsInRect(pbarRect1_5, HeadPos[0], HeadPos[1]) ) { gameover = TRUE; return; } if (IsInRect(pbarRect2_5, HeadPos[0], HeadPos[1]) )

{ gameover = TRUE; return; } while(1) { if (pause == TRUE) { TextOut(pdc,100,115,Game_Paused_Caption_16,TRUE, FONTSIZE_SMALL);//在屏幕上显示pause的提示 Song(); OSTimeDly(200); continue; } if (gameover == TRUE) { TextOut(pdc,100,115,Game_Over_Caption_16,TRUE, FONTSIZE_SMALL); //在屏幕上显示gameover的提示 Song(); OSTimeDly(200); continue; } if (gamepass == TRUE) {

TextOut(pdc,100,115,Game_Pass_Caption_16,TRUE,FONTSIZE_SMALL); //在屏幕上显示gamepass的提示

Song(); OSTimeDly(200); continue; }

7. 加速,计分,计时功能

加速:使用变量m,m一开始的值为1,按键给m赋不同的值

case 1: HeadPos[0] = HeadPos[0] + m; break; case 2: HeadPos[0] = HeadPos[0] - m; break; case 3: HeadPos[1] = HeadPos[1] - m; break;

case 4:

HeadPos[1] = HeadPos[1] + m; break;

计分:在吃到豆子后,用式子mscore=number*2-n*0.01+1计算分数。 void scoresco(int sco) {

U16 sco_16[10];

Int2Unicode(sco,sco_16);////////////

TextOut(pdc,280,115+60,sco_16,TRUE,FONTSIZE_SMALL); }

计时:根据系统时间计时 void time(int tim) {

U16 tim_16[10];

Int2Unicode(tim,tim_16);

TextOut(pdc,270,115+40,tim_16,TRUE,FONTSIZE_SMALL); }

8. 监听键盘

监听键盘动作: 任务通过等待消息而处于挂起状态,当任务接到消息以后,则处于就绪状态,然后开始判断所接受到的这个消息是不是需要处理,如果是执行相应的处理函数,最后,删除所接收到的消息,继续挂起等待下一条消息 while(1) { pMsg = WaitMessage(0); switch (pMsg->Message) { case OSM_KEY: onKey(pMsg->WParam, pMsg->LParam); } DeleteMessage(pMsg); OSTimeDly(200); }

七、小结

本次课程设计,从一开始的无从下手,到了解一些基本知识,理清头绪,到

最后终于完成,我收获很大。

在编程时我碰到了很多的困难,在这个时候就需要我多与别人交流.三人行必有我师,也许在一次和别人不经意的谈话中, 就可以迸出灵感的火花. 在编程的过程中我也看到了有良好的编程风格是十分重要的,至少在时间效率上就体现了这一点.养成良好的习惯,代码的缩进编排,变量的命名规则要始终保持一致,这些都是提高我们编程的注意点. 还有在变成中最能体现简单的原则. 所以我要尽量思考讨论简单的程序, 这样简单的方法更容易被人理解,更容易实现,也更容易维护.遇到问题时要优先考虑最简单的方案,只有简单方案不能满足要求时再考虑复杂的方案.

通过这次课程设计,不仅掌握了知识,更锻炼我的处理问题的能力,让我在处理问题时,能沉着应对。其实很多问题,看起来很复杂,可是只要你用心做,就能找到解决方案。

谢谢大家的帮助,在今后的学习中我会更加努力。

八、参考文献

[1] ARM嵌入式系统基础教程(第2版) 周立功 、王祖麟、陈明计 、严寒亮 、张斌 北京航空航天大学出版社

[2] 嵌入式实时操作系统及应用开发 罗蕾 北京航空航天大学出版社

本文来源:https://www.bwwdw.com/article/w257.html

Top