找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 5284|回复: 6
打印 上一主题 下一主题
收起左侧

STM32F103ZET6贪吃蛇游戏程序设计

  [复制链接]
跳转到指定楼层
楼主
ID:462746 发表于 2019-1-5 21:47 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
基于STM32F103的贪吃蛇

单片机源程序如下:
  1. ///***************************************************
  2. //程序名:贪吃蛇
  3. //平台:STM32F103ZET6精英版
  4. //
  5. //程序功能:贪吃蛇游戏,吃到食物后,速度增加,长度变长,咬到自己游戏结束,复位重新开始
  6. //
  7. //程序调用:自制手柄按键,AD产生随机数,正点原子4.3寸屏幕,FreeRTOS操作系统
  8. //
  9. //设计思路:创建若干任务,按键任务控制蛇方向,显示任务控制屏幕的显示与坐标更新,事件任务为数据处理任务,并控制着其他任务能否运行
  10. //
  11. //知识准备:AD采样,GPIO设置,FreeRTOS创建任务与挂起
  12. //
  13. //注意:屏幕被裁剪成4.3寸版本,其它屏幕请修改为原程序
  14. //
  15. ///****************************************************

  16. //系统文件
  17. #include "sys.h"
  18. #include "delay.h"
  19. #include "usart.h"
  20. #include "stdio.h"
  21. #include "stdlib.h"
  22. #include "math.h"
  23. //内部驱动
  24. #include "rand.h"
  25. //外围电路
  26. #include "led.h"
  27. #include "key.h"
  28. #include "lcd_4_3.h"
  29. //用户算法
  30. #include "my_task.h"

  31. #define GAME_WIDTH        480        //设置屏幕分辨率为800*480,为了维持正方形,分成80*48等分
  32. #define GAME_HEIGHT        800
  33. #define GAME_XPART  48
  34. #define GAME_YPART  80

  35. enum sta //枚举状态
  36. {
  37.         OFF,//0
  38.         ON        //1
  39. } station;

  40. enum dir //枚举贪吃蛇方向
  41. {
  42.         UP,                //上
  43.         DOWN,        //下
  44.         LEFT,        //左
  45.         RIGHT        //右
  46. }direction;

  47. typedef struct  //贪吃蛇重要参数集
  48. {                                                                                    
  49.         u16 speed;                //速度
  50.         u16 length;     //长度
  51.         u16 energybuf;  //能量缓存
  52.         u16 width;      //蛇宽        //未使用
  53.         u16 life;       //生命数        //未使用
  54.         u16 firstx;     //蛇头坐标x
  55.         u16 firsty;     //蛇头坐标y
  56.         u16 lastx;      //蛇尾坐标x
  57.         u16 lasty;      //蛇尾坐标y
  58.         u16 color;      //蛇颜色        //未使用
  59. }snakes;
  60. snakes snake;

  61. void Snake_Init(snakes * snake)//贪吃蛇参数初始化
  62. {
  63.         snake->speed=5;
  64.         snake->length=10;
  65.         snake->energybuf=0;
  66.         snake->width=1;
  67.         snake->life=3;
  68.         snake->firstx=4;
  69.         snake->firsty=5;
  70.         snake->lastx=0;
  71.         snake->lasty=0;
  72.         snake->color=RED;
  73. }

  74. typedef struct  //食物重要参数集
  75. {                                                                                    
  76.         u16  energy ;//食物能量
  77.         u16  x ;        //食物坐标x
  78.         u16  y ;        //食物坐标y
  79. }apples;
  80. apples apple;

  81. void Apple_Init(apples * apple)//食物参数初始化
  82. {
  83.         apple->energy=1;
  84. //        apple->x=24;        //坐标由随机数计算出来
  85. //        apple->y=40;
  86. }

  87. typedef struct  //事件重要参数集
  88. {                                                                                    
  89.         u16  Process ;//是否游戏暂停
  90.         u16  GameSta ;//是否游戏结束
  91.         u16  AppleSta;//食物状态
  92.         u16  Direction;//蛇前进方向
  93. }events;
  94. events event;

  95. void Event_Init(events * event)//事件参数初始化
  96. {
  97.         event->Process=ON;
  98.         event->GameSta=ON;
  99.         event->AppleSta=ON;
  100.         event->Direction=DOWN;//初始方向向下
  101. }
  102. typedef struct  //贪吃蛇坐标参数
  103. {
  104.         u16 x;
  105.         u16 y;
  106. }axiss;
  107. axiss snake_axis[100];  //设蛇身最大长度可以达到100

  108. void Display(u16 x,u16 y,u16 color)//按照80*48的坐标映射到800*480的屏幕上
  109. {
  110.         LCD_Fill(GAME_WIDTH/GAME_XPART*x,GAME_HEIGHT/GAME_YPART*y,GAME_WIDTH/GAME_XPART*(x+1),GAME_HEIGHT/GAME_YPART*(y+1),color);
  111. }

  112. void DisplayInit(void) //蛇身初始化,默认在该位置,并蛇头向下
  113. {
  114.         u16 i;
  115.         for(i=0;i<5;i++)//将初始坐标存入蛇身坐标内
  116.         {
  117.                 snake_axis[i].x=i;
  118.                 snake_axis[i].y=0;
  119.         }
  120.                 for(i=5;i<10;i++)
  121.         {
  122.                 snake_axis[i].x=4;
  123.                 snake_axis[i].y=i-4;
  124.         }
  125.         LCD_Clear(WHITE);
  126.         for(i=0;i<10;i++)
  127.         {
  128.                 Display(snake_axis[i].x,snake_axis[i].y,RED);//按照初始坐标打印出蛇身
  129.         }
  130. }

  131. void GameOver(void)//游戏结束函数,打印"GAME OVER",挂起核心数据处理函数
  132. {
  133.         LCD_ShowString(180,388,200,24,24,"GAME OVER !");
  134.         vTaskSuspend(EVENTTask_Handler);
  135. }

  136. //全局变量//////////////////////////////////////

  137. int main(void)
  138. {         
  139.         delay_init();                     //延时函数初始化         
  140.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);         //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
  141.         Rand_Adc_Init();        //随机数初始化
  142.          LED_Init();                             //LED端口初始化
  143.         KEY_PLAY_Init();        //按键初始化,自制按键上/下/左/右/暂停,分别连接PF0/1/2/3/4,低电平接通
  144.         LCD_Init(0);//默认为竖屏模式
  145.         DisplayInit();//打印初始蛇身
  146.         Snake_Init(&snake);//贪吃蛇参数初始化
  147.         Apple_Init(&apple);//食物参数初始化
  148.         Event_Init(&event);//事件参数初始化
  149.         xTaskCreate((TaskFunction_t )start_task,            //任务函数
  150.                 (const char*    )"start_task",          //任务名称
  151.                 (uint16_t       )START_STK_SIZE,        //任务堆栈大小
  152.                 (void*          )NULL,                  //传递给任务函数的参数
  153.                 (UBaseType_t    )START_TASK_PRIO,       //任务优先级
  154.                 (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
  155.     vTaskStartScheduler();          //开启任务调度
  156. }

  157. //开始任务任务函数
  158. void start_task(void *pvParameters)
  159. {
  160.     taskENTER_CRITICAL();           //进入临界区
  161.     //创建贪吃蛇任务
  162.     xTaskCreate((TaskFunction_t )snake_task,            
  163.                 (const char*    )"snake_task",           
  164.                 (uint16_t       )SNAKE_STK_SIZE,
  165.                 (void*          )NULL,                                
  166.                 (UBaseType_t    )SNAKE_TASK_PRIO,        
  167.                 (TaskHandle_t*  )&SNAKETask_Handler);   
  168.     //创建食物任务
  169.     xTaskCreate((TaskFunction_t )apple_task,     
  170.                 (const char*    )"apple_task",   
  171.                 (uint16_t       )APPLE_STK_SIZE,
  172.                 (void*          )NULL,
  173.                 (UBaseType_t    )APPLE_TASK_PRIO,
  174.                 (TaskHandle_t*  )&APPLETask_Handler);        
  175.     //创建事件任务
  176.     xTaskCreate((TaskFunction_t )event_task,     
  177.                 (const char*    )"event_task",   
  178.                 (uint16_t       )EVENT_STK_SIZE,
  179.                 (void*          )NULL,
  180.                 (UBaseType_t    )EVENT_TASK_PRIO,
  181.                 (TaskHandle_t*  )&EVENTTask_Handler);  
  182.         //创建显示任务
  183.     xTaskCreate((TaskFunction_t )display_task,     
  184.                 (const char*    )"display_task",   
  185.                 (uint16_t       )DISPLAY_STK_SIZE,
  186.                 (void*          )NULL,
  187.                 (UBaseType_t    )DISPLAY_TASK_PRIO,
  188.                 (TaskHandle_t*  )&DISPLAYTask_Handler);  
  189.         //创建闪烁任务
  190.     xTaskCreate((TaskFunction_t )led_task,     
  191.                 (const char*    )"led_task",   
  192.                 (uint16_t       )LED_STK_SIZE,
  193.                 (void*          )NULL,
  194.                 (UBaseType_t    )LED_TASK_PRIO,
  195.                 (TaskHandle_t*  )&LEDTask_Handler);  
  196.         //创建输入任务
  197.     xTaskCreate((TaskFunction_t )key_task,     
  198.                 (const char*    )"key_task",   
  199.                 (uint16_t       )KEY_STK_SIZE,
  200.                 (void*          )NULL,
  201.                 (UBaseType_t    )KEY_TASK_PRIO,
  202.                 (TaskHandle_t*  )&KEYTask_Handler);  
  203.     vTaskDelete(StartTask_Handler); //删除开始任务
  204.     taskEXIT_CRITICAL();            //退出临界区
  205. }

  206. //事件任务函数,核心数据处理函数
  207. void event_task(void *pvParameters)
  208. {
  209.         while(1)
  210.         {
  211.                 if(event.GameSta==ON)//如果游戏正常则继续
  212.                 {
  213.                         if(event.Process==ON)//如果没有暂停则继续
  214.                         {
  215.                                 switch(event.Direction)//检测按键情况,根据方向调整蛇头坐标
  216.                                 {
  217.                                         case UP:
  218.                                         {
  219.                                                 snake.firsty-=1;
  220.                                                 if(snake.firsty>GAME_YPART-1)
  221.                                                 {
  222.                                                         snake.firsty=GAME_YPART-1;
  223.                                                 }
  224.                                         }break;
  225.                                         case DOWN:
  226.                                         {
  227.                                                 snake.firsty+=1;
  228.                                                 if(snake.firsty>GAME_YPART-1)
  229.                                                 {
  230.                                                         snake.firsty=0;
  231.                                                 }
  232.                                         }break;
  233.                                         case LEFT:
  234.                                         {
  235.                                                 snake.firstx-=1;
  236.                                                 if(snake.firstx>GAME_XPART-1)
  237.                                                 {
  238.                                                         snake.firstx=GAME_XPART-1;
  239.                                                 }
  240.                                         }break;
  241.                                         case RIGHT:
  242.                                         {
  243.                                                 snake.firstx+=1;
  244.                                                 if(snake.firstx>GAME_XPART-1)
  245.                                                 {
  246.                                                         snake.firstx=0;
  247.                                                 }
  248.                                         }break;
  249.                                 }
  250.                                 snake.lastx=snake_axis[0].x;//保存下蛇尾坐标
  251.                                 snake.lasty=snake_axis[0].y;
  252.                                 if(snake.firstx==apple.x&&snake.firsty==apple.y)//如果此时的坐标与食物坐标相同
  253.                                 {
  254.                                         event.AppleSta=OFF;        //食物被吃掉
  255.                                         snake.energybuf+=apple.energy;//蛇的能量加一
  256.                                         vTaskResume(APPLETask_Handler);        //使能生成食物函数               
  257.                                 }
  258.                                 vTaskResume(DISPLAYTask_Handler);        //使能显示函数                        
  259.                         }
  260.                 }else GameOver();//如果游戏为结束状态则游戏结束
  261.                 delay_ms(1000/snake.speed);        //按照蛇的速度调整此核心数据处理函数的时间间隔        
  262.         }
  263. }

  264. //输入任务函数,根据按键调整蛇的方向,数据处理函数之前最后一次按键视为有效
  265. void key_task(void *pvParameters)
  266. {
  267.         u8 key;
  268.     while(1)
  269.     {
  270.                 key=KEY_PLAY_Scan(0);//按键扫描函数,此处是自制的手柄,上/下/左/右/暂停,分别连接PF0/1/2/3/4,低电平接通
  271.                 switch(key)
  272.                 {
  273.                         case KEY_UP_PRES:
  274.                         {
  275.                                 if(event.Direction!=DOWN)//如果当前方向与按键方向相反,则不响应
  276.                                 event.Direction=UP;
  277.                         }break;
  278.                         case KEY_DOWN_PRES:
  279.                         {
  280.                                 if(event.Direction!=UP)
  281.                                 event.Direction=DOWN;
  282.                         }break;
  283.                         case KEY_LEFT_PRES:
  284.                         {
  285.                                 if(event.Direction!=RIGHT)
  286.                                 event.Direction=LEFT;
  287.                         }break;
  288.                         case KEY_RIGHT_PRES:
  289.                         {
  290.                                 if(event.Direction!=LEFT)
  291.                                 event.Direction=RIGHT;
  292.                         }break;
  293.                         case KEY_PASS_PRES://按下切换暂停/继续状态
  294.                         {
  295.                                 event.Process=!event.Process;
  296.                         }break;               
  297.                 }
  298.         delay_ms(20);//每20ms响应一次
  299.     }
  300. }

  301. //食物任务函数,食物被吃掉时触发此函数
  302. void apple_task(void *pvParameters)
  303. {
  304.         u16 flag,i;
  305.     while(1)
  306.     {
  307.                 flag=1;
  308.                 while(flag)//生成新的食物坐标
  309.                 {
  310.                         flag=0;
  311.                         apple.x=Get_Rand()%(u16)(GAME_XPART);
  312.                         apple.y=Get_Rand()%(u16)(GAME_YPART);
  313.                         for(i=0;i<snake.length;i++) //食物坐标不能在蛇身上
  314.                         {
  315.                                 if(snake_axis[i].x==apple.x&&snake_axis[i].y==apple.y)
  316.                                 {
  317.                                         flag++;
  318.                                 }
  319.                         }
  320.                 }
  321.                 Display(apple.x,apple.y,RED);//按照坐标将食物显示出来
  322.                 vTaskSuspend(APPLETask_Handler);//挂起任务        
  323.     }
  324. }

  325. //贪吃蛇任务函数 ,此任务作废
  326. void snake_task(void *pvParameters)
  327. {
  328.     while(1)
  329.     {
  330.         //LED0=!LED0;
  331.         delay_ms(500);
  332.     }
  333. }   

  334. //显示任务函数,显示蛇头,消去蛇尾,并更新蛇的坐标
  335. void display_task(void *pvParameters)
  336. {
  337.         u16 i;
  338.     while(1)
  339.     {
  340.                 Display(snake.firstx,snake.firsty,RED);//显示蛇头
  341.         if(snake.energybuf==0) //如果没有吃到食物
  342.                 {
  343.                         Display(snake.lastx,snake.lasty,WHITE);//消去蛇尾
  344.                         for(i=0;i<snake.length-1;i++)
  345.                         {
  346.                                 snake_axis[i].x=snake_axis[i+1].x;//挨个更新坐标
  347.                                 snake_axis[i].y=snake_axis[i+1].y;
  348.                         }        
  349.                 }else //如果吃到了食物
  350.                 {
  351.                         snake.energybuf--;//能量缓冲区减一
  352.                         snake.length++; //蛇身加一
  353. //                        if(snake.length%2==0)snake.speed++;
  354.                         snake.speed++; //由于地图很大,故设置蛇身加一则速度加一
  355.                 }        
  356.                 snake_axis[snake.length-1].x=snake.firstx;//最新的坐标更新为蛇头的坐标
  357.                 snake_axis[snake.length-1].y=snake.firsty;
  358.                 for(i=0;i<snake.length-1;i++)//检查有没有咬到自己
  359.                 {
  360.                         if(snake_axis[i].x==snake.firstx&&snake_axis[i].y==snake.firsty)
  361.                         {
  362.                                 event.GameSta=OFF; //遍历蛇身坐标是否与蛇头坐标相同
  363.                         }
  364.                 }
  365.                 vTaskSuspend(DISPLAYTask_Handler);
  366.     }
  367. }

  368. //闪烁任务函数,证明程序没有死机
  369. void led_task(void *pvParameters)
  370. {
  371.     while(1)
  372.     {
  373.         LED0=!LED0;
  374.         delay_ms(500);
  375.     }
  376. }
复制代码

所有资料51hei提供下载:
贪吃蛇游戏F1.7z (356.21 KB, 下载次数: 140)



评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏6 分享淘帖 顶2 踩
回复

使用道具 举报

沙发
ID:461502 发表于 2019-1-15 22:46 来自手机 | 只看该作者
小白求助,我就改主函数里分辨率的定义   我用stm32战舰v3
回复

使用道具 举报

板凳
ID:461502 发表于 2019-1-15 22:49 来自手机 | 只看该作者
小白求助,我就改了主函数分辨率的定义  用stm32战舰v3不能实现效果   蛇的初始位置什么的,是不是也要自己定义,看不懂代码
回复

使用道具 举报

地板
ID:777709 发表于 2020-6-14 22:55 | 只看该作者
小白求助,这个需要用到proteus吗
回复

使用道具 举报

5#
ID:1087401 发表于 2023-7-5 19:14 | 只看该作者
xiaokeai5023 发表于 2020-6-14 22:55
小白求助,这个需要用到proteus吗

好像用不到
回复

使用道具 举报

6#
ID:1106310 发表于 2023-12-28 17:41 | 只看该作者
感谢楼主的代码,但是烧录之后屏幕没有反应,用的是正点原子F103ZET6精英板
回复

使用道具 举报

7#
ID:1010295 发表于 2024-1-4 23:00 | 只看该作者
小白求助,这个需要自己设计电路吗
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表