基于51单片机的简易密码锁


前言

在翻之前的项目中,找到了一个学习完51单片机时候的拿来练手的一个小项目,因为时间隔着有点长,自己尽力回忆记录下来,这个小项目是用51开发板来进行的,主要用到的材料包含51开发板,LCD1602液晶显示屏,继电器,淘宝上买的一个5v驱动锁,注意我定义的引脚与大家的是否相同。


一、密码锁所含有的功能

1、可以通过矩阵按键进行输入和修改密码并储存所修改的密码
2、可以通过LCD1602液晶显示屏对输入密码提示
3、可以在LCD1602液晶显示屏上显示此时的温度
4、通过继电器进行解锁和上锁

二、所实现功能模块的代码

1.储存功能

使用I2C总线进行存储

#include<I2C.h>
/*
typedef unsigned int u16;
typedef unsigned char u8;
*/
void delay10us()   //误差 0us
{
    unsigned char a,b;
    for(b=1;b>0;b--)
        for(a=2;a>0;a--);
}


void IICstart()
{

	SCL=1;
	SDA=1;
	delay10us();
	SDA=0;
	SCL=1;
	delay10us();
	SCL=0;
	delay10us();
	
/*	SDA=1;
	delay10us();
	SCL=1;
	delay10us();//建立时间是SDA保持时间>4.7us
	SDA=0;
	delay10us();//保持时间是>4us
	SCL=0;			
	delay10us();	
	 */
}
void IICstop()
{

	SDA=0;
	SCL=1;
	delay10us();
	SDA=1;
	SCL=1;
	delay10us();
 
 /*
  	SDA=0;
	delay10us();
	SCL=1;
	delay10us();//建立时间大于4.7us
	SDA=1;
	delay10us();
 */
}
uchar IICsend(uchar dat)
{
	uint m=0,n=0;
	for(n=0;n<8;n++)
	{
		SDA=dat>>7;
		dat=dat<<1;
		delay10us();
		SCL=1;
		delay10us();
		SCL=0;
		delay10us();
	}
	SDA=1;
	delay10us();
	SCL=1;
	delay10us();
	while(SDA)
	{
		m++;
		if(m>200)
		{
			SCL=0;
		//	SDA=0;
			delay10us();
			return 1;
		}	
	}
	SCL=0;
	delay10us();
	return 0;
}
uchar IICreceive()
{
	uint a=0,dat=0;
	SDA=1;
	delay10us();
	for(a=0;a<8;a++)
	{
	//	SCL=1;
		delay10us();
		dat<<=1;
		dat|=SDA;
		SCL=1;
		delay10us();
		SCL=0;
		delay10us();
	}
	return dat;
}
void AT24C02send(uchar arr,uchar dat)
{
	IICstart();
	IICsend(0xa0);
	IICsend(arr);	
	IICsend(dat);
	IICstop();
}
uchar AT24C02receive(uchar arr)
{
	uchar num;
	IICstart();
	IICsend(0xa0);
	IICsend(arr);
	IICstart();
	IICsend(0xa1);
	num=IICreceive();
	IICstop();
	return num;
}

2.读取外部温度功能

使用的是DS18B20,DS18B20是常用的数字温度传感器,其输出的是数字信号,具有体积小,硬件开销低,抗干扰能力强,精度高的特点。DS18B20数字温度传感器接线方便,封装成后可应用于多种场合

#include"ds18b20.h"

void Delay1ms()   //误差 0us
{
    unsigned char a,b,c;
    for(c=1;c>0;c--)
        for(b=142;b>0;b--)
            for(a=2;a>0;a--);
}

 
void Delay80us(void)   //误差 0us
{
    unsigned char a,b;
    for(b=11;b>0;b--)
        for(a=2;a>0;a--);
}
void Delay15us(void)   //误差 0us
{
    unsigned char a;
    for(a=6;a>0;a--);
}
void Delay60us(void)   //误差 0us
{
    unsigned char a,b;
    for(b=3;b>0;b--)
        for(a=8;a>0;a--);
}
void delay480us(void)   //误差 0us
{
    unsigned char a,b;
    for(b=53;b>0;b--)
        for(a=3;a>0;a--);
}
 
uchar DS18B20start()
{
	uint i;
	DSPORT=0;
	delay480us();
	DSPORT=1;
	Delay80us();
	while(DSPORT)
	{
		Delay1ms();
	 	i++;
		if(i>5)
		{
			return 0;
		}
	}
	return 1;
}

