蓝桥杯单片机国赛第三届(超级详细注释)

直接看题目先。
全部程序:提取码:azxy
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include "STC15F2K60S2.h"
#include "intrins.h"
#include "ds1302.h" 
#include "iic.h"
typedef unsigned char u8;
typedef unsigned int u16;

sbit TX=P1^0;//超声波发射引脚
sbit RX=P1^1;//超声波接收引脚

sbit H1=P3^0;
sbit H2=P3^1;
sbit H3=P3^2;
sbit H4=P3^3;
			 //矩阵键盘引脚定义
sbit L1=P4^4;
sbit L2=P4^2;
sbit L3=P3^5;
sbit L4=P3^4;

u8 code duan[12]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf};//0~9

u8 mima[6]={6,5,4,3,2,1};//正确密码存储数组
u8 mima1[6]={10,10,10,10,10,10};//输入时显示的密码数组
u8 store_addr[6]={0x00,0x01,0x02,0x03,0x04,0x05};//密码存储在EEPROM的地址

extern unsigned char Time[3];//DS1302时分秒数组
u16 dat=0;//超声波测得的距离
u8 flag=0;//矩阵键盘按键按标志
u8 key_num=0;//按键键值
u8 model=0;//数码管显示的界面0~3 0:时间界面 1:密码待输入界面 2:修改密码界面 3:输入要修改的密码界面
u8 num=0;//记录密码长度
u8 relay=0;//继电器标志
u8 been=0;//蜂鸣器标志
u16 count=0;//继电器响应时间标志
u16 count1=0;//蜂鸣器响应时间标志
u8 error_flag=0;//密码错误次数记录
u8 aa;//保证第一次上电密码是654321

void delay(u16 i)//延迟函数
{
	while(i--);
}

void Delay5ms()//延迟5MS,方便EEPROM连续写
{
	unsigned char i, j;

	i = 59;
	j = 90;
	do
	{
		while (--j);
	} while (--i);
}

void Chan_hc573(u8 chose)//锁存器选择
{
	switch(chose)
	{
		case 4:
			P2 = (P2 & 0x1f) | 0x80; break;
		case 5:
			P2 = (P2 & 0x1f) | 0xa0; break;
		case 6:
			P2 = (P2 & 0x1f) | 0xc0; break;
		case 7:
			P2 = (P2 & 0x1f) | 0xe0; break;
	}
	P2 = (P2 & 0x1f) | 0x00;
}

void Init_system()//初始化开发板,关闭LED灯,蜂鸣器,继电器
{
	P0=0x00;
	Chan_hc573(5);
	P0=0xff;
	Chan_hc573(4);
}

void Smg_display(u8 com,u8 dat)//控制数码管的位选和段选
{
	P0=0xff;
	Chan_hc573(7);
	P0=0x01<<com;
	Chan_hc573(6);
	P0=dat;
	Chan_hc573(7);
	delay(1000);
}

void Close_smg()//数码管消隐
{
	P0=0x00;
	Chan_hc573(6);
	P0=0xff;
	Chan_hc573(7);
}

void Wire_mima()//密码复位和保证第一次上电密码是654321
{
	Wire_dat(store_addr[0],6);Delay5ms();
	Wire_dat(store_addr[1],5);Delay5ms();
	Wire_dat(store_addr[2],4);Delay5ms();
	Wire_dat(store_addr[3],3);Delay5ms();
	Wire_dat(store_addr[4],2);Delay5ms();
	Wire_dat(store_addr[5],1);Delay5ms();
}

void Read_mima()//读取EEPROM里面的密码
{
	u8 i=0;
	for(i=0;i<6;i++) {mima[i]=Read_dat(store_addr[i]);}
}
//========================================//
//================自动门状态==============//
//========================================//
void Delay12us()//延迟12US,用于超声波发射
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 36;
	while (--i);
}

void scan_csp()//超声波发射40Khz的频率
{
	u8 i=0;
	for(i=0;i<8;i++)
	{
		TX=1;
		Delay12us();
		TX=0;
		Delay12us();
	}
}

