第十一届蓝桥杯单片机组省赛模拟试题——智能门锁

试题可以直接在官网下载(试题链接),十分神奇地考了矩阵键盘。

  • 按键功能:
    在这里插入图片描述在这里插入图片描述在这里插入图片描述
    矩阵键盘扫描方法很多,这里采用逐行扫描的方法:每次将某一行(R1、R2、R3、R4)置低电平,然后去读每一列(C1、C2、C3、C4)的电平。如果读出某一列为低电平,则表示该行该列所对应的按键被按下,按键注意去抖动。(可能实现起来比较暴力,图是白嫖的)
    在这里插入图片描述
    在这里插入图片描述

  • 源码:

(1)IIC驱动:

//IIC.h
#include<reg52.h>
sbit SDA = P2^1;      //数据线
sbit SCL = P2^0;      //时钟线

void Delay_IIC(unsigned char t);        //延时函数
void IIC_Start(void);                   //起始信号
void IIC_Stop(void);                    //停止信号
void IIC_Ack(unsigned char ackbit);     //产生应答
bit IIC_WaitAck(void);                  //等待应答
void IIC_SendByte(unsigned char byte);	//发送数据
unsigned char IIC_RecByte(void);        //接收数据


//IIC.c
#include<IIC.h>

void Delay_IIC(unsigned char time)
{
	while(time--);
}

void IIC_Start(void)
{
	SDA = 1;
	SCL = 1;
	Delay_IIC(5);
	SDA = 0;        //在SCL高电平期间,SDA由高变低
	Delay_IIC(5);
	SCL = 0;	
}

void IIC_Stop(void)
{
	SDA = 0;
	SCL = 1;
	Delay_IIC(5);
	SDA = 1;	//在SCL高电平期间,SDA由高变低
	Delay_IIC(5);
}

void IIC_Ack(unsigned char ackbit)
{
	if(ackbit) 	
		SDA = 0;	//产生应答信号
	else 
		SDA = 1;	//产生非应答信号
	Delay_IIC(5);
	SCL = 1;
	Delay_IIC(5);	        //第9个时钟周期
        SCL = 0;
	SDA = 1; 		//释放SDA线
	Delay_IIC(5);
}

bit IIC_WaitAck(void)
{
	SDA = 1;
	Delay_IIC(5);
	SCL = 1;
	Delay_IIC(5);	
 
	if(SDA)    		//在SCL高电平期间,SDA为高电平,从机非应答。
	{   
		SCL = 0;
		IIC_Stop();
		return 0;
	}
	else 			//在SCL高电平期间,SDA为低电平,从机有应答。
	{ 
		SCL = 0;
		return 1;
	}	
}

void IIC_SendByte(unsigned char byt)
{
	unsigned char i;
	for(i=0;i<8;i++)		//循环发送8位数据
	{   
		if(byt & 0x80) 		//数据位是高电平
		{	
			SDA = 1;
		}
		else 			//数据位是低电平
		{
			SDA = 0;
		}
 
		Delay_IIC(5);
		SCL = 1;		//SCL高电平期间,SDA的数据要保持稳定
		byt <<= 1;		//发送的数据左移,准备发送下一位
		Delay_IIC(5);		//等待SDA的数据被读取
		SCL = 0;
	}
}

unsigned char IIC_RecByte(void)
{
	unsigned char da;
	unsigned char i;
	for(i=0;i<8;i++)
	{   
		SCL = 1;
		Delay_IIC(5);		//在SCL高电平期间,读取SDA的数据
		da <<= 1;
		if(SDA) 
			da |= 0x01;
		SCL = 0;
		Delay_IIC(5);
	}
	return da;
}

(2)主程序:

#include<reg52.h>
#include"IIC.h"

sfr P4 = 0xC0;     //定义P4口
sbit R1 = P3^0;	   //定义矩阵键盘的行
sbit R2 = P3^1;
sbit R3 = P3^2;
sbit R4 = P3^3;
sbit C1 = P4^4;	   //定义矩阵键盘的列
sbit C2 = P4^2;
sbit C3 = P3^5;
sbit C4 = P3^4;
sbit L1 = P0^0;	   //定义LED
sbit L7 = P0^6;
sbit L8 = P0^7;
sbit relay = P0^4;    //定义继电器

