草庐IT

51单片机简易电子密码锁

寻あ逸 2023-08-26 原文

由于作业需求,在昨天天晚上写了一个通过lcd1602,i2c,eeprom,按键,实现的可以设置密码的简易电子锁,

 

 首先点击k15(回车键)进入

 进入后可以点击0-9按键输入6位密码,错误则显示error,5s后重新显示密码输入页面,密码正确则进入。

 

 进入后可以点击Esc键设置密码,进入设置密码界面

输入密码后显示设置成功并显示新密码 

main.c文件代码还有待改进,还可以添加功能

首先先要进行多.c文件创建。话不多说,直接上代码。

lcd1602.c

#include <reg52.h>
#define LCD1602_DB P0
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E = P1^5;
/* 等待液晶准备好 */
void LcdWaitReady()
{
 unsigned char sta;
 
 LCD1602_DB = 0xFF;
 LCD1602_RS = 0;
 LCD1602_RW = 1;
 do {
 LCD1602_E = 1;
 sta = LCD1602_DB; //读取状态字
 LCD1602_E = 0;
 } while (sta & 0x80); //bit7 等于 1 表示液晶正忙,重复检测直到其等于 0 为止
}
/* 向 LCD1602 液晶写入一字节命令,cmd-待写入命令值 */
void LcdWriteCmd(unsigned char cmd)
{
 LcdWaitReady();
 LCD1602_RS = 0;
 LCD1602_RW = 0;
 LCD1602_DB = cmd;
 LCD1602_E = 1;
 LCD1602_E = 0;
}
/* 向 LCD1602 液晶写入一字节数据,dat-待写入数据值 */
void LcdWriteDat(unsigned char dat)
{
 LcdWaitReady();
 LCD1602_RS = 1;
 LCD1602_RW = 0;
  LCD1602_DB = dat;
 LCD1602_E = 1;
 LCD1602_E = 0;
}
/* 设置显示 RAM 起始地址,亦即光标位置,(x,y)-对应屏幕上的字符坐标 */
void LcdSetCursor(unsigned char x, unsigned char y)
{
 unsigned char addr;
 
 if (y == 0) //由输入的屏幕坐标计算显示 RAM 的地址
 addr = 0x00 + x; //第一行字符地址从 0x00 起始
 else
 addr = 0x40 + x; //第二行字符地址从 0x40 起始
 LcdWriteCmd(addr | 0x80); //设置 RAM 地址
}
/* 在液晶上显示字符串,(x,y)-对应屏幕上的起始坐标,str-字符串指针 */
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str)
{
 LcdSetCursor(x, y); //设置起始地址
 while (*str != '\0') //连续写入字符串数据,直到检测到结束符
 {
 LcdWriteDat(*str++);
 } 
}
//写数字
void LcdShowDat(unsigned char x, unsigned char y, unsigned char dat)
{
   LcdSetCursor(x,y);
   LcdWriteDat(dat);
}
/* 区域清除,清除从(x,y)坐标起始的 len 个字符位 */
void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len)
{
 LcdSetCursor(x, y); //设置起始地址
 while (len--) //连续写入空格
 {
 LcdWriteDat(' ');
 } }
/* 初始化 1602 液晶 */
void InitLcd1602()
{
 LcdWriteCmd(0x38); //16*2 显示,5*7 点阵,8 位数据接口
 LcdWriteCmd(0x0C); //显示器开,光标关闭
 LcdWriteCmd(0x06); //文字不动,地址自动+1
 LcdWriteCmd(0x01); //清屏
}

 i2c.c

