基于51單片機的俄羅斯方塊設計
基于51單片機的俄羅斯方塊設計,基于,51,單片機,俄羅斯方塊,設計
#include
#include"pic.c"
#include
#define LCD_DATA P2
#define button_delay 150 //按鍵延時
#define button_acceleration 65 //按鍵加速度閾值
#define GAME_LOCATION 30
sbit button_a = P3^4;
sbit button_b = P3^5;
sbit up = P3^2;
sbit down = P3^0;
sbit left = P3^1;
sbit right = P3^3;
sbit speaker=P3^6;
sbit LCD_RS=P1^0;
sbit LCD_RW=P1^1;
sbit LCD_E=P1^2;
sbit LCD_CS2=P1^4; //右屏選擇(左右屏有時候相反)
sbit LCD_CS1=P1^3; //左屏選擇
sbit LCD_RST=P3^7;
unsigned int up_reg=button_delay; //按鍵up累加器
unsigned int down_reg=button_delay; //按鍵down累加器
unsigned int left_reg=button_delay; //按鍵left累加器
unsigned int right_reg=button_delay; //按鍵right累加器
unsigned int button_a_reg=button_delay; //按鍵button_a累加器
unsigned int button_b_reg=button_delay; //按鍵button_b累加器
unsigned int right_acceleration=0; //按鍵right加速度寄存器
unsigned int left_acceleration=0; //按鍵left加速度寄存器
unsigned int idata Box_Ram[19];//定義游戲點陣緩存10*16
unsigned char box_down_reg;//定義方塊下落累加寄存器
unsigned char time0_reg;//定義定時器0累加寄存器
unsigned char next_mode;//定義下一個方塊的類型
unsigned char next_shape;//定義下一個方塊的形狀
unsigned int destroy_row_num=0;//定義所消的行數(shù)
unsigned char speed_num=0;//定義游戲速度等級
unsigned char level_num;//定義游戲難度等級
bit game_over_flag;//游戲結束標志位置0表示游戲未結束
bit pause_game_flag;//游戲暫停標志位置0表示游戲未暫停
struct
{
unsigned char mode;//類型
unsigned char shape;//形狀
unsigned char x;//x坐標
unsigned char y;//y坐標
unsigned int box;//定義方塊緩存
}s_box; //定義方塊結構體
//LCD檢測忙狀態(tài)函數(shù)
void LCD_check_busy()
{
unsigned char temp;
LCD_RS=0;
LCD_RW=1;
do
{
LCD_DATA=0xff;
LCD_E=1;
temp=LCD_DATA;
LCD_E=0;
}while((temp&0x80)==0x80);
}
//寫指令代碼(cs為0選左屏,cs為1選右屏)
void LCD_W_code(unsigned char tpcode,bit cs)
{
LCD_RS=0;
LCD_RW=0;
LCD_CS2=~cs;
LCD_CS1=cs;
LCD_DATA=tpcode;
LCD_E=1;
_nop_();
LCD_E=0;
}
//寫顯示數(shù)據(jù)(cs為0選左屏,cs為1選右屏)
void LCD_W_data(unsigned char tpdata,bit cs)
{
LCD_check_busy();
LCD_RS=1;
LCD_RW=0;
LCD_CS2=~cs;
LCD_CS1=cs;
LCD_DATA=tpdata;
LCD_E=1;
_nop_();
LCD_E=0;
}
//LCD初始化函數(shù)
void LCD_initialize()
{
LCD_RST=0;
_nop_();
_nop_();
LCD_RST=1;
LCD_W_code(0x3f,0); //開顯示設置
LCD_W_code(0xc0,0); //設置顯示起始行為第一行
LCD_W_code(0xb8,0); //頁面地址設置
LCD_W_code(0x40,0); //列地址設為0
LCD_W_code(0x3f,1);
LCD_W_code(0xc0,1);
LCD_W_code(0xb8,1);
LCD_W_code(0x40,1);
}
//LCD清屏函數(shù)
void LCD_clear()
{
unsigned char i,j;
for(j=0;j<8;j++)
{
LCD_W_code(0xb8+j,0);
LCD_W_code(0x40,0);
LCD_W_code(0xb8+j,1);
LCD_W_code(0x40,1);
for(i=0;i<64;i++)
{
LCD_W_data(0x00,0);
LCD_W_data(0x00,1);
}
}
}
//LCD顯示字符串函數(shù)(word表示要顯示的字符串,
//length表示要顯示的字符串寬度,
//x表示首字符所在行數(shù),
//y表示首字符所在列數(shù))
void LCD_display_word(unsigned char word[],
unsigned int length,
unsigned char x,
unsigned char y)
{
unsigned char i;
for(i=0;i>(12-j))&0x0001==1 )
{
tpdata=0x0f;
}
if( (Box_Ram[2*i+1]>>(12-j))&0x0001==1 )
{
tpdata|=0xf0;
}
LCD_display_byte(GAME_LOCATION+1+j*4,i,tpdata);
LCD_display_byte(GAME_LOCATION+2+j*4,i,0xbb&tpdata);
LCD_display_byte(GAME_LOCATION+3+j*4,i,0xdd&tpdata);
LCD_display_byte(GAME_LOCATION+4+j*4,i,tpdata);
}
}
}
//基本按鍵程序(返回0表示沒按鍵被按下,返回1表示down被按下,返回2表示up被按下,返回3表示button_a被按下,返回4表示left被按下,返回5表示right被按下)
//游戲中按鍵識別程序(有優(yōu)先級,從高到低依次是button_a_reg>down>left>right>up)
unsigned char basic_button()
{
unsigned char tpflag=0;
if(button_b==0)
{
if(button_b_reg>4;
}//先將現(xiàn)有的方塊從游戲點陣緩存中刪除
temp=tpbox;
for(i=0;i<4;i++)
{
if((((temp&0x000f)<<(9-tpx))&Box_Ram[3-i+tpy])!=0x0000)
{
tpflag=0;
}
temp=temp>>4;
}//檢查方塊是否和原有圖形重疊,重疊置標志位tpflag為0,不重疊不置標志位,即tpflag為1
temp=s_box.box;
for(i=0;i<4;i++)
{
Box_Ram[3-i+s_box.y]|=((temp&0x000f)<<(9-s_box.x));
temp=temp>>4;
}//在游戲點陣緩存中恢復原有方塊
return(tpflag);
}
//方塊緩存數(shù)據(jù)函數(shù)(輸入方塊類型和形狀即可獲得方塊緩存數(shù)據(jù))
unsigned int box_read_data(unsigned char tpmode,unsigned char tpshape)
{
unsigned int tpbox;
switch(tpmode)
{
case 0: switch(tpshape)
{
case 0: tpbox=0xf000;break;
case 1: tpbox=0x4444;break;
case 2: tpbox=0xf000;break;
case 3: tpbox=0x4444;break;
default:;
}break;
case 1: switch(tpshape)
{
case 0: tpbox=0xe800;break;
case 1: tpbox=0xc440;break;
case 2: tpbox=0x2e00;break;
case 3: tpbox=0x88c0;break;
default:;
}break;
case 2: switch(tpshape)
{
case 0: tpbox=0xe200;break;
case 1: tpbox=0x44c0;break;
case 2: tpbox=0x8e00;break;
case 3: tpbox=0xc880;break;
default:;
}break;
case 3: switch(tpshape)
{
case 0: tpbox=0xcc00;break;
case 1: tpbox=0xcc00;break;
case 2: tpbox=0xcc00;break;
case 3: tpbox=0xcc00;break;
default:;
}break;
case 4: switch(tpshape)
{
case 0: tpbox=0xc600;break;
case 1: tpbox=0x4c80;break;
case 2: tpbox=0xc600;break;
case 3: tpbox=0x4c80;break;
default:;
}break;
case 5: switch(tpshape)
{
case 0: tpbox=0x6c00;break;
case 1: tpbox=0x8c40;break;
case 2: tpbox=0x6c00;break;
case 3: tpbox=0x8c40;break;
default:;
}break;
case 6: switch(tpshape)
{
case 0: tpbox=0x4e00;break;
case 1: tpbox=0x8c80;break;
case 2: tpbox=0xe400;break;
case 3: tpbox=0x4c40;break;
default:;
}break;
default:;
}
return(tpbox);
}
//方塊載入函數(shù)
void box_load()
{
s_box.box=box_read_data(s_box.mode,s_box.shape);
}
//方塊映射游戲點陣緩存函數(shù)(參數(shù)是原來方塊的位置、緩存,先消去原有位置的方塊)
void box_to_Box_Ram(unsigned char tpx,unsigned char tpy,unsigned int tpbox)
{
unsigned char i;
unsigned int temp;
temp=tpbox;
for(i=0;i<4;i++)
{
Box_Ram[3-i+tpy]=Box_Ram[3-i+tpy]&(~((temp&0x000f)<<(9-tpx)));
temp=temp>>4;
}//從游戲點陣緩存中刪除以前的方塊
temp=s_box.box;
for(i=0;i<4;i++)
{
Box_Ram[3-i+s_box.y]=((temp&0x000f)<<(9-s_box.x))|Box_Ram[3-i+s_box.y];
temp=temp>>4;
}//在游戲點陣緩存中加入新的方塊
}
//顯示數(shù)字函數(shù)(
//x表示x坐標,
//y表示y坐標,
//tpdata表示要顯示的數(shù)字)
//顯示速度函數(shù)
void show_num(unsigned char x,
unsigned char y,
unsigned char tpdata)
{
unsigned char i;
for(i=0;i<4;i++)
{
LCD_display_byte(x+i,y,num_data[tpdata*4+i]);
}
}
void show_speed_num(unsigned char x,unsigned char y)
{
show_num(x,y,speed_num);
}
//顯示得分函數(shù)
void show_score_num(unsigned char x,unsigned char y)
{
show_num(x,y,destroy_row_num/10000);
show_num(x+=5,y,(destroy_row_num%10000)/1000);
show_num(x+=5,y,(destroy_row_num%1000)/100);
show_num(x+=5,y,(destroy_row_num%100)/10);
show_num(x+=5,y,destroy_row_num%10);
}
//消行函數(shù)
void destroy_row()
{
unsigned char i,j=0;
unsigned char tpflag[4]={0,0,0,0};//最多一次只能消四行,所以設置四個標志位即可,初值為0
for(i=0;i<16;i++)
{
if((Box_Ram[i]&0x3ffc)==0x3ffc)
{
tpflag[j]=i+1;//tpflag為0表示不標志,1表示第0行緩存為0xffff,n表示第n+1行緩存為0xffff
destroy_row_num++;//消除的行數(shù)加一
/*如不把Box_Ram[19]定義成idata類型的話加入這段代碼顯示數(shù)據(jù)區(qū)就溢出了*/
if(destroy_row_num%30==0&&speed_num!=9)
{
speed_num++;//消夠三十行游戲速度加一
show_speed_num(13,4);//調用顯示游戲速度函數(shù)
}
/*如不把Box_Ram[19]定義成idata類型的話加入這段代碼顯示數(shù)據(jù)區(qū)就溢出了*/
j++;
if(j==4)
{
break;
}//檢查完有四行要消除則退出檢查循環(huán)
}
}//依次檢測是否有行緩存為0xffff,如果是則標志tpflag為此行的行號
for(j=0;j<4;j++)
{
if(tpflag[j]!=0)
{
for(i=tpflag[j]-1;i>0;i--)
{
Box_Ram[i]=Box_Ram[i-1];
Box_Ram[0]=0x2004;
}
}
}//被標志的行依次被上一行所取代,即被消去
show_score_num(3,1);
}
//顯示下一個方塊函數(shù)
void show_next_box()
{
unsigned char i,tpdata;
unsigned int temp;
temp=box_read_data(next_mode,next_shape);
for(i=0;i<4;i++)
{
tpdata=0x00;
if( ((temp>>(15-i))&0x0001)==1 )
{
tpdata=0x0f;
}
if( ((temp>>(11-i))&0x0001)==1 )
{
tpdata|=0xf0;
}
LCD_display_byte(7+i*4,6,tpdata);
LCD_display_byte(8+i*4,6,0xbb&tpdata);
LCD_display_byte(9+i*4,6,0xdd&tpdata);
LCD_display_byte(10+i*4,6,tpdata);
tpdata=0x00;
if( ((temp>>(7-i))&0x0001)==1 )
{
tpdata=0x0f;
}
if( ((temp>>(3-i))&0x0001)==1 )
{
tpdata|=0xf0;
}
LCD_display_byte(7+i*4,7,tpdata);
LCD_display_byte(8+i*4,7,0xbb&tpdata);
LCD_display_byte(9+i*4,7,0xdd&tpdata);
LCD_display_byte(10+i*4,7,tpdata);
}
}
//方塊生成函數(shù)
void box_build()
{
s_box.mode=next_mode;
s_box.shape=next_shape;
s_box.x=3;
s_box.y=0;
next_mode=TL0%7;//產(chǎn)生隨機數(shù),但是是偽隨機的
next_shape=TL0%4;//產(chǎn)生隨機數(shù),但是是偽隨機的
show_next_box();//放到game_execute()函數(shù)中不知道為什么就是不正常顯示
}
void game_button()
{
switch(basic_button())
{
case 3: if(s_box.y!=0)//3表示button_a被按下
{
EA=0;//關中斷,如果不關的話可能引起游戲顯示混亂
speaker=0;
if(s_box.shape==3&&check_cover(s_box.x,s_box.y,box_read_data(s_box.mode,0)))
{
s_box.shape=0;
box_load();
box_to_Box_Ram(s_box.x,s_box.y,box_read_data(s_box.mode,3));
}
else if(check_cover(s_box.x,s_box.y,box_read_data(s_box.mode,0)))
{ if(check_cover(s_box.x,s_box.y,box_read_data(s_box.mode,s_box.shape+1)))
{
s_box.shape++;
box_load();
box_to_Box_Ram(s_box.x,s_box.y,box_read_data(s_box.mode,s_box.shape-1));
}
}
EA=1;//開中斷
speaker=1;
}break;
case 1: if(s_box.y!=0)//1表示down被按下
{
EA=0;//關中斷,如果不關的話可能引起游戲顯示混亂
speaker=0;
while(check_cover(s_box.x,s_box.y+1,s_box.box))//檢測是否能下降,指導不能再下降為止
{
s_box.y++;
box_to_Box_Ram(s_box.x,s_box.y-1,s_box.box);
}
destroy_row();
box_build();
box_load();
// game_over_flag=check_game_over();//游戲結束標志位置1表示游戲結束
// next_box();
box_to_Box_Ram(s_box.x,s_box.y,s_box.box);
EA=1;//開中斷
speaker=1;
}break;
case 4: if(s_box.y!=0)//4表示left被按下
{
EA=0;//關中斷,如果不關的話可能引起游戲顯示混亂
speaker=0;
if(check_cover(s_box.x-1,s_box.y,s_box.box))
{
s_box.x--;
box_to_Box_Ram(s_box.x+1,s_box.y,s_box.box);
}
EA=1;//開中斷
speaker=1;
}break;
case 5: if(s_box.y!=0)//5表示right被按下
{
EA=0;//關中斷,如果不關的話可能引起游戲顯示混亂
speaker=0;
if(check_cover(s_box.x+1,s_box.y,s_box.box))
{
s_box.x++;
box_to_Box_Ram(s_box.x-1,s_box.y,s_box.box);
}
EA=1;//開中斷
speaker=1;
}break;
case 2: //2表示up被按下
speaker=0;
pause_game_flag=~pause_game_flag;//游戲暫停標志取反
while(up==0);
speaker=1;
break;
default:;
}
}
//檢查游戲結束函數(shù)(游戲結束返回1,游戲沒有結束返回0)
bit check_game_over()
{
unsigned char i;
bit tpflag=0;
unsigned int temp;
temp=s_box.box;
for(i=0;i<4;i++)
{
if((((temp&0x000f)<<(9-s_box.x))&Box_Ram[3-i+s_box.y])!=0x0000)
{
tpflag=1;
}
temp=temp>>4;
}//檢查新建方塊是否和原有圖形重疊,重疊置標志位tpflag為1,不重疊不置標志位,即tpflag為0
return(tpflag);
}
//游戲執(zhí)行函數(shù)(控制方塊下落,檢測是否到底,如果到底調用消行函數(shù))
void game_execute()
{
if(box_down_reg<20-(speed_num<<1))
{
box_down_reg++;
}
else
{
box_down_reg=0;
if(check_cover(s_box.x,s_box.y+1,s_box.box))
{
s_box.y++;
box_to_Box_Ram(s_box.x,s_box.y-1,s_box.box);
}//檢測是否還可以下降,如果還能下降則繼續(xù)下降
else
{
destroy_row();
box_build();
box_load();
game_over_flag=check_game_over();//游戲結束標志位置1表示游戲結束
box_to_Box_Ram(s_box.x,s_box.y,s_box.box);
box_down_reg=(20-(speed_num<<1)-1);//為了使方塊一出現(xiàn)就能變換形狀,所以需要盡快使得方塊下降一行,不知道為什么最高行不能變換形狀
}//如果不能下降則調用消行函數(shù)檢查是否可以消行,之后重新建立方塊
}
}
//選擇游戲速度函數(shù)
void select_speed()
{
unsigned char i;
bit tpflag=1;//置循環(huán)標志位為1
LCD_clear();
for(i=0;i<128;i++)
{
LCD_display_byte(i,0,0xff);
LCD_display_byte(i,7,0xff);
}
LCD_display_byte(60,4,0x7f);
LCD_display_byte(59,4,0x3e);
LCD_display_byte(58,4,0x1c);
LCD_display_byte(57,4,0x08);
LCD_display_byte(67,4,0x7f);
LCD_display_byte(68,4,0x3e);
LCD_display_byte(69,4,0x1c);
LCD_display_byte(70,4,0x08);
LCD_display_word(speed_data,24,3,52);
show_speed_num(62,4);
while(tpflag)
{
switch(basic_button())
{
case 4: if(speed_num!=0)
{
speaker=0;
speed_num--;
show_speed_num(62,4);
speaker=1;
}
while(left==0);
break;
case 5: if(speed_num!=9)
{
speaker=0;
speed_num++;
show_speed_num(62,4);
speaker=1;
}
while(right==0);
break;
case 6: tpflag=0;
speaker=0;
while(button_b==0);
speaker=1;
break;
default:;
}
}//選擇游戲速度循環(huán)
}
//游戲開始顯示畫面
void game_start_show()
{
bit tpflag=1;//置循環(huán)標志位為1
LCD_full_draw(start_pic);
while(tpflag)
{
switch(basic_button())
{
case 6: tpflag=0;
speaker=0;
while(button_b==0);
speaker=1;
break;
default:;
}
}//game_start_show循環(huán)
}
//游戲初始化函數(shù)
void game_initialize()
{
box_down_reg=0;
next_mode=6;
next_shape=2;
destroy_row_num=0;
game_over_flag=0;
pause_game_flag=0;
LCD_clear();
time0_reg=0;
display_basic();
LCD_display_word(score_data,24,0,3);
LCD_display_word(speed_data,24,3,3);
show_score_num(3,1);
show_speed_num(13,4);
}
//定時器0初始化函數(shù)
void time0_initialize()
{
TMOD=0x03;//定時器0,16位工作方式
TR0=1; //啟動定時器
ET0=1; //打開定時器0中斷
//默認中斷優(yōu)先級為低
EA=1; //打開總中斷
}
//俄羅斯方塊游戲主函數(shù)
void Tetris_main()
{
unsigned char i;
for(i=0;i<19;i++)
{
Box_Ram[i]=Box_Ram_data[i];
};//載入游戲初始顯示畫面
LCD_draw(mpic);
game_over_flag=0;//游戲結束標志位置0表示游戲未結束
box_build();
box_load();
// next_box();
box_to_Box_Ram(s_box.x,s_box.y,s_box.box);
box_down_reg=(20-(speed_num<<1)-1);//為了使方塊一出現(xiàn)就能變換形狀,所以需要盡快使得方塊下降一行,不知道為什么最高行不能變換形狀
time0_initialize();
while(!game_over_flag)//如果游戲結束標志位置1,表示游戲結束,打破循環(huán),調用游戲結束畫面顯示函數(shù)
{
game_button();
}
EA=0;//游戲結束后關中斷
}
//游戲結束畫面顯示函數(shù)
void game_over_show()
{
unsigned char i;
bit tpflag=1;//置循環(huán)標志位為1
LCD_full_draw(over_pic);
while(button_a==0);
while(tpflag)
{
switch(basic_button())
{
case 6: tpflag=0;
speaker=0;
while(button_b==0);
speaker=1;
break;
default:;
}
}//game over畫面循環(huán)
LCD_clear();
for(i=0;i<128;i++)
{
LCD_display_byte(i,0,0xff);
LCD_display_byte(i,7,0xff);
}
LCD_display_word(score_data,24,3,52);
show_score_num(52,4);
tpflag=1;
while(tpflag)
{
switch(basic_button())
{
case 6: tpflag=0;
speaker=0;
while(button_b==0);
speaker=1;
break;
default:;
}
}//游戲得分顯示循環(huán)
}
void main()
{
LCD_initialize();
LCD_clear();
while(1)
{
game_start_show();
select_speed();
game_initialize();//調用游戲初始化函數(shù),初始化游戲所有變量以及在液晶屏上顯示基本的信息
Tetris_main();
game_over_show();
}
}
//定時器0中斷服務
void timer0() interrupt 1
{
TH0=0x00;
TL0=0x00;
if(time0_reg<10)
{
time0_reg++;
}
else
{
time0_reg=0;
if(pause_game_flag==0)
{
game_execute();
refurbish_display();
}
}
}
unsigned int code Box_Ram_data[]=
{
0x2004,0x2004,0x2004,0x2004,0x2004,0x2004,0x2004,0x2004, //十六行
0x2004,0x2004,0x2004,0x2004,0x2004,0x2004,0x2004,0x2004,
0xffff,0x0000,0x0000//多出來的三行是為了能讓方塊落到最低位置
//多出來的第一行置0xffff用于檢測方塊釋放到底
//顯示行初值為0x2004表示左右邊界
};//游戲點陣緩存10*16(用前10位表示)(1表示亮,0表示滅)
unsigned char code speed_data[]=
{
0x46,0x49,0x49,0x31,0x00,0x7F,0x09,0x09,
0x06,0x00,0x7F,0x49,0x49,0x49,0x00,0x7F,
0x49,0x49,0x49,0x00,0x7F,0x41,0x41,0x3E,
};//speed字模
unsigned char code score_data[]=
{
0x46,0x49,0x49,0x31,0x00,0x3E,0x41,0x41,
0x22,0x00,0x3E,0x41,0x41,0x3E,0x00,0x7F,
0x11,0x29,0x46,0x00,0x7F,0x49,0x49,0x49
};//score字模
unsigned char code num_data[]=
{
0x7F,0x41,0x41,0x7F,//0字模
0x00,0x00,0x00,0x7F,//1字模
0x79,0x49,0x49,0x4F,//2字模
0x49,0x49,0x49,0x7F,//3字模
0x0F,0x08,0x08,0x7F,//4字模
0x4F,0x49,0x49,0x79,//5字模
0x7F,0x49,0x49,0x79,//6字模
0x01,0x01,0x01,0x7F,//7字模
0x7F,0x49,0x49,0x7F,//8字模
0x4F,0x49,0x49,0x7F,//9字模
};//數(shù)字字模
unsigned char code mpic[]=
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x40,0x60,0x70,0x78,0x70,0x60,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0
收藏