这是我以前做的一个贪吃蛇课程设计。
此次的ARM7最小系统的课程设计是基于LPC2124以及LCD-CSTN20GG0906N7CUN6和按键电路。采用ISIS7 Professional软件自行搭建电路。里面带有实验报告,里面都是干货,希望对大家有用。
目录
1. 课程设计内容
2. 课程设计目的
3. 背景知识
4. 工具/准备工作 4
5. 设计步骤与方法 4
5.1. 步骤1:在Proteus仿真软件上设计电路图 4
5.2. 步骤2:画蛇 5
5.2.1. 步骤1.1:蛇的产生 5
5.3. 步骤3:食物的产生 6
5.4. 步骤4:蛇的移动 7
5.4.1. 步骤1.1:按键的扫描 8
5.5. 步骤5:蛇节的增长 8
5.5.1. 步骤1.1:判断蛇是否吃到食物 9
5.5.2. 步骤1.2:速度的增长 10
5.6. 步骤6:游戏是否结束 10
5.7. 步骤7:LCDHDG12864L-6的显示 11
5.8. 步骤8:重新开始与暂停按键 11
6. 设计结果及分析 12
7. 设计结论 13
8. 问题及心得体会 13
9. 对本设计过程及方法、手段的改进建议 14
10. 参考文献 14
11. 课程设计评价(教师) 14
全民贪吃蛇
1. 课程设计内容此次ARM7最小系统的课程设计是基于LPC2124以及LCD-CSTN20GG0906N7CUN6和按键电路。采用ISIS 7 Professional软件自行搭建电路。本系统的特点是性能高、成本低并且耗能小等特点。 2. 课程设计目的此次课程设计的主要目的是让我们提高代码的熟练度,以及发挥自主创新,将平时上课学到的理论知识与实验知识相结合。让我们对arm7芯片以及ISIS 7 Professional仿真模拟器更为了解。 - 熟练掌握向量中断IRQ的初始化和对中断的处理过程。
- 熟练掌握定时器的控制和使用方法。
- 熟练掌握C语言的使用原理及编程方法。
- 熟练掌握仿真电路图的连接方法。
- 熟练掌握两个不同的液晶显示器的显示控制原理及编程方法。
3. 背景知识ARM处理器的三大特点是:耗电少功能强、16位/32位双指令集和合作伙伴众多。 1、体积小、低功耗、低成本、高性能; 2、支持Thumb(16位)/ARM(32位)双指令集,能很好的兼容8位/16位器件; 3、大量使用寄存器,指令执行速度更快; 4、大多数数据操作都在寄存器中完成; 5、寻址方式灵活简单,执行效率高; 6、指令长度固定。 设计原理:贪吃蛇游戏是一款经典的游戏,既简单又耐玩。通过控制蛇头方向吃蛋,使得蛇变长,从而获取积分。用游戏把子上下左右控制蛇的方向,寻找吃的东西,每吃一口就能得到一定的积分,而且蛇的身子会越吃越长,身子越长玩的难度就越大,不能碰墙,不能咬到自己的身体,更不能咬自己的尾巴,等到了一定的分数,就能过关,然后继续玩下一关。
4. 工具/准备工作- CodeWarrior project file软件。
- ISIS 7 Professional软件。
- 了解Proteus仿真软件和ARM编程软件的使用方法。
- 了解ARM7芯片的编程方法。
5. 设计步骤与方法5.1.步骤1:在Proteus仿真软件上设计电路图 5.2.步骤2:画蛇由于采用的是LCD-CSTN20GG0906N7CUN6,首先根据其数据手册对该LCD进行绘画,先写入命令对其进行写操作WR_CMD(0xBC);WR_DATA(0); WR_DATA(00); WR_DATA(00); 然后写入命令WR_CMD(0x75);意为写入x轴,后接x轴的具体数据以及x轴的最大值。紧接着写入WR_CMD(0x15);意为写入y轴,后接y轴的具体数据以及y轴的最大值。 void snake_display(int x,int y) { int i,j; WR_CMD(0xBC); WR_DATA(0); WR_DATA(00); WR_DATA(00); WR_CMD(0x75); WR_DATA(x); WR_DATA(63); WR_CMD(0x15); WR_DATA(y); WR_DATA(95); WR_CMD(0x5c); for(j=0;j<1;j++) { for(i=0;i<1;i++){ WR_DATA(0xf8);//0xff WR_DATA(0x00); }} } 5.2.1步骤1.1:蛇的产生首先创建一个蛇的结构体Point,该结构体内存储了蛇的x、y坐标,以及蛇的长度和蛇的方向,然后通过rand函数产生随机数存在Snake的x、y中,即蛇的初始位置是随机的,然后再通过snake_init()函数中对蛇进行初始化,然后通过循环在snake_display(int x,int y)函数中把蛇画出来。 - void snake_init()
- {
- int i;
- snake_state();
- Snake.x[0] = rand()%62;
- Snake.y[0] = rand()%95;
- Snake.x[1] = Snake.x[0]+1;
- Snake.y[1] = Snake.y[0];
- Snake.x[2] = Snake.x[1] +1;
- Snake.y[2] = Snake.y[0] ;
- Snake.num = 3;
- for(i=0;i<Snake.num;i++)
- snake_display(Snake.x[i],Snake.y[i]);
- }
复制代码
5.3.步骤3:食物的产生首先创建一个食物的结构体point,在该结构体的基础上定义一个数组food_map,通过这个结构体对地图进行修改。该结构体内存储了食物的x、y坐标,以及食物的长度,然后通过rand函数产生随机数保存在food_map中,即食物的出现位置是随机的,然后再通过food_init()函数中对食物进行初始化,然后通过循环在food_display(int x,int y)函数中把食物画出来(食物的画法与步骤一中蛇的画法是一样的,只不过食物的颜色是黄色,蛇的颜色是红色,便于确认)。 当蛇吃了一定数量的食物时,通过调用食物产生函数,重新产生新的食物,以保证食物的存在性。
- void food()
- {
- int i;
- if(create_food==1)
- {
- for(i=food_long;i<food_long+8;i++)
- {
- food_map[i].x=rand()%62;
- food_map[i].y=rand()%93;
- }
- food_long+=8;
- create_food=0;
- }
- }
- void food_map_init()
- {
- int i;
- food();
- for(i=0;i<food_long;i++)
- food_display(food_map[i].x,food_map[i].y);
- }
复制代码
5.4.步骤4:蛇的移动若相应方向的按键被按下,则蛇就会往相应的方向移动。首先要把蛇之前走过的痕迹擦掉(擦掉的函数如同画蛇,把颜色数据改为背景色即可),然后将蛇从最后个节点向前一个节点移动,这是蛇的最核心算法。最后通过switch case判断哪一个按键被按下,然后对蛇头的方向,即Snake.direction进行修改。蛇的移动函数放在中断中,每次发生中断都会重新画蛇(蛇的移动即把原来的蛇尾巴擦掉,然后根据新的坐标重新画蛇)。当按键没有被按下时,蛇会按照上一次按下按键的方向去移动(初始化时默认向右)。
- void snake_move()
- {
- char i;
- int j;
- if(flag==1)
- {
- Snake.num++;
- flag=0;
- }
- for(i=0;i<food_long;i++)
- food_display(food_map[i].x,food_map[i].y);
- clear_snake(Snake.x[Snake.num-1],Snake.y[Snake.num-1]);
- for(i = Snake.num - 1; i > 0; i--) // 将蛇从最后个节点向前一个节点移动
- {
- Snake.x[i] = Snake.x[i - 1];
- Snake.y[i] = Snake.y[i - 1];
- }
- snake_state();
- switch(Snake.direction)
- {
- case 1:
- Snake.x[0] -=1 ;
- break;
- case -1:
- Snake.x[0] +=1 ;
- break;
- case 2:
- Snake.y[0] -=1 ;
- break;
- case -2:
- Snake.y[0] +=1 ;
- break;
- }
- for(i=0;i<Snake.num;i++)
- snake_display(Snake.x[i],Snake.y[i]);
- }
复制代码
5.4.1. 步骤1.1:按键的扫描 通过IOxPIN来确定按键是否按下,由于按键接在P1.20-P1.23,因此是I01PIN。通过读取各个按键的状态封装在snake_state函数里。如果上键按下,Snake.directio则相应为上(Snake.direction是蛇的结构体Point里的成员用于确定蛇的方向)。其余方向的设置同上。 void snake_state() { if(!(IO1PIN&key_w))Snake.direction=1;//上 else if(!(IO1PIN&key_s))Snake.direction= -1;//下 else if(!(IO1PIN&key_a))Snake.direction= 2;//左 else if(!(IO1PIN&key_d))Snake.direction= -2;//右 }
5.5.步骤5:蛇节的增长 如果蛇吃到了食物,此时的蛇应该变长,即Snake.num++,在蛇尾的部分添加一个点即可,新的一节先放在看不见的位置,下次循环就取前一节的位置。先把这条蛇擦掉,然后重新画蛇。 if(flag==1) { Snake.num++; flag=0; }
5.5.1. 步骤1.1:判断蛇是否吃到食物 如果蛇头的x、y坐标与food_map中的任一x、y坐标均相等,则说明蛇已经吃了食物,此时要对食物进行擦除(食物的擦除与食物的绘画是一样的,只需要把颜色改为跟背景色一样即可)。
- void checkeat()
- {
- int i,time;
- for(i=0;i<food_long;i++)
- { if((Snake.y[0]==food_map[i].y)&&(Snake.x[0]==food_map[i].x)||(Snake.y[Snake.num-1]==food_map[i].y)&&(Snake.x[Snake.num-1]==food_map[i].x))
- {
- flag=1;
- NUMBER++;//
- if(NUMBER>9)NUMBER=0;
- print_score();//
- eat++;
- if(eat%4==0)create_food=1;
- if(eat%1==0)
- {
- LEVEL++;
- speed-=50;
- if(LEVEL>9)LEVEL=0;
- print_level();
- }
- clear_food(food_map[i].x,food_map[i].y);
- food_map[i].x=NULL;
- food_map[i].y=NULL;
- break;
- }
- }
- }
复制代码
5.5.2. 步骤1.2:速度的增长 当蛇吃到一定数量的食物时,游戏的等级会增加,此时蛇的速度就会变快,通过改变定时器的定时时长可以实现,即改变T0TR,首先设定一个speed变量值,当游戏中的LEVEL++时,speed的值减小,此时定时器的定时时长会缩短,会,从而导致蛇的移动扫描变快,因而实现了蛇的速度的变化。 void speed_change() { T0PR = speed; }
5.6.步骤6:游戏是否结束 如果蛇跑出了LCD所能表示的范围,即蛇头小于0或者大于63,蛇尾小于0或者大于95,则视为死亡,游戏结束,显示结束图片。如果需要继续游戏则按下重新开始按钮。 如果蛇头撞上了身体的任意环节(通过循环来一节节地进行判断),则视为死亡,游戏结束,显示结束图片。如果需要继续游戏则按下重新开始按钮。 反之,游戏继续。 如果最终的等级大于9级,则游戏胜利,显示胜利图片。如果需要继续游戏则按下重新开始按钮。
- void snake_life()
- {
- int i;
- if(Snake.x[0] < 0 || Snake.x[0] > 63 || Snake.y[0] < 0 || Snake.y[0] >95) die=1;
- for(i=3;i<Snake.num;i++)
- {
- if(Snake.x[i]==Snake.x[0]&&Snake.y[i]==Snake.y[0])
- {
- die=1;
- break;
- }
- }
- }
复制代码
5.7.步骤7:LCDHDG12864L-6的显示 该LCD用来显示游戏的名称、制作人、等级以及分数。根据该LCD的数据手册首先写入的是y的值,然后写入x的值,来确定所写的字的位置,先写字的上半部分,然后重新写入y的值,x的值不变,再写字的下半部分。 每次蛇吃到食物时,分数就会增加。分数的字模放在一个数组里,通过下标调用显示。当蛇吃到了一定数量的食物之后,等级就会随之增加,等级的字模也是放在一个数据里,通过下标调用显示。 void print_level() { int j; WR_CMD1(0xb0+4); WR_CMD1(0x10); WR_CMD1(0x00+51); for(j=0;j<8;j++) { WR_DATA1(number[LEVEL][0][j]); } WR_CMD1(0xb0+5); WR_CMD1(0x10); WR_CMD1(0x00+51); for(j=0;j<8;j++) { WR_DATA1(number[LEVEL][1][j]); } }
5.8.步骤8:重新开始与暂停按键 设置两个按键用于重新开始游戏以及暂停游戏。 重新开始游戏时,先把显示蛇的LCD的屏幕清屏。把成绩与等级初始化为0,然后对游戏的所有参数重新进行初始化。 暂停按钮的实现十分简单,只需要把蛇的移动方向设为空即可。此时,蛇不会再移动。若要继续游戏,则按任意方向键继续。 void button() { if(!(IO1PIN&restart)) { LCD_clear(); die=0; LEVEL=0; NUMBER=0; flag=0; eat=0; speed=1000; Snake.direction=NULL; food_map_init(); create_food=0; score_init(); print_score(); print_level(); snake_init(); food_map_init(); } if(!(IO1PIN&stop)) { Snake.direction=NULL; } } 6. 设计结果及分析设计结果:蛇的出现位置与食物的出现位置都是随机的。当按下相对应的按键时,蛇会往那个方向走,当蛇吃到食物时,分数会增加。当蛇吃到一定数量的食物时,等级和速度也会随之增加,当食物少于一定数量时,会出现新的食物。当等级达到9时,游戏结束,玩家胜出,播放胜利图片。如果蛇吃到自身,或者撞到墙,则游戏结束,玩家失败,播放失败图片。 设计分析:这个程序的关键是表示蛇的图形以及蛇的移动。用一个小矩形表示蛇的一节身体,身体每长一节,增加一个矩形块。如果不按任何键,蛇自行在当前方向上前移,当游戏者按了有效的方向键后,蛇头朝着指定的方向移动,一步移动一节身体,所以当按了有效的方向键后,先确定蛇头的位置,然后蛇身体随着蛇头移动,图形的实现是从蛇头的新位置开始画出蛇,这时原来蛇的位置和新蛇的位置差一个单位,所以看起来蛇会多一节身体,所以将蛇的最后一节用背景色覆盖。食物的出现和消失也是画矩形块和覆盖矩形块。
7. 设计结论经过奋战我的课程设计终于完成了。在没有做课程设计以前觉得课程设计只是对嵌入式最小系统这门课程所学知识的单纯总结,但是通过这次做课程设计发现自己的看法有点太片面。课程设计不仅是对前面所学知识的一种检验,而且也是对自己能力的一种提高。通过这次课程设计使我明白了自己原来知识还比较欠缺。自己要学习的东西还太多,以前老是觉得自己什么东西都会,什么东西都懂,有点眼高手低。通过这次课程设计,我才明白学习是一个长期积累的过程,在以后的工作、生活中都应该不断的学习,提高自己的知识和综合素质。 8. 问题及心得体会通过这次的课程设计让我学到了许多编程知识,提高运用C语言解决实际问题的能力,巩固了C语言的语法规则、掌握和理解,还提高了自学以及查阅资料的能力。我发现只有理论知识是不够的,仅仅运用课上所学无法编实际问题,实际执行过程与预想不一致,经常会出现bug。还因为在仿真软件中用错了一个LCD显示器,导致程序卡在那里好几天,当时用的是12864-6,一直卡在蛇的显示,非常考验我的逻辑与耐心。后来一怒之下换了一个实验中讲过的LCD显示器。必须把所学的理论知识和自身的实践相结合,在编程中我学到了许多编程知识,还学会了如何去看器件的数据手册,在实际编程中,我遇到许多困难,例如:蛇的头部与食物的检测问题,蛇的移动与显示问题,蛇的转向问题,食物的随机生成问题等等,我通过在网上搜集资料与自己的分析。最后,终于完成了这个游戏。 9. 对本设计过程及方法、手段的改进建议在食物的设计的方面,可以放上带道具的食物,使蛇吃完后具有保护,穿墙等特种功能,而且难度逐渐变难。还有设计一些障碍物,当蛇撞到障碍物时死亡。
全部资料51hei下载地址:
Snake.zip
(281.7 KB, 下载次数: 122)
|