bit led = 0;               //LED状态位
bit over = 0;			   //密码修改完成标志位(0表示未修改完成,1表示修改完成)
unsigned char seg = 15;	   //数码管显示第1位
unsigned char stat = 15;   //密码操作状态位(14表示输入密码,10表示修改密码)
unsigned char status = 0;  //密码输入正确状态下有无操作标志位(0表示有操作,1表示无操作);密码错误状态下初始化标志位
unsigned char count = 0;   //定时器计数(50ms计时)
unsigned long pass = 0;    //输入密码(定义为32bit)
unsigned long cipher = 0;  //存放密码
unsigned char num = 0;     //记录存放密码的个数
unsigned char code SEG_code[16] = 
	{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
	 0x80,0x90,0xc6,0x86,0x8c,0xC8,0xbf,0xff};	              
	                       //定义共阳数码管段码内容:0~9,C,E,P,N,—,


/*========================================端口选择========================================*/
//端口选择
void Select_HC138(unsigned char n)
{
	switch(n)
	{
		case 0:
			P2 = (P2 & 0x1f) | 0x00;
			break;
		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;
	}
}
/*========================================================================================*/


/*======================================密码存取操作======================================*/
//24C02字节写操作
void Write_24C02(unsigned char addr,unsigned char dat)
{
	IIC_Start();           //IIC总线起始信号
	IIC_SendByte(0xa0);	   //写设备地址
	IIC_WaitAck();         //等待从机应答
	IIC_SendByte(addr);    //写内存字节地址
	IIC_WaitAck();         //等待从机应答
	IIC_SendByte(dat);     //写目标数据
	IIC_WaitAck();         //等待从机应答
	IIC_Stop();            //IIC总线停止信号
}

//24C02读字节操作
unsigned char Read_24C02(unsigned char addr)
{
	unsigned char temp;
	//进行一个伪写操作
	IIC_Start();            //IIC总线起始信号
	IIC_SendByte(0xa0);     //写设备地址
	IIC_WaitAck();          //等待从机应答
	IIC_SendByte(addr);     //写内存字节地址
	IIC_WaitAck();          //等待从机应答

	IIC_Start();            //IIC总线起始信号
	IIC_SendByte(0xa1);     //读设备地址
	IIC_WaitAck();          //等待从机应答
	temp = IIC_RecByte();   //读取目标数据
	IIC_Ack(0);             //产生一个非应答信号
	IIC_Stop();             //IIC总线停止信号
	return temp;
}

//24C02写字节延时函数
void Delay_24C02(unsigned int time)
{
	while(time--);
}
/*========================================================================================*/


/*=======================================系统初始化=======================================*/
//初始化
void Init_system()
{
	Select_HC138(5);
	P0 = 0x00;       //关闭蜂鸣器和继电器
	Select_HC138(4);
	P0 = 0xff;       //关闭LED
	Select_HC138(6);
	P0 = 0x00;       //熄灭数码管
	Select_HC138(0);

	led = 0;         //LED状态位
	over = 0;		 //密码修改完成标志位(0表示未修改完成,1表示修改完成)
	seg = 15;	     //数码管显示第1位          
	stat = 15;       //密码操作状态位(14表示输入密码,10表示修改密码)
	status = 0;      //密码输入正确状态下有无操作标志位(0表示有操作,1表示无操作);密码错误状态下初始化标志位
	count = 0;       //定时器计数(50ms计时)
	pass = 0;        //输入密码(定义为32bit)
	cipher = 0;      //存放密码
	num = 0;         //记录存放密码的个数
}

//初始化密码
void Init_pass()
{
	Write_24C02(0x01,0x88);  //保存密码低8位
	Delay_24C02(1000);
	Write_24C02(0x02,0x88);  //保存密码中8位
	Delay_24C02(1000);
	Write_24C02(0x03,0x88);  //保存密码高8位
	Delay_24C02(1000);		 //初始密码为0x888888
}
/*========================================================================================*/


/*=======================================数码管显示=======================================*/
//数码管延时函数
void Delay_tube(unsigned char t)
{
	while(t--);
} 

//数码管显示
void Show_tube(unsigned char position,unsigned char value)
{
	Select_HC138(7);
	P0 = 0xff;        //熄灭
	Select_HC138(6);
	P0 = 0x01 << position;
	Select_HC138(7);
	P0 = value;
}

void Show_all()
{
	Select_HC138(7);
	P0 = 0xff;
	Select_HC138(6);
	P0 = 0xff;
}

//数码管动态显示
void Display_cypher()	           //密码操作
{
	Show_tube(0,SEG_code[seg]);   //14显示—,10显示C
	Delay_tube(100);

	if(num >= 6)
	{
		Show_tube(2,SEG_code[(pass>>24)%16]);   //取pass十万位(6)
		Delay_tube(100);
	}
	if(num >= 5)
	{
		Show_tube(3,SEG_code[(pass>>20)%16]);   //取pass万位(5)
		Delay_tube(100);
	}
	if(num >= 4)
	{
		Show_tube(4,SEG_code[(pass>>16)%16]);   //取pass千位(4)
		Delay_tube(100);
	}
	if(num >= 3)
	{
		Show_tube(5,SEG_code[(pass>>12)%16]);   //取pass百位(3)
		Delay_tube(100);
	}
	if(num >= 2)
	{
		Show_tube(6,SEG_code[(pass>>8)%16]);    //取pass十位(2)
		Delay_tube(100);
	}
	if(num >= 1)
	{
		Show_tube(7,SEG_code[(pass>>4)%16]);	//取pass个位(1)
		Delay_tube(100);
	}

	Show_all();
}

