单片机制作“电子炸弹”(可实现安放与拆除功能)

灵感来源:
一位室友非常喜欢打"CS GO",经常因残局对炸弹的安放或拆除成功而高兴地大喊大叫(手动滑稽),于是我萌生了一个有趣的想法,用单片机来模拟出一个没有伤害的电子炸弹。
电子炸弹的具体功能:
首先用LCD1602来模拟电子炸弹的显示屏,在刚开始时,上边显示“Dismantle Bomb!”、“remote 6!”的字样,然后安放炸弹的人在输入“0748”后炸弹开始倒计时,允许输入错误无数次,直到输入成功为止。炸弹最多坚持30s,1s减一次,剩余的时间显示在屏幕的右下角。30s到20s之间,每2s响一次;20s到10s之间,每1s响一次;最后10s,每1s响2次。并且当响声的频率变快时,屏幕上会显示“COME ON!”的字样,然后再次恢复初始的显示。输入“0487”可以拆除炸弹,拆除成功后响两声,然后出现“Good job!”的字样。如果到时间未拆除或者连续三次密码输入失败,则拆除炸弹失败,会有5s中的长响,并且屏幕上有“LOSER!”的字样出现。
源代码:

#include <reg52.h>
#include <intrins.h>
#include <string.h>

typedef unsigned char uchar;
typedef unsigned int uint;

sbit DU=P2^6;
sbit WE=P2^7;//锁存器IO口位定义
sbit BEEP=P2^3;//蜂鸣器位定义
sbit RS=P3^5;
sbit RW=P3^6;
sbit EN=P3^4;//LCD1602对应管脚位定义

uchar code SMGwei[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//1-8位数码管
uchar code SMGduan[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};//0-9数字
uchar zifu1[]="Dismantle Bomb!";
uchar zifu2[]="remote  6!";
uchar zifu3[]="COME ON!";
uchar zifu4[]="LOSER!";
uchar zifu5[]="Good job!";//表示五个要显示的字符串
uchar key,wei,flag,flbg,ms,s=30,T,F,x;//flag、flbg、T、F为标志位,s为炸弹总倒计时30s,x来记录密码错误的次数
uchar tabel1[4],tabel2[4];//1用来储存输入的开炸弹密码,2用来储存输入的拆炸弹密码
uchar tabel3[16]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};//LCD1602第一行可见地址
uchar tabel4[16]={0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f};//LCD1602第二行可见地址
/******矩阵键盘输入模块******/
void delay(uint z)
{
	uint x,y;
	for(x=z;x>0;x--)
		for(y=114;y>0;y--);
}

void keyscan1()//矩阵键盘
{
	uchar temp;
	P3=0XFF;
	P3=0XFE;
	temp=P3&0XF0;
	if(temp!=0xf0)
	{
		delay(20);
		P3=0XFE;
		temp=P3&0XF0;
		if(temp!=0xf0)
		{
			temp=P3;
			switch(temp)
			{
				case 0xee:key=0;wei++;break;
				case 0xde:key=1;wei++;break;
				case 0xbe:key=2;wei++;break;
				case 0x7e:key=3;wei++;break;
			}
			while(temp!=0xf0)
			{
				temp=P3&0XF0;
			}
		}
	}
	P3=0XFF;
	P3=0XFD;
	temp=P3&0XF0;
	if(temp!=0xf0)
	{
		delay(20);
		P3=0XFD;
		temp=P3&0XF0;
		if(temp!=0xf0)
		{
			temp=P3;
			switch(temp)
			{
				case 0xed:key=4;wei++;break;
				case 0xdd:key=5;wei++;break;
				case 0xbd:key=6;wei++;break;
				case 0x7d:key=7;wei++;break;
			}
			while(temp!=0xf0)
			{
				temp=P3&0XF0;
			}
		}
	}
	P3=0XFF;
	P3=0XFB;
	temp=P3&0XF0;
	if(temp!=0xf0)
	{
		delay(20);
		P3=0XFB;
		temp=P3&0XF0;
		if(temp!=0xf0)
		{
			temp=P3;
			switch(temp)
			{
				case 0xeb:key=8;wei++;break;
				case 0xdb:key=9;wei++;break;
				case 0xbb:break;
				case 0x7b:break;
			}
			while(temp!=0xf0)
			{
				temp=P3&0XF0;
			}
		}
	}
	P3=0XFF;
	P3=0XF7;
	temp=P3&0XF0;
	if(temp!=0xf0)
	{
		delay(20);
		P3=0XF7;
		temp=P3&0XF0;
		if(temp!=0xf0)
		{
			temp=P3;
			switch(temp)
			{
				case 0xe7:break;
				case 0xd7:break;
				case 0xb7:break;
				case 0x77:break;
			}
			while(temp!=0xf0)
			{
				temp=P3&0XF0;
			}
		}
	}
}
/******储存打开炸弹的密码******/
void c1()
{
	if(wei==1)
		tabel1[0]=key;
	if(wei==2)
		tabel1[1]=key;
	if(wei==3)
		tabel1[2]=key;
	if(wei==4)
	{
		tabel1[3]=key;
		T=1;
	}
}
/******储存拆除炸弹的密码******/
void c2()
{
	if(wei==1)
		tabel2[0]=key;
	if(wei==2)
		tabel2[1]=key;
	if(wei==3)
		tabel2[2]=key;
	if(wei==4)
	{
		tabel2[3]=key;
		F=1;
	}
}
/******秒表计时******/
void Timer0Init()//50ms中断一次
{
	TMOD|=0X01;
	TH0=(65536-46082)/256;
	TL0=(65536-46082)%256;
	TR0=1;
	ET0=1;
}
/******模拟炸弹“滴滴”的响声******/
void didi()
{
	if((s<=30&&s>20)&&(s%2!=0))//炸弹30s倒计时,在20s之前2s响一次
	{
		BEEP=0;
		delay(50);
		BEEP=1;
	}
	if(s<=20&&s>10)//20s到10s之间1s响一次
	{
		BEEP=0;
		delay(50);
		BEEP=1;
	}
	if(s<=10&&s>0)//最后10s时1s响两次
	{
		BEEP=0;
		delay(25);
		BEEP=1;
		delay(100);
		BEEP=0;
		delay(25);
		BEEP=1;
	}
}
/********LCD1602********/
void Read_Busy()
{
	uchar busy;
	P0=0XFF;
	RS=0;
	RW=1;
	do
	{
		EN=1;
		busy=P0;
		EN=0;
	}while(busy&0x80);
}

