本帖最后由 LUQIMAI 于 2020-6-10 12:52 编辑
* 名称:基于STC51的简易密码锁
* 硬件配置:IAP15W4K61S4+LCD1602+PCF8574+4*4矩阵键盘
* 功能说明:键盘表
* [ 0,1,2,3,
* 4,5,6,7,
* 8,9,a,b,
* c,d,e,f
* ]
* 按键说明:f:确认键;e:设置键;b:上锁键
* 初始密码:1234
* 开门流程:通电,输入初始密码,按f键,提示open,同时P2灯亮代表开门。
* 修改密码流程:通电后,按e键,首先输入旧的密码(第一次设置时,旧密码就是初始密码),按照LCD提示操作即可。
* 上锁流程:开门后,按下b键,上锁,P2灯灭
* 灯光说明:流水灯-在修改密码时,每一步成功都会有流水灯提示
* 全亮灯-门开
* 全灭灯-门关
* 不足之处:1.不知是单片机识别出现问题,还是键盘读入的问题。有时输入正确的密码,但提示错误,慢慢的按键输入,出错的几率会低些
* 2.需要一直通电,如果重新供电后,修改的密码会失效,所以离应用到实际生活还有距离
* 3.暂时这些,有问题望指
* 如果有不同想法的朋友,欢迎一起交流学习!!
硬件连接图如下:
硬件连接的详细接法程序中有,不清楚的也可以直接回帖问我
注意注意!!!!做了几处修改,下面是优化版
注意注意!!!!做了几处修改,下面是优化版
注意注意!!!!做了几处修改,下面是优化版
修改: 1.添加删除功能
2.优化了几处错误,基本完成所有密码锁的功能
如有发现其他错误,望不惜赐教!!
此处是优化版的源码,请下载这个:
优化版.rar
(83.14 KB, 下载次数: 44)
(有几处错误)单片机源程序如下:
- #include <stc15.h>
- #include "intrins.h"
- #define uchar unsigned char
- #define uint unsigned int
- #define KeyPort P0 //外接键盘,低四位接行,高四位接列
- uchar KEY_TABLE[16] =
- {
- 0xEE, 0xDE, 0xBE, 0x7E,
- 0xED, 0xDD, 0xBD, 0x7D,
- 0xEB, 0xDB, 0xBB, 0x7B,
- 0xE7, 0xD7, 0xB7, 0x77
- };
- int flag1=0; //修改密码标志位
- uchar num,i=0,j;
- uchar passwordtemp[16],password[16]={'1','2','3','4'},newpassword[16]; //输入的密码,默认的密码
- uchar inputtimes; //密码输入错误次数
- uint n,m; //输入密码长度,实际密码长度
- bit Flag=0;\
- uchar light,light1=0,light2=0xf0; //设置成功,开锁,错误,
-
- sbit SCL = P1^0;
- sbit SDA = P1^1;
- char ADDR = 0x4e; // PCF8574 T 模块的地址码
- //char ADDR = 0x7e; // PCF8574 AT 模块的地址码
- //***************************** 延时 y ms ***********************************************
- void delay1(int y) //
- {
- ;
- while(y--)
- {
- unsigned char a,b,c;
- for(c=1;c>0;c--)
- for(b=142;b>0;b--)
- for(a=2;a>0;a--);
- }
- }
- //******************************** IIC 串口开始 ********************************************
- void IIC_start(void)
- {
- SDA=1;
- _nop_();
- SCL=1;
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- SDA=0;
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- SCL=0;
- _nop_();
- _nop_();
-
- }
- //********************************** IIC 串口写1个字节 ******************************************
- void IIC_writeByte(char temp)
- {
- char i;
- for(i=0;i<8;i++)
- {
- SDA=(bit)(temp & 0x80) ; // 根据规定1602的数据最高位必须为 1
- temp <<=1;
- _nop_();
- _nop_();
- SCL=1;
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- SCL=0;
- }
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- SDA=1;
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- SCL=1;
- _nop_();
- _nop_();
- _nop_();
- while(SDA);
- _nop_();
- SCL=0;
- }
- //******************************** 1602写命令 ********************************************
- void LCD_write_command(char comm)
- {
- char tmp;
- IIC_start(); // 串口开始
- IIC_writeByte(ADDR); // 先选PCF 8574T 的地址 (应该是相当于选中的意思吧)
-
- tmp = comm & 0xF0; // 与0xf0 应该是取第四位的意思吧
- tmp |= 0x0C; //保留高4位为指令的高四位,低四位为 RS = 0, RW = 0, EN = 1
- IIC_writeByte(tmp); //从串口送出
- delay1(20);
- tmp &= 0xFB; //Make EN = 0
- IIC_writeByte(tmp);
-
- tmp = (comm & 0x0F) << 4 ; //将指令的低四位 送到高位置保存
- tmp |= 0x0C; //RS = 0, RW = 0, EN = 1
- IIC_writeByte(tmp);
- delay1(20);
- tmp &= 0xFB; // Make EN = 0
- IIC_writeByte(tmp);
-
- }
- //******************************** 1602写数据 ********************************************
- void LCD_write_data(char data1)
- {
- char tmp;
- IIC_start();
- IIC_writeByte(ADDR); // 先选PCF 8574T 的地址 (应该是相当于选中的意思吧)
-
- tmp = data1 & 0xF0;
- tmp |= 0x0D; //RS = 1, RW = 0, EN = 1
- IIC_writeByte(tmp);
- delay1(20);
- tmp &= 0xFB; //Make EN = 0
- IIC_writeByte(tmp);
-
- tmp = (data1 & 0x0F) << 4 ;
- tmp |= 0x0D; //RS = 0, RW = 0, EN = 1
- IIC_writeByte(tmp);
- delay1(20);
- tmp &= 0xFB ; // Make EN = 0
- IIC_writeByte(tmp);
- }
- //******************************** 1602初始化 ********************************************
- void Init_Lcd(void)
- {
- LCD_write_command(0x33); //将8位总线转为4位总线
- delay1(50) ;
- LCD_write_command(0x32); //
- delay1(50) ;
- LCD_write_command(0x28); // 4位数据线,显示2行,5*7点阵字符 !如果是0x38 则为8位数据线,显示2行,5*7点阵字符
- delay1(50) ;
- LCD_write_command(0x0C); // 开显示,关闭光标,不闪烁
- delay1(50) ;
- LCD_write_command(0x06); // 设定输入方式,增量不位移
- delay1(50) ;
- LCD_write_command(0x01); // 清屏
- delay1(50) ;
- }
- //*************************************** 在指定位置显示字符串 *************************************
- void Write_LCD(int x, int y, char *str)
- {
- char addr;
- if( x < 0)
- {
- x = 0;
- }
- if(x > 15)
- {
- x = 15;
- }
- if(y<0)
- {
- y = 0;
- }
- if(y > 1)
- {
- y = 1;
- }
-
- addr = 0x80 + 0x40 * y + x; // Move cursor 移动光标
- LCD_write_command(addr);
- while (*str)
- {
- LCD_write_data(*str++);
- }
- }
- //-------------------------------------------- 显示字符串的函数 ----------------------------------------------------
- void LCD_write_word(unsigned char *s) //显示字符串的函数
- {
- while(*s>0)
- {
- LCD_write_data(*s);
- s++;
-
- }
- }
- //********************************* 指定位置显示一个字符*******************************************
- /*
- void Print_Char (unsigned char line,unsigned char num,unsigned char date)
- {
- LCD_write_command(line+num);
- LCD_write_data(date);
- }
- */
- //按指定位置显示一个字符(针对1602液晶)-用在温度显示
- void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData)
- {
- Y &= 0x1;
- X &= 0xF; //限制X不能大于15,Y不能大于1
- if (Y) X |= 0x40; //当要显示第二行时地址码+0x40;
- X |= 0x80; // 算出指令码
- LCD_write_command(X); //这里不检测忙信号,发送地址码
- LCD_write_data(DData);
- }
- //--------------------------------------初始化----------------------------------------------------------------
- void initial()
- {
- P1M0=0;
- P1M1=0;
- P2M0=0;
- P2M1=0;
- P0M0=0;
- P0M1=0;
- }
- /*------------------------------------------------
- 按键扫描函数,返回扫描键值
- ------------------------------------------------*/
- unsigned char KeyScan(void) //键盘扫描函数,使用行列反转扫描法
- {
- uchar cord_h,cord_l; //行列值中间变量
- uchar tempt;
- uint i;
- KeyPort=0xf0; //列线输出全为0
- cord_l=KeyPort&0xf0; //读入行线值
- if(cord_l!=0xf0) //先检测有无按键按下
- {
- delay1(10); //去抖
- if(cord_l!=0xf0)
- {
- cord_l=KeyPort&0xf0; //读入列线值
- KeyPort=0x0f; //行线输出为0
- cord_h=KeyPort&0x0f; //读入行线值
- while((KeyPort&0x0f)!=0x0f);//等待松开并输出
- tempt = cord_l+cord_h;
- for(i=0;i<16;i++)
- if(KEY_TABLE[i]==tempt)
- break;
- return(KEY_TABLE[i]);
- }
- }
- return(0xff); //返回该值
- }
- /*------------------------------------------------
- 按键值处理函数,返回扫键值
- ------------------------------------------------*/
- unsigned char KeyPro(void)
- {
- switch(KeyScan())
- {
- case 0xee:return '0';break;//0 按下相应的键显示相对应的码值
- case 0xde:return '1';break;//1
- case 0xbe:return '2';break;//2
- case 0x7e:return '3';break;//3
- case 0xed:return '4';break;//4
- case 0xdd:return '5';break;//5
- case 0xbd:return '6';break;//6
- case 0x7d:return '7';break;//7
- case 0xeb:return '8';break;//8
- case 0xdb:return '9';break;//9
- case 0xbb:return 'a';break;//a
- case 0x7b:return 'b';break;//b
- case 0xe7:return 'c';break;//c
- case 0xd7:return 'd';break;//d
- case 0xb7:return 'e';break;//e
- case 0x77:return 'f';break;//f
- default:return 0xff;break;
- }
- }
- //--------------------------------------------------------流水灯--------------------------------------------------------
- void RuningLight() //流水灯
- {
- light=0xfe;
- P2=light;
- for(j=0;j<8;j++)
- { light<<=1 ;
- delay1(250);
- P2=light;
- }
- P2=0xff;
-
-
- }
- //--------------------------------------------------------密码验证--------------------------------------------------------
- void VriefytPassword() //验证密码
- { if(flag1)
- m=sizeof(newpassword); //记录新密码的长度
- else
- m=sizeof(password); //记录旧密码的长度
- n=sizeof(passwordtemp); //记录输入密码的长度
- if(m==n) //长度判断
- {
- if(flag1) //修改密码后
- for(j=0;j<sizeof(passwordtemp);j++) //是否一一对应
- { if(passwordtemp[j]==newpassword[j])
- Flag=0;
- else
- { Flag=1;
- break;
- }
- }
- else //修改密码前
- for(j=0;j<sizeof(passwordtemp);j++) //是否一一对应
- { if(passwordtemp[j]==password[j])
- Flag=0;
- else
- { Flag=1;
- break;
- }
- }
- }
- else
- { Flag=1;
- Write_LCD(0,1," "); //清除该行
- delay1(10);
- Write_LCD(0,1,"Wrong length!"); //密码正确显示的信息
- delay1(10);
- }
- if(Flag) //输入错误提示和输入错误总次数判断,限定三次
- {
- Write_LCD(0,1," "); //清除该行
- delay1(10);
- Write_LCD(0,1,"Wrong Retry!"); //输入错误提示
- for(j=0;j<sizeof(passwordtemp);j++) //清除输入的错误密码
- passwordtemp[j]='\0';
- inputtimes++; //连续输入错误,则次数累加
- i=0;
- if(inputtimes==3)
- {
- Write_LCD(0,1," ");//清除该行
- delay1(10);
- Write_LCD(0,1,"Wrong 3 times!");//密码错误,提示重新输入
- while(1)
- P2=light2; //停止该位置,重启电源后才能输入,实际实用中则需要等到一定时间后才能再次输入。
- }
- }
- else
- {
- Write_LCD(0,1," ");//清除该行
- delay1(10);
- Write_LCD(0,1,"Right Open!>>>>");//密码正确显示的信息
- P2=light1;
- inputtimes=0;//输入正确则次数清零,重新计数
- Flag=0; //清除正确标志
- i=0;
-
- }
- }
- //--------------------------------------------------------修改密码时的密码验证--------------------------------------------------------
- void SetPassword() //设置新密码时,先验证
- { while(1)
- { num=KeyPro(); //扫描键盘
- if(num!=0xff) //如果扫描是按键有效值则进行处理
- {
- if(i==0)
- Write_LCD(0,1," ");//清除该行
- if(num=='f') //确定键按下,进入判断密码对错
- {
- i=0;
- if(flag1)
- m=sizeof(newpassword);
- else
- m=sizeof(password);
- n=sizeof(passwordtemp);
-
- if(m==n) //长度判断
- { if(flag1)
- for(j=0;j<sizeof(passwordtemp);j++) //是否一一对应
- { if(passwordtemp[j]==newpassword[j])
- Flag=0;
- else
- { Flag=1;
- break;
- }
- }
- for(j=0;j<sizeof(passwordtemp);j++) //是否一一对应
- { if(passwordtemp[j]==password[j])
- Flag=0;
- else
- { Flag=1;
- break;
- }
- }
- }
- else
- { Flag=1;
- Write_LCD(0,1," "); //清除该行
- delay1(10);
- Write_LCD(0,1,"Wrong length!"); //密码正确显示的信息
- delay1(10);
- i=0;
- P2=light2;
- inputtimes++;
-
- }
- if(Flag) //输入错误提示和输入错误总次数判断,限定三次
- {
- Write_LCD(0,1," "); //清除该行
- delay1(10);
- Write_LCD(0,1,"Wrong Retry!"); //密码错误
- for(j=0;j<sizeof(passwordtemp);j++)
- passwordtemp[j]='\0';
- inputtimes++; //连续输入错误,则次数累加
- if(inputtimes==3)
- {
- Write_LCD(0,1," ");//清除该行
- delay1(10);
- Write_LCD(0,1,"Wrong 3 times!");//密码错误,提示重新输入
- while(1)
- P2=light2; //停止该位置,重启电源后才能输入,实际实用中则需要等到一定时间后才能再次输入。
- }
- }
- else
- {
- Write_LCD(0,1," ");//清除该行
- delay1(10);
- Write_LCD(0,1,"Right Password!");//密码正确
- RuningLight();
- i=0;
- inputtimes=0;//输入正确则次数清零,重新计数
- Flag=0; //清除正确标志
- break;
- }
- }
-
- else //没有按确定键,保存键值
- {
- if(i<16)
- {
- passwordtemp[i]=num; //保存密码
- DisplayOneChar(i,1,num); //输入的密码用"*"代替
- i++; //计数,不能超过16位
- }
- else
- { Write_LCD(0,1," ");//清除该行
- delay1(10);
- Write_LCD(0,1,"Too Many Retry");
- inputtimes++;
- i=0;
- P2=light2;
- }
- }
- }
- }
- }
- //--------------------------------------------------------新密码输入--------------------------------------------------------
- void NewPasswordInput() //新密码输入
- {
- while(1)
- { num=KeyPro(); //扫描键盘
- if(num!=0xff) //如果扫描是按键有效值则进行处理
- {
- if(i==0)
- Write_LCD(0,1," ");//清除该行
- if(num=='f')
- { i=0;
- RuningLight();
- break;
- }
- else if(i<16) //新密码不能超过16位
- {
- newpassword[i]=num; //保存密码
- DisplayOneChar(i,1,num); //显示新密码
- i++; //计数,不能超过16位
- }
- else
- { i=0;
- Write_LCD(0,1," "); //清除该行
- delay1(10);
- Write_LCD(0,1,"Too Many Retry"); //输入过多,报错
- delay1(10);
- for(j=0;j<sizeof(newpassword);j++) //清除新密码
- newpassword[j]='\0';
- P2=light2;
- Write_LCD(0,1," "); //清除该行
- delay1(10);
- Write_LCD(0,1,"new password!"); //重新输入新密码
- delay1(10);
-
- }
- }
- }
- }
- //--------------------------------------------------------确认修改的密码--------------------------------------------------------
- void AgainInput() //新密码确认
- {
- while(1)
- {
- num=KeyPro(); //扫描键盘
- if(num!=0xff) //如果扫描是按键有效值则进行处理
- { if(i==0)
- Write_LCD(0,1," ");//清除该行
- if(num=='f')
- {
- i=0;
- n=sizeof(passwordtemp);
- m=sizeof(newpassword);
- if(m==n) //长度判断
- {
- for(j=0;j<sizeof(passwordtemp);j++) //是否一一对应
- { if(passwordtemp[j]==newpassword[j])
- Flag=0;
- else
- { Flag=1;
- break;
- }
- }
- }
- else
- Flag=1;
- if(Flag) //输入错误提示和输入错误总次数判断,限定三次
- {
- Write_LCD(0,1," "); //清除该行
- delay1(10);
- Write_LCD(0,1,"Difference!!"); //密码正确显示的信息
- for(j=0;j<sizeof(passwordtemp);j++) //清除密码
- passwordtemp[j]='\0';
- i=0;
- Flag=0;
- P2=light2;
- }
-
- else
- {
- Write_LCD(0,1," ");//清除该行
- delay1(10);
- Write_LCD(0,1,"Right!"); //密码正确显示的信息
- delay1(10);
- Write_LCD(0,1," ");//清除该行
- delay1(10);
- Write_LCD(0,1,"Finish!");
- RuningLight();
- Write_LCD(0,1," ");//清除该行
- delay1(10);
- Write_LCD(0,1,"Right Open>>>");//清除该行
- P2=light1;
- inputtimes=0;//输入正确则次数清零,重新计数
- Flag=0; //清除正确标志
- i=0;
- break;
- }
- }
- else if(i<16) //密码不能超过16位
- {
- passwordtemp[i]=num; //保存密码
- DisplayOneChar(i,1,num); //显示新密码
- i++; //计数,不能超过16位
- }
- else
- { Write_LCD(0,1," "); //清除该行
- delay1(10);
- Write_LCD(0,1,"Too Many Retry"); //输入过多,报错
- delay1(10);
- for(j=0;j<sizeof(passwordtemp);j++) //清除密码
- passwordtemp[j]='\0';
- i=0; //重新输入
- P2=light2;
- Write_LCD(0,1," "); //清除该行
- delay1(10);
- Write_LCD(0,1,"Input New Again!"); //重新输入新密码
- delay1(10);
- }
- }
-
- }
- }
- //--------------------------------------------------------主函数--------------------------------------------------------
- void main()
- {
- initial();
- Init_Lcd(); //初始化液晶屏
- delay1(10); //延时用于稳定,可以去掉
- Write_LCD(0,0," Welcome! "); //写入第一行信息,主循环中不再更改此信息,所以在while之前写入
- delay1(10);
- Write_LCD(0,1,"Input password!"); //写入第二行信息,提示输入密码
- delay1(10);
- while (1) //主循环
- {
- num=KeyPro(); //扫描键盘
- if(num!=0xff) //如果扫描是按键有效值则进行处理
- {
- if(i==0)
- Write_LCD(0,1," "); //清除该行
- if(num=='e') //设置新密码
- { P2=0xff;
- for(j=0;j<sizeof(newpassword);j++) //释放存储新密码数组
- newpassword[j] = '\0';
- Write_LCD(0,1,"Set Password!");
- delay1(10);
- Write_LCD(0,1," "); //清除该行
- delay1(10);
- Write_LCD(0,1,"Input Password!"); //用户验证
- delay1(10);
- SetPassword(); //验证密码
- P2=0xff;
- Write_LCD(0,1," "); //清除该行
- delay1(10);
- Write_LCD(0,1,"New Password!"); //新密码输入
- delay1(10);
- NewPasswordInput(); //新密码输入
- P2=0xff;
- Write_LCD(0,1," ");
- delay1(10);
- Write_LCD(0,1,"Again Input!!");
- AgainInput(); //新密码确认
- for(j=0;j<sizeof(passwordtemp);j++) //释放存储输入密码数组
- passwordtemp[j] = '\0';
- flag1=1; //修改密码标志位
- }
- else if(num=='f') //确定键按下,进入判断密码对错
- { i=0; //清除输入标志
- VriefytPassword(); //密码认证
- }
- else if(num=='b') //上锁
- {
- P2=0xff;
- delay1(10);
- Write_LCD(0,1," "); //清除该行
- delay1(10);
- Write_LCD(0,1,"Input password!");
- i=0;
- for(j=0;j<sizeof(passwordtemp);j++)
- passwordtemp[j] ='\0';
- }
- else //没有按确定键,上锁键,设置键,保存键值
- {
- if(i<16)
- {
- passwordtemp[i]=num; //保存密码
- DisplayOneChar(i,1,num); //输入的密码用"*"代替
- i++; //计数,不能超过16位
- }
- else
- { Write_LCD(0,1," ");//清除该行
- delay1(10);
- Write_LCD(0,1,"Too Many Retry");
- inputtimes++;
- i=0;
- }
- }
- }
- }
- }
复制代码
所有程序51hei提供下载:
基于STC51的简易密码锁.rar
(50.08 KB, 下载次数: 22)
|