void Try_cj()//超声波所测得的距离
{
	TMOD &= 0x0F;
	EA=1;
	ET0=1;
	TH0=0;
	TL0=0;
	TF0=0;
	scan_csp();
	TR0=1;
	while((TF0==0)&(RX==1));
	TR0=0;
	if(TF0==1)
	{
		TF0=0;
		dat=999;
	}
	else
	{
		dat=(TH0<<8)|TL0;
		dat=dat*0.017+3;//测得的距离
	}
	if(dat<30) {relay=1;TR1=1;}//小于30cm继电器打开,定时器1打开
}

void Display_time()//显示从DS1302中所读取到的时间
{
	Smg_display(0,duan[Time[0]/16]); //时
	Smg_display(1,duan[Time[0]%16]); 
	Smg_display(2,duan[11]); 
	Smg_display(3,duan[Time[1]/16]); //分
	Smg_display(4,duan[Time[1]%16]); 
	Smg_display(5,duan[11]);
	Smg_display(6,duan[Time[2]/16]); //秒
	Smg_display(7,duan[Time[2]%16]); 
	Close_smg();					 //数码管消隐
}
//========================================//
//================密码门状态==============//
//========================================//
void Display_mima()//密码门状态时所显示的界面
{
	if(model==1)
	{
		Smg_display(0,duan[11]); //显示 —
		Smg_display(1,duan[11]); //显示 —
	}
	else if(model==2)
	{
		Smg_display(0,duan[10]); //数码管熄灭 
		Smg_display(1,duan[11]); //显示 —
	}
	else if(model==3)
	{
		Smg_display(0,duan[11]);//显示 —
		Smg_display(1,duan[10]); //数码管熄灭
	}	
	Smg_display(2,duan[mima1[0]]); 
	Smg_display(3,duan[mima1[1]]); 
	Smg_display(4,duan[mima1[2]]); 
	Smg_display(5,duan[mima1[3]]); //显示所输入的密码
	Smg_display(6,duan[mima1[4]]); 
	Smg_display(7,duan[mima1[5]]);
	Close_smg();
}

void Clear_mima()//用于密码输入正确或错误时清除已输入的密码,回到待输入状态
{
	u8 i;
	for(i=0;i<6;i++) {mima1[i]=10;}
}

void Key_down()//矩阵键盘的程序,通过按下的按键获取键值
{
	H1=0;
	H2=H3=H4=L1=L2=L3=L4=1;
	if(L1==0)					//键值0
	{
		delay(1000);
		if(L1==0)
		{
			flag=1;
			while(L1==0) {}
			key_num=0;
		}
	}
	else if(L2==0)				//键值1
	{
		delay(1000);
		if(L2==0)
		{
			flag=1;
			while(L2==0) {}
			key_num=1;
		}
	}
	else if(L3==0)				//键值2
	{
		delay(1000);
		if(L3==0)
		{
			flag=1;
			while(L3==0) {}
			key_num=2;
		}
	}
	else if(L4==0)				//键值3
	{
		delay(1000);
		if(L4==0)
		{
			flag=1;
			while(L4==0) {}
			key_num=3;
		}
	}
	H2=0;
	H1=H3=H4=L1=L2=L3=L4=1;
	if(L1==0)					//键值4
	{
		delay(1000);
		if(L1==0)
		{
			flag=1;
			while(L1==0) {}
			key_num=4;
		}
	}
	else if(L2==0)				//键值5		
	{
		delay(1000);
		if(L2==0)
		{
			flag=1;
			while(L2==0) {}
			key_num=5;
		}
	}
	else if(L3==0)				//键值6
	{
		delay(1000);
		if(L3==0)
		{
			flag=1;
			while(L3==0) {}
			key_num=6;
		}
	}
	else if(L4==0)				//键值7
	{
		delay(1000);
		if(L4==0)
		{
			flag=1;
			while(L4==0) {}
			key_num=7;
		}
	}
	H3=0;
	H2=H1=H4=L1=L2=L3=L4=1;
	if(L1==0)					//键值8
	{
		delay(1000);
		if(L1==0)
		{
			flag=1;
			while(L1==0) {}
			key_num=8;
		}
	}
	else if(L2==0)				//键值9
	{
		delay(1000);
		if(L2==0)
		{
			flag=1;
			while(L2==0) {}
			key_num=9;
		}
	}
	else if(L3==0)				//设置键
	{
		delay(1000);
		if(L3==0)
		{	
			while(L3==0) {}
			if(model==0) {model=2;}
		}
	}
	else if(L4==0)				//复位键
	{
		delay(1000);
		if(L4==0)
		{
			while(L4==0) {}
			mima[0]=6;mima[1]=5;mima[2]=4;
			mima[3]=3;mima[4]=2;mima[5]=1;
			Wire_mima();
		}
	}
	H4=0;
	H2=H3=H1=L1=L2=L3=L4=1;
	if(L3==0)
	{
		delay(1000);
		if(L3==0)				//确认键
		{
			count=0;count1=0;
			while(L3==0) {}
			key_num=14;
		}
	}
	else if(L4==0)				//返回键
	{
		delay(1000);
		if(L4==0)
		{
			while(L4==0) {}
			if((model==2)||(model==3)) {model=0;} 
			num=0;
			Clear_mima();
		}
	}
}