void Write_cmd(uchar cmd)
{
	Read_Busy();
	RS=0;
	RW=0;
	P0=cmd;
	EN=1;
	EN=0;
}

void Write_dat(uchar dat)
{
	Read_Busy();
	RS=1;
	RW=0;
	P0=dat;
	EN=1;
	EN=0;
}

void LCD1602Init()										   
{
	Write_cmd(0x38);
	Write_cmd(0x0c);
	Write_cmd(0x01);
}

void LCDWrite1(uchar dat[])//在第一行写数据的函数
{
	uchar i=0;
	while(i<strlen(dat))
	{
		Write_cmd(tabel3[i]|0x80);
		delay(2);
		Write_dat(dat[i]);
		i++;
	}
}

void LCDWrite2(uchar dat[])//在第二行写数据的函数
{
	uchar i=0;
	while(i<strlen(dat))
	{
		Write_cmd(tabel4[i]|0x80);
		delay(2);
		Write_dat(dat[i]);
		i++;
	}
}

void main()
{
	Timer0Init();
	LCD1602Init();
	LCDWrite1(zifu1);
	LCDWrite2(zifu2);//一系列的初始化
	while(1)
	{
		if(flag==0)//炸弹未打开时
		{
			keyscan1();
			c1();
			if(T==1)//输入密码满四位后
			{
				if(tabel1[0]==0&&tabel1[1]==7&&tabel1[2]==4&&tabel1[3]==8)//判断开启密码是否正确
				{
					flag=1;//正确的标志位,表示炸弹开始倒计时
					wei=0;//数组地址归零
					T=0;//输入密码完成标志位归零
					EA=1;//打开总中断,表示倒计时开始
				}
				else
				{
					wei=0;
					T=0;
				}
			}
		}
		if(flag==1)//炸弹开始倒计时后
		{
			Write_cmd(0x4e|0x80);
			Write_dat((s/10+'0'));
			Write_cmd(0x4f|0x80);
			Write_dat((s%10+'0'));//在LCD1602右下角显示剩余秒数
			if(s==20||s==10)//当蜂鸣器发声频率改变时,屏幕上显示“COME ON!”字样
			{
				Write_cmd(0x01);
				LCDWrite1(zifu3);
				delay(50);
			}
			if((s!=0)&&(s!=20&&s!=10))//显示完“COME ON!”后再次换回原来的显示
			{
				LCDWrite1(zifu1);
				LCDWrite2(zifu2);
			}			
			keyscan1();
			c2();
			if(F==1)//拆弹密码是否输入完成
			{
				if(tabel2[0]==0&&tabel2[1]==4&&tabel2[2]==8&&tabel2[3]==7)//判断拆弹密码是否正确
				{
					flbg=1;//正确的标志位
					wei=0;
					F=0;
					EA=0;//关闭中断
					Write_cmd(0x01);
					LCDWrite1(zifu5);
					BEEP=0;
					delay(25);
					BEEP=1;
					delay(150);
					BEEP=0;
					delay(25);
					BEEP=1;//蜂鸣器连响两次表示拆弹成功
					delay(1000);
				}
				else
				{
					wei=0;
					x++;
					F=0;
				}	
			}
			if((s==0&&flag==1)||x==3)//判断是否拆弹失败
			{
				EA=0;
				Write_cmd(0x01);
				LCDWrite1(zifu4);
				delay(50);
				BEEP=0;
				delay(5000);
				BEEP=1;//蜂鸣器长鸣5s表示失败
				flag=2;//标志位置2
				delay(1000);
			}
		}
		if(flag==2||flbg==1)//结束后清屏
		{
			Write_cmd(0x01);
		}					
	}
}

void Timer0() interrupt 1
{
	TH0=(65536-46082)/256;
	TL0=(65536-46082)%256;
	ms++;
	if(ms==20)//计时1s后改变数码管中要显示的数据
	{
		s--;
		ms=0;
		if(s==0)
		{
			EA=0;//关闭中断
		}
		didi();
	}
}

写在后边:
运用的模块一般的单片机上都有,没有什么特别的。LCD1602的显示个人习惯问题吧,还是更喜欢这样写。还有更加精简的LCD1602显示方式可以使代码长度进一步缩短,各位感兴趣的话可以查一查(提示:指针)。
如果觉得博主写的不错的话,各位小伙伴记得点一个赞哈~

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值