好久不写51单片机了,最近做了一个贪食蛇游戏机。
用到的模块包括:8x8点阵,LCD1602,矩阵键盘、红外遥控器、光敏电阻、EEPROM、电机驱动等
实现的功能包括:投币检测,红外、键盘、上位机三种操作模式、最高分记录、震动反馈等
在B站传了一个验收时候的视频,所有的功能的介绍都在视频里强烈建议直接戳视频看
强烈建议直接戳视频看
强烈建议直接戳视频看:
https://www.bilibili.com/video/BV1Ba4y1Y7oz?share_source=copy_web
上位机界面:
51是用keil建的工程,上位机是QT写的。附件包含了全部的代码,51和上位机都有嗷~
单片机源程序如下:
- #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();
- }
复制代码
所有资料51hei提供下载:
51单片机贪食蛇.rar
(18.36 MB, 下载次数: 32)
|