void DS18B20write(uchar dat)
{
	uint j;
	for(j=0;j<8;j++)
	{
		DSPORT=0;
		Delay15us();
		DSPORT=dat&0x01;
		Delay60us();
		DSPORT=1;
		dat>>=1;
	}
}

uchar DS18B20read()
{
	uint i,j;
	uchar bi,byte;
	for(i=0;i<8;i++)
	{
		DSPORT=0;
		j++;
		DSPORT=1;
		Delay15us();
		bi=DSPORT;
		byte=(byte>>1)|(bi<<7);
		Delay60us();
	}	
	return byte;
}

void DS18B20change()
{
	DS18B20start();
	Delay1ms();
	DS18B20write(0xcc);
	DS18B20write(0x44);	
//	Delay1ms(100);
}
void DS18B20get()
{
	DS18B20start();
	Delay1ms();
	DS18B20write(0xcc);
	DS18B20write(0xbe);	
}

int DS18B20readtemp()
{
	uint temp=0;
	uchar tml,tmh;
	DS18B20change();
	DS18B20get();
	tml=DS18B20read();
	tmh=DS18B20read();
	temp=tmh;
	temp<<=8;
	temp|=tml;
	return temp;
}

3.用LCD1602实现的显示功能

关于LCD1602的详细中文手册
链接:https://pan.baidu.com/s/1uFq66_MQEsx0fI2qJy5yXg
提取码:fuel

#include "lsd1602.h"


#define RS_CLR RS=0 
#define RS_SET RS=1

#define RW_CLR RW=0 
#define RW_SET RW=1 

#define EN_CLR EN=0
#define EN_SET EN=1

#define DataPort P0

void delay1ms(unsigned char c)   //误差 0us
{
    unsigned char a,b;
    for(;c>0;c--)
        for(b=142;b>0;b--)
            for(a=2;a>0;a--);
}


/*------------------------------------------------
              写入命令函数
------------------------------------------------*/
 void LCD_Write_Com(unsigned char com) 
 {  
 RS_CLR; 
 RW_CLR;  
 DataPort= com;
  EN_SET; 
delay1ms(1); 
 EN_CLR;
 }
/*------------------------------------------------
              写入数据函数
------------------------------------------------*/
 void LCD_Write_Data(unsigned char Data) 
 { 
 RS_SET; 
 RW_CLR;  
 DataPort= Data; 
 EN_SET;
 delay1ms(1);
 EN_CLR;
 }

/*------------------------------------------------
                清屏函数
 */
 void LCD_Clear(void) 
 { 
 LCD_Write_Com(0x01); 
 delay1ms(5);
 }	   
/*------------------------------------------------
              写入字符串函数
-----------------------------------------------
 void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s) 
 {           
 while (*s) 
 	{     
 LCD_Write_Char(x,y,*s);     
 s++;  
 x++;   
 	}
 }
/*------------------------------------------------
              写入字符函数
------------------------------------------------*/
void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data) 
 {     
 if (y == 0) 
 	{     
 	LCD_Write_Com(0x80 + x);     
 	}    
 else 
 	{     
 	LCD_Write_Com(0xC0 + x);     
 	}        
 LCD_Write_Data( Data);  
 }
/*------------------------------------------------
             写入字符串函数
------------------------------------------------*/
void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)
{
   while(*s)
   {
      if(y==0)
	  {
	     LCD_Write_Com(0x80+x);
	  }
	  else
	  {
	     LCD_Write_Com(0xC0+x);
	  }
	  LCD_Write_Data(*s);
	  s++;
	  x++;
   }
}
       //初始化函数
 void LCD_Init(void) 
 {
   	 LCD_Write_Com(0x38);
	 LCD_Write_Com(0x0c);
	 LCD_Write_Com(0x06);
	 LCD_Write_Com(0x01);
//	 LCD_Write_Com(0x18);
	 LCD_Write_Com(0x80);
     delay1ms(5);			 
   }
   

4.主程序

部分主程序,还有一部分自定义子函数在百度网盘中,大家自取。注意连接继电器的引脚。

