找回密码
 立即注册

QQ登录

只需一步,快速开始

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

51单片机和LCD12864贪吃蛇(程序+Proteus仿真)

[复制链接]
跳转到指定楼层
楼主
为使用51单片机的制作贪吃蛇的源代码
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)


单片机源程序如下:
  1. #include <12864.h>
  2. #include <tcs_resource.h>
  3. #define num 15

  4. //用来描述蛇在液晶上面一个点的结构体
  5. struct she
  6. {
  7.         unsigned char x,y,value;
  8.         unsigned char direction;        
  9. };

  10. struct loop_queue  //利用循环数组实现循环队列,拐点不会超过15个。所以大小选15足以。
  11. {   
  12.         unsigned int front;
  13.         unsigned int rear;
  14.         unsigned int count;
  15.         struct she dat[num];
  16. };
  17. //不熟悉数组实现循环队列的可以在纸上画一下操作过程

  18. struct she tou,wei,food; //蛇的头尾节点  和食物点
  19. struct loop_queue queue;//循环队列  蛇转弯则形成拐点 将拐点入队列  尾巴到达拐点则拐点出队列

  20. unsigned char flag_exit_game; //返回标志  为1时  从贪吃蛇游戏返回菜单
  21. unsigned char flag_game_over; //结束标志  为1时  显示游戏结束画面 同时使返回标志为1
  22. unsigned char tcs_stop;//暂停标志
  23. unsigned char tcs_nd; //难度标志
  24. unsigned char tcs_dt; //地图标志
  25. unsigned char tcs_key_flag;  //贪吃蛇在一次移动中   只允许捕获一次按键  也就是只能产生一个拐点
  26. unsigned char tcs_key_state; //用于按键状态机的实现

  27. unsigned int  tcs_df;//得分
  28. unsigned int  tcs_highest_df;//最高得分···没有使用这个  因为不知道要怎么在运行时写单片机的ROM

  29.          
  30. void tcs_game();
  31. void tcs_bianjie(unsigned int );  //贪吃蛇边界  
  32. void tcs_game_initial();         //游戏初始化

  33. void timer0_initial();          //定时器0初始化
  34. void timer1_initial();         
  35. void food_produce();          //产生食物

  36. unsigned char test_point_exist(unsigned char x, unsigned char y, unsigned char value);

  37. void queue_initial(struct loop_queue *q);  
  38. void queue_in(struct loop_queue *q,struct she h)  ;
  39. struct she queue_out(struct loop_queue *q)  ;

  40. void wei_you();
  41. void wei_xia();
  42. void wei_zuo();
  43. void wei_shang();
  44. void tcs_game()
  45. {
  46.         struct she check;  //用与暂存蛇即将移动的点  从而进行判断
  47.         unsigned char he;
  48.         unsigned char hui,i;

  49.         flag_exit_game=0;
  50.         flag_game_over=0;
  51.         tcs_key_state=0;
  52.         tcs_df=0;
  53.         tcs_stop=1;

  54.         draw_lcd_picture(&tcs_picture[0]); //贪吃蛇游戏画面
  55.         delay_ms(2000);
  56.         clear_lcd();//清屏12864
  57.         tcs_bianjie(tcs_dt); //贪吃蛇游戏边界
  58.         tcs_game_initial();//贪吃蛇初始化  主要是设定头蛇尾  并显示蛇
  59.         timer0_initial(); //定时器0工作在方式2  8位自动装初值  只计数不中断 用来产生随机数
  60.         timer1_initial(); //定时器1工作在方式1        16位     10ms中断一次扫描键盘

  61.         do
  62.         {
  63.                 food_produce();//产生一个随机数 即随即产生x y value3个值确定一个点为食物
  64.                 he=test_point_exist(food.x,food.y,food.value);//检测产生的点的地方是否已经有点存在
  65.         }
  66.         while(he==food.value);//如果随机数产生在蛇的身体以及边界上 则重新再产生一个  
  67.         draw_lcd_point(food.x,food.y,food.value);//显示食物 先读取这个点所在8位的情况 以免破坏现场
  68.         
  69.         queue_initial(&queue);//初始化循环队列  使队列空  front rear 即头尾指针为0
  70.         check.x=0;
  71.         check.y=0;
  72.         check.value=0;
  73.         check.direction=0;
  74.         for(i=0;i<num;i++)
  75.                 queue.dat[i]=check;

  76.         while(1)
  77.         {
  78.                 if(flag_exit_game==1)//如果游戏过程中按下返回键  则立即结束游戏  返回菜单
  79.                         break;
  80.                 if(flag_game_over==1)
  81.                         break;
  82.                
  83.                 ET1=1;   //允许扫描按键          贪吃蛇游戏难度决定蛇2次移动之间的间隔时间  也就是检测按键的时间
  84.                 tcs_key_flag=0; //允许捕获按键  在检测按键时间内  只允许生产一个拐点  即捕获到一个有效的
  85.                                                 //按键以后  就置一这个变量 不允许再增加拐点
  86.                 while(tcs_stop==0);
  87.                 switch(tcs_nd)
  88.                 {
  89.                         case 0:  {delay_ms(10); break;}
  90.                         case 1:  {delay_ms(50); break;}
  91.                         case 2:  {delay_ms(100); break;}
  92.                 }
  93.                 ET1=0;

  94.                 switch(tou.direction)//这个switch用来根据头的x y value值以及当前头移动的方向
  95.                                                          //取出头要到达的下一个点  存入check中  来判断是否撞墙 吃到食物 什么的···
  96.                 {        
  97.                         case 0: {         
  98.                                                 check.x=tou.x;
  99.                                                 if(tou.y==63)  
  100.                                                         check.y=0;
  101.                                                 else
  102.                                                         check.y=tou.y+1;
  103.                                                 check.value=tou.value;
  104.                                                  break ;
  105.                                         }//->向右y+1 其他不变

  106.                         case 1: {                                          //↓ 向下y不变
  107.                                                 check.y=tou.y;
  108.                                                  if(tou.value==0x80)// value==0x80 则 x+1 value=0x01
  109.                                                 {
  110.                                                         if(tou.x==7)
  111.                                                                 check.x=0;
  112.                                                         else
  113.                                                                 check.x=tou.x+1;
  114.                                                         check.value=0x01;
  115.                                                 }
  116.                                                 else
  117.                                                 {
  118.                                                         check.x=tou.x;
  119.                                                         check.value=tou.value<<1;
  120.                                                 }
  121.                                             break ;
  122.                                         }

  123.                         case 2: {         
  124.                                                 check.x=tou.x;
  125.                                                 if(tou.y==0)  
  126.                                                         check.y=63;
  127.                                                 else
  128.                                                         check.y=tou.y-1;
  129.                                                 check.value=tou.value;
  130.                                                 break ;
  131.                                         }//<-向左y-1 其他不变

  132.                         case 3: {                                            //↑         向上y不变
  133.                                                 check.y=tou.y;
  134.                                                  if(tou.value==0x01)// value==0x01 则 x-1 value=0x80
  135.                                                 {
  136.                                                         if(tou.x==0)
  137.                                                                 check.x=7;
  138.                                                         else
  139.                                                                 check.x=tou.x-1;
  140.                                                         check.value=0x80;
  141.                                                 }
  142.                                                 else
  143.                                                 {
  144.                                                         check.x=tou.x;
  145.                                                         check.value=tou.value>>1;
  146.                                                 }
  147.                                             break ;
  148.                                         }        
  149.                 }                        
  150.                         
  151.                 he=test_point_exist(check.x,check.y,check.value); // 检测头移动的下一个点是否几经有点存在
  152.                 if(he==check.value) //如果前面的点已经存在        则可以是食物 或者是墙和蛇身
  153.                 {
  154.                         if(        (check.x==food.x) && (check.y==food.y) && (check.value==food.value) )//吃到食物
  155.                         {
  156.                                 tcs_df++;
  157.                                 cs1=1; cs2=0;
  158.                                 write_lcd_shuzi(2,32,tcs_shuzi[tcs_df/100]       );                         //显示分数的百位
  159.                                 write_lcd_shuzi(2,40,tcs_shuzi[ (tcs_df%100)/10] );                                //                        十
  160.                                 write_lcd_shuzi(2,48,tcs_shuzi[tcs_df%10]        );                                //          个
  161.                                 cs1=0; cs2=1;
  162.                                 food.direction=tou.direction;//食物本没有方向 但是为了下面的语句
  163.                                 tou=food; //食物就成了头···头的方向依然不变
  164.                                 hui=wei.direction; //先记录下尾巴本来的移动方向  
  165.                                 wei.direction=611;//这次行动尾巴不动  这样蛇身就自然加一了···611代表下面的尾巴不动
  166.                                 do
  167.                                 {
  168.                                         food_produce();
  169.                                         he=test_point_exist(food.x,food.y,food.value);
  170.                                 }
  171.                                 while(he==food.value);  
  172.                                 draw_lcd_point(food.x,food.y,food.value);//上面有说过  这个是产生并显示食物
  173.                         }
  174.                         else//撞到身体或者墙
  175.                         {
  176.                                 flag_game_over=1; // 游戏结束
  177.                         }
  178.                 }
  179.                 else  //什么都么有遇到 继续前进
  180.                 {
  181.                         check.direction=tou.direction;//这么做的理由同上 为了保持头当前的移动方向
  182.                         tou=check;
  183.                         draw_lcd_point(tou.x,tou.y,tou.value);//画出此时的头
  184.                 }

  185.                 //下面是对尾巴的操作               
  186.                
  187.                 if (  (wei.x == queue.dat[queue.front].x) && ( wei.y==queue.dat[queue.front].y ) && ( wei.value== queue.dat[queue.front].value)  ) //如果尾巴到达拐点
  188.                 {
  189.                          check=queue_out(&queue);         
  190.                         wei.direction=check.direction; //尾巴按照拐点指示的方向走
  191.                 }
  192.                 else //如果没有到达拐点 尾巴移动的方向不变
  193.                 {
  194.                                 
  195.                 }                                                
  196.                                                                
  197.                 switch( wei.direction ) //这个是尾巴的移动  以及尾巴节点数据的修改
  198.                 {
  199.                         case 0:        { wei_you(); break; }
  200.                         case 1:        { wei_xia(); break; }                                                                                
  201.                         case 2:        { wei_zuo(); break; }
  202.                         case 3:        { wei_shang();         break; }        
  203.                         case 611: { wei.direction=hui;  break;}  //这次移动 吃到实物   还原尾巴本来的移动方向
  204.                   }                                                
  205.         }        
  206.         TR0=0;
  207.         TR1=0;
  208.         ET1=0;  //退出游戏时 关闭游戏过程中用到的定时器0和1  然后定时器中断也要关闭


  209. }


  210. void timer0_initial()
  211. {         
  212.         TMOD=(0xf0 & TMOD) | 0x01; //定时器0 8位自动装初值(TH0->TL0)定时器模式   用来产生随机数
  213.         TH0=0x00;
  214.         TL0=0x00;
  215.         TR0=1;   
  216. }

  217. void timer1_initial()
  218. {         
  219.         EA=1;
  220.         TMOD= (0x0f & TMOD) | 0x10 ; //定时器一16位定时器模式         10ms中断一次  用来扫描键盘
  221.         TH1=0xDC;//10ms中断一次
  222.         TL1=0x00;
  223.         TR1=1;   
  224. }


  225. void timer1() interrupt 3
  226. {        
  227.         unsigned char tou_last_direction; //记录头原来的移动方向
  228.         
  229.         TR1=0;
  230.         TH1=0xDC;               
  231.         TL1=0x00;

  232.         switch (tcs_key_state)
  233.         {
  234.                 case 0:        {                // 按键初始态
  235.                                         if ( (P2 & 0x7f) != 0x7f )   tcs_key_state = 1;        // 键被按下,状态转换到键确认态
  236.                                                 else          tcs_key_state = 0;
  237.                                         break;
  238.                                 }
  239.         
  240.                 case 1:        {                        // 按键确认态
  241.                                         if ( (P2 & 0x7f) != 0x7f)
  242.                                         {
  243.                                                 // 按键仍按下,此时确定记录一次有效按键
  244.                                                         tcs_key_state = 2;        // 状态转换到键释放态
  245.                                                         if(tcs_key_flag==0)
  246.                                                         {
  247.                                                                 tou_last_direction=tou.direction;
  248.                                                                 switch(P2 & 0x7f)
  249.                                                                 {        
  250.                                                                         case 0x6f:        break; // 确定键  不进行任何操作
  251.                                                                         case 0x5f:        {  flag_exit_game=1;   break; }//返回键           结束游戏 返回菜单
  252.                                                                         case 0x7e:        {  tou.direction=3;break; }//↑
  253.                                                                         case 0x7b:        {  tou.direction=2;break; }//←
  254.                                                                         case 0x7d:        {  tou.direction=1;break; }//↓
  255.                                                                         case 0x77:        {  tou.direction=0;break; }//->         
  256.                                                                         case 0x3f:  {  if(tcs_stop==0) tcs_stop=1; else tcs_stop=0; break;}//暂停键
  257.                                                                 }
  258.                                                 
  259.                                                                 if( (tou.direction==tou_last_direction) || (tou.direction+tou_last_direction==2) || (tou.direction+tou_last_direction==4) )
  260.                                                                 //如果本来就是按此方向移动   或者按键给的方向与蛇移动方向相反 则不做任何操作 此次按键无效
  261.                                                                 {
  262.                                                                         tou.direction=tou_last_direction; //   还原头的移动方向
  263.                                                                 }
  264.                                                                 else //否则记录此头结点成为拐点           
  265.                                                                 {                        
  266.                                                                         queue_in(&queue,tou);
  267.                                                                         tcs_key_flag=1; //已经捕获到一次按键信息 这次移动拐点已经产生 关闭捕获键盘
  268.                                                                 }
  269.                                                         }
  270.                                         }
  271.                                         else
  272.                                         {
  273.                                                 tcs_key_state = 0;        // 按键已抬起,转换到按键初始态
  274.                                                 break;
  275.                                         }
  276.                                 }

  277.                 case 2:        {           //等待按键弹起状态
  278.                                                 if ( (P2 & 0x7f) == 0x7f) tcs_key_state=0;        //按键已释放,转换到按键初始态
  279.                                                 else          tcs_key_state = 2;
  280.                                                 break;
  281.                                 }        
  282.         }                        
  283.         TR1=1;
  284. }


  285. void food_produce()
  286. {
  287.                  unsigned char j1,j2;
  288.                 unsigned char code a[]={0x01,0x02,0x04,0x08, 0x10,0x20,0x40,0x80,0x01,0x02,0x04,0x08, 0x10,0x20,0x40,0x80};
  289.                 unsigned char code b[]={0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7};
  290.                     
  291.                 j1 = TL0 & 0x0f;  //取TL0的低四位
  292.                 food.value = a[j1];  //用0-15的数确定食物的value值
  293.                
  294.                 j2 = (TL0 & 0xf0)>>4; //取TL0的高4位
  295.                 food.x=b[j2];                        //同理确定食物的x值                                       
  296.                
  297.                 j2=j1+j2; // 0-30            
  298.                
  299.                 food.y=j2 +( TL0 & 0x0f ) + ( (TL0 & 0xf0)>>4 ) + 1;//3次0-15的值和一次1-16的值相加,产生1-61的数        

  300. }

  301. unsigned char test_point_exist(unsigned char x, unsigned char y, unsigned char value)
  302. {
  303.         unsigned char he;
  304.         set_lcd_xy(x,y);
  305.         he=read_lcd_dat();
  306.         he=read_lcd_dat();
  307.         he=he&value; //如果液晶上这个点是亮的 则he为value 若不亮则为 0
  308.         return (he);
  309. }

  310. void tcs_game_initial()
  311. {
  312.         unsigned char i;
  313.         cs1=0;        cs2=1;
  314.         tou.x=0;
  315.         tou.y=5;
  316.         tou.value=0x08;      //初始化头
  317.         tou.direction=0;    // 初始化头移动方向为向右

  318.         wei.x=0;
  319.         wei.y=1;
  320.         wei.value=0x08;                //初始化蛇尾
  321.         wei.direction=0;
  322.         
  323.         for(i=wei.y;i<=tou.y;i++)
  324.                 draw_lcd_point(tou.x,i,tou.value);//形成蛇
  325. }




  326. void tcs_bianjie(unsigned int he)
  327. {
  328.         unsigned char i;
  329.         cs1=0;        cs2=1;
  330.         set_lcd_xy(0,0);
  331.         for(i=0;i<=63;i++)
  332.                 write_lcd_dat(0x01);

  333.         set_lcd_xy(7,0);
  334.         for(i=0;i<=63;i++)
  335.                 write_lcd_dat(0x80);
  336.         
  337.         for(i=0;i<=7;i++)
  338.         {
  339.                 set_lcd_xy(i,0);
  340.                 write_lcd_dat(0xff);

  341.                 set_lcd_xy(i,63);
  342.                 write_lcd_dat(0xff);
  343.         }

  344.         if(he==2)
  345.         {
  346.                
  347.                 unsigned char i,j;
  348.                 for(i=0;i<8;i++)
  349.                 {         
  350.                         cs1=0;cs2=1;
  351.                         set_lcd_xy(i,0);
  352.                         for(j=0;j<64;j++)
  353.                                 write_lcd_dat(tcs_dt2[i*64+j]);
  354.                 }               
  355.         }
  356.         
  357.         if(he==3)
  358.         {
  359.                 unsigned char i,j;
  360.                 for(i=0;i<8;i++)
  361.                 {         
  362.                         cs1=0;cs2=1;
  363.                         set_lcd_xy(i,0);
  364.                         for(j=0;j<64;j++)
  365.                                 write_lcd_dat(tcs_dt3[i*64+j]);
  366.                 }
  367.         }        

  368.         if(he==4)
  369.         {
  370.                 unsigned char i,j;
  371.                 for(i=0;i<8;i++)
  372.                 {         
  373.                         cs1=0;cs2=1;
  374.                         set_lcd_xy(i,0);
  375.                         for(j=0;j<64;j++)
  376.                                 write_lcd_dat(tcs_dt4[i*64+j]);
  377.                 }
  378.         }        


  379.         cs1=1;        cs2=0;
  380.         write_lcd_hanzi(0,0,df[0]);
  381.         write_lcd_hanzi(0,16,df[1]);

  382.         write_lcd_shuzi(2,32,tcs_shuzi[tcs_df/100]       );                         //显示分数的百位
  383.         write_lcd_shuzi(2,40,tcs_shuzi[ (tcs_df%100)/10] );                                //                        十
  384.         write_lcd_shuzi(2,48,tcs_shuzi[tcs_df%10]        );                                //          个

  385.         write_lcd_hanzi(4,0,jb[0]);
  386.         write_lcd_hanzi(4,16,jb[1]);

  387.         switch(tcs_nd)
  388.         {
  389.                 case 0 :  { write_lcd_hanzi(6,32,tcs_bt[0]); write_lcd_hanzi(6,48,tcs_bt[1]);  break;}
  390.                 case 1 :  { write_lcd_hanzi(6,32,tcs_kn[0]); write_lcd_hanzi(6,48,tcs_kn[1]);  break;}
  391.                 case 2 :  { write_lcd_hanzi(6,32,tcs_jd[0]); write_lcd_hanzi(6,48,tcs_jd[1]);  break;}
  392.         }
  393. }

  394. void queue_initial(struct loop_queue *q)   
  395. ……………………

  396. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码

所有资料51hei提供下载:
贪吃蛇.zip (201.45 KB, 下载次数: 45)


评分

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

查看全部评分

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

使用道具 举报

沙发
ID:390416 发表于 2021-1-15 20:48 | 只看该作者
你这图画得,龙飞凤舞。可以用标号的 更好看
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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