#include <reg52.h>
#include <intrins.h>
#define I2CDelay() {_nop_();_nop_();_nop_();_nop_();}
sbit I2C_SCL = P3^7;
sbit I2C_SDA = P3^6;
/* 产生总线起始信号 */
void I2CStart()
{
 I2C_SDA = 1; //首先确保 SDA、SCL 都是高电平
 I2C_SCL = 1;
 I2CDelay();
 I2C_SDA = 0; //先拉低 SDA
 I2CDelay();
 I2C_SCL = 0; //再拉低 SCL
}
/* 产生总线停止信号 */
void I2CStop()
{
 I2C_SCL = 0; //首先确保 SDA、SCL 都是低电平
 I2C_SDA = 0;
 I2CDelay();
 I2C_SCL = 1; //先拉高 SCL
 I2CDelay();
 I2C_SDA = 1; //再拉高 SDA
 I2CDelay();
}
/* I2C 总线写操作,dat-待写入字节,返回值-从机应答位的值 */
bit I2CWrite(unsigned char dat)
{
 bit ack; //用于暂存应答位的值
 unsigned char mask; //用于探测字节内某一位值的掩码变量
 for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行
 {
 if ((mask&dat) == 0) //该位的值输出到 SDA 上
 I2C_SDA = 0;
 else
 I2C_SDA = 1;
 I2CDelay();
 I2C_SCL = 1; //拉高 SCL
 I2CDelay();
 I2C_SCL = 0; //再拉低 SCL,完成一个位周期
 }
 I2C_SDA = 1; //8 位数据发送完后,主机释放 SDA,以检测从机应答
 I2CDelay();
 I2C_SCL = 1; //拉高 SCL
 ack = I2C_SDA; //读取此时的 SDA 值,即为从机的应答值
 I2CDelay();
 I2C_SCL = 0; //再拉低 SCL 完成应答位,并保持住总线
 return (~ack); //应答值取反以符合通常的逻辑:
 //0=不存在或忙或写入失败,1=存在且空闲或写入成功
}
/* I2C 总线读操作,并发送非应答信号,返回值-读到的字节 */
unsigned char I2CReadNAK()
{
 unsigned char mask;
 unsigned char dat;
 I2C_SDA = 1; //首先确保主机释放 SDA
 for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行
 {
 I2CDelay();
 I2C_SCL = 1; //拉高 SCL
 if(I2C_SDA == 0) //读取 SDA 的值
 dat &= ~mask; //为 0 时,dat 中对应位清零
 else
 dat |= mask; //为 1 时,dat 中对应位置 1
 I2CDelay();
 I2C_SCL = 0; //再拉低 SCL,以使从机发送出下一位
 }
 I2C_SDA = 1; //8 位数据发送完后,拉高 SDA,发送非应答信号
 I2CDelay();
 I2C_SCL = 1; //拉高 SCL
 I2CDelay();
 I2C_SCL = 0; //再拉低 SCL 完成非应答位,并保持住总线
 return dat;
}
/* I2C 总线读操作,并发送应答信号,返回值-读到的字节 */
unsigned char I2CReadACK()
{
 unsigned char mask;
 unsigned char dat;
 I2C_SDA = 1; //首先确保主机释放 SDA
 for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行
 {
 I2CDelay();
 I2C_SCL = 1; //拉高 SCL
 if(I2C_SDA == 0) //读取 SDA 的值
 dat &= ~mask; //为 0 时,dat 中对应位清零
 else
 dat |= mask; //为 1 时,dat 中对应位置 1
 I2CDelay();
 I2C_SCL = 0; //再拉低 SCL,以使从机发送出下一位
 }
 I2C_SDA = 0; //8 位数据发送完后,拉低 SDA,发送应答信号
 I2CDelay();
 I2C_SCL = 1; //拉高 SCL
 I2CDelay();
 I2C_SCL = 0; //再拉低 SCL 完成应答位,并保持住总线
 return dat;
}

eeprom.c

#include <reg52.h>
extern void I2CStart();
extern void I2CStop();
extern unsigned char I2CReadACK();
extern unsigned char I2CReadNAK();
extern bit I2CWrite(unsigned char dat);
/* E2 读取函数,buf-数据接收指针,addr-E2 中的起始地址,len-读取长度 */
void E2Read(unsigned char *buf, unsigned char addr, unsigned char len)
{
 do { //用寻址操作查询当前是否可进行读写操作
 I2CStart();
 if (I2CWrite(0x50<<1)) //应答则跳出循环,非应答则进行下一次查询
 {
 break;
 }
 I2CStop();
 } while(1);
 I2CWrite(addr); //写入起始地址
 I2CStart(); //发送重复启动信号
 I2CWrite((0x50<<1)|0x01); //寻址器件,后续为读操作
 while (len > 1) //连续读取 len-1 个字节
 {
 *buf++ = I2CReadACK(); //最后字节之前为读取操作+应答
 len--;
 }
 *buf = I2CReadNAK(); //最后一个字节为读取操作+非应答
 I2CStop();
}
/* E2 写入函数,buf-源数据指针,addr-E2 中的起始地址,len-写入长度 */
void E2Write(unsigned char *buf, unsigned char addr, unsigned char len)
{
 while (len > 0)
 {
 //等待上次写入操作完成
 do { //用寻址操作查询当前是否可进行读写操作
 I2CStart();
 if (I2CWrite(0x50<<1)) //应答则跳出循环,非应答则进行下一次查询
 {
 break;
 }
 I2CStop();
 } while(1);
 //按页写模式连续写入字节
 I2CWrite(addr); //写入起始地址
 while (len > 0)
 {
 I2CWrite(*buf++); //写入一个字节数据
 len--; //待写入长度计数递减
 addr++; //E2 地址递增
 if ((addr&0x07) == 0) //检查地址是否到达页边界,24C02 每页 8 字节,
 { //所以检测低 3 位是否为零即可
 break; //到达页边界时,跳出循环,结束本次写操作
 }
 }
 I2CStop();
 } }