void final()//检验密码是否正确
{
 unsigned char num,i=0,j;                  
 unsigned char passwordtemp[16];      	 												 //最大输入16个
 unsigned char inputtimes;            												  //密码输入错误次数
 unsigned char passwordlength,PLEN;   												  //输入密码长度,实际密码长度
 bit Flag;
 
 PLEN=sizeof(pass)/sizeof(pass[0]);
 													//用于计算出实际密码长度
 relay=0;
 while(1)
 {
	 num=KeyPro(); 
 if(num!=0xff)  																				//如果扫描是按键有效值则进行处理
   { 
   	   if(num==12)
	   {
	   		LCD_Write_String(0,1,"again!");
			delay1ms(1000);
			delay1ms(1000);
			delay1ms(1000);
			delay1ms(1000);
			delay1ms(1000);
	   		LCD_Write_String(0,1,"                ");
			i=0;	
	   }
	   
	   else if(num==14)
	   {
	     uint ppp,qqq=0,rrr=1;
	   	 if(rrr==1)
		 {
			temp(DS18B20readtemp());
			delay1ms(100);
			for(ppp=0;ppp<7;ppp++)
			{
				LCD_Write_Com(0x88+qqq);
				LCD_Write_Data(duan[ppp]);
				qqq++;
			}

			
		 }	
	   }
	   else
	   {
    		if(i==0)  														  											//输入是第一个字符的时候需要把改行清空,方便观看密码
	 			 LCD_Write_String(0,1,"                ");													//清除该行
			if(i<16)
		  {
	  			 if(num==13)
			   {
		 			  passwordtemp[i]=passwordtemp[i-1];
      	  			  LCD_Write_String(i-1,1," ");
				      i=i-2;	
	   		  }
	 			 else
			   {
	   				 passwordtemp[i]=num;
      				 LCD_Write_Char(i,1,'*');
	 		   }																				//输入的密码用"*"代替
	    }
	 		i++;                                                                      				 //输入数值累加
			if(i==PLEN)																				//输入按键值15或者密码输入到最大值16,表示输入结束,需要进行比对
		  {
     		 	 	passwordlength=i; 																	 //计算输入密码长度
			  		i=0; 																					 //计数器复位
	 			    if(passwordlength==PLEN)															//长度相等则比较,否则直接输出错误
	 			   {
	   					  Flag=1;																					//先把比较位置1
	   					  for(j=0;j<PLEN;j++)																			//循环比较8个数值,如果有一个不等 则最终Flag值为0
	      				  Flag=Flag&&(passwordtemp[j]==pass[j]);												//比较输入值和已有密码
        		   }
     			   if(Flag)																					//如果比较全部相同,标志位置1
	 			  {
	   					 LCD_Write_String(0,1,"                ");																//清除该行
       					 LCD_Write_String(0,1,"Right Open!>>>>");													//密码正确显示的信息
       					 inputtimes=0;																				//输入正确则次数清零,重新计数
        				 Flag=0; 
						 delay1ms(100);
					     relay=1;     																						//清除正确标志
				}
	 		 else 
	   	   {
				LCD_Write_String(0,1,"                ");												//清除该行
				LCD_Write_String(0,1,"Wrong! Retry!");														//密码错误,提示重新输入
				inputtimes++;																									//连续输入错误,则次数累加
				if(inputtimes==3)
		 		 {
		 				  LCD_Write_String(0,1,"                ");														//清除该行
		 				  LCD_Write_String(0,1,"Wrong 3 times!");													//密码错误,提示重新输入
		 				  while(1);																		//停止该位置,重启电源后才能输入,实际实用中则需要等到一定时间后才能再次输入。
		 		 }
				 delay1ms(100);
		  		 relay=0;
		  }
	   }
	  }
    }	
  }
}	



void main()
{
// relay=0;
 LCD_Init();        																			 //初始化液晶屏
 delay1ms(10);        																					//延时用于稳定,可以去掉
 LCD_Clear();       																								 //清屏
 LCD_Write_String(0,0,"Welcome!");   																 //写入第一行信息,主循环中不再更改此信息,所以在while之前写入
 LCD_Write_String(0,1,"Input password!");   																 //写入第二行信息,提示输入密码
 fourkey();
 final();//对密码输入进行检测的函数
}


完整代码链接:https://pan.baidu.com/s/1708KS-Ompgb6gnn7UFNEaA
提取码:dodo

三、总结

如果要进行比较繁琐的项目时候,要养成分模块编写的习惯,不能将所有子函数和模块功能都放到主函数中进行编写,可以根据功能分成多个.c文件,这样之后进行别项目要用到相关模块功能时候可以进行调用,对于这个密码琐,主要就是通过I2C功能模块、LCD1602功能模块、DS18B20功能模块进行组合搭建,然后在主函数里面进行写一些自定义函数实现所需要的功能,最后在主函数中进行调用。



学习从零开始,到无穷结束
  • 5
    点赞
  • 30
    收藏
  • 打赏
    打赏
  • 4
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页
评论 4

打赏作者

Justin-Wei

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值