void Display_open()        		   //密码输入正确
{
	Show_tube(0,SEG_code[0]);
	Delay_tube(100);

	Show_tube(4,SEG_code[0]);	   //显示open
	Delay_tube(100);
	Show_tube(5,SEG_code[12]);
	Delay_tube(100);
	Show_tube(6,SEG_code[11]);
	Delay_tube(100);
	Show_tube(7,SEG_code[13]);
	Delay_tube(100);

	Show_all();
}
/*========================================================================================*/


/*======================================密码检验操作======================================*/
//检验密码是否正确
void Check_pass()
{
	if(num == 6 && stat == 15 && seg == 14)
	{
		unsigned char c;
		c = Read_24C02(0x03);   //读高8位
		cipher |= c;
		cipher <<= 8;
	
		c = Read_24C02(0x02);	//读中8位
		cipher |= c;
		cipher <<= 8;
	
		c = Read_24C02(0x01);   //读低8位
		cipher |= c;

		if(cipher == (pass>>4))      //密码正确,显示open
		{	
			stat = 0;
			seg = 0;
			TR0 = 1;            //开定时器中断
			num = 0;
			pass = 0;	
			Select_HC138(5);
			P0 = 0x00;
			relay = 1;          //继电器吸合
			Select_HC138(0);
		}
		else if(cipher != (pass>>4))	   //密码错误
		{
			TR0 = 1;            //开定时器中断
			Select_HC138(4);
			L1 = led;           //点亮L1(5秒)
			Select_HC138(0);
			if(status == 1)    
			{
				Init_system();	//初始化
				TR0 = 0;		//关定时器中断
			}
		}
	}
}

//修改密码
void Reset_pass()
{ 
	if(status == 1 && stat == 0)    //5秒内无操作
	{
		Init_system();      //初始化
		TR0 = 0;			//关定时器中断
	}
	else if(status == 0 && stat == 10)    //5秒内有操作
	{
		TR0 = 0;            //关定时器中断
		pass = 0;
		num = 0;
		stat = 14;          //重新输入密码
	}
}

//密码修改完成
void Reset_over()
{
	if(over == 1)	     //再次按下S12,密码修改完成
	{
		cipher = (pass>>4);
		Write_24C02(0x01,cipher%256);  //保存密码低8位
		Delay_24C02(1000);
		Write_24C02(0x02,(cipher>>8)%256);  //保存密码中8位
		Delay_24C02(1000);
		Write_24C02(0x03,(cipher>>16)%256);  //保存密码高8位
		Delay_24C02(1000);
		Init_system();	
	}
}
/*========================================================================================*/


/*=========================================定时器=========================================*/
//定时器0中断初始化
void Init_timer0()
{
	TMOD = 0x01;
	TH0 = (65535 - 50000) / 256;
	TL0 = (65535 - 50000) % 256;    //50ms定时

	EA = 1;     //打开总中断
	ET0 = 1;    //打开定时器0的中断
	TR0 = 0;    //关闭定时器0
}

//定时器0中断服务函数
void Service_timer0() interrupt 1
{
	TH0 = (65535 - 50000) / 256;
	TL0 = (65535 - 50000) % 256;
	count++;

	if(count == 100)     //计时满5S
	{
		count = 0;
		led = 1;
		status = 1;
	}
}
/*========================================================================================*/


/*========================================按键处理========================================*/
//按键延时函数
void Delay_keys()
{
	unsigned char i,j;
	i = 108;
	j = 145;
	do
	{
		while(--j);
	}while(--i);
}

