单片机大型试验——“老鼠穿墙吃蛋糕”小游戏

说在前头:整个代码工程以及实验报告在个人上传资源中,如有需要请去下载,博客中只是部分代码,完整的代码工程

 

《单片机原理与实践》课程大作业

 

 

“老鼠吃蛋糕”技术报告

 

                      摘    要 

单片机开发小游戏很普遍,而且网上也有各种开源文件。至于我,开始想实现个迷宫,后来发觉迷宫太静态了,拓展空间不大。后来想到了方块扩散,感觉方块扩散实现步数存在着最优路径,但尝试后发现难度极大且涉及深度学习算法,于是就两者结合设计了“老鼠吃蛋糕”这存在最优路径且拓展性高的游戏。

该游戏采用触摸屏、显示屏、红外遥控、串口通信、数码管、定时器、蜂鸣器、LED灯等外设来实现该游戏,功能相对广泛。其中显示屏用作游戏界面以及交互界面的显示,红外用来控制,串口是信息显示,定时器实时处理并作相应的反馈,蜂鸣器和LED则是指示作用。其中各个模块的功能相对简单,但结合起来就不简单了,具体的困难会在后续章节中提出。

关键词: 迷宫;方块扩散;最优路径;串口;定时器

 

1. 系统方案

 

本系统主要由显示屏、触摸屏、红外遥控、串口通信、数码管等模块组成。由于是在学校下发的单片机开发板上设计,所以并不涉及各个模块的品牌、类型选择,下面简述一下游戏某一功能实现模块的选择

 

1.1游戏操纵方案的论证与选择

方案一:触摸屏控制。触摸屏控制是比较普遍的,比如我们的智能手机以及部分笔记本电脑,但对于该开发板来说并不适用。主要有两点:① 由于制作工艺及器件的老化存在着噪点,即使采用中值法去噪效果较好,但那么小的显示屏摆放好几个操作按键也不免会误触碰;② 由于显示屏本身就是240*400大小,若再放置好几个按键则对后期的游戏设计界面有很大的影响。

方案二:红外遥控。比如以前的彩色电视就可以通过红外遥控来玩自带的俄罗斯方块、推箱子之类的游戏。用红外来控制效果也确实不错,既不用那么近盯着显示屏,也可以选择个舒服的姿势玩游戏,唯一的缺点就是偶尔会解码错误,主要是因为人为按按键不规则以及信号传送过程会有干扰。

    经过对以上两种方案的对比,选择方案二相对来说更为可靠。既可以避免触摸屏的噪点问题和游戏界面的影响,红外遥控的解码错误也是在可接受的范围内。

 

1.2 游戏相关信息显示方案的论证与选择

方案一:显示屏显示。提示信息和游戏界面在一处显示对玩家来说比较方便,边观察游戏界面就可以看到提示信息。但同样出现界面大小问题,不能显示太多信息,同时由于不支持输出显示中文,所以对于游戏玩家不太方便。

方案二:串口助手显示。可以通过PC端串口助手接受单片机串口传送的信息,既能输出显示中文,又有较大的显示界面,查看信息比较容易,但是游戏玩家需要不断地在显示屏和PC端串口助手界面间转换视角,容易分神,不利于游戏的控制。

方案三:数码管显示。数码管只能显示数字及字符,显示内容比较单一,但是数码管比较醒目,可以用来显示时间之类的信息。

综合以上三种方案,决定使用三种方案来显示不同的信息。用方案一中显示屏来显示游戏开始界面、关卡选择以及游戏结束界面;方案二串口助手来显示游戏开始,游戏操作说明,游戏操作过程的步数、时间信息以及游戏的相关提示信息;方案三数码管醒目地显示倒计时,对游戏玩家有提示作用。

 

1.3 系统方案

 

1.3.1.系统方框图

