找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 6060|回复: 1
收起左侧

51单片机16x16点阵俄罗斯方块程序 带详细注释

[复制链接]
ID:633559 发表于 2020-1-21 20:10 | 显示全部楼层 |阅读模式
一年前做的小玩意了,硬件丢失了,程序还在,以前都是写在一个文件内,翻出来分成几个文件,程序注释写的很详细,希望对初入单片机的师兄有所帮助 1.png

单片机源程序如下:
  1. //=========================================================================================================================================
  2. //软件功能:俄罗斯方块
  3. //硬件描述:最小系统+4x(8x8点阵)+ (1x(HC154)+3x(74HC14D)) + 1x(HC595)
  4. //程序描述:74595.c中有串进并出程序
  5. //          delay.c中有延迟函数
  6. //          dispaly.c中有开始画面显示函数、点阵显示函数、数码管显示函数
  7. //          random.c中有随机图案缓存函数
  8. //          anjian.c中按键函数、图案触底函数、消行函数、旋转图案数据改变函数、显示清除函数
  9. //          timinit.c中中断初始化函数
  10. //功能概括描述:16x16点阵用于显示、5个按键有旋转按键、上、下、左、右按键、数码管用于显示得分
  11. //              上按键用于开始游戏键、下按键用于图案加速下落键、左、右按键用于图案左右移动
  12. //              有图案触底判断、图案左右碰壁判断、消行判断
  13. //=========================================================================================================================================
  14. #include "reg52.h"                                                 //52头文件
  15. #include "stdlib.h"                                                 //随机函数rand()调用头文件
  16. #include "74595.h"                   //74HC595使用所需
  17. #include "delay.h"
  18. #include "display.h"
  19. #include "random.h"
  20. #include "anjian.h"
  21. #include "timinit.h"
  22. #define uchar unsigned char                 
  23. #define uint unsigned int
  24. sbit KS=P3^4;                                                                                                                //下键
  25. //sbit BEEP=P2^7;                                                                                                      //蜂鸣器
  26. uchar code DUANMA[11]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff};                //数码管显示0~9,0xff不显示;
  27. uint code KAITOU[3][8]={{0x3c10,0x2820,0x3bfc,0x2480,0x2520,0x3bfc,0x2020,0x20a8},            //陈发两字;
  28.                         {0x2124,0x2020,0x2060,0x2020,0x0000,0x0000,0x0210,0x0428},
  29.                                                 {0x0844,0x1080,0x3ffc,0x03f0,0x04a0,0x0840,0x10a0,0x2110}};
  30. uint HUANCUN[16],CUN[16],XZA[4],KKK;                                                                                                                            //显示数组HUANCUN[16],存储随机图案数组CUN[16]
  31. uchar q,n,k,m=1,x=0,t=0,JILV=1,a=0,ZUOJIA=7,G=7,XUANZUAN=0,R=0;                                                               //ZUOJIA对应16*16的竖,左到右编号为0~15
  32. //=========================================================================================================================================                                                                                                                                 
  33. void main()                                                                        //主函数
  34. {
  35.         init();                                                                          //初始化
  36.         KT();                                                                                  //开头显示启动游戏界面
  37.         while(1)
  38.         {
  39.                 a=rand()%6;                                                        //随机数0~5
  40.                 XIANSI();                                                                //点阵显示函数
  41.                 if(TR0==0){                                                        //判断定时器是否关着           
  42.                         SMG();                                                                //数码管显示函数
  43.                         if(KEY==0){                                                //如果开始键按下        
  44.                           QC_HC();                                          //清除缓存显示函数
  45.                           KKK=0;                                                  //分数清零
  46.                           TR0=1;                                                  //启动定时器
  47.                         }        
  48.                 }                  
  49.   }
  50. }
  51. //=========================================================================================================================================
  52. //变量说明:t:用于计时,配合向下按键的判断,来决定图案的下落速度
  53. //          CBN:图案下落速度变量
  54. //          x,m,JILV:初始状态m=JILV=1,x=0(x是HUANCUN[]里的数据编号,从0开始)
  55. //程序描述:先忽略按键部分,理顺开始按键(KEY)按下后的整个流程
  56. //          判断定时时间CBN与t的值是否相等,到时间先判断图案还能不能下落,既触底(m=JILV),如果没触底清除当前图案,显示下落一行后的图案
  57. //          初始状态是m=JILV=1,会先运行一次XH();和AJ_PD();
  58. //          添加按键功能,首先调用按键函数ANJIAN();        
  59. //          如果按键有变化那么ZUOJIA和XUANZUAN都将发生改变,此时不管t为何值都要执行相应程序
  60. //          根据ZUOJIA的改变来变动CUN[]的图案数据
  61. //          如果CUN[]改变,要从新调用AJ_PD();求JILV的值
  62. //          如果旋转按键状态XUANZUAN改变,变动CUN[]里的数据,判断此时的图案会不会与原来的“积木”重叠,如果重叠,CUN[]变回原来的数据,AJ_PD();重新调用
  63. //=========================================================================================================================================        
  64. void timer() interrupt 1
  65. {                                                                                          
  66.         static uchar CBN;                                                           //定义变量CBN用于控制图案下落的速度
  67.         TH0=(65536-5000)/256;                                     //定时器初始化
  68.         TL0=(65536-5000)%256;
  69.         t++;                                                                             //计时(t)累加
  70.         ANJIAN();                                                                                           //调用按键子程序
  71.         if(KS==0){                                                           //判断向下按键(KS)有没有按         
  72.           if(t>30)t=0;                                                       //如果计时(t)大于30,t清零重新开始计时
  73.           CBN=20;                                                                                           //减短图案下落的时间
  74.         }else
  75.            CBN=80;                                                                                   //图案平时下落速度
  76. /*----------------------------------------------------------------------------------------------------------------------------------------*/
  77.         if((t==CBN)||(ZUOJIA!=G)||(XUANZUAN!=R)){           //计时(t)到了所设定的时间“或”左右和旋转按键三键中的一个按了一下
  78.           if(m==JILV){                                                                               //如果图案下落到预先扫描的指定行           
  79.             x=0;                                                                                           //各种变量初始化
  80.                   m=1;
  81.                   G=7;
  82.                   R=0;
  83.                   JILV=1;
  84.                   ZUOJIA=7;
  85.                   XUANZUAN=0;
  86.                   XIAOHAN();                                                                               //调用消行子程序
  87.                 /*---------------------------------------------------------------------------------*/
  88.                         if(HUANCUN[0]!=0x0000){                                                 //点阵顶部是否亮起,如果亮起表示该结束了            
  89.               QC_HC();                                                                          //清除缓存
  90.               HUANCUN[2]=0x0ff0;                                             //结束显示笑脸图案
  91.               HUANCUN[3]=0x1008;
  92.                       HUANCUN[4]=0x2424;
  93.                      HUANCUN[5]=0x2a54;
  94.                      HUANCUN[6]=0x2004;
  95.                      HUANCUN[7]=0x2004;
  96.                      HUANCUN[8]=0x2004;
  97.                      HUANCUN[9]=0x2004;
  98.                       HUANCUN[10]=0x2244;
  99.                      HUANCUN[11]=0x2184;
  100.                     HUANCUN[12]=0x1008;
  101.                      HUANCUN[13]=0x0ff0;
  102.               TR0=0;                                                                                 //定时器关闭
  103.                     return;                                                                                 //跳出中断
  104.             }
  105.                 /*---------------------------------------------------------------------------------*/
  106.                   XH();                                                                                           //图案对应数组
  107.                   AJ_PD();                                                                                 //从上至下行扫描得到指定行变量(JILV)
  108.           }else{                                                                                                 //如果图案还在下落                                                                  
  109.              m--;                                                                                           //变量(m)控制图案数组与缓存数组的数据运算
  110.                    x=m-1;                                                                                   //缓存变量(x)返回上个图案值         
  111.                    for(n=0;n<m;n++){   
  112.                      HUANCUN[x]&=(~CUN[n]);                                         //上个图案清除
  113.                      x--;
  114.              }
  115.                    x=m;                                                                                           //缓存变量(x)加一为下个图案显示做准备
  116.              m++;
  117.            }
  118.         /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*/
  119.     if(ZUOJIA!=G){                                                                      //如果左右键状态发生一次改变           
  120.                   for(n=0;n<16;n++){        
  121.                     if(ZUOJIA>G){                                                              //如果是右键发生的变动               
  122.                             CUN[n]>>=1;
  123.                           }else
  124.                                    CUN[n]<<=1;
  125.                   }
  126.                   AJ_PD();                                                                                //重新扫描求JILV
  127.                   x--;
  128.                   m--;
  129.                   G=ZUOJIA;
  130.           }
  131.           if(XUANZUAN!=R){                                                                    //旋转键状态改变一次   
  132.             x--;
  133.                   m--;
  134.                   XH();                                                                                          //调用XH()子程序,为了显示旋转后的图案,注意:这时图案是在正中间,即ZUOJIA=7的位置
  135.                   HPP();                                                                      //调用旋转子程序
  136.                   for(n=0;n<m;n++){     
  137.                     if(CUN[n]!=0){                  
  138.                       if((HUANCUN[x]&CUN[n])!=0){                            //如果旋转后的图案会和已存在图案重合               
  139.                               if(XUANZUAN==0)XUANZUAN=4;                    //4种旋转图案对应状态值改回未变图案前的状态
  140.                           else XUANZUAN--;
  141.                               for(n=0;n<4;n++){                                                //图案也要变回去              
  142.                           CUN[n]=XZA[n];
  143.                         }
  144.                               break;
  145.                             }
  146.                     }
  147.                     x--;
  148.             }
  149.                   AJ_PD();                                                                      //调用扫描子程序,求新的JILV
  150.             x=m-1;
  151.                   R=XUANZUAN;                                                              //把旋转图案状态值赋值给变量R,只有旋转按钮再次按下,才会再进来
  152.           }
  153.         /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*/
  154.           for(n=0;n<m;n++){                                                        
  155.                   HUANCUN[x]|=CUN[n];                                          //把将要显示的图案对应的数组送入缓存数组
  156.                   x--;
  157.           }
  158.           m++;
  159.           t=0;                            //计时变量清零
  160.         }
  161. }
  162. //=========================================================================================================================================        