//矩阵键盘
void Press_keys()
{	
//==============================扫描第一行================================	
	R1 = 0;		  
	R2 = R3 = R4 = 1;
	C1 = C2 = C3 = C4 = 1;

	if(C1 == 0 && stat == 14)
	{
		Delay_keys();
		if(C1 == 0)
		{
			pass |= 0x00;
			pass <<= 4;
			num++;
		}
		while(C1 == 0);
	}

	else if(C2 == 0 && stat == 14)
	{
		Delay_keys();
		if(C2 == 0)
		{
			pass |= 0x01;
			pass <<= 4;
			num++;
		}
		while(C2 == 0);
	}

	else if(C3 == 0 && stat == 14)
	{
		Delay_keys();
		if(C3 == 0)
		{
			pass |= 0x02;
			pass <<= 4;
			num++;
		}
		while(C3 == 0);
	}

	else if(C4 == 0 && stat == 14)
	{
		Delay_keys();
		if(C4 == 0)
		{
			pass |= 0x03;
			pass <<= 4;
			num++;
		}
		while(C4 == 0);
	}

	if(num >= 6)
	{
		stat = 15;
		Select_HC138(4);
		P0 = 0xff;
		Select_HC138(0);
		if(seg == 10)      //在修改密码状态下,将stat置0
		{
			stat = 0;
			over = 1;     //输入完成
		}
	}
//========================================================================				

//==============================扫描第二行================================
	R2 = 0;		  
	R1 = R3 = R4 = 1;
	C1 = C2 = C3 = C4 = 1;
	if(C1 == 0 && stat == 14)
	{
		Delay_keys();
		if(C1 == 0)
		{
			pass |= 0x04;
			pass <<= 4;
			num++;
		}
		while(C4 == 0);
	}

	else if(C2 == 0 && stat == 14)
	{
		Delay_keys();
		if(C2 == 0)
		{
			pass |= 0x05;
			pass <<= 4;
			num++;
		}
		while(C2 == 0);
	}

	else if(C3 == 0 && stat == 14)
	{
		Delay_keys();
		if(C3 == 0)
		{
			pass |= 0x06;
			pass <<= 4;
			num++;
		}
		while(C3 == 0);
	}

	else if(C4 == 0 && stat == 14)
	{
		Delay_keys();
		if(C4 == 0)
		{
			pass |= 0x07;
			pass <<= 4;
			num++;
		}
		while(C4 == 0);
	}

	if(num >= 6)
	{
		stat = 15;
		Select_HC138(4);
		P0 = 0xff;
		Select_HC138(0);
		if(seg == 10)      //在修改密码状态下,将stat置0
		{
			stat = 0;
			over = 1;
		}
	}
//========================================================================

//==============================扫描第三行================================
	R3 = 0;		  
	R1 = R2 = R4 = 1;
	C1 = C2 = C3 = C4 = 1;
	if(C1 == 0 && stat == 14)
	{
		Delay_keys();
		if(C1 == 0)
		{
			pass |= 0x08;
			pass <<= 4;
			num++;
		}
		while(C1 == 0);
	}

	else if(C2 == 0 && stat == 14)
	{
		Delay_keys();
		if(C2 == 0)
		{
			pass |= 0x09;
			pass <<= 4;
			num++;
		}
		while(C2 == 0);
	}
	
	if(num >= 6)
	{
		stat = 15;
		Select_HC138(4);
		P0 = 0xff;
		Select_HC138(0);
		if(seg == 10)      //在修改密码状态下,将stat置0
		{
			stat = 0;
			over = 1;
		}
	}
//========================================================================

//==============================扫描第四行================================
	R4 = 0;		  
	R1 = R2 = R3 = 1;
	C1 = C2 = C3 = C4 = 1;

	if(C2 == 0 && stat == 14)	 //清除全部数字
	{
		Delay_keys();
		if(C2 == 0)
		{
			if(num < 6)
			{
				pass = 0;
				num = 0;
			}	
		}
		while(C2 == 0);
	}

	else if(C3 == 0 && stat == 0)	  //修改密码
	{
		Delay_keys();
		if(C3 == 0)
		{
			stat = 10;
			seg = 10;
			Select_HC138(4);
			P0 = 0xff;
			L8 = 0;		        //点亮L8
			Select_HC138(0);
			Reset_over();      //再次按下,修改完成
		}
		while(C3 == 0);
	}

	else if(C4 == 0 && stat == 15 && seg != 10)	 //开始输入密码
	{
		Delay_keys();
		if(C4 == 0)
		{
			stat = 14;
			seg = 14;
			Select_HC138(4);
			P0 = 0xff;
			L7 = 0;		        //点亮L7
			Select_HC138(0);
		}
		while(C4 == 0);
	}
}
/*========================================================================================*/


/*=========================================主函数=========================================*/
//主函数
void main()
{
	Init_timer0();
	Init_system();
	Init_pass();
	while(1)
	{
		switch(seg)
		{
			case 14:
			case 10:
				Display_cypher();
				break;
			case 0:
				Display_open();
				break;
		}
		Press_keys();
		Check_pass();
		Reset_pass();
	}
}
/*========================================================================================*/
发布了4 篇原创文章 · 获赞 4 · 访问量 1394

猜你喜欢

转载自blog.csdn.net/qq_41412394/article/details/105266987
今日推荐