1.3.2系统工作原理

    本系统开始在显示屏显示指导信息,等待玩家触摸显示屏或红外遥控来进行游戏相关的选择和控制,在游戏开始后定时器开始计时并通过数码管来显示,在操纵期间显示屏右上角也会提供倒计时的显示来提示玩家,PC端串口助手会显示相关操作信息来帮助玩家进行游戏。其中红外接收管接受遥控信息(PN4中断),进行解码得到控制信息并进行相应的程序处理,如此循环反复。玩家若及时地控制“老鼠”吃到“蛋糕”则挑战成功,显示屏和PC端串口助手会显示胜利的相关信息,蜂鸣器和LED也会作出反应;反之则挑战失败,会有失败的相应信息输出显示。  

 

 

2. 设计与论证

2.1 系统的硬件设计

 

        由于使的单片机开发板为学校下发,故没有进行系统的硬件设计,只是进行了硬件模块的使用选择。

 

2.2 系统软件设计

 

3. 测试

3.1硬件电路的测试方法

          由于使用的单片机开发板为学校下发,故没有进行系统的硬件设计,只是进行了硬件模块的性能测试及性能测试,下面主要对触摸屏及红外遥控的性能进行简要说明。

     触摸屏:由于制作工艺及器件的老化存在着噪点,即使采用中值法去噪效果较好,但那么小的显示屏若要真正设计几个触控按键难免会误触碰,对于时间紧张的游戏来说无疑是不适用的,所以只是在开始界面提供了触摸功能。

     红外遥控:用红外来控制效果也确实不错,既能较远距离进行控制,又能有个好姿态。唯一不足的就是偶尔会出现解码错误的情况,这跟程序中有多个中断以及红外信号传输存在干扰有关,经过对解码时序进行合理的修改后,虽然效果改善了,但仍存在,不过是在可接受范围内。

 

 

3.2 项目特色说明

       该游戏的特色主要有三点:

① 数码管多位数字同时显示。该开发板上的五个米字管是由三块PCA9557PWR芯片同时控制一片LEDS05441芯片来进行显示的,所以一次只能控制一个米字管,所以很多同学的10~15只用16进制的A~F来表达。所以要“同时”显示两个米字管,只能通过让它们交替显示的频率非常快,快到我们人眼观察不到。刚开始把程序段放在主程序的while(1)循环里,但这样严重干扰了红外遥控的中断信号接收,整个程序的时序是乱的,然后开了个定时器,通过设置定时器的频率来控制米字管,但又碰到了中断优先级问题,于是又折腾了一番,一切解决之后,终于可以同时显示两位数字了,三位、四位也同理。现在回过头感觉这几个问题很简单,但开始遇到问题、分析问题、解决问题花费了较多时间,感觉收获蛮多。

② 游戏本身的设定。刚开始想实现个迷宫,后来发觉迷宫太静态了,拓展空间不大。后来想到了方块扩散,感觉方块扩散实现步数存在着最优路径,于是就想把最优路径找出来,但尝试后发现难度极大且涉及深度学习算法,于是就两者结合设计了“老鼠吃蛋糕”这存在最优路径且拓展性高的游戏。第一关是最基础的,也没什么亮点;第二关也只是加了常见的“转向块”,对游戏难度有一定的提升;第三关则采用了三个分块区域,只有经过一定路径使用“传送块”功能才能进行区域的跳转,为什么要区域跳转呢?因为我在定时器里设计了一个随机函数,定时器的频率决定了蛋糕在三个区域跳转的频率,然后随机函数则是进一步决定蛋糕跳转频率以及跳转区域,这游戏玩法想了挺久才想到的,自身感觉这样的游戏设定蛮好的。

③ 模块的集成。该游戏涉及了课上实验的较多模块,如触摸屏、显示屏、红外遥控、串口通信、数码管、定时器、蜂鸣器、LED灯,虽然这些模块并不那么难,但是要把各个模块结合起来用并应用在游戏当中就不那么容易了,之间有严格的逻辑关系以及单独情况的处理,就像上面说的中断优先级问题一样,其他模块也存在相互干扰的情况,比如中断里面不能再嵌套中断服务函数以及延时函数,否则严重影响程序的时序,严重可能使板子进入自锁状态。

 

3.3 测试结果

     测试结果在视频和附录二中有详细说明,这里就不再重复了。

 

4.参考文献

 

