一年前做的小玩意了,硬件丢失了,程序还在,以前都是写在一个文件内,翻出来分成几个文件,程序注释写的很详细,希望对初入单片机的师兄有所帮助
单片机源程序如下:
- //=========================================================================================================================================
- //软件功能:俄罗斯方块
- //硬件描述:最小系统+4x(8x8点阵)+ (1x(HC154)+3x(74HC14D)) + 1x(HC595)
- //程序描述:74595.c中有串进并出程序
- // delay.c中有延迟函数
- // dispaly.c中有开始画面显示函数、点阵显示函数、数码管显示函数
- // random.c中有随机图案缓存函数
- // anjian.c中按键函数、图案触底函数、消行函数、旋转图案数据改变函数、显示清除函数
- // timinit.c中中断初始化函数
- //功能概括描述:16x16点阵用于显示、5个按键有旋转按键、上、下、左、右按键、数码管用于显示得分
- // 上按键用于开始游戏键、下按键用于图案加速下落键、左、右按键用于图案左右移动
- // 有图案触底判断、图案左右碰壁判断、消行判断
- //=========================================================================================================================================
- #include "reg52.h" //52头文件
- #include "stdlib.h" //随机函数rand()调用头文件
- #include "74595.h" //74HC595使用所需
- #include "delay.h"
- #include "display.h"
- #include "random.h"
- #include "anjian.h"
- #include "timinit.h"
- #define uchar unsigned char
- #define uint unsigned int
- sbit KS=P3^4; //下键
- //sbit BEEP=P2^7; //蜂鸣器
- uchar code DUANMA[11]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff}; //数码管显示0~9,0xff不显示;
- uint code KAITOU[3][8]={{0x3c10,0x2820,0x3bfc,0x2480,0x2520,0x3bfc,0x2020,0x20a8}, //陈发两字;
- {0x2124,0x2020,0x2060,0x2020,0x0000,0x0000,0x0210,0x0428},
- {0x0844,0x1080,0x3ffc,0x03f0,0x04a0,0x0840,0x10a0,0x2110}};
- uint HUANCUN[16],CUN[16],XZA[4],KKK; //显示数组HUANCUN[16],存储随机图案数组CUN[16]
- 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
- //=========================================================================================================================================
- void main() //主函数
- {
- init(); //初始化
- KT(); //开头显示启动游戏界面
- while(1)
- {
- a=rand()%6; //随机数0~5
- XIANSI(); //点阵显示函数
- if(TR0==0){ //判断定时器是否关着
- SMG(); //数码管显示函数
- if(KEY==0){ //如果开始键按下
- QC_HC(); //清除缓存显示函数
- KKK=0; //分数清零
- TR0=1; //启动定时器
- }
- }
- }
- }
- //=========================================================================================================================================
- //变量说明:t:用于计时,配合向下按键的判断,来决定图案的下落速度
- // CBN:图案下落速度变量
- // x,m,JILV:初始状态m=JILV=1,x=0(x是HUANCUN[]里的数据编号,从0开始)
- //程序描述:先忽略按键部分,理顺开始按键(KEY)按下后的整个流程
- // 判断定时时间CBN与t的值是否相等,到时间先判断图案还能不能下落,既触底(m=JILV),如果没触底清除当前图案,显示下落一行后的图案
- // 初始状态是m=JILV=1,会先运行一次XH();和AJ_PD();
- // 添加按键功能,首先调用按键函数ANJIAN();
- // 如果按键有变化那么ZUOJIA和XUANZUAN都将发生改变,此时不管t为何值都要执行相应程序
- // 根据ZUOJIA的改变来变动CUN[]的图案数据
- // 如果CUN[]改变,要从新调用AJ_PD();求JILV的值
- // 如果旋转按键状态XUANZUAN改变,变动CUN[]里的数据,判断此时的图案会不会与原来的“积木”重叠,如果重叠,CUN[]变回原来的数据,AJ_PD();重新调用
- //=========================================================================================================================================
- void timer() interrupt 1
- {
- static uchar CBN; //定义变量CBN用于控制图案下落的速度
- TH0=(65536-5000)/256; //定时器初始化
- TL0=(65536-5000)%256;
- t++; //计时(t)累加
- ANJIAN(); //调用按键子程序
- if(KS==0){ //判断向下按键(KS)有没有按
- if(t>30)t=0; //如果计时(t)大于30,t清零重新开始计时
- CBN=20; //减短图案下落的时间
- }else
- CBN=80; //图案平时下落速度
- /*----------------------------------------------------------------------------------------------------------------------------------------*/
- if((t==CBN)||(ZUOJIA!=G)||(XUANZUAN!=R)){ //计时(t)到了所设定的时间“或”左右和旋转按键三键中的一个按了一下
- if(m==JILV){ //如果图案下落到预先扫描的指定行
- x=0; //各种变量初始化
- m=1;
- G=7;
- R=0;
- JILV=1;
- ZUOJIA=7;
- XUANZUAN=0;
- XIAOHAN(); //调用消行子程序
- /*---------------------------------------------------------------------------------*/
- if(HUANCUN[0]!=0x0000){ //点阵顶部是否亮起,如果亮起表示该结束了
- QC_HC(); //清除缓存
- HUANCUN[2]=0x0ff0; //结束显示笑脸图案
- HUANCUN[3]=0x1008;
- HUANCUN[4]=0x2424;
- HUANCUN[5]=0x2a54;
- HUANCUN[6]=0x2004;
- HUANCUN[7]=0x2004;
- HUANCUN[8]=0x2004;
- HUANCUN[9]=0x2004;
- HUANCUN[10]=0x2244;
- HUANCUN[11]=0x2184;
- HUANCUN[12]=0x1008;
- HUANCUN[13]=0x0ff0;
- TR0=0; //定时器关闭
- return; //跳出中断
- }
- /*---------------------------------------------------------------------------------*/
- XH(); //图案对应数组
- AJ_PD(); //从上至下行扫描得到指定行变量(JILV)
- }else{ //如果图案还在下落
- m--; //变量(m)控制图案数组与缓存数组的数据运算
- x=m-1; //缓存变量(x)返回上个图案值
- for(n=0;n<m;n++){
- HUANCUN[x]&=(~CUN[n]); //上个图案清除
- x--;
- }
- x=m; //缓存变量(x)加一为下个图案显示做准备
- m++;
- }
- /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*/
- if(ZUOJIA!=G){ //如果左右键状态发生一次改变
- for(n=0;n<16;n++){
- if(ZUOJIA>G){ //如果是右键发生的变动
- CUN[n]>>=1;
- }else
- CUN[n]<<=1;
- }
- AJ_PD(); //重新扫描求JILV
- x--;
- m--;
- G=ZUOJIA;
- }
- if(XUANZUAN!=R){ //旋转键状态改变一次
- x--;
- m--;
- XH(); //调用XH()子程序,为了显示旋转后的图案,注意:这时图案是在正中间,即ZUOJIA=7的位置
- HPP(); //调用旋转子程序
- for(n=0;n<m;n++){
- if(CUN[n]!=0){
- if((HUANCUN[x]&CUN[n])!=0){ //如果旋转后的图案会和已存在图案重合
- if(XUANZUAN==0)XUANZUAN=4; //4种旋转图案对应状态值改回未变图案前的状态
- else XUANZUAN--;
- for(n=0;n<4;n++){ //图案也要变回去
- CUN[n]=XZA[n];
- }
- break;
- }
- }
- x--;
- }
- AJ_PD(); //调用扫描子程序,求新的JILV
- x=m-1;
- R=XUANZUAN; //把旋转图案状态值赋值给变量R,只有旋转按钮再次按下,才会再进来
- }
- /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*/
- for(n=0;n<m;n++){
- HUANCUN[x]|=CUN[n]; //把将要显示的图案对应的数组送入缓存数组
- x--;
- }
- m++;
- t=0; //计时变量清零
- }
- }
- //=========================================================================================================================================
复制代码- #include "anjian.h"
- //=========================================================================================================================================
- extern uchar XUANZUAN,ZUOJIA,JILV,m,n,x,k,G;
- extern uint HUANCUN[16],CUN[16],XZA[4],KKK;
- //=========================================================================================================================================
- //程序描述:判断右键(YOU)按下否?
- // 先把图案对应竖位置的状态变量ZUOJIA改变,再判断图案可不可以移动,如果不可以ZUOJIA改回来
- // m,x确实有点绕
- // 左键同上
- // 旋转按键按下,改变图案状态变量XUANZUAN,保存当前图案CUN[]到XZA[]
- //=========================================================================================================================================
- void ANJIAN() //按键子程序
- {
- static uchar i=1,s=1,j=1; //定义三个静态变量i,s,j表示右,左,旋转按键的状态值
- if(YOU!=s){ //如果右键状态值发生改变
- if(s!=0){ //判断状态值是否为0,如果不是,说明右键按下了
- ZUOJIA++; //图案未动,图案对应竖的状态值先加一
- m--; //x,m数组间交换变化所定义的变量
- x=m-1;
- for(n=0;n<m;n++){
- if(CUN[n]!=0){ //如果CUN[n]为0,就不用浪费时间判断什么了
- if(((HUANCUN[x]&((CUN[n]|(CUN[n]/2))-CUN[n]))!=0)||((CUN[n]&0x0001)!=0)){ //CUN[n]/2右移一位
- m++; //CUN[n]|(CUN[n]/2)右移一位后和之前的数或,(CUN[n]|(CUN[n]/2))-CUN[n]或了的数再减去最开始的数,就是右移后的图案
- ZUOJIA--; //HUANCUN[x]&((CUN[n]|(CUN[n]/2))-CUN[n])!=0上面的数再和显示数组里的数与,判断运算后的数是否为0
- s=YOU; //上面的运算就是判断”图案向右移动是否会碰到已经存在的图案“
- return; //(CUN[n]&0x0001)!=0这句判断图案有没有碰到右边的边界
- } //m++;ZUOJIA--;进入if说明图案不能变,那么前面改变的值都要改回来,s=YOU;既然程序要跳出来了,按键的状态值也要改变的,防止又进来了
- }
- x--;
- }
- x=m;
- m++;
- }
- s=YOU;
- }
- if(ZUO!=i){ //左键类同右键
- if(i!=0){
- ZUOJIA--;
- m--;
- x=m-1;
- for(n=0;n<m;n++){
- if(CUN[n]!=0){
- if(((HUANCUN[x]&((CUN[n]|(CUN[n]<<1))-CUN[n]))!=0)||(CUN[n]>=0x8000)){
- m++;
- ZUOJIA++;
- i=ZUO;
- return;
- }
- }
- x--;
- }
- x=n;
- m++;
- }
- i=ZUO;
- }
- if(XZ!=j){ //旋转按键
- if(j!=0){
- XUANZUAN=(XUANZUAN+1)%4; //如果旋转按键按下了,这里4种图案是按逆时针变化
- for(n=0;n<4;n++){
- XZA[n]=CUN[n]; //图案先保存在数组XZA[]中
- }
- }
- j=XZ;
- }
- }
- //=========================================================================================================================================
- //思维说明:在XH()程序中知道图案细分成15种,目的是要写一个程序让这15种图案下落后碰到下面的"积木"停下来(记下这一行的编号存在变量JILV里)
- //程序描述:图案保存在数组CUN[]中,CUN[0]是图案的底部,CUN[1]是图案中部,CUN[2]是图案上部,CUN[3]可以不作考虑
- // 16x16点阵有16行,以图案当前在点阵处在的位置开始与点阵已经存在“积木”做运算,判断再下落是否会重叠
- // 首先,判断CUN[0]是否触底了,即,(HUANCUN[n]&CUN[0])!=0
- // 其次,判断CUN[1]是否触底了,(CUN[1]|CUN[0])-CUN[0]这是取CUN[1]和CUN[0]在垂直方向上没有重合的部分
- // 最后,判断CUN[2]是否触底了,这种情况只有一种图案会发生
- // 总结,一种图案出现改变也好,都要调用这个函数找出触底位置
- // 当n>1后,运行一次for就判断一个图案分成3段的触底情况
- //=========================================================================================================================================
- void AJ_PD() //从上至下行扫描得到指定行变量(JILV)子程序
- {
- for(n=m-1;n<16;n++){ //扫描缓存数组
- if((HUANCUN[n]&CUN[0])!=0) break; //如果图案底层触底,跳出扫描
- else
- if((n>0)&&((HUANCUN[n-1]&((CUN[1]|CUN[0])-CUN[0]))!=0)) break; //否则,图案底层上面层触底,跳出扫描
- if((n>1)&&((HUANCUN[n-2]&((CUN[2]|CUN[1])-CUN[1]))!=0)) break; //图案底层上面层的上面层触底,跳出扫描
- }
- JILV=n+1; //记录JILV
- }
- //=========================================================================================================================================
- //我看到一个教程里说不要在for(i=0;i<6;i++)里面改变变量(i)的值,但我不知道会出什么问题
- //程序描述:从点阵的最下行向上开始判断
- //=========================================================================================================================================
- void XIAOHAN()
- { //消行子程序
- uint i;
- uchar u;
- i=KKK;
- for(n=16;n>0;n--){ //扫描16行
- if(HUANCUN[n-1]==0xffff){ //如果某行全亮
- for(u=n;u>1;u--){
- HUANCUN[u-1]=HUANCUN[u-2]; //上面一行覆盖下面一行
- }
- HUANCUN[0]=0x0000;
- n++;
- KKK++;
- } //最上面一行清零
- } //16行全扫描后,结束子程序
- if(KKK==(i+2))KKK+=2; //如果同时消除2行加4分
- else
- if(KKK==(i+3))KKK+=6; //如果同时消除3行加9分
- else
- if(KKK==(i+4))KKK+=12; //如果同时消除4行加16分
- else
- if(KKK>(i+4))KKK+=20; //如果同时消除5行加25分
- }
- //=========================================================================================================================================
- //思维说明:XH();中CUN[]只存改变后的图案,位置是在ZUOJIA=7,现在要写段程序根据ZUOJIA的值改变CUN[]的数据,即左右移动后的图案数据CUN[]
- //程序描述:比如当ZUOJIA=6时,CUN[]前4位数据(图案只用到前4位)都左移1位
- // 如果图案碰到了边界,重新调用XH();函数一次,CUN[]数据移动到能移动的最远位置
- //=========================================================================================================================================
- void HPP() //旋转子程序一部分
- {
- if(ZUOJIA<=7){ //如果图案在点阵的左半边
- for(k=0;k<(7-ZUOJIA);k++){ //7-ZUOJIA表示图案需要移动的位数
- for(n=0;n<4;n++){ //图案左移一位
- CUN[n]<<=1;
- if(CUN[n]>=0x8000){ //在该图案左移的过程中,判断有没有碰到点阵左边的边界
- XH(); //调用XH()子程序,该图案在ZUOJIA=7的位置;
- for(n=0;n<4;n++){ //该图案左移k+1个位,因为是k=0图案开始一位位左移,所以是k+1
- CUN[n]<<=k+1;
- }
- ZUOJIA=6-k; //图案对应竖变量也要跟着变化,6-k即7-(k+1)
- G=6-k; //图案的竖位置变了,左右按键的状态变量也要随之改变
- return; //跳出子程序
- }
- }
- }
- }else{ //否则图案在点阵右半边
- for(k=0;k<(ZUOJIA-7);k++){ //以下类同上面
- for(n=0;n<4;n++){
- CUN[n]>>=1;
- if((CUN[n]&0x0001)!=0){
- XH();
- for(n=0;n<4;n++){
- CUN[n]>>=k+1;
- }
- ZUOJIA=8+k;
- G=8+k;
- return;
- }
- }
- }
- }
- }
- //=========================================================================================================================================
- void QC_HC() //缓存(显示)数组清零
- {
- for(n=0;n<16;n++){
- HUANCUN[n]=0x0000;
- }
- }
- //=========================================================================================================================================
复制代码
所有资料51hei提供下载:
俄罗斯方块.rar
(73.91 KB, 下载次数: 51)
|