void Mima_scan()//输入密码时每次按下所读取到的键值
{
	u8 dat=0;
	Key_down();
	if(flag==1)//按键按下标志
	{
		if(model==0) {model=1;}//初始化时显示的是时间界面,按下键盘就到待输入密码界面
		switch(key_num)//通过键值读取所按下的数字
		{
			case 0: dat=0; break;
			case 1: dat=1; break;
			case 2: dat=2; break;
			case 3: dat=3; break;
			case 4: dat=4; break;
			case 5: dat=5; break;
			case 6: dat=6; break;
			case 7: dat=7; break;
			case 8: dat=8; break;
			case 9: dat=9; break;
		}
		mima1[num]=dat;//把读取到的数字存储到输入时显示的密码数组里
		num++;//每按下一次,存储位置加一
		if(num==6) {num=6;}//密码输入完成
		flag=0;//返回0,防止程序一直运行
	}
}

void Right_mima()//输入密码与正确密码进行比对
{
	u8 i=0;
	if(key_num==14)//代表确认键按下
	{
		if(num==6)//代表密码输入完成
		{
			if(model==1)//待输入密码界面
			{
				if((mima1[0]==mima[0])&&(mima1[1]==mima[1])&&(mima1[2]==mima[2])&&(mima1[3]==mima[3])&&(mima1[4]==mima[4])&&(mima1[5]==mima[5]))
				{							//如果密码正确
					relay=1;				//继电器打开
					TR1=1;					//定时器打开
					num=0;					//清零,等待下一次输入
					error_flag=0;			//错误次数清零
				}
				else						//密码错误
				{
					num=0;					//清零,等待下一次输入
					error_flag++;			//错误次数加一
					model=1;				//返回等待输入界面
					Clear_mima();			//清除刚才输入错误的密码
					if(error_flag==3)		//如果错误了三次
					{
						TR1=1;				//定时器打开
						been=1;				//蜂鸣器打开
						model=0;			//返回时间显示界面
					}
				}
			}
			else if(model==2)				//修改密码界面
			{
				if((mima1[0]==mima[0])&&(mima1[1]==mima[1])&&(mima1[2]==mima[2])&&(mima1[3]==mima[3])&&(mima1[4]==mima[4])&&(mima1[5]==mima[5]))
				{							//如果密码正确
					model=3;				//跳转到输入要修改密码的界面
					if(num==6)				//要修改的密码输入完成
					{
						if(model==3) {Clear_mima();}//清除刚才输入正确的密码
						num=0;				//清零,等待下一次输入
					}
					error_flag=0;			//错误次数清零
				}
				else						//密码错误
				{
					num=0;					//清零,等待下一次输入
					Clear_mima();			//清除刚才输入错误的密码
					error_flag++;			//错误次数加一
					model=2;				//返回到修改密码界面
					if(error_flag==3)		//如果错误了三次
					{
						TR1=1;				//定时器打开
						been=1;				//蜂鸣器打开
						model=0;			//返回时间显示界面
					}
				}
			}
			else if(model==3)				//输入要修改密码的界面
			{
				for(i=0;i<6;i++) {mima[i]=mima1[i];}//把输入的密码存储到真正的密码数组里面
				for(i=0;i<6;i++) {Wire_dat(store_addr[i],mima1[i]);Delay5ms();}//把输入的密码存储到EEPORM里面
				model=0;					//返回时间显示界面
				num=0;						//清零,等待下一次输入
				Clear_mima();				//清除刚才输入修改后的密码
			}
		}
	}
}