1)陈朋. Cortex-M4实验指导书. 浙江工业大学出版社. 2014.9

2)陈朋. Cortex-M4原理与实践. 浙江工业大学出版社. 2014.9

3) 王日明, 廖锦松, 申柏化. 轻松玩转ARM 微控制器--基于Kinetis K60. 北京航空航天大学出版社. 2014.9

4) 刘火良, 杨森. STM32库开发实战指南. 机械工业出版社. 2017.2


附录一  电路原理图

 

米字管电路图

红外IR电路图

蜂鸣器电路图

TFTLCD液晶显示电路图

 

附录二 实物图

注:第三关游戏界面,其中黄色箭头指的是“传送块”,红色箭头是“蛋糕”随机出现的位置,深蓝色箭头是“老鼠”所在位置。

注:游戏说明界面,深蓝色箭头所指内容是游戏说明,黄色箭头是“退出”键,品红箭头是关卡“easy”键,黑色箭头是关卡“mid”键,白色箭头是关卡“hard”键。

注:这是游戏开始界面,白色是游戏名,黑色是游戏说明引导键,红色是“退出”键,黄色是“easy”键,绿色是“mid”键,橙色是“hard”键。

注:PC端串口助手关于游戏相关信息的显示界面。

注:游戏第二关界面,红色箭头为“转向块”,黄色箭头为“蛋糕”,橙色箭头为“老鼠”。

注:游戏第一关界面,红色箭头为“老鼠”,黄色箭头为“蛋糕”。

 

 

注明:完整代码和实验报告在博主的个人资源中,博客只是简单描述,如有需要自行前往下载,完整代码工程

代码附录

#include <stdbool.h>

#include <stdint.h>

#include <string.h>

#include "inc/hw_memmap.h"

#include "inc/hw_types.h"

#include "inc/hw_epi.h"

#include "inc/hw_nvic.h"

#include "inc/hw_ints.h"

#include "driverlib/epi.h"

#include "driverlib/gpio.h"

#include "driverlib/sysctl.h"

#include "driverlib/rom.h"

#include "driverlib/rom_map.h"

#include "driverlib/pin_map.h"

#include "driverlib/systick.h"

#include "driverlib/interrupt.h"

#include "driverlib/ssi.h"

#include "driverlib/fpu.h"

#include "driverlib/timer.h"

#include "utils/uartstdio.h"

#include "TFTinit/TFT_400x240_OTM4001A_16bit.h"

//#include "TFTinit/picture.h"

#include "TOUCHinit/TOUCH_TSC2046.h"

#include "EPIinit/EPIinit.h"

#include "math.h"

#include "inc/hw_i2c.h"

#include "driverlib/i2c.h"

 

*********************************************************************

// System clock rate in Hz.

//

//*****************************************************************************

 

/触摸屏//

uint32_t g_ui32SysClock;

extern uint32_t GetData[6];

uint32_t N=0;//打中的只数

uint32_t TouchXData[6];

uint32_t TouchYData[6];

uint32_t TouchZData[6];   //Z is for pressure measurement

 

 

#define _NOP() _nop()

 

//*********************************************************************

//*********************************************************************

#define I2C0_MASTER_BASE 0x40020000

#define I2C0_SLAVE_BASE 0x40020000

 

//*********************************************************************

// 地址、寄存器等定义部分

//*********************************************************************

//*********************************************************************

//

// 设定slave(从)模块的地址。这是一个7-bit的地址加上RS位,具体形式如下:

//                      [A6:A5:A4:A3:A2:A1:A0:RS]

// RS位是一个指示位,如果RS=0,则说明是主发送数据,从接收数据;RS=1说明是主接收数据,从发送数据

//

//*********************************************************************

//U21控制4个米字管和特殊管脚的亮灭

#define I2C0_ADDR_TUBE_SEL      0x30  //00110000

//U22控制米字管7~14管脚对应的码段

#define I2C0_ADDR_TUBE_SEG_LOW    0x32  //00110010

//U23控制米字管15~18管脚对应的码段

#define I2C0_ADDR_TUBE_SEG_HIGH  0x34   //00110100

//U24控制LED光柱

 