下面通过函数调用,首先,先建立两个函数

bit CmpMemory(unsigned char *ptr1, unsigned char *ptr2, unsigned char len)//比较字符串是否相等
{
 while (len--)
 {
 if (*ptr1++ != *ptr2++) //遇到不相等数据时即刻返回 0
 {
 return 0;
 }
 }
 return 1; //比较完全部长度数据都相等则返回 1 }
}

用来比较字符串是否相等

unsigned char key_board()//按键检测,放入while循环
{
   uchar temp; //P2口数据存储
   uchar count;
   uchar num_key=20;      //返回的键值
   static uchar key_old=0xfe;//键盘扫描循环初始值
   static uchar  key_code;

   P2=key_old;
   temp=P2;
 if(temp!=key_old)//判断是否有按键按下
    {
      count++;
      if(count==10)
        {
            F0=1;
            key_code=temp;
        }
    } 
  else
      {
           count=0;
         if(F0==1)
         {
              F0=0;
              switch(key_code)//读出键值
             {
                 //P2^0线设置为低电平 
                  case 0xEE: num_key=10;  break; 
                  case 0xDE: num_key=11;  break;
                  case 0xBE: num_key=12;  break;                                
                  case 0x7E: num_key=13;  break;            
                  //P2^1线设置为低电平 
                  case 0xED: num_key=7;  break;
                  case 0xDD: num_key=8;  break; 
                  case 0xBD: num_key=9;  break; 
                  case 0x7D: num_key=14;  break;                                   
                  //P2^2线 设置为低电平 
                  case 0xEB: num_key=4;  break;
                  case 0xDB: num_key=5; break;                           
                  case 0xBB: num_key=6; break;               
                  case 0x7B: num_key=15; break;   
                  //P2^3线 设置为低电平                                                   
                  case 0xE7: num_key=1; break; 
                  case 0xD7: num_key=2; break;
                  case 0xB7: num_key=3; break;
                  case 0x77: num_key=16; break; 
                                                                                                                                        
               }
              
             }
              
          key_old=_crol_(key_old,1);//改变扫描线 
              if(key_old==0xef)
               {
                  key_old=0xfe;
               }
         }
    return(num_key);    //返回键值
}

按键没有使用定时器,直接通过while循环检测按键,直接调用函数

下面是mian.c函数主要内容

