视频演示效果:
纳新作品——小霸王游戏机
目录:
目录
视频演示效果:
目录:
前言:
一、连接方式:
1.1 控制引脚
1.2. 显示模块
1.3. 定时器
1.4. 游戏逻辑与硬件结合
1.5. 中断处理
二、源码分析:
2.1. 全局变量与常量定义
2.2. 游戏初始化函数
2.3. 游戏状态与逻辑
2.4. 辅助功能
2.5. 显示与输入
2.6. 中断处理
2.7. 主函数
2.8. 源码整合:
完整工程源码获取方式:
前言:
51单片机自制小霸王游戏机不仅是一个充满乐趣的DIY项目,而且蕴含了深刻的教育和技术意义。通过这一过程,可以加深对嵌入式系统原理的理解,锻炼实践操作能力,同时也是一种对经典电子游戏文化的致敬和传承。
首先,这一项目使得制作者能够深入理解51单片机的内部结构和工作原理。51单片机作为一种基础的微控制器,其编程和硬件接口设计对于电子工程的学习者来说是基础且关键的。在自制游戏机的实践中,制作者需要对51单片机的I/O端口、定时器、中断系统等有深入的了解和运用,这有助于提升对微控制器应用的熟练度。
其次,自制小霸王游戏机的过程是对电子电路设计能力的考验和提升。从电路板的布局设计到元件的选择,再到焊接和调试,每一步都需要精细的操作和严谨的思考。这不仅能够锻炼动手能力,还能培养解决实际问题的能力。
此外,该项目具有浓厚的文化意义。小霸王游戏机是许多80后甚至70后的童年记忆,通过自制游戏机,可以体验复古游戏带来的乐趣,同时也是对那个时代电子游戏文化的一种纪念和传承。
这一项目能够激发创新思维和独立解决问题的能力。在制作过程中,可能会遇到各种预料之外的挑战,比如如何优化电路设计以减小体积,如何编写更高效的代码来提升游戏性能,如何使游戏机界面更友好等。这些问题的解决,都需要制作者发挥创新思维,寻找最佳解决方案。
最后,自制小霸王游戏机也是一种良好的技术交流方式。制作者可以将自己的设计分享给他人,通过社区交流获取反馈和建议,从而不断提升自己的技术水平,同时也促进了技术爱好者和同行之间的交流与合作。
一、连接方式:
1.1 控制引脚
-
P2口:用于控制方向和确认操作。
0x04
: 表示向上移动。0x10
: 表示向下移动。0x01
: 表示向左移动。0x40
: 表示向右移动。0x80
: 表示确认操作。
-
P3口:用于控制蜂鸣器。
P3^7
(loud
): 控制蜂鸣器的开关状态。
-
P0口:用于检测键盘输入,具体检测到哪个键由P0的值决定。
P0=0xf7
: 检测第一列键盘输入。P0=0xfb
: 检测第二列键盘输入。P0=0xfd
: 检测第三列键盘输入。P0=0xfe
: 检测第四列键盘输入。
1.2. 显示模块
相关接口
- LCD接口:用于显示游戏信息和状态。
- 涉及到LCD的初始化、位置设置、数据写入等操作。
wr_i_lcd
和wr_d_lcd
函数用于写入指令和数据到LCD。
该代码主要涉及对 LCD 的控制,LCD 通常通过多个引脚与微控制器连接。在本代码中,使用了一些特定的控制信号:
-
RS (Register Select):选择寄存器信号,用于选择指令寄存器或数据寄存器。
rs=0
表示指令寄存器,rs=1
表示数据寄存器。 -
RW (Read/Write):读写控制信号。
rw=0
表示写操作,rw=1
表示读操作。 -
E (Enable):使能信号。LCD 在
E
由高变低的瞬间捕获数据。 -
busy_lcd():检查 LCD 的忙碌状态,确保在其准备好之前不会发送新的指令或数据。
数据线
- lcddata:用于发送指令或数据到 LCD 的数据线。在特定指令下,将数据或命令传递给 LCD。
电路接口功能
-
延时函数 (
delaynms
):用于产生精确的时间延迟,以满足 LCD 的指令和数据处理的时间要求。通过软件延时的方式确保 LCD 在指令或数据写入后有充足的时间来完成操作。 -
初始化函数 (
init_lcd
):对 LCD 进行初始化设置,包括配置 LCD 的指令集、显示控制和清屏等操作。 -
图像处理函数 (
img_disp
,lcd_set_dot
,clear_pic
):这些函数用于在 LCD 上绘制图像或图形,通过操控特定的寄存器和位来改变显示内容。
硬件连接
-
接口引脚:通常 LCD 与微控制器之间会有多个 GPIO 口连接,上述信号(RS, RW, E, lcddata)都需要连接到微控制器的相应引脚。
-
电源与接地:LCD 显示屏需要连接合适的电源和接地,以确保稳定工作。
工作原理
-
指令与数据传输:通过上述信号的组合,微控制器可以发送不同的命令和数据到 LCD,实现各种显示效果。
-
忙碌检测:在执行新的指令或写入数据之前,通过
busy_lcd()
检测 LCD 的忙碌状态,以防止由于过快的指令发送导致的数据丢失或指令失效。
1.3. 定时器
- 定时器设置:
- 使用定时器(如T1)来产生随机数,用于游戏逻辑中的随机生成(如食物位置的随机生成)。
TH1
和TL1
分别用于设置定时器高位和低位。
1.4. 游戏逻辑与硬件结合
- 游戏状态:通过变量
flag
来控制不同的游戏状态,如贪吃蛇、飞机大战等。 - 随机数生成:利用定时器的计数值来随机生成游戏中的元素位置。
1.5. 中断处理
- 中断服务程序:负责处理按键输入。
- 对应的中断例程负责检测按键输入,并根据输入修改游戏状态或控制动作(如改变方向、选择菜单选项等)。
电路接口主要包括按键检测、LCD显示、蜂鸣器控制等部分。通过这些硬件接口,代码实现了一个简单的游戏控制系统。每个硬件接口的功能都通过相应的端口和位定义清晰地映射到特定的操作上,如移动、选择、确认等。
二、源码分析:
2.1. 全局变量与常量定义
代码开头定义了一些全局变量和常量,用于控制游戏的状态和行为。
- 字符串常量:用于显示在 LCD 上的不同游戏名称和提示信息,如
name
、game1
、loser
等。 - 全局变量:如
p
、px
、length
、flag
、slect
等,这些变量用于控制菜单选择、游戏状态、长度计数等。 - 硬件接口:
loud
是一个特殊位(sbit),用于控制蜂鸣器的开关。
#include "includes.h"
#include "intrins.h"
#include "stdlib.h"/*fx=0 ÓÒ fx=1 ×ófx=2 ÉÏfx=3 ÏÂ*/
/*P2 ÉÏ 0x04 ÏÂ 0x10 ×ó 0x01 ÓÒ 0x40 È·ÈÏ 0x80 */
uchar code name[]={"С°ÔÍõÓÎÏ·»ú"};
uchar code game1[]={"Ì°³ÔÉß"};
uchar code game2[]={"´òµØÊó"};
uchar code game3[]={"·É»ú´óÕ½"};
uchar code game4[]={"ͼƬ"};
uchar code loser[]={"ÄãÊäÁË"};
uchar code lxy1[]={"¼ÌÐø"};
uchar code lxy2[]={"·µ»Ø"};
uchar code cj[]={"³¤¶È: "};
uchar code fjs[]={"·É»ú: "};
char code mouse[]={"ÀÏÊó: "};
uchar p=1,px=1;
uchar length=9;
uchar flag=0,slect=0,choose=0;
uchar ran1,ran2,food=0;
uchar planex[3]={28,30,0};
uchar zidan_x[10]={0},zidan_y[10]={0};
uchar boom_x[10]={0},boom_y[10]={0};
char dishu=0;
sbit loud=P3^7;
struct xx
{char x[22];char y[22];char head;char tail;char dir;
}snake;
2.2. 游戏初始化函数
snake_init()
:初始化贪吃蛇游戏的状态,在 LCD 上绘制初始蛇的形状,并设置蛇的坐标、头尾指针和方向。
void snake_init(void)
{lcd_set_dot(1,1,0);lcd_set_dot(2,1,0);lcd_set_dot(3,1,0);lcd_set_dot(4,1,0);lcd_set_dot(5,1,0);lcd_set_dot(6,1,0);snake.x[0]=1;snake.y[0]=1;snake.x[1]=2;snake.y[1]=1;snake.x[2]=3;snake.y[2]=1;snake.x[3]=4;snake.y[3]=1;snake.x[4]=5;snake.y[4]=1;snake.x[5]=6;snake.y[5]=1;snake.head=5;snake.tail=0;snake.dir=0;
}
2.3. 游戏状态与逻辑
die()
:处理游戏失败的情况,显示失败信息并等待用户选择继续或返回。cata()
:菜单选择功能,显示可选游戏列表,并根据用户输入更新选择。game_snake()
:贪吃蛇游戏的核心逻辑,包括蛇的移动、食物生成、碰撞检测等。game_plane()
:飞机大战游戏的逻辑,处理子弹和敌机的碰撞、飞机的移动等。game_mouse()
:打地鼠游戏逻辑,随机生成地鼠位置,并检测玩家是否成功击中。
void die(void)
{uchar i;loud=0;flag=5;clear_pic();wr_i_lcd(0x30);wr_i_lcd(0x01);i=0;lcd_pos(0,2);while(loser[i]!='\0'){wr_d_lcd(loser[i]);i++;}i=0;lcd_pos(2,0); while(lxy1[i]!='\0'){wr_d_lcd(lxy1[i]);i++; }i=0;lcd_pos(2,5); while(lxy2[i]!='\0'){wr_d_lcd(lxy2[i]);i++; }lcd_pos(3,0); wr_d_lcd('o');delay(1000);loud=1;while(flag==5);
}
uchar cata(void)
{uchar i;i=0;slect=0;lcd_pos(0,0);wr_d_lcd('o');lcd_pos(0,1);while(game1[i]!='\0'){wr_d_lcd(game1[i]);i++;}lcd_pos(1,1);i=0;while(game3[i]!='\0'){wr_d_lcd(game2[i]);i++;}lcd_pos(2,1);i=0;while(game3[i]!='\0'){wr_d_lcd(game3[i]);i++;}lcd_pos(3,1);i=0;while(game4[i]!='\0'){wr_d_lcd(game4[i]);i++;}while(flag==0);
}
void game_snake(void)
{uchar i,k,j;k=0;food=0;length=6;lcd_pos(3,0);i=0;while(cj[i]!='\0'){wr_d_lcd(cj[i]);i++; }wr_d_lcd(length+48);while(1){if(food==0){while(1)//·ÀÖ¹²úÉúµÄʳÎï³öÏÖÔÚÉß×Ô¼ºÉíÉÏ{ran1=TH1%59+1;ran2=TL1%30+1;food=1;i=snake.tail;while(i!=snake.head){if(snake.x[i]==ran1&&snake.y[i]==ran2){break;}i++;if(i==22) i=0;if(i==snake.head){if(snake.x[i]==ran1&&snake.y[i]==ran2){i=snake.head-1;break;}} }if(i==snake.head) break; }lcd_set_dot(ran1,ran2,0);}i=snake.tail;while(i!=snake.head)//ÉßÒ§µ½×Ô¼º{if(snake.x[i]==snake.x[snake.head]&&snake.y[i]==snake.y[snake.head]){die(); break; }i++;if(i==22) i=0;}if(i!=snake.head) break;if(snake.x[snake.head] >= 60 || snake.y[snake.head] >= 30 ||snake.x[snake.head] == 0 ||snake.y[snake.head] == 0) //ÉßÅöǽ{die(); break; }snake.head++;if(snake.head==22){snake.head=0;if(snake.dir==0) //ÓÒ{snake.x[snake.head]=snake.x[21]+1;snake.y[snake.head]=snake.y[21]; }else if(snake.dir==1) //×ó{snake.x[snake.head]=snake.x[21]-1;snake.y[snake.head]=snake.y[21]; }else if(snake.dir==2) //ÏÂ{ snake.x[snake.head]=snake.x[21];snake.y[snake.head]=snake.y[21]+1;}else if(snake.dir==3) //ÉÏ{snake.x[snake.head]=snake.x[21];snake.y[snake.head]=snake.y[21]-1; } }else{if(snake.dir==0){snake.x[snake.head]=snake.x[snake.head-1]+1;snake.y[snake.head]=snake.y[snake.head-1]; }else if(snake.dir==1){snake.x[snake.head]=snake.x[snake.head-1]-1;snake.y[snake.head]=snake.y[snake.head-1]; }else if(snake.dir==2){snake.x[snake.head]=snake.x[snake.head-1];snake.y[snake.head]=snake.y[snake.head-1]+1;}else if(snake.dir==3){snake.x[snake.head]=snake.x[snake.head-1];snake.y[snake.head]=snake.y[snake.head-1]-1;}}lcd_set_dot(snake.x[snake.head],snake.y[snake.head],0);if(snake.x[snake.head]!=ran1 || snake.y[snake.head]!=ran2)//Éßû³Ôµ½Ê³Îï{lcd_set_dot(snake.x[snake.tail],snake.y[snake.tail],1);snake.tail++;if(snake.tail==22) snake.tail=0;}else //Éß³Ôµ½Ê³Îï{if(k<90) k+=10;loud=0;length++;wr_i_lcd(0x30);// wr_i_lcd(0x01);lcd_pos(3,3);wr_d_lcd(length/10+48);wr_d_lcd(length%10+48);food=0;}for(i=0;i<100;i++)for(j=0;j<100-k;j++) time_1ms(1000);loud=1; }
}
2.4. 辅助功能
plane_init()
:初始化飞机的位置和状态。back_ground()
:绘制游戏背景,比如边界和初始分数。picture()
:显示图片的功能,通过选择不同的图片页来更新显示内容。mouse_back()
:初始化打地鼠游戏的背景。
void plane_init(void)
{uchar i;lcd_set_dot(29,29,0);lcd_set_dot(28,30,0);lcd_set_dot(30,30,0);lcd_set_dot(29,30,0);for(i=0;i<10;i++){zidan_x[i]=0;zidan_y[i]=0;boom_x[i]=0;boom_y[i]=0; }planex[0]=28; planex[1]=30;
}void back_ground(void)
{uchar i;clear_pic();wr_i_lcd(0x30);wr_i_lcd(0x01);for(i=0;i<=60;i++){ lcd_set_dot(i,0,0);lcd_set_dot(i,31,0); }for(i=0;i<=32;i++){lcd_set_dot(0,i,0);lcd_set_dot(60,i,0); }if(flag==3){lcd_pos(3,0);i=0;while(fjs[i]!='\0'){wr_d_lcd(fjs[i]);i++;}wr_d_lcd(length+48);}
}
void game_plane(void)
{uchar i,t=0,j=0;while(1){i=0;while(i<10){j=0;while(j<10){if(((zidan_x[i])==boom_x[j]) && ((zidan_y[i]-1==boom_y[j]) || (zidan_y[i]==boom_y[j])) && (zidan_x[i]!=0) && (zidan_y[i] != 0)){lcd_set_dot(zidan_x[i],zidan_y[i],1);lcd_set_dot(boom_x[j],boom_y[j],1);zidan_y[i]=0;zidan_x[i]=0;boom_x[j]=0;boom_y[j]=0;wr_i_lcd(0x30);length++;lcd_pos(3,3);if(length<10) wr_d_lcd(length+48);else{wr_d_lcd(length/10+48);wr_d_lcd(length%10+48);}} j++;}i++;}i=0;while(i<10){j=0;while(j<10){if(((zidan_x[i])==boom_x[j]) && (zidan_y[i]-1==boom_y[j]) && (zidan_x[i]!=0) && (zidan_y[i] != 0)){lcd_set_dot(zidan_x[i],zidan_y[i],1);lcd_set_dot(boom_x[j],boom_y[j],1);zidan_y[i]=0;zidan_x[i]=0;boom_x[j]=0;boom_y[j]=0;} j++;}if((boom_x[i] ==planex[0]&& boom_y[i] ==29) || (boom_x[i] ==planex[1]&& boom_y[i] ==29) || ((boom_x[i] ==(planex[1]+planex[0])/2) && boom_y[i] ==29) ){die();break; }i++;}if(i!=10) break;i=0;t++;ran2=TL1%59;while(i<10){if(zidan_x[i]!=0){if(zidan_y[i]==1){lcd_set_dot(zidan_x[i],zidan_y[i],1);zidan_y[i]=0;zidan_x[i]=0;}else{ lcd_set_dot(zidan_x[i],zidan_y[i],1); busy_lcd();zidan_y[i]--;lcd_set_dot(zidan_x[i],zidan_y[i],0);} } i++;}i=0;while(boom_x[i]!=0) i++;if(t==4){t=0;boom_x[i]=ran2+1;boom_y[i]=1;}i=0;while(i<10){if(boom_x[i]!=0){if(boom_y[i]==30){lcd_set_dot(boom_x[i],boom_y[i],1);boom_y[i]=0;boom_x[i]=0; }else{ lcd_set_dot(boom_x[i],boom_y[i],1); busy_lcd();boom_y[i]++;lcd_set_dot(boom_x[i],boom_y[i],0); }}i++;} for(i=0;i<200;i++)for(j=0;j<50;j++){ // if(i==70) loud=1;time_1ms(1000);} }
}
void picture(void)
{img_disp(tab1);while(1){if(p!=px){if(px==4){flag=0;break;}clear_pic();p=px;if(p==1) img_disp(tab1);// else if(p==2) img_disp(tab1); else if(p==2) img_disp(tab3); } }
}
void mouse_back(void)
{char i,j;clear_pic();wr_i_lcd(0x30);wr_i_lcd(0x01); for(i=0;i<4;i++)for(j=0;j<4;j++){lcd_pos(i,j);wr_d_lcd('o'); }i=0;lcd_pos(1,4);while(mouse[i]!='\0'){wr_d_lcd(mouse[i]);i++; }wr_d_lcd(length+48);
}
2.5. 显示与输入
lcd_pos()
:设置 LCD 的显示位置,用于更新屏幕上的字符位置。lcd_set_dot()
:在特定坐标设置点,用于绘制图形或游戏元素。clear_pic()
:清除屏幕内容,为下一次显示做好准备。
void game_mouse(void)
{uchar i,j,k;k=0;length=0;while(1){ran1=TH1%4;ran2=TL1%4;lcd_pos(ran1,ran2);wr_d_lcd(0x02);dishu=1;loud=0;for(i=0;i<200;i++)for(j=0;j<200-k;j++){ if(i==70) loud=1;time_1ms(1000);}if(k<150) k+=10;if(dishu==1){die(); break;}lcd_pos(1,7); length++;if(length<10) wr_d_lcd(length+48);else{wr_d_lcd(length/10+48); wr_d_lcd(length%10+48); }lcd_pos(ran1,ran2);wr_d_lcd('o');}
}
2.6. 中断处理
T0_()
:中断服务程序,用于处理按键输入,根据按键的不同改变游戏状态或控制游戏行为。包括方向键和确认键的处理。
void T0_() interrupt 3
{/*P2 ÉÏ 0x04 ÏÂ 0x10 ×ó 0x01 ÓÒ 0x40 È·ÈÏ 0x80 */uchar temp,i,j;if(flag==1||flag==2){TH1=(65536-30000)/256;TL1=(65536-30000)%256;}else{TH1=(65536-200)/256;TL1=(65536-200)%256; }P2=0x00;temp=P2;if(temp!=0x00){time_1ms(20);if(temp!=0x00){ switch(temp){case 0x04:{loud=0;if(flag==0){lcd_pos(slect,0);wr_d_lcd(' ');if(slect==0) slect==3;else slect--;lcd_pos(slect,0);wr_d_lcd('o');while(P2!=0x00);time_1ms(50); } else if(flag==1)if(snake.dir!=2) snake.dir=3;loud=1;}break;case 0x10:{loud=0;if(flag==0){lcd_pos(slect,0);wr_d_lcd(' ');if(slect==3) slect==0;else slect++;lcd_pos(slect,0);wr_d_lcd('o');while(P2!=0x00);time_1ms(20);}else if(flag==1)if(snake.dir!=3) snake.dir=2;loud=1;}break; case 0x01:{loud=0;if(flag==5){if(choose != 0){lcd_pos(3,0);wr_d_lcd('o');lcd_pos(3,6);wr_d_lcd(' ');choose=0;while(P2!=0x00);time_1ms(20); } } else if(flag==1){if(snake.dir!=0) snake.dir=1;}else if(flag==3){if(planex[0]>1) {planex[0]--;planex[1]--;lcd_set_dot(planex[0],30,0); busy_lcd();if((planex[0]-1)!=0) lcd_set_dot(planex[0]-1,30,1); busy_lcd();lcd_set_dot(planex[1],30,0); busy_lcd();if((planex[1]+1)!=113) lcd_set_dot(planex[1]+1,30,1); busy_lcd();lcd_set_dot((planex[0]+planex[1])/2,29,0); busy_lcd();lcd_set_dot((planex[0]+planex[1])/2+1,29,1); busy_lcd();while(P2!=0x00){i=0;while(i<10){j=0;while(j<10){if(((zidan_x[i])==boom_x[j]) && (zidan_y[i]-1==boom_y[j]) && (zidan_x[i]!=0) && (zidan_y[i] != 0)){lcd_set_dot(zidan_x[i],zidan_y[i],1);lcd_set_dot(boom_x[j],boom_y[j],1);zidan_y[i]=0;zidan_x[i]=0;boom_x[j]=0;boom_y[j]=0;wr_i_lcd(0x30);length++;lcd_pos(3,3);if(length<10) wr_d_lcd(length+48);else{wr_d_lcd(length/10+48);wr_d_lcd(length%10+48);}} j++;}i++;} }time_1ms(20);} }else if(flag==4){if(px>1) px--;else if(px==1) px=2;while(P2!=0x00);time_1ms(20);}loud=1; }break; case 0x40:{loud=0;if(flag==5){if(choose != 1){lcd_pos(3,6);wr_d_lcd('o');lcd_pos(3,0);wr_d_lcd(' ');choose=1;while(P2!=0x00);time_1ms(20); } } else if(flag==1){if(snake.dir!=1) snake.dir=0;}else if(flag==3){if(planex[1]<59){planex[1]++;planex[0]++;lcd_set_dot(planex[0],30,0); busy_lcd();if((planex[0]-1)!=0) lcd_set_dot(planex[0]-1,30,1); busy_lcd();lcd_set_dot(planex[1],30,0); busy_lcd();if((planex[1]+1)!=60) lcd_set_dot(planex[1]+1,30,1); busy_lcd();lcd_set_dot((planex[0]+planex[1])/2,29,0); busy_lcd();lcd_set_dot((planex[0]+planex[1])/2-1,29,1); busy_lcd();while(P2!=0x00){i=0;while(i<10){j=0;while(j<10){if(((zidan_x[i])==boom_x[j]) && (zidan_y[i]-1==boom_y[j]) && (zidan_x[i]!=0) && (zidan_y[i] != 0)){lcd_set_dot(zidan_x[i],zidan_y[i],1);lcd_set_dot(boom_x[j],boom_y[j],1);zidan_y[i]=0;zidan_x[i]=0;boom_x[j]=0;boom_y[j]=0;wr_i_lcd(0x30);length++;lcd_pos(3,3);if(length<10) wr_d_lcd(length+48);else{wr_d_lcd(length/10+48);wr_d_lcd(length%10+48);}} j++;}i++;}}time_1ms(20);} }else if(flag==4){if(px<2) px++;else if(px==2) px=1;while(P2!=0x00);time_1ms(20);}loud=1; }break; case 0x80: {loud=0;if(flag==0){if(slect==0) flag=1;else if(slect==1) flag=2;else if(slect==2) flag=3;else if(slect==3) flag=4;while(P2!=0x00);time_1ms(20);}else if(flag==5){if(choose==0){flag=slect+1;}else if(choose==1) flag=0;choose=0;while(P2!=0x00);time_1ms(20);}else if(flag==3){i=0;while(zidan_x[i]!=0) i++;zidan_x[i]=planex[0]+1;zidan_y[i]=28;while(P2!=0x00);time_1ms(100); } else if(flag==4){px=4;while(P2!=0x00);time_1ms(100);}loud=1; }break; }} }/*0x77 0xb7 0xd7 0xe70x7b 0xbb 0xdb 0xeb0x7d 0xbd 0xdd 0xed0x7e 0xbe 0xde 0xee */P0=0xf7;temp=P0;if(temp!=0xf7){time_1ms(20);if(temp!=0xf7){switch(temp){case 0x77:{if(ran1==0&&ran2==0) dishu=0; }break;case 0xb7:{if(ran1==0&&ran2==1) dishu=0; }break;case 0xd7:{if(ran1==0&&ran2==2) dishu=0;}break;case 0xe7:{if(ran1==0&&ran2==3) dishu=0; }break;}// while(P0!=0xf7);time_1ms(20);}}P0=0xfb;temp=P0;if(temp!=0xfb){time_1ms(20);if(temp!=0xfb){switch(temp){case 0x7b:{if(ran1==1&&ran2==0) dishu=0; }break;case 0xbb:{if(ran1==1&&ran2==1) dishu=0; }break;case 0xdb:{if(ran1==1&&ran2==2) dishu=0;}break;case 0xeb:{if(ran1==1&&ran2==3) dishu=0; }break;}// while(P0!=0xfb);time_1ms(20);}}P0=0xfd;temp=P0;if(temp!=0xfd){time_1ms(20);if(temp!=0xfd){switch(temp){case 0x7d:{if(ran1==2&&ran2==0) dishu=0; }break;case 0xbd:{if(ran1==2&&ran2==1) dishu=0; }break;case 0xdd:{if(ran1==2&&ran2==2) dishu=0;}break;case 0xed:{if(ran1==2&&ran2==3) dishu=0; }break;}// while(P0!=0xfd);time_1ms(20);}}P0=0xfe;temp=P0;if(temp!=0xfe){time_1ms(20);if(temp!=0xfe){switch(temp){case 0x7e:{ if(ran1==3&&ran2==0) dishu=0; }break;case 0xbe:{if(ran1==3&&ran2==1) dishu=0; }break;case 0xde:{if(ran1==3&&ran2==2) dishu=0; }break;case 0xee:{if(ran1==3&&ran2==3) dishu=0; }break;}// while(P0!=0xfe);time_1ms(20);}}
}
2.7. 主函数
main()
:程序的入口,初始化 LCD,显示起始画面,进入主循环。根据不同的flag
值进入相应的游戏模块或功能模块。
void main()
{char i=0,j;loud=1;init_lcd(); //³õʼ»¯img_disp(tab1); //ÏÔʾͼÏñdelaynms(1000);wr_i_lcd(0x30);wr_i_lcd(0x01);lcd_pos(1,1);while(name[i]!='\0') {wr_d_lcd(name[i]);i++;if(i%2!=0)loud=0;delay(20000);}clear_pic();wr_i_lcd(0x30);wr_i_lcd(0x01);TMOD=0x10;TH1=(65536-8000)/256;TL1=(65536-8000)%256;EA=1; ET1=1;TR1=1;while(1){clear_pic();wr_i_lcd(0x30);wr_i_lcd(0x01);cata();if(flag==1){while(flag==1){food=0;back_ground();snake_init();game_snake();} }if(flag==2){ while(flag==2){/*Ò»ÅÅ16¸öµØÊó00 01 02 0310 11 12 1320 21 22 2330 31 32 33*/length=0;mouse_back();game_mouse();} }if(flag==3){while(flag==3){length=0;back_ground();plane_init();game_plane();} }if(flag==4){px=1;p=1;clear_pic();wr_i_lcd(0x30);wr_i_lcd(0x01);picture(); }}}
2.8. 源码整合:
#include "includes.h"
#include "intrins.h"
#include "stdlib.h"// 方向代码:0 右,1 左,2 上,3 下
// P2 端口:上 0x04,下 0x10,左 0x01,右 0x40,确认 0x80uchar code name[] = {"小龟游戏机"};
uchar code game1[] = {"贪吃蛇"};
uchar code game2[] = {"打地鼠"};
uchar code game3[] = {"飞机大战"};
uchar code game4[] = {"图片"};
uchar code loser[] = {"你输了"};
uchar code lxy1[] = {"继续"};
uchar code lxy2[] = {"返回"};
uchar code cj[] = {"长度: "};
uchar code fjs[] = {"飞机: "};
char code mouse[] = {"老鼠: "};// 全局变量定义
uchar p = 1, px = 1;
uchar length = 9;
uchar flag = 0, slect = 0, choose = 0;
uchar ran1, ran2, food = 0;
uchar planex[3] = {28, 30, 0};
uchar zidan_x[10] = {0}, zidan_y[10] = {0};
uchar boom_x[10] = {0}, boom_y[10] = {0};
char dishu = 0;sbit loud = P3^7; // 定义蜂鸣器的开关// 贪吃蛇的结构体
struct xx {char x[22];char y[22];char head;char tail;char dir;
} snake;// 贪吃蛇初始化函数
void snake_init(void) {lcd_set_dot(1, 1, 0);lcd_set_dot(2, 1, 0);lcd_set_dot(3, 1, 0);lcd_set_dot(4, 1, 0);lcd_set_dot(5, 1, 0);lcd_set_dot(6, 1, 0);snake.x[0] = 1; snake.y[0] = 1;snake.x[1] = 2; snake.y[1] = 1;snake.x[2] = 3; snake.y[2] = 1;snake.x[3] = 4; snake.y[3] = 1;snake.x[4] = 5; snake.y[4] = 1;snake.x[5] = 6; snake.y[5] = 1;snake.head = 5;snake.tail = 0;snake.dir = 0;
}// 游戏失败处理函数
void die(void) {uchar i;loud = 0; // 关闭蜂鸣器flag = 5; // 设置标志为5,表示游戏结束clear_pic();wr_i_lcd(0x30);wr_i_lcd(0x01);i = 0;lcd_pos(0, 2);while (loser[i] != '\0') {wr_d_lcd(loser[i]);i++;}i = 0;lcd_pos(2, 0); while (lxy1[i] != '\0') {wr_d_lcd(lxy1[i]);i++; }i = 0;lcd_pos(2, 5); while (lxy2[i] != '\0') {wr_d_lcd(lxy2[i]);i++; }lcd_pos(3, 0); wr_d_lcd('o');delay(1000);loud = 1; // 打开蜂鸣器while (flag == 5);
}// 游戏菜单选择
uchar cata(void) {uchar i;i = 0;slect = 0;lcd_pos(0, 0);wr_d_lcd('o');lcd_pos(0, 1);while (game1[i] != '\0') {wr_d_lcd(game1[i]);i++;}lcd_pos(1, 1);i = 0;while (game3[i] != '\0') {wr_d_lcd(game2[i]);i++;}lcd_pos(2, 1);i = 0;while (game3[i] != '\0') {wr_d_lcd(game3[i]);i++;}lcd_pos(3, 1);i = 0;while (game4[i] != '\0') {wr_d_lcd(game4[i]);i++;}while (flag == 0);
}// 贪吃蛇游戏逻辑
void game_snake(void) {uchar i, k, j;k = 0;food = 0;length = 6;lcd_pos(3, 0);i = 0;while (cj[i] != '\0') {wr_d_lcd(cj[i]);i++; }wr_d_lcd(length + 48);while (1) {if (food == 0) {while (1) { // 确保生成的食物不在蛇身上ran1 = TH1 % 59 + 1;ran2 = TL1 % 30 + 1;food = 1;i = snake.tail;while (i != snake.head) {if (snake.x[i] == ran1 && snake.y[i] == ran2) {break;}i++;if (i == 22) i = 0;if (i == snake.head) {if (snake.x[i] == ran1 && snake.y[i] == ran2) {i = snake.head - 1;break;}} }if (i == snake.head) break; }lcd_set_dot(ran1, ran2, 0);}i = snake.tail;while (i != snake.head) { // 蛇碰到自己if (snake.x[i] == snake.x[snake.head] && snake.y[i] == snake.y[snake.head]) {die(); break; }i++;if (i == 22) i = 0;}if (i != snake.head) break;if (snake.x[snake.head] >= 60 || snake.y[snake.head] >= 30 || snake.x[snake.head] == 0 || snake.y[snake.head] == 0) { // 蛇撞墙die(); break; }snake.head++;if (snake.head == 22) {snake.head = 0;if (snake.dir == 0) { // 右snake.x[snake.head] = snake.x[21] + 1;snake.y[snake.head] = snake.y[21]; } else if (snake.dir == 1) { // 左snake.x[snake.head] = snake.x[21] - 1;snake.y[snake.head] = snake.y[21]; } else if (snake.dir == 2) { // 下snake.x[snake.head] = snake.x[21];snake.y[snake.head] = snake.y[21] + 1;} else if (snake.dir == 3) { // 上snake.x[snake.head] = snake.x[21];snake.y[snake.head] = snake.y[21] - 1; } } else {if (snake.dir == 0) {snake.x[snake.head] = snake.x[snake.head - 1] + 1;snake.y[snake.head] = snake.y[snake.head - 1]; } else if (snake.dir == 1) {snake.x[snake.head] = snake.x[snake.head - 1] - 1;snake.y[snake.head] = snake.y[snake.head - 1]; } else if (snake.dir == 2) {snake.x[snake.head] = snake.x[snake.head - 1];snake.y[snake.head] = snake.y[snake.head - 1] + 1;} else if (snake.dir == 3) {snake.x[snake.head] = snake.x[snake.head - 1];snake.y[snake.head] = snake.y[snake.head - 1] - 1;}}lcd_set_dot(snake.x[snake.head], snake.y[snake.head], 0);if (snake.x[snake.head] != ran1 || snake.y[snake.head] != ran2) { // 蛇没有吃到食物lcd_set_dot(snake.x[snake.tail], snake.y[snake.tail], 1);snake.tail++;if (snake.tail == 22) snake.tail = 0;} else { // 蛇吃到食物if (k < 90) k += 10;loud = 0;length++;wr_i_lcd(0x30);// wr_i_lcd(0x01);lcd_pos(3, 3);wr_d_lcd(length / 10 + 48);wr_d_lcd(length % 10 + 48);food = 0;}for (i = 0; i < 100; i++)for (j = 0; j < 100 - k; j++) time_1ms(1000);loud = 1; }
}// 飞机初始化函数
void plane_init(void) {uchar i;lcd_set_dot(29, 29, 0);lcd_set_dot(28, 30, 0);lcd_set_dot(30, 30, 0);lcd_set_dot(29, 30, 0);for (i = 0; i < 10; i++) {zidan_x[i] = 0;zidan_y[i] = 0;boom_x[i] = 0;boom_y[i] = 0; }planex[0] = 28; planex[1] = 30;
}// 游戏背景绘制
void back_ground(void) {uchar i;clear_pic();wr_i_lcd(0x30);wr_i_lcd(0x01);for (i = 0; i <= 60; i++) { lcd_set_dot(i, 0, 0);lcd_set_dot(i, 31, 0); }for (i = 0; i <= 32; i++) {lcd_set_dot(0, i, 0);lcd_set_dot(60, i, 0); }if (flag == 3) {lcd_pos(3, 0);i = 0;while (fjs[i] != '\0') {wr_d_lcd(fjs[i]);i++;}wr_d_lcd(length + 48);}
}// 飞机大战游戏逻辑
void game_plane(void) {uchar i, t = 0, j = 0;while (1) {i = 0;while (i < 10) {j = 0;while (j < 10) {if (((zidan_x[i]) == boom_x[j]) && ((zidan_y[i] - 1 == boom_y[j]) || (zidan_y[i] == boom_y[j])) && (zidan_x[i] != 0) && (zidan_y[i] != 0)) {lcd_set_dot(zidan_x[i], zidan_y[i], 1);lcd_set_dot(boom_x[j], boom_y[j], 1);zidan_y[i] = 0;zidan_x[i] = 0;boom_x[j] = 0;boom_y[j] = 0;wr_i_lcd(0x30);length++;lcd_pos(3, 3);if (length < 10) wr_d_lcd(length + 48);else {wr_d_lcd(length / 10 + 48);wr_d_lcd(length % 10 + 48);}} j++;}i++;}i = 0;while (i < 10) {j = 0;while (j < 10) {if (((zidan_x[i]) == boom_x[j]) && (zidan_y[i] - 1 == boom_y[j]) && (zidan_x[i] != 0) && (zidan_y[i] != 0)) {lcd_set_dot(zidan_x[i], zidan_y[i], 1);lcd_set_dot(boom_x[j], boom_y[j], 1);zidan_y[i] = 0;zidan_x[i] = 0;boom_x[j] = 0;boom_y[j] = 0;} j++;}if ((boom_x[i] == planex[0] && boom_y[i] == 29) || (boom_x[i] == planex[1] && boom_y[i] == 29) || ((boom_x[i] == (planex[1] + planex[0]) / 2) && boom_y[i] == 29)) {die();break; }i++;}if (i != 10) break;i = 0;t++;ran2 = TL1 % 59;while (i < 10) {if (zidan_x[i] != 0) {if (zidan_y[i] == 1) {lcd_set_dot(zidan_x[i], zidan_y[i], 1);zidan_y[i] = 0;zidan_x[i] = 0;} else { lcd_set_dot(zidan_x[i], zidan_y[i], 1); busy_lcd();zidan_y[i]--;lcd_set_dot(zidan_x[i], zidan_y[i], 0);} } i++;}i = 0;while (boom_x[i] != 0) i++;if (t == 4) {t = 0;boom_x[i] = ran2 + 1;boom_y[i] = 1;}i = 0;while (i < 10) {if (boom_x[i] != 0) {if (boom_y[i] == 30) {lcd_set_dot(boom_x[i], boom_y[i], 1);boom_y[i] = 0;boom_x[i] = 0; } else { lcd_set_dot(boom_x[i], boom_y[i], 1); busy_lcd();boom_y[i]++;lcd_set_dot(boom_x[i], boom_y[i], 0); }}i++;} for (i = 0; i < 200; i++)for (j = 0; j < 50; j++) { // if (i == 70) loud = 1;time_1ms(1000);} }
}// 图片显示函数
void picture(void) {img_disp(tab1);while (1) {if (p != px) {if (px == 4) {flag = 0;break;}clear_pic();p = px;if (p == 1) img_disp(tab1);// else if (p == 2) img_disp(tab1); else if (p == 2) img_disp(tab3); } }
}// 打地鼠背景初始化
void mouse_back(void) {char i, j;clear_pic();wr_i_lcd(0x30);wr_i_lcd(0x01); for (i = 0; i < 4; i++)for (j = 0; j < 4; j++) {lcd_pos(i, j);wr_d_lcd('o'); }i = 0;lcd_pos(1, 4);while (mouse[i] != '\0') {wr_d_lcd(mouse[i]);i++; }wr_d_lcd(length + 48);
}// 打地鼠游戏逻辑
void game_mouse(void) {uchar i, j, k;k = 0;length = 0;while (1) {ran1 = TH1 % 4;ran2 = TL1 % 4;lcd_pos(ran1, ran2);wr_d_lcd(0x02);dishu = 1;loud = 0;for (i = 0; i < 200; i++)for (j = 0; j < 200 - k; j++) { if (i == 70) loud = 1;time_1ms(1000);}if (k < 150) k += 10;if (dishu == 1) {die(); break;}lcd_pos(1, 7); length++;if (length < 10) wr_d_lcd(length + 48);else {wr_d_lcd(length / 10 + 48); wr_d_lcd(length % 10 + 48); }lcd_pos(ran1, ran2);wr_d_lcd('o');}
}// 主函数入口
void main() {char i = 0, j;loud = 1;init_lcd(); // 初始化LCDimg_disp(tab1); // 显示图片delaynms(1000);wr_i_lcd(0x30);wr_i_lcd(0x01);lcd_pos(1, 1);while (name[i] != '\0') {wr_d_lcd(name[i]);i++;if (i % 2 != 0) loud = 0;delay(20000);}clear_pic();wr_i_lcd(0x30);wr_i_lcd(0x01);TMOD = 0x10;TH1 = (65536 - 8000) / 256;TL1 = (65536 - 8000) % 256;EA = 1; ET1 = 1;TR1 = 1;while (1) {clear_pic();wr_i_lcd(0x30);wr_i_lcd(0x01);cata();if (flag == 1) {while (flag == 1) {food = 0;back_ground();snake_init();game_snake();} }if (flag == 2) { while (flag == 2) {/* 一个4x4的打地鼠格子00 01 02 0310 11 12 1320 21 22 2330 31 32 33 */length = 0;mouse_back();game_mouse();} }if (flag == 3) {while (flag == 3) {length = 0;back_ground();plane_init();game_plane();} }if (flag == 4) {px = 1;p = 1;clear_pic();wr_i_lcd(0x30);wr_i_lcd(0x01);picture(); }}
}// 中断处理函数
void T0_() interrupt 3 {/* P2 上 0x04 下 0x10 左 0x01 右 0x40 确认 0x80 */uchar temp, i, j;if (flag == 1 || flag == 2) {TH1 = (65536 - 30000) / 256;TL1 = (65536 - 30000) % 256;} else {TH1 = (65536 - 200) / 256;TL1 = (65536 - 200) % 256; }P2 = 0x00; // 初始化P2为0temp = P2;if (temp != 0x00) {time_1ms(20);if (temp != 0x00) { switch (temp) {case 0x04: { // 上键loud = 0;if (flag == 0) { // 菜单导航lcd_pos(slect, 0);wr_d_lcd(' ');if (slect == 0) slect = 3;else slect--;lcd_pos(slect, 0);wr_d_lcd('o');while (P2 != 0x00);time_1ms(50); } else if (flag == 1) { // 贪吃蛇游戏中if (snake.dir != 2) snake.dir = 3;}loud = 1;} break;case 0x10: { // 下键loud = 0;if (flag == 0) { // 菜单导航lcd_pos(slect, 0);wr_d_lcd(' ');if (slect == 3) slect = 0;else slect++;lcd_pos(slect, 0);wr_d_lcd('o');while (P2 != 0x00);time_1ms(20);} else if (flag == 1) { // 贪吃蛇游戏中if (snake.dir != 3) snake.dir = 2;}loud = 1;} break; case 0x01: { // 左键loud = 0;if (flag == 5) { // 游戏失败后if (choose != 0) {lcd_pos(3, 0);wr_d_lcd('o');lcd_pos(3, 6);wr_d_lcd(' ');choose = 0;while (P2 != 0x00);time_1ms(20); } } else if (flag == 1) { // 贪吃蛇游戏中if (snake.dir != 0) snake.dir = 1;} else if (flag == 3) { // 飞机大战游戏中if (planex[0] > 1) {planex[0]--;planex[1]--;lcd_set_dot(planex[0], 30, 0); busy_lcd();if ((planex[0] - 1) != 0) lcd_set_dot(planex[0] - 1, 30, 1); busy_lcd();lcd_set_dot(planex[1], 30, 0); busy_lcd();if ((planex[1] + 1) != 113) lcd_set_dot(planex[1] + 1, 30, 1); busy_lcd();lcd_set_dot((planex[0] + planex[1]) / 2, 29, 0); busy_lcd();lcd_set_dot((planex[0] + planex[1]) / 2 + 1, 29, 1); busy_lcd();while (P2 != 0x00) {i = 0;while (i < 10) {j = 0;while (j < 10) {if (((zidan_x[i]) == boom_x[j]) && (zidan_y[i] - 1 == boom_y[j]) && (zidan_x[i] != 0) && (zidan_y[i] != 0)) {lcd_set_dot(zidan_x[i], zidan_y[i], 1);lcd_set_dot(boom_x[j], boom_y[j], 1);zidan_y[i] = 0;zidan_x[i] = 0;boom_x[j] = 0;boom_y[j] = 0;wr_i_lcd(0x30);length++;lcd_pos(3, 3);if (length < 10) wr_d_lcd(length + 48);else {wr_d_lcd(length / 10 + 48);wr_d_lcd(length % 10 + 48);}} j++;}i++;} }time_1ms(20);} } else if (flag == 4) { // 图片显示中if (px > 1) px--;else if (px == 1) px = 2;while (P2 != 0x00);time_1ms(20);}loud = 1; } break; case 0x40: { // 右键loud = 0;if (flag == 5) { // 游戏失败后if (choose != 1) {lcd_pos(3, 6);wr_d_lcd('o');lcd_pos(3, 0);wr_d_lcd(' ');choose = 1;while (P2 != 0x00);time_1ms(20); } } else if (flag == 1) { // 贪吃蛇游戏中if (snake.dir != 1) snake.dir = 0;} else if (flag == 3) { // 飞机大战游戏中if (planex[1] < 59) {planex[1]++;planex[0]++;lcd_set_dot(planex[0], 30, 0); busy_lcd();if ((planex[0] - 1) != 0) lcd_set_dot(planex[0] - 1, 30, 1); busy_lcd();lcd_set_dot(planex[1], 30, 0); busy_lcd();if ((planex[1] + 1) != 60) lcd_set_dot(planex[1] + 1, 30, 1); busy_lcd();lcd_set_dot((planex[0] + planex[1]) / 2, 29, 0); busy_lcd();lcd_set_dot((planex[0] + planex[1]) / 2 - 1, 29, 1); busy_lcd();while (P2 != 0x00) {i = 0;while (i < 10) {j = 0;while (j < 10) {if (((zidan_x[i]) == boom_x[j]) && (zidan_y[i] - 1 == boom_y[j]) && (zidan_x[i] != 0) && (zidan_y[i] != 0)) {lcd_set_dot(zidan_x[i], zidan_y[i], 1);lcd_set_dot(boom_x[j], boom_y[j], 1);zidan_y[i] = 0;zidan_x[i] = 0;boom_x[j] = 0;boom_y[j] = 0;wr_i_lcd(0x30);length++;lcd_pos(3, 3);if (length < 10) wr_d_lcd(length + 48);else {wr_d_lcd(length / 10 + 48);wr_d_lcd(length % 10 + 48);}} j++;}i++;}}time_1ms(20);} } else if (flag == 4) { // 图片显示中if (px < 2) px++;else if (px == 2) px = 1;while (P2 != 0x00);time_1ms(20);}loud = 1; } break; case 0x80: { // 确认键loud = 0;if (flag == 0) { // 在菜单中选择游戏if (slect == 0) flag = 1;else if (slect == 1) flag = 2;else if (slect == 2) flag = 3;else if (slect == 3) flag = 4;while (P2 != 0x00);time_1ms(20);} else if (flag == 5) { // 游戏失败后选择继续或返回if (choose == 0) {flag = slect + 1;} else if (choose == 1) flag = 0;choose = 0;while (P2 != 0x00);time_1ms(20);} else if (flag == 3) { // 飞机大战游戏中i = 0;while (zidan_x[i] != 0) i++;zidan_x[i] = planex[0] + 1;zidan_y[i] = 28;while (P2 != 0x00);time_1ms(100); } else if (flag == 4) { // 图片显示中px = 4;while (P2 != 0x00);time_1ms(100);}loud = 1; } break; }} }// 检测P0口的输入,判断打地鼠游戏中的鼠标位置P0 = 0xf7;temp = P0;if (temp != 0xf7) {time_1ms(20);if (temp != 0xf7) {switch (temp) {case 0x77: {if (ran1 == 0 && ran2 == 0) dishu = 0; } break;case 0xb7: {if (ran1 == 0 && ran2 == 1) dishu = 0; } break;case 0xd7: {if (ran1 == 0 && ran2 == 2) dishu = 0;} break;case 0xe7: {if (ran1 == 0 && ran2 == 3) dishu = 0; } break;}time_1ms(20);}}P0 = 0xfb;temp = P0;if (temp != 0xfb) {time_1ms(20);if (temp != 0xfb) {switch (temp) {case 0x7b: {if (ran1 == 1 && ran2 == 0) dishu = 0; } break;case 0xbb: {if (ran1 == 1 && ran2 == 1) dishu = 0; } break;case 0xdb: {if (ran1 == 1 && ran2 == 2) dishu = 0;} break;case 0xeb: {if (ran1 == 1 && ran2 == 3) dishu = 0; } break;}time_1ms(20);}}P0 = 0xfd;temp = P0;if (temp != 0xfd) {time_1ms(20);if (temp != 0xfd) {switch (temp) {case 0x7d: {if (ran1 == 2 && ran2 == 0) dishu = 0; } break;case 0xbd: {if (ran1 == 2 && ran2 == 1) dishu = 0; } break;case 0xdd: {if (ran1 == 2 && ran2 == 2) dishu = 0;} break;case 0xed: {if (ran1 == 2 && ran2 == 3) dishu = 0; } break;}time_1ms(20);}}P0 = 0xfe;temp = P0;if (temp != 0xfe) {time_1ms(20);if (temp != 0xfe) {switch (temp) {case 0x7e: { if (ran1 == 3 && ran2 == 0) dishu = 0; } break;case 0xbe: {if (ran1 == 3 && ran2 == 1) dishu = 0; } break;case 0xde: {if (ran1 == 3 && ran2 == 2) dishu = 0; } break;case 0xee: {if (ran1 == 3 && ran2 == 3) dishu = 0; } break;}time_1ms(20);}}
}