//PCA9557内部寄存器,也称子地址

#define PCA9557_REG_INPUT 0x00

#define PCA9557_REG_OUTPUT 0x01

#define PCA9557_REG_PolInver 0x02

#define PCA9557_REG_CONFIG 0x03

 

//*************************************************************************************

 #define NUM 0

 

#define EASY_I 8

#define EASY_J 7

#define MID_I 14

#define MID_J 8

#define HARD_I 26

#define HARD_J 12

 

 

/红外///

volatile uint32_t  ui32SysClock;

volatile uint32_t  error= 0;

volatile uint32_t  count= 0;

unsigned char  IrData[4];

 

 

int begin_easy_x=50,begin_easy_y=120;

int begin_mid_x=40,begin_mid_y=60;

int begin_hard_x=30,begin_hard_y=2;

 

int easy_i = 1,easy_j = 3,easy_end_x=6,easy_end_y=3;

int mid_i = 9,mid_j = 1,mid_end_x=6,mid_end_y=3;

int hard_i = 6,hard_j = 3;hard_cake_i=2,hard_cake_j=7;

 

int easy_time = 70,mid_time = 230,hard_time = 600,easy_R,easy_G,easy_B;

int mid_tra_x1 = 4,mid_tra_x2 = 6,mid_tra_y1=4,mid_tra_y2=5;

int easy_end = 0,mid_end=0;hard_end=0,easy_begin=0,mid_begin=0,hard_begin=0;

int hand_u=0,hand_d=0,hand_r=0,hand_l=0;

int kong_flag=0,time=0,time_flag=0;

int flag=0,cake_flag;

int touch_flag=0;

int level=0;

 

int min(int a,int b)

{

if(a<b)  return a;

else      return b;

}

 

int max(int a,int b)

{

if(a<b)  return b;

else      return a;

}

 

long hand(int color)

{

  if((color==1)||(color==2)||(color==4))  return 0X7D7C;//浅蓝色

  if(color==5)  return 0xF800;//蓝色

  if(color==3)  return 0xFFFF;//白

 

}

 

 

//1 背景;2 老鼠;3 蛋糕;4 转向块;5 传送洞

int piexl_easy_ori[8][7]={   {0,0,0,0,0,0,0},

                     {0,0,1,2,0,0,0},

     {0,1,1,1,1,1,0},

 {0,1,1,1,0,1,0},

 {0,0,0,0,0,1,0},

 {0,1,1,1,1,1,0},

 {0,1,1,3,0,0,0},

 {0,0,0,0,0,0,0}  };

 

int piexl_mid_ori[14][8]={ {0,0,0,0,0,0,0,0},{0,0,1,1,0,1,0,0},{0,0,1,1,1,1,1,0},{0,1,1,1,1,1,1,0},{0,1,1,0,4,1,0,0},

                  {0,1,1,1,1,1,1,0},{0,0,1,3,1,4,1,0},{0,0,1,0,1,1,1,0},{0,0,1,0,0,0,0,0},{0,2,1,1,1,0,0,0},

                  {0,0,1,1,1,1,1,0},{0,1,1,1,1,1,1,0},{0,1,1,1,1,0,0,0},{0,0,0,0,0,0,0,0}    };

 

int piexl_hard_ori[26][12]={ {0,0,0,0,0,0,0,0,0,0,0,0},{0,1,1,1,0,1,1,0,0,0,0,0},

{0,1,0,1,1,1,1,3,0,0,0,0},{0,1,1,1,0,1,1,0,0,0,0,0},

{0,1,1,1,1,1,1,5,0,0,0,0},{0,1,0,1,1,1,1,0,0,0,0,0},

{0,1,1,2,1,1,1,0,0,0,0,0},{0,1,0,0,1,1,1,1,0,0,0,0},

                    {0,1,1,1,1,1,1,1,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0},

 

                    {0,1,1,1,0,0,0,1,1,1,0,0},{0,0,5,1,1,1,1,1,1,1,1,0},

                    {0,1,1,1,1,1,1,1,0,1,1,0},{0,1,1,1,1,1,1,0,0,0,0,0},

                    {0,1,0,1,1,1,1,1,0,0,0,0},{0,1,1,1,1,1,0,1,0,0,0,0},

                    {0,1,1,1,1,1,1,1,0,0,0,0},{0,0,0,1,3,1,0,1,0,0,0,0},

                    {0,0,0,0,0,0,0,0,0,0,0,0},

 

                    {0,1,1,1,0,0,0,1,1,0,0,0},{0,1,0,1,1,1,1,1,1,0,0,0},

{0,1,1,1,1,5,0,1,1,0,0,0},{0,1,1,0,1,1,1,1,0,0,0,0},

{0,1,0,1,1,0,1,3,1,0,0,0},{0,1,1,1,1,0,1,1,0,0,0,0},

{0,0,0,0,0,0,0,0,0,0,0,0}  };

 

