好久不写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(); }
复制代码全部回复 1
- 103 主题
- 405 帖子
- 1765 积分
身份:LV4 高级技术员
E币:1701
发消息
看看