void Been_stat()//蜂鸣器打开与关闭
{
	if(been==1)//打开蜂鸣器
	{
		P06=1;P04=0;
		Chan_hc573(5);
	}
	else		//关闭蜂鸣器
	{
		if(relay==0)//继电器关闭状态,如果不加这个判断,TR1会马上关闭,到时候继电器不能正常运行
		{
			TR1=0;//关闭定时器1
			P06=0;P04=0;
			Chan_hc573(5);
		}
	}
}

void Mima_stat()//区分两种门所要显示的界面
{
	if(model==0) {Display_time();}//公共界面,时间显示界面
	else         {Display_mima();}//密码门状态显示界面
}
//========================================//
//================全部程序整合============//
//========================================//
void all_stat()//公共继电器的处理
{
	if((Time[0]>0x06)&&(Time[0]<0x22))//自动门状态
	{
		if(TR1==0) {Try_cj();}		//继电器关闭状态下才发射超声波
		Display_time();				//显示时间
		if(relay==1)				//如果继电器打开
		{
			P04=1;P06=0;
			Chan_hc573(5);
		}
		else						//继电器关闭状态
		{
			TR1=0;					//定时器1也关闭
			count=0;				//时间清零
			P04=0;P06=0;
			Chan_hc573(5);
		}
	}
	else							//密码门状态
	{
		Mima_stat();				//区分两种门所要显示的界面				
		Mima_scan();				//输入密码时每次按下所读取到的键值
		Right_mima();				//输入密码与正确密码进行比对
		Been_stat();				//蜂鸣器打开与关闭
		if(relay==1)				//如果继电器打开
		{
			P04=1;P06=0;
			Chan_hc573(5);
		}
		else						//继电器关闭状态
		{
			if(been==0)				//蜂鸣器关闭状态,如果不加这个判断,TR1会马上关闭,到时候蜂鸣器不能正常运行
			{
				TR1=0;				//关闭定时器1
				P04=0;P06=0;
				Chan_hc573(5);
			}
		}
	}
}


void Timer1Init(void)		//定时器1 定时1ms 12.000MHz
{
	AUXR |= 0x40;		
	TMOD &= 0x0F;		
	TL1 = 0x20;		
	TH1 = 0xD1;		
	TF1 = 0;				
	ET1 = 1;
	EA  = 1;
}

void Service_T1() interrupt 3 //定时器中断服务函数
{
	count++;				  //继电器时间自加
	count1++;				  //蜂鸣器时间自加
	if(count==5000)			  //继电器时间达到5S
	{
		count=0;			 //继电器时间清零
		relay=0;			 //关闭继电器
		model=0;			 //返回时间显示界面
		Clear_mima();		 //清除刚才输入的密码
	}
	if(count1==3000)		 //蜂鸣器时间达到3S
	{
		error_flag=0;		 //输入错误次数清零
		count1=0;			 //蜂鸣器时间清零
		been=0;				 //关闭蜂鸣器
		if(relay==0) {Clear_mima();} 清除刚才输入的密码
	}
}

void First_mima()//保证第一次开发板上电密码是654321
{
	aa=Read_dat(0x55);//随便从EEPROM中选择一个地址读取它的值
	if(aa!=99)        //正常读取到的值为255,所以几率很小是99,如果怕是99的话可以多读取几个地址,这样几率会小更多
	{
		Wire_mima();//读取到不是99,就把密码654321写进去
		Wire_dat(0x55,99);//然后把刚才读取的地址写入99,下次就不会运行这一段程序了
	}
	Read_mima();//读取EEPROM里面的密码,并赋值给密码存储数组,用于密码对比
}

void main()//主函数
{
	Init_system();//初始化开发板,关闭LED灯,蜂鸣器,继电器
	Timer1Init();//定时器1 定时1ms 12.000MHz
	wire_ds1302();//写入初始化时间
	Read_mima();//读取EEPROM里面的密码
	First_mima();//保证第一次开发板上电密码是654321
	while(1)
	{
		all_stat();//公共继电器的处理
		Key_down();//矩阵键盘的程序,通过按下的按键获取键值
		read_ds1302();//读取DS1302里面的时间
	}
}
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一心向月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值