int piexl_easy[8][7],piexl_mid[14][8],piexl_hard[26][12];

 

void recover(int a,int b)

{

int i,j;

if(a==8)

{

for(i=0;i<a;i++)

for(j=0;j<b;j++)

{

  piexl_easy[i][j] = piexl_easy_ori[i][j];

 // piexl_easy[i][j][1] = 0;

}

easy_i=1;

easy_j=3;

 

}

 

else if(a==14)

{

for(i=0;i<a;i++)

for(j=0;j<b;j++)

  piexl_mid[i][j] = piexl_mid_ori[i][j];

mid_i=9;

mid_j=1;

 

}

    else if(a==26)

        {

        for(i=0;i<a;i++)

for(j=0;j<b;j++)

{

  piexl_hard[i][j]= piexl_hard_ori[i][j];

}

        hard_i=6;

        hard_j=3;

 

        }

 

}

 

 

void exit_recover()

{

TFTLCD_ShowString(52,180,"    exit(0)    ",RED,LIGHTBLUE);

TFTLCD_ShowString(52,230,"    easy(1)    ",RED,LIGHTBLUE);

TFTLCD_ShowString(52,280,"    mid (3)    ",RED,LIGHTBLUE);

TFTLCD_ShowString(52,330,"    hard(9)    ",RED,LIGHTBLUE);

easy_begin=0;

mid_begin=0;

hard_begin=0;

easy_time = 80;

mid_time = 200;

hard_time = 600;

 

hand_u=0;

hand_l=0;

hand_r=0;

hand_d=0;

 

touch_flag=0;

recover(8,7);

recover(14,8);

recover(26,12);

IntDisable(INT_TIMER0B);

TimerDisable(TIMER0_BASE, TIMER_B);

}

 

 

void end_game()

{

TFTLCD_CLEAR(BLACK);

TFTLCD_ShowString(52,30,"  Time over !  ",WHITE,LIGHTBLUE);

TFTLCD_ShowString(52,80,"    you fail   ",RED,LIGHTBLUE);

UARTprintf("很遗憾,游戏时间到,闯关失败!可重玩一次或进入下一关或直接退出。\n");

    UARTprintf("退出--0;简单--1;中等--3;困难--9\n");

 

exit_recover();

 

}

 

void instructions()

{

TFTLCD_CLEAR(BLACK);

TFTLCD_ShowString(52,20,"this is a instructions about games:you need to control the mouse by 2|4|6|8 to eat the cake. Only complete the game within a certain time, can you beat the game.",RED,LIGHTBLUE);

UARTprintf("游戏说明:该游戏总共有三关,通过红外遥控可选择关卡。你所假扮的“老鼠”必须在规定时间内通过利用各种道具“吃到蛋糕”,这样才算挑战成功.其中有转向块和传送块,可以对“老鼠”位置进行改变,然后最后一关蛋糕还用到的随机函数来确定蛋糕在哪个位置。\n");

TFTLCD_ShowString(52,180,"    exit(0)    ",RED,LIGHTBLUE);

TFTLCD_ShowString(52,230,"    easy(1)    ",RED,LIGHTBLUE);

TFTLCD_ShowString(52,280,"    mid (3)    ",RED,LIGHTBLUE);

TFTLCD_ShowString(52,330,&#

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值