#include <reg52.h>
#include <intrins.h>
#define uchar unsigned char
unsigned char buf[4];
extern void LcdWaitReady();
extern void LcdWriteCmd(unsigned char cmd);
extern void LcdWriteDat(unsigned char dat);
extern void LcdSetCursor(unsigned char x, unsigned char y);
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
extern void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len);
extern void InitLcd1602();
extern void LcdShowDat(unsigned char x, unsigned char y, unsigned char dat);
extern void E2Write(unsigned char *buf, unsigned char addr, unsigned char len);
extern void E2Read(unsigned char *buf, unsigned char addr, unsigned char len);
bit CmpMemory(unsigned char *ptr1, unsigned char *ptr2, unsigned char len);
unsigned char key_board();
unsigned char cnt=0;
unsigned char a[2],b[2]="1",c1=0,c2[10],c3[10],d=0,e=0;
bit f=0,g=0;
void delay_ms(unsigned int i)
{
 unsigned char k;
 while(i--)
{
  for(k=110;k>0;k--);
}
}
void main()
{
    int i=0;
    unsigned char temp;
	unsigned char mm[]="123456";
	unsigned char str[]="HELLO";
	unsigned char str2[]="Set Password";
	unsigned char str1[]="Input Password";
	unsigned char str3[]="yes";
	TMOD=0x01;
	TH0=0x4C;
	TL0=0x00;
	IE=0x82;
	TR0=1;
    InitLcd1602();
	LcdShowStr(2,0,str);		 
	while(1)
	{
		c1=key_board();

		if(d==1)
		{ 

		  if(c1<=10)
		  {
		  LcdWriteDat(42);
		  if(c1==10)
		  c2[e]=48;
		  else
		  c2[e]=c1+48;
		  e++;
		  }else if(c1==12&&f==0)
		  {
		   e=0;
		   E2Read(c3,20,6);
		   if(CmpMemory(c2,c3,6)==1)
		   {
		   LcdAreaClear(0,0,16);
		   LcdAreaClear(0,1,16);
		   LcdShowStr(2,0,"right");
		   f=1;
		   }else
		   {
		   LcdAreaClear(0,0,16);
		   LcdAreaClear(0,1,16);
		   LcdShowStr(2,0,"Error");
		   delay_ms(5000);
		   d=0;
		   } 
		  }else if(c1==12&&g==1)
		  {
		   e=0;
		   E2Write(c2,20,6);
		   E2Read(c3,20,6);
		   LcdAreaClear(0,0,16);
		   LcdAreaClear(0,1,16);
		   LcdShowStr(2,0,"set succeeded");
		   LcdShowStr(2,1,c3);
		  }
		}
		  if(c1==12&&d==0)
		{
		LcdShowStr(2,0,str1);
		LcdWriteCmd(0x80+0x40+0x03);
		LcdWriteCmd(0x0f);
		  d=1;
		}
		if(f==1&&c1==11&&g==0)
		{
		   LcdAreaClear(0,0,16);
		   LcdAreaClear(0,1,16);
		   LcdShowStr(2,0,"Input Password");
		   LcdWriteCmd(0x80+0x40+0x03);
		   LcdWriteCmd(0x0f);
		   e=0;
		   g=1;
		}	  
	 	   
	} 	
}
void clock() interrupt 1
{
   cnt++;
   TH0=0x4C;
   TL0=0x00;
   if(cnt>=20)
   {
	 
	 cnt=0;
   }
}

bit CmpMemory(unsigned char *ptr1, unsigned char *ptr2, unsigned char len)//比较字符串是否相等
{
 while (len--)
 {
 if (*ptr1++ != *ptr2++) //遇到不相等数据时即刻返回 0
 {
 return 0;
 }
 }
 return 1; //比较完全部长度数据都相等则返回 1 }
}
unsigned char key_board()//按键检测,放入while循环
{
   uchar temp; //P2口数据存储
   uchar count;
   uchar num_key=20;	  //返回的键值
   static uchar key_old=0xfe;//键盘扫描循环初始值
   static uchar  key_code;

   P2=key_old;
   temp=P2;
 if(temp!=key_old)//判断是否有按键按下
    {
	  count++;
	  if(count==10)
		{
			F0=1;
			key_code=temp;
		}
	} 
  else
      {
  	     count=0;
		 if(F0==1)
		 {
		 	 F0=0;
		 	 switch(key_code)//读出键值
			 {
			     //P2^0线设置为低电平 
                  case 0xEE: num_key=10;  break; 
                  case 0xDE: num_key=11;  break;
                  case 0xBE: num_key=12;  break;                                
                  case 0x7E: num_key=13;  break;            
                  //P2^1线设置为低电平 
                  case 0xED: num_key=7;  break;
                  case 0xDD: num_key=8;  break; 
                  case 0xBD: num_key=9;  break; 
                  case 0x7D: num_key=14;  break;                                   
                  //P2^2线 设置为低电平 
                  case 0xEB: num_key=4;  break;
                  case 0xDB: num_key=5; break;                           
                  case 0xBB: num_key=6; break;               
                  case 0x7B: num_key=15; break;   
                  //P2^3线 设置为低电平                                                   
                  case 0xE7: num_key=1; break; 
                  case 0xD7: num_key=2; break;
                  case 0xB7: num_key=3; break;
                  case 0x77: num_key=16; break; 
                                                                                                                                        
               }
			  
			 }
			  
		  key_old=_crol_(key_old,1);//改变扫描线 
			  if(key_old==0xef)
			   {
			  	key_old=0xfe;
			   }
	     }
	return(num_key);	//返回键值
}

main.c函数里有一些没有用上的值和数组 