复制代码
  1. #include "anjian.h"
  2. //=========================================================================================================================================
  3. extern uchar XUANZUAN,ZUOJIA,JILV,m,n,x,k,G;
  4. extern uint HUANCUN[16],CUN[16],XZA[4],KKK;
  5. //=========================================================================================================================================
  6. //程序描述:判断右键(YOU)按下否?
  7. //          先把图案对应竖位置的状态变量ZUOJIA改变,再判断图案可不可以移动,如果不可以ZUOJIA改回来
  8. //          m,x确实有点绕
  9. //          左键同上
  10. //          旋转按键按下,改变图案状态变量XUANZUAN,保存当前图案CUN[]到XZA[]
  11. //=========================================================================================================================================
  12. void ANJIAN()                                                                                                                                                                                                   //按键子程序
  13. {
  14.   static uchar i=1,s=1,j=1;                                                                                                                                                             //定义三个静态变量i,s,j表示右,左,旋转按键的状态值
  15.   if(YOU!=s){                                                                                                                                                                                                                                                                                                                    //如果右键状态值发生改变   
  16.           if(s!=0){                                                                                                                                                                                                                                                                                                                    //判断状态值是否为0,如果不是,说明右键按下了
  17.             ZUOJIA++;                                                                                                                                                                                                                                                                                                            //图案未动,图案对应竖的状态值先加一
  18.                         m--;                                                                                                                                                                                                                                                                                                                      //x,m数组间交换变化所定义的变量
  19.                   x=m-1;
  20.                   for(n=0;n<m;n++){                                    
  21.                           if(CUN[n]!=0){                                                                                                                                                                                                                                                                                   //如果CUN[n]为0,就不用浪费时间判断什么了
  22.                             if(((HUANCUN[x]&((CUN[n]|(CUN[n]/2))-CUN[n]))!=0)||((CUN[n]&0x0001)!=0)){                                   //CUN[n]/2右移一位                                                                                                                                                                                                                                                                                                                                                                        
  23.                                     m++;                                                                                                                                                                                                                                                                                                           //CUN[n]|(CUN[n]/2)右移一位后和之前的数或,(CUN[n]|(CUN[n]/2))-CUN[n]或了的数再减去最开始的数,就是右移后的图案
  24.                                     ZUOJIA--;                                                                                                                                                                                                                                                                                     //HUANCUN[x]&((CUN[n]|(CUN[n]/2))-CUN[n])!=0上面的数再和显示数组里的数与,判断运算后的数是否为0
  25.                                      s=YOU;                                                                                                                                                                                                                                                                                                   //上面的运算就是判断”图案向右移动是否会碰到已经存在的图案“
  26.                                     return;                                                                                                                                                                                                                                                                                                   //(CUN[n]&0x0001)!=0这句判断图案有没有碰到右边的边界
  27.                             }                                                                                                                                                                                                                                                                                                                                   //m++;ZUOJIA--;进入if说明图案不能变,那么前面改变的值都要改回来,s=YOU;既然程序要跳出来了,按键的状态值也要改变的,防止又进来了
  28.                           }
  29.                           x--;
  30.                   }         
  31.             x=m;
  32.             m++;
  33.           }
  34.           s=YOU;
  35.         }
  36.   if(ZUO!=i){                                                                                                                                                                                                              //左键类同右键   
  37.           if(i!=0){
  38.             ZUOJIA--;
  39.                   m--;
  40.       x=m-1;
  41.                   for(n=0;n<m;n++){                                    
  42.                           if(CUN[n]!=0){
  43.                             if(((HUANCUN[x]&((CUN[n]|(CUN[n]<<1))-CUN[n]))!=0)||(CUN[n]>=0x8000)){
  44.                                     m++;
  45.                                     ZUOJIA++;
  46.                                      i=ZUO;
  47.                                     return;
  48.                             }
  49.                           }
  50.                           x--;
  51.                   }
  52.             x=n;
  53.             m++;
  54.           }
  55.           i=ZUO;
  56.   }
  57.         if(XZ!=j){                                                                                                                                                                         //旋转按键
  58.           if(j!=0){
  59.             XUANZUAN=(XUANZUAN+1)%4;                                                                                                                         //如果旋转按键按下了,这里4种图案是按逆时针变化
  60.                   for(n=0;n<4;n++){
  61.                     XZA[n]=CUN[n];                                                                                                                                             //图案先保存在数组XZA[]中
  62.                   }   
  63.           }
  64.           j=XZ;
  65.         }
  66. }
  67. //=========================================================================================================================================
  68. //思维说明:在XH()程序中知道图案细分成15种,目的是要写一个程序让这15种图案下落后碰到下面的"积木"停下来(记下这一行的编号存在变量JILV里)
  69. //程序描述:图案保存在数组CUN[]中,CUN[0]是图案的底部,CUN[1]是图案中部,CUN[2]是图案上部,CUN[3]可以不作考虑
  70. //          16x16点阵有16行,以图案当前在点阵处在的位置开始与点阵已经存在“积木”做运算,判断再下落是否会重叠
  71. //          首先,判断CUN[0]是否触底了,即,(HUANCUN[n]&CUN[0])!=0
  72. //          其次,判断CUN[1]是否触底了,(CUN[1]|CUN[0])-CUN[0]这是取CUN[1]和CUN[0]在垂直方向上没有重合的部分
  73. //          最后,判断CUN[2]是否触底了,这种情况只有一种图案会发生
  74. //          总结,一种图案出现改变也好,都要调用这个函数找出触底位置
  75. //                当n>1后,运行一次for就判断一个图案分成3段的触底情况
  76. //=========================================================================================================================================
  77. void AJ_PD()                                                                                                                                                                                                                                    //从上至下行扫描得到指定行变量(JILV)子程序
  78. {   
  79.         for(n=m-1;n<16;n++){                                                                                                                                                                                       //扫描缓存数组
  80.           if((HUANCUN[n]&CUN[0])!=0) break;                                                                                                                                    //如果图案底层触底,跳出扫描                                 
  81.             else
  82.               if((n>0)&&((HUANCUN[n-1]&((CUN[1]|CUN[0])-CUN[0]))!=0)) break;  //否则,图案底层上面层触底,跳出扫描
  83.                 if((n>1)&&((HUANCUN[n-2]&((CUN[2]|CUN[1])-CUN[1]))!=0)) break;      //图案底层上面层的上面层触底,跳出扫描                  
  84.   }
  85.         JILV=n+1;                                                                                                                                                                                  //记录JILV                                                                                                           
  86. }
  87. //=========================================================================================================================================
  88. //我看到一个教程里说不要在for(i=0;i<6;i++)里面改变变量(i)的值,但我不知道会出什么问题
  89. //程序描述:从点阵的最下行向上开始判断
  90. //=========================================================================================================================================
  91. void XIAOHAN()
  92. {                                                                                                                                               //消行子程序
  93.         uint i;
  94.         uchar u;
  95.         i=KKK;
  96.         for(n=16;n>0;n--){                                                                                 //扫描16行         
  97.                 if(HUANCUN[n-1]==0xffff){                                                 //如果某行全亮
  98.                   for(u=n;u>1;u--){
  99.                     HUANCUN[u-1]=HUANCUN[u-2];                         //上面一行覆盖下面一行
  100.                   }
  101.                   HUANCUN[0]=0x0000;
  102.                   n++;
  103.                   KKK++;
  104.                 }                                                                                                                               //最上面一行清零
  105.         }                                                                                                                                       //16行全扫描后,结束子程序
  106.         if(KKK==(i+2))KKK+=2;                                                                         //如果同时消除2行加4分
  107.           else
  108.             if(KKK==(i+3))KKK+=6;                                                         //如果同时消除3行加9分
  109.               else
  110.                 if(KKK==(i+4))KKK+=12;                                 //如果同时消除4行加16分
  111.                         else
  112.                     if(KKK>(i+4))KKK+=20;                         //如果同时消除5行加25分                                          
  113. }
  114. //=========================================================================================================================================
  115. //思维说明:XH();中CUN[]只存改变后的图案,位置是在ZUOJIA=7,现在要写段程序根据ZUOJIA的值改变CUN[]的数据,即左右移动后的图案数据CUN[]
  116. //程序描述:比如当ZUOJIA=6时,CUN[]前4位数据(图案只用到前4位)都左移1位
  117. //          如果图案碰到了边界,重新调用XH();函数一次,CUN[]数据移动到能移动的最远位置
  118. //=========================================================================================================================================
  119. void HPP()                                                                                                                              //旋转子程序一部分
  120. {                                                                                                                                          
  121.         if(ZUOJIA<=7){                                                                                                            //如果图案在点阵的左半边
  122.                 for(k=0;k<(7-ZUOJIA);k++){                                                                //7-ZUOJIA表示图案需要移动的位数                  
  123.                   for(n=0;n<4;n++){                                                                                    //图案左移一位         
  124.                                 CUN[n]<<=1;                                                                                 
  125.                           if(CUN[n]>=0x8000){                                                                          //在该图案左移的过程中,判断有没有碰到点阵左边的边界
  126.                                   XH();                                                                                                  //调用XH()子程序,该图案在ZUOJIA=7的位置;
  127.                             for(n=0;n<4;n++){                                                                          //该图案左移k+1个位,因为是k=0图案开始一位位左移,所以是k+1                       
  128.                                     CUN[n]<<=k+1;
  129.                             }
  130.                                   ZUOJIA=6-k;                                                                                       //图案对应竖变量也要跟着变化,6-k即7-(k+1)
  131.                                   G=6-k;                                                                                                 //图案的竖位置变了,左右按键的状态变量也要随之改变
  132.                                   return;                                                                                                 //跳出子程序
  133.                                 }
  134.                   }
  135.                 }
  136.         }else{                                                                                                                           //否则图案在点阵右半边
  137.                  for(k=0;k<(ZUOJIA-7);k++){                                                                 //以下类同上面                  
  138.                    for(n=0;n<4;n++){              
  139.                                  CUN[n]>>=1;
  140.                            if((CUN[n]&0x0001)!=0){
  141.                                    XH();
  142.                              for(n=0;n<4;n++){
  143.                                      CUN[n]>>=k+1;
  144.                        }
  145.                                    ZUOJIA=8+k;
  146.                                    G=8+k;
  147.                                    return;
  148.                                  }
  149.        }
  150.                  }
  151.          }
  152. }
  153. //=========================================================================================================================================
  154. void QC_HC()                                                                           //缓存(显示)数组清零
  155. {
  156.         for(n=0;n<16;n++){
  157.                 HUANCUN[n]=0x0000;
  158.         }   
  159. }
  160. //=========================================================================================================================================
复制代码


所有资料51hei提供下载:
俄罗斯方块.rar (73.91 KB, 下载次数: 51)



回复

使用道具 举报

ID:183659 发表于 2020-1-27 20:37 | 显示全部楼层
新年快乐~
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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