1.jpg


好久不写51单片机了,最近做了一个贪食蛇游戏机。

用到的模块包括:8x8点阵,LCD1602,矩阵键盘、红外遥控器、光敏电阻、EEPROM、电机驱动等
实现的功能包括:投币检测,红外、键盘、上位机三种操作模式、最高分记录、震动反馈等

在B站传了一个验收时候的视频,所有的功能的介绍都在视频里强烈建议直接戳视频看
强烈建议直接戳视频看
强烈建议直接戳视频看:
https://www.bilibili.com/video/BV1Ba4y1Y7oz?share_source=copy_web
上位机界面:
2.png

51是用keil建的工程,上位机是QT写的。附件包含了全部的代码,51和上位机都有嗷~
3.png

单片机源程序如下:
#include <reg52.h>
  • #include "typedef.h"
  • #include "LCD1602.h"
  • #include "LED88.h"
  • #include "key.h"
  • #include "IR.h"
  • #include "24c02.h"
  • #include "UART.h"
  • #include "XPT2046.h"
  • sfr  T2MOD = 0xc9;   //补充寄存器T2MOD定义
  • sbit motor = P2^2;   //电机接口
  • u8 time = 0;        //每50ms加一
  • u8 game_state = 0;  //指示游戏进行状态
  • u8 game_level = 1;  //游戏难度等级。每吃到八个点增加一级
  • u16 ADC_value = 0;  //光敏电阻采值
  • u8 control_mode = 0;//控制模式,1为板载按键,2为红外遥控
  • u8 score_record = 0;//最高分记录,保存在24c02中
  • extern u8 length;   //蛇身长度
  • extern u8 direction;//前进方向
  • extern u8 food_x;   //食物点坐标
  • extern u8 food_y;
  • extern u8 xdata snake_x[64];  //蛇身坐标
  • extern u8 xdata snake_y[64];
  • extern u8 key_value;    //按键操作
  • extern u8 IR_value;     //红外遥控按键操作
  • extern u8 irtime;       //红外遥控计时标志位
  • extern u8 UART_data;    //串口接收内容
  • sbit test = P1^1;
  • void main()
  • {
  •         motor = 0;     //关闭电机
  •         LCD1602_Init();//1602初始化
  •         I2C_init();    //I2C初始化
  •         LED88_clean(); //8x8点阵清屏
  •         //从24C02中读取游戏最高分记录
  •         score_record = I2C_ReceiveData(0);//从AT24C02第0个单元中读取数据
  •         //使能定时器2 50ms 用于贪食蛇、LCD闪烁等计时        
  •         T2MOD = 0;      //向上计数
  •     T2CON = 0;      //16位自动重装模式
  •     TL2 = 0x00;     //设置定时初值
  •     TH2 = 0x4C;
  •     RCAP2L = 0x00;  //设置定时重载值
  •     RCAP2H = 0x4C;
  •         ET2=1;
  •         TR2=1;
  •         //使能总中断开关
  •         EA = 1;        
  •         
  •         //刚上电时ADC读值不稳定,延迟2s启动
  •         LCD1602_Dis_Str(0, 0, "Welcome!");
  •                
  •         while(1)
  •         {
  •                 u8 i,last_state,flag;
  •                
  •                 test = ~test;
  •                
  •                 //模式更新第一次进入while时,flag会被置0
  •                 if(game_state!=last_state)
  •                         flag = 0;
  •                 else
  •                         flag = 1;
  •                 last_state = game_state;
  •                 switch(game_state)
  •                 {
  •                         //未开始游戏,需要投币
  •                         case 0:
  •                                 //AD采值要放在LCD刷新之前,否则受引脚共用的影响会出错
  •                                 ADC_value = (ADC_read(AD_CH0));
  •                                 //INSERT COIN每1s闪烁
  •                                 if(time/20%2==0)
  •                                         LCD1602_Dis_Str(0, 1, "INSERT COIN");
  •                                 else
  •                                         LCD1602_Dis_Str(0, 1, "           ");
  •                                 
  •                                 //如果光敏被遮住,认为投入硬币,开始游戏
  •                                 if(ADC_value>1500)
  •                                 {
  •                                         ADC_value = 0;
  •                                         LCD1602_clean(); //LCD清屏
  •                                         snake_food();    //随机初始化一个食物的位置
  •                                         game_state = 1;  //进入操作方式选择
  •                                 }
  •                                 else
  •                                         delay_ms(10);  //ADC采值不能过于频繁,否则会出错
  •                         break;
  •                                 
  •                         //选择操作方式
  •                         case 1:
  •                                 //通过flag变量确保以下内容只执行一次
  •                                 if(flag==0)
  •                                 {
  •                                         LCD1602_Dis_Str(0, 0,  "Select Ctrl Mode");
  •                                         LCD1602_Dis_Str(0, 1,  "1:Key 2:IR 3:PC");
  •                                 }
  •                                 
  •                                 key_scan();        //扫描矩阵键盘
  •                                 if(key_value==16)  //按S2选择板载键盘操作
  •                                 {
  •                                         control_mode = 1;
  •                                         key_value = 0;   //按键标志位清零
  •                                 }
  •                                 else if(key_value==17)  //按S3选择红外遥控器操作
  •                                 {
  •                                         control_mode = 2;
  •                                         key_value = 0;   //按键标志位清零
  •                                        
  •                                         //使能定时器1 用于红外遥控
  •                                         TMOD|= 0x20;
  •                                         TH1  = 0x00;
  •                                         TL1  = 0x00;
  •                                         ET1  = 1;
  •                                         TR1  = 1;   
  •                                         //使能外部中断 用于红外遥控
  •                                         IT0 = 1;
  •                                         EX0 = 1;
  •                                 }
  •                                 else if(key_value==18)
  •                                 {
  •                                         control_mode = 3;
  •                                         key_value = 0;   //按键标志位清零
  •                                        
  •                                         UART_init();
  •                                 }
  •                                 if(control_mode!=0)
  •                                 {
  •                                         //倒计时三秒开始
  •                                         LCD1602_clean();
  •                                         LCD1602_Dis_Str(8, 0, "3");
  •                                         delay_ms(1000);
  •                                         LCD1602_Dis_Str(8, 0, "2");
  •                                         delay_ms(1000);
  •                                         LCD1602_Dis_Str(8, 0, "1");
  •                                         delay_ms(1000);
  •                                        
  •                                         //LCD清屏,显示游戏界面信息
  •                                         LCD1602_clean();
  •                                         LCD1602_Dis_Str(0, 0,  "level:");
  •                                         LCD1602_Dis_Str(0, 1,  "score:");
  •                                        
  •                                         game_state = 2;  //进入游戏模式                                       
  •                                         time = 0;       //T0累加位清零
  •                                 }
  •                         break;
  •                         
  •                         //游戏进行中
  •                         case 2:                        
  •                                 //刷新游戏界面
  •                                 for(i=0; i<length; i++)
  •                                         LED88_point(snake_x[i], snake_y[i]);  //显示蛇
  •                                 LED88_point(food_x, food_y); //显示食物
  •                                 
  •                                 //获取用户操作
  •                                 if(control_mode==1)
  •                                         key_scan();   //扫描矩阵键盘
  •                                 else if(control_mode==2)
  •                                         IR_process(); //处理红外遥控
  • //                                else if(control_mode==3)
  • //                                        UART_receive();
  •                                 
  •                                 //手动调节难度等级
  •                                 if(key_value==18 || IR_value==18 || UART_data==18)
  •                                 {
  •                                         key_value = 20;
  •                                         IR_value  = 20;
  •                                         UART_data = 20;
  •                                         if(game_level<9)
  •                                                 game_level++;
  •                                         time = 0;
  •                                 }        
  •                                 else if(key_value==17 || IR_value==17 || UART_data==17)
  •                                 {
  •                                         key_value = 20;
  •                                         IR_value  = 20;
  •                                         UART_data = 20;
  •                                         if(game_level>1)
  •                                                 game_level--;
  •                                         time = 0;
  •                                 }        
  •                                 //显示难度等级和得分
  •                                 LCD1602_Dis_Str(7, 0, u8_to_str(game_level));
  •                                 LCD1602_Dis_Str(7, 1, u8_to_str((length-3)/10));
  •                                 LCD1602_Dis_Str(8, 1, u8_to_str((length-3)%10));
  •                                 //如果打破纪录,显示new record字样
  •                                 if((length-3)>score_record)
  •                                 {
  •                                         LCD1602_Dis_Str(11, 0, "new");
  •                                         LCD1602_Dis_Str(10, 1, "record");
  •                                 }
  •                                 //如果长度达到64,直接结束游戏
  •                                 if(length==64)
  •                                 {
  •                                         LCD1602_clean();
  •                                         LED88_clean();
  •                                         game_state = 4;
  •                                         time = 0;
  •                                 }
  •                                 //判断是否暂停
  •                                 if(key_value==16 || IR_value==16 || UART_data==16)  //按下暂停键
  •                                 {
  •                                         LCD1602_clean();
  •                                         LCD1602_Dis_Str(0, 0,  "-----Pause-----");
  •                                         game_state = 3;
  •                                         key_value = 20;     //清空按键值,否则会直接触发继续
  •                                         IR_value  = 20;
  •                                         UART_data = 20;
  •                                 }
  •                         break;
  •                         
  •                         //游戏暂停
  •                         case 3:
  •                                 ET0 = 0;  //关闭T0,蛇保持静止
  •                         
  •                                 //刷新游戏界面
  •                                 for(i=0; i<length; i++)
  •                                         LED88_point(snake_x[i], snake_y[i]);  //显示蛇
  •                                 LED88_point(food_x, food_y); //显示食物
  •                         
  •                                 //获取用户操作
  •                                 if(control_mode==1)
  •                                         key_scan();   //扫描矩阵键盘
  •                                 else if(control_mode==2)
  •                                         IR_process(); //处理红外遥控
  • //                                else if(control_mode==3)
  • //                                        UART_receive();
  •                         
  •                                 //判断是否继续
  •                                 if(key_value==16 || IR_value==16 || UART_data==16)   //按下继续键
  •                                 {
  •                                         LCD1602_clean(); //LCD清屏,显示游戏信息
  •                                         LCD1602_Dis_Str(0, 0,  "level:");
  •                                         LCD1602_Dis_Str(0, 1,  "score:");
  •                                         key_value = 20;  //清空按键值,否则会直接触发继续
  •                                         IR_value  = 20;
  •                                         UART_data = 20;
  •                                         game_state = 2;  //回到游戏状态(2)
  •                                         TH0 = 0x4C;
  •                                         TL0 = 0x00;
  •                                         ET0 = 1;
  •                                         time = 0;
  •                                 }               
  •                         break;
  •                         //死亡
  •                         case 4:
  •                                 //如果没有打破纪录
  •                                 if((length-3)<=score_record)
  •                                 {
  •                                         //GAME OVER闪烁
  •                                         if(time/20%2==0)
  •                                                 LCD1602_Dis_Str(0, 0, "GAME OVER");
  •                                         else
  •                                                 LCD1602_Dis_Str(0, 0, "         ");        
  •                                        
  •                                         //通过变量flag使得以下内容只执行一次
  •                                         if(flag==0)
  •                                         {
  •                                                 LCD1602_clean();
  •                                                 LED88_clean();
  •                                                 //显示得分
  •                                                 LCD1602_Dis_Str(0,  1, "Your score:");
  •                                                 LCD1602_Dis_Str(11, 1, u8_to_str((length-3)/10));
  •                                                 LCD1602_Dis_Str(12, 1, u8_to_str((length-3)%10));
  •                                         }
  •                                 }
  •                                 //如果打破纪录
  •                                 else
  •                                 {
  •                                         //通过变量flag使得以下内容只执行一次
  •                                         if(flag==0)
  •                                         {
  •                                                 LCD1602_clean();
  •                                                 LED88_clean();
  •                                                 //将新纪录保存至24c02
  •                                                 I2C_TransmitData(0, length-3);
  •                                                 //显示得分
  •                                                 LCD1602_Dis_Str(0,  1, "New record:");
  •                                                 LCD1602_Dis_Str(11, 1, u8_to_str((length-3)/10));
  •                                                 LCD1602_Dis_Str(12, 1, u8_to_str((length-3)%10));
  •                                         }
  •                                         //CONGRATULATIONS!闪烁
  •                                         if(time/20%2==0)
  •                                                 LCD1602_Dis_Str(0, 0, "CONGRATULATIONS!");
  •                                         else
  •                                                 LCD1602_Dis_Str(0, 0, "                ");
  •                                 }
  •                                 //按S5可以清除最高分记录
  •                                 key_scan();
  •                                 if(key_value==19)
  •                                 {
  •                                         I2C_TransmitData(0, 0);
  •                                         score_record = 0;
  •                                         LCD1602_clean();
  •                                         LCD1602_Dis_Str(0, 0, "Reset complete");
  •                                        
  •                                         key_value = 20;
  •                                         delay_ms(1000);
  •                                         time = 200; //清除完之后延迟一秒直接退回到投币界面
  •                                 }
  •                                 if(time==200)
  •                                 {
  •                                         LCD1602_clean();
  •                                         LCD1602_Dis_Str(0, 0, "Welcome!");
  •                                         score_record = length-3;
  •                                         control_mode = 0;
  •                                         game_state = 0;
  •                                         game_level = 1;
  •                                         length = 3;
  •                                         direction = 4;
  •                                         snake_x[0] = 2;
  •                                         snake_x[1] = 1;
  •                                         snake_x[2] = 0;
  •                                         snake_y[0] = 0;
  •                                         snake_y[1] = 0;
  •                                         snake_y[2] = 0;
  •                                         delay_ms(100);
  •                                 }
  •                         break;        
  •                 }        
  •         }
  • }
  • //定时器0中断服务函数
  • //void timer0_ISR(void) interrupt 1
  • //{
  • //}
  • //定时器1中断服务函数
  • void timer1_ISR(void) interrupt 3
  • {
  •         irtime++;  //用于计数2个下降沿之间的时间
  • }
  • //定时器2中断服务函数
  • void timer2_ISR(void) interrupt 5
  • {
  •         TF2 = 0;  //T2需要手动清除标志位
  •         
  •         time++;
  •         if(time==255)
  •                 time = 0;
  •         if(time==(18-game_level) && game_state==2)
  •         {
  •                 time = 0;
  •                 //计算移动
  •                 snake_move();
  •                 //判断是否吃到食物
  •                 snake_grow();
  •                 //吃一个食物点一盏LED
  •                 P1 = 0xff>>((length-3)%8);
  •                 //判断是否死亡
  •                 snake_death();               
  •         }
  • }
  • //外部中断0服务函数
  • void EX0_ISR(void) interrupt 0
  • {
  •         IR_interrupt_process();
  • }
  • 复制代码
    工程文件:
    4.png

    51单片机贪食蛇.rar (18.36 MB, 下载次数: 0)
    举报
    您需要登录后才可以评论 登录 立即注册
    全部回复 1
    • 103 主题
    • 405 帖子
    • 1765 积分
    身份:LV4 高级技术员
    E币:1701
    看看
    回复楼主
    您需要登录后才可以评论 登录 立即注册