有关51单片机简易电子密码锁的更多相关文章

  1. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  2. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  3. ruby-on-rails - 在 Rails 中自定义 "Password confirmation doesn' t 匹配密码 - 2

    有没有办法在Rails中为确认字段自定义消息?例如在设计中我必须输入密码和password_confirmation并且错误消息是:Passwordconfirmationdoesn'tmatchPassword我可以更改事件记录语言环境消息(“不匹配”),但它会在该语言环境消息的开头和结尾输出密码确认和密码,所以我得到如下内容:"PasswordconfirmationmustmatchPassword"有没有办法将其更改为不同的字符串?PasswordconfirmationandPasswordmustmatch.编辑另一件事是拥有完全自定义的消息,例如:'Setpassword

  4. ruby-on-rails - 验证电子邮件地址是 Paypal 用户 - 2

    我想验证一个电子邮件地址是否是PayPal用户。是否有API调用来执行此操作?是否有执行此操作的ruby​​库?谢谢 最佳答案 GetVerifiedStatus来自PayPal'sAdaptiveAccounts平台会为您做这件事。PayPal没有任何codesamples或SDKs用于Ruby中的自适应帐户,但我确实找到了编写codeforGetVerifiedStatusinRuby的人.您需要更改该代码以检查他们拥有的帐户类型的唯一更改是更改if@xml['accountStatus']!=nilaccount_status

  5. ruby-on-rails - Ruby on Rails - 需要在每周的特定时间将消息发送到电子邮件 - 2

    我想知道我应该如何着手这个项目。我需要每周向人们发送一次电子邮件。但是,这必须在每周的特定时间自动生成并发送。编码有多难?我需要知道是否有任何书籍可以提供帮助,或者你们中的任何人是否可以指导我。它必须使用ruby​​onrails进行编程。因此有一个网络服务和数据库集成。干杯 最佳答案 为什么这么复杂?您只需安排工作。您可以使用Delayed::Job例如。Delayed::Job让您可以使用run_at符号在特定时间安排作业,如下所示:Delayed::Job.enqueue(SendEmailJob.new(...),:run_

  6. ruby-on-rails - 我如何比较 'Bcrypt' Gem解密的密码和加密的密码 - 2

    我正在尝试对某些帖子的评论使用简单的身份验证。用户使用即时ID和密码输入评论我使用“bcrypt”gem将密码存储在数据库中。在comments_controller.rb中像这样@comment=Comment.new(comment_params)bcrypted_pwd=BCrypt::Password.create(@comment.user_pwd)@comment.user_pwd=bcrypted_pwd当用户想要删除他们的评论时,我使用data-confirm-modalgem来确认数据在这部分,我必须解密用户输入的密码以与数据库中的加密密码进行比较我怎样才能解密密码,

  7. ruby-on-rails - 如何在记录更新期间从验证中排除密码字段? ( rails 3.0.4, ruby 1.9.2) - 2

    我有一个允许更新用户记录的表单。它包含:password和:password_confirmation字段,但我不希望在数据库中已存储加密密码时对它们运行验证。View文件中的字段:'ConfirmPassword'%>在互联网上搜索时,我发现了这段代码,我认为它是针对以前版本的Ruby/Rails的。(我会把它放在我的用户模型中。)validates_presence_of:password,:on=>create由于我的用户模型中密码验证的语法不同(如下),我对我需要的语法感到困惑。validates:password,:presence=>true,:confirmation=>

  8. ruby-on-rails - Devise 在更改密码后注销用户 - 2

    我正在使用devise,当用户更改密码时,网站会将他们注销。我在网上读到,添加sign_in可以解决问题但不起作用,并且当密码更改时用户会注销。这是我的代码if@user.errors[:base].empty?and@user.update_attributes(params[:user])sign_in(current_user,:bypass=>true)flash[:success]="Useraccounthasbeensuccessfullyupdated"redirect_toedit_user_path(params[:site_id],@user)elserender

  9. ruby - 存储外部 API 的密码 - 最佳实践 - 2

    如果我构建了一个应用程序来访问来自Gmail、Twitter和Facebook的一些数据,并且我希望用户只需输入一次他们的身份验证信息,并且在几天或几周后重置,那会怎样是在Ruby中动态执行此操作的最佳方法吗?我看到很多人只是拥有他们客户/用户凭证的配置文件,如下所示:gmail_account:username:myClientpassword:myClientsPassword这看起来a)非常不安全,b)如果我想为成千上万的用户存储此类信息,它就无法工作。推荐的方法是什么?我希望能够在这些服务之上构建一个界面,因此每次用户进行交易时都必须输入凭据是不可行的。

  10. ruby-on-rails - 最灵活的 Rails 密码安全实现 - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭8年前。Improvethisquestion我需要实现具有各种灵活需求的密码安全。这些要求基本上取自Sanspasswordpolicy:Strongpasswordshavethefollowingcharacteristics:Containatleastthreeofthe

随机推荐