|
基于51单片机密码锁,1602显示屏,矩阵键盘以及继电器
10、读写存储并显示\
11最终程序-按键修改完密码后进行存储\
1、液晶显示\
2、读取矩阵键盘键码\
3、六个按键码的显示\
4、将程序3输入的6个键码显示改为隐藏\
5、判断输入的6位数和系统默认密码是否一样\
6、再程序5的基础上加入定时器\
7、在程序6的基础上加入声音和计数器\
8、加入修改开锁密码的功能\
9、读存储器并显示\
- //编译环境: KEIL UVISION2
- //单片机晶振:12M 单片机型号AT89S52
- //单片机晶振: 无特殊要求
- //作者:兴向荣电子元件店
- //功能:实现4组密码锁,4组密码均可以修改,修改后掉电可以保持
- #include <reg52.h>
- #include <intrins.h>
- #define uchar unsigned char
- #define uint unsigned int
- //==============LCD1602接口连接方法=====================
- /*-----------------------------------------------------
- |DB0-----P0.0 | DB4-----P0.4 | RW-------P2.3 |
- |DB1-----P0.1 | DB5-----P0.5 | RS-------P2.4 |
- |DB2-----P0.2 | DB6-----P0.6 | E--------P2.2 |
- |DB3-----P0.3 | DB7-----P0.7 |
- ---------------------------------------------------*/
- //===============================AT24C16控制引脚定义===================
- sbit sda_24c16=P2^0;//定义24C16串行数据线 第5脚
- sbit scl_24c16=P2^1;//定义24C16串行时钟线 第6脚
- //================================================*/
- #define LCM_Data P0 //LCD1602数据接口
- #define Busy 0x80 //用于检测LCM状态字中的Busy标识
- sbit LCM_RW = P2^6; //读写控制输入端,LCD1602的第五脚
- sbit LCM_RS = P2^5; //寄存器选择输入端,LCD1602的第四脚
- sbit LCM_E = P2^7; //使能信号输入端,LCD1602的第6脚
- #define key_port P3//用KEY_PORT代替P3
- sbit beek_c =P2^2;//定义蜂鸣器控制口
- sbit lock_c =P2^3;//定义锁信号输出口
- //**************函数声明***************************************
- void delay_3us();//3US的延时程序
- void nack_24c16();//24C16非应答信号
- void stop_24c16();//停止通讯信号
- void star_24c16();//启动信号
- void cack_24c16();//检测应答信号
- void mack_24c16();//发送应答信号
- void w1byte_24c16(uchar byte1);//向24C16写入一字节的数据
- uchar rd1byte_24c16(void);
- void read_24c16();//读数据操作
- void write_24c16();//写入16字节的数据操作
- void init_t0();//定时器0初始化函数
- void WriteDataLCM (uchar WDLCM);//LCD模块写数据
- void WriteCommandLCM (uchar WCLCM,BuysC); //LCD模块写指令
- uchar ReadStatusLCM(void);//读LCD模块的忙标
- void DisplayOneChar(uchar X,uchar Y,uchar ASCII);//在第X+1行的第Y+1位置显示一个字符
- void LCMInit(void);//LCD初始
- void delayms(uint ms);//1MS基准延时程序
- void delay_50us(unsigned int t);// 延时50*t(us)
- void DisplayListChar(uchar X,uchar Y,uchar delayms, uchar code *DData);
- void judge_xianshi(void);//显示处理程序
- void read_key(void);//按键键码读取子函数,返回相应的键码
- uchar keybord_value;//全局变量,存储最终的键码,其值为0-F
- void read_password(void);//6位数据的读取,也就是密码的读取
- uchar deal_password(void);//输入密码和系统码对比,看是否符合开锁条件
- //返回值为1就开锁,返回值为0为密码错误
- void makesurekeydeal();//确认键处理程序
- void cancelkeydeal();//取消键处理程序
- //***********矩阵键盘键码****************************************
- const uchar tabl2[16]={0xee,0xed,0xeb,0xe7,0xde,0xdd,0xdb,0xd7,0xbe,
- // s1 s2 s3 s4 s5 s6 s7 s8 s9
- 0xbd,0xbb,0xb7,0x7e,0x7d,0x7b,0x77};
- // s10 s11 s12 s13 s14 s15 s16
- uchar system[4][6];//定义 4*6个数据作为系统的密码 也就是4组6位数
- uchar passin[6];//定义全局变量,储存输入的6个按键数据
- uchar temppassin[6];//输入的数据暂存
- uchar flag1;//按键键入是否成功的标准,为1成功 为0失败
- uchar passcount;//键入次数的统计
- uchar flag2;//密码是否对的标志单位 ,为1成功 ,为0失败
- uchar flag3,flag4,flag5,flag6,flag7,flag12;
- uchar t0_crycle;
- uchar error_count;//密码输入次数
- //***********************主程序******************************
- main()
- {
- passcount=0;
- flag5=0;
- flag7=0;
- flag6=0;
- flag4=0;
- flag3=0;
- flag1=0;
- beek_c=1;//关闭蜂鸣器
- lock_c=0;//关闭锁
- LCMInit();//液晶初始化设置
- init_t0();
- TR0=0;//关闭定时器T0
- error_count=0;
- read_24c16();//单片机上电的时候先读取4组密码
- while(1)
- {
- read_key();//读取键码
- read_password();//把键码转为密码
- judge_xianshi();//显示
- }
- }
- //****************************************************
- //写入数据操作
- void write_24c16()
- {
- uchar i,j;
- write1:
- star_24c16();//发送启动脉冲
- w1byte_24c16(0xa0);//写24C16的芯片地址,高四位固定为1010,选择第一区,写操作
- cack_24c16();// 读取应答或非应答信号
- if(flag12)goto write1;//判断
- w1byte_24c16(01);//写入24C16的内部地址
- cack_24c16();
- if(flag12)goto write1;
- for(j=0;j<2;j++)//先写入12个字节的数据
- {
- for(i=0;i<6;i++)
- {
- w1byte_24c16(system[j][i]);
- cack_24c16();
- if(flag12)goto write1;
- }
- }
- stop_24c16();//写数据完毕,发送停止脉冲
- write2:
- star_24c16();//发送启动脉冲
- w1byte_24c16(0xa0);//写24C16的芯片地址,高四位固定为1010,选择第一区,写操作
- cack_24c16();// 读取应答或非应答信号
- if(flag12)goto write2;//判断
- w1byte_24c16(17);//写入24C16的内部地址
- cack_24c16();
- if(flag12)goto write2;
- for(j=2;j<4;j++)//写入后面24字节的数据
- {
- for(i=0;i<6;i++)
- {
- w1byte_24c16(system[j][i]);
- cack_24c16();
- if(flag12)goto write2;
- }
- }
- stop_24c16();//写数据完毕,发送停止脉冲
- }
- //***************************************************
- //读数据操作
- void read_24c16()
- { uchar i,j;
- read1:
- for(j=0;j<2;j++)//读取前面12字节的数据
- {
- for(i=0;i<6;i++)
- {
- nack_24c16();
- star_24c16();//发送启动脉冲
- w1byte_24c16(0xa0);//24C16的芯片地址,高四位固定为1010,选择第一区,写操作
- cack_24c16();
- if(flag12)goto read1;
- w1byte_24c16(j*6+i+1);//写入24C16的内部地址,选择第二页
- cack_24c16();
- if(flag12)goto read1;
- nack_24c16();
- stop_24c16();//重新开始
- star_24c16();//
- w1byte_24c16(0xa1);// 24C16的芯片地址,高四位固定为1010,选择第一区,读操作
- cack_24c16();
- if(flag12)goto read1;
- system[j][i]=rd1byte_24c16();
- }
- }
- mack_24c16();
- nack_24c16();
- stop_24c16();
- read2:
- for(j=2;j<4;j++)
- {
- for(i=0;i<6;i++)
- {
- nack_24c16();
- star_24c16();//发送启动脉冲
- w1byte_24c16(0xa0);//24C16的芯片地址,高四位固定为1010,选择第一区,写操作
- cack_24c16();
- if(flag12)goto read2;
- w1byte_24c16(j*6+i+5);//写入24C16的内部地址,选择第二页
- cack_24c16();
- if(flag12)goto read2;
- nack_24c16();
- stop_24c16();//重新开始
- star_24c16();//
- w1byte_24c16(0xa1);// 24C16的芯片地址,高四位固定为1010,选择第一区,读操作
- cack_24c16();
- if(flag12)goto read2;
- system[j][i]=rd1byte_24c16();
- }
- }
- mack_24c16();
- nack_24c16();
- stop_24c16();
- for(j=0;j<4;j++)//
- {
- for(i=0;i<6;i++)
- {
- if(system[j][i]>15)system[j][i]=1;//出厂芯片读出的是FF,这样就没有密码可以输入了,防止防止这种任何密码都无效的情况,我们设置出厂4组密码都是111 111
- }
- }
- }
- //****************************************************
- //显示处理程序
- void judge_xianshi()
- {
- uchar i;
- _nop_();
- if(flag2==0)//开关初始显示
- {
- DisplayListChar(0,0,0, "Electronics Lock");//
- DisplayListChar(1,0,0, "Password:");
- for(i=0;i<passcount&&i<6;i++)//显示输入的6个数据
- {
- DisplayOneChar( 1, 9+i, '*'); //第一行改为隐藏
- // //第二行改为真实显示,可以把这句删除就隐藏密码了
- }
- }
- else if(flag2==5)
- {
- DisplayListChar(0,0,0, "Change passwor ");
- DisplayOneChar( 0,15,flag5+0x30);
- DisplayListChar(1,0,0, "0: n: ");
-
- for(i=0;i<6;i++)
- {
- DisplayOneChar( 1,i+2,system[flag5-1][i]+0x30);
- }
- for(i=0;i<6;i++)DisplayOneChar( 1,i+10,temppassin[i]+0x30);
- }
- }
- //6位数据的读取,也就是密码的读取
- void read_password()
- {
- _nop_();
- if(flag1==1 )//判断键入是否成功
- {
- flag1=0;
- if(passcount<6 && flag5==0)
- {
- passin[passcount]=keybord_value;
- passcount++;
- }
- if(passcount<6 && flag5!=0)
- {
- if(keybord_value!=15 && keybord_value!=14 )
- {
- flag7=1;
- temppassin[passcount]=keybord_value;
- passcount++;
- }
- }
- cancelkeydeal();//取消键处理程序
- makesurekeydeal();//确认键处理程序
- if(keybord_value==15 && flag7==0)//判断是否为设置键
- {
- flag5++;
- if(flag5>=5)flag5=1;
- }
- }
- }
- //************************************************
- //确认键处理程序
- void makesurekeydeal()
- {
- uchar i;
- _nop_();
- if(keybord_value==12 && flag7==0 )//判断时候为确认键
- {
- flag2=deal_password();// 判断输入的密码是否正确
- passcount=0;
- if(flag3==0)//按第一次确认
- {
- flag3=1;
- if(flag2==1)//输入的密码正确
- {
- beek_c=0;//开蜂鸣器
- lock_c=1;//开锁
- flag6=1;
- DisplayListChar(1,0,0, "Opening........");//在液晶的第二行显示字符"OPERING
- TR0=1;
- flag4=0;
- error_count=0;
- while(flag4<=20);//密码正确开锁信号闭合2秒 蜂鸣器响2秒
- flag4=0;
- flag2=0;
- flag3=0;
- TR0=0;
- WriteCommandLCM(0x01,1); //清除显示一下
- beek_c=1;//关闭蜂鸣器
- lock_c=0;//断开锁信号输出
-
- }
- else if(flag2==2)//输入的密码错误
- {
- beek_c=0;
- TR0=1;
- flag6=0;
- error_count++;//密码输入错误后,计数器+1
- DisplayListChar(1,0,0, "Input Error TIS");
- DisplayOneChar( 1,12,error_count+0x30);//显示输入多少次错误
- while(flag4<=5);//密码输入错误蜂鸣器响0.5秒,关0.5秒的频率响2次
- beek_c=1;
- flag4=0;
- while(flag4<=5);
- beek_c=0;
- flag4=0;
- while(flag4<=5);
- beek_c=1;
- flag4=0;
- flag2=0;
- flag3=0;
- TR0=0;
- if(error_count>=3)//如果密码输入3次错误经锁定2.54分钟或需要重新断电
- {
- DisplayListChar(0,0,0, "Input Error 3 Ts");
- DisplayListChar(1,0,0, "locked 3 minutes");
- TR0=1;
- while(flag4<=254);//锁定时间过后系统恢复
- DisplayListChar(0,0,0, "Electronics Lock");
- TR0=0;
- error_count=0;
- flag4=0;
- }
- }
- }
- /* else if(flag3==1)//第二次按确认
- {
- flag2=0;
- flag3=0;
- }*/
- WriteCommandLCM(0x01,1); //清除显示一下
- }
- if(keybord_value==12 && flag7==1 )//确认修改密码
- {
- for(i=0;i<6;i++)
- {
- system[flag5-1][i]=temppassin[i];//把暂存单元的数据传给对应的开始密码
- temppassin[i]=0;//暂存单元清零,下次修改密码可以再次利用
- }
- write_24c16();
- flag7=0;//各种标志单元清零
- flag2=0;
- flag3=0;
- flag5=0;
- flag6=0;
- passcount=0; //按键次数计数清零
- WriteCommandLCM(0x01,1); //清除显示一下
- }
- }
- //************************************************
- //取消键处理程序
- void cancelkeydeal()
- {
- uchar i;
- _nop_();
- if(keybord_value==13 )//判断是否为取消键
- {
- if(flag7==0)
- {
- for(i=0;i<passcount;i++)
- {
- passin[i]=0;
- }//清零
- passcount=0;//按取消键后,键入按键次数的计数单元清零
- if(flag3==1)//取消显示密码错误
- {
- flag2=0;
- flag3=0;
- }
- }
- else if(flag7==1)
- {
- flag7=0;
- flag2=0;
- flag3=0;
- flag5=0;
- flag6=0;
- passcount=0;
-
- }
- WriteCommandLCM(0x01,1); //清除显示一下
- }
- }
- uchar deal_password()//输入密码和系统码对比,看是否符合开锁条件
- {
- uchar i,j,temp2,temp;
- temp2=0;
- _nop_();
- for(j=0;j<4;j++)//依次判断4组
- {
- temp=0;
- for(i=0;i<6;i++)
- {
- if(system[j][i]==passin[i])temp++;//如果系统密码和键入的密码对应的位一样,则暂存单元自加1
- }
- if(temp==6)
- {
- temp2=1;
- return(1);//如果TEMP为6,证明键入的密码和4组系统密码中的一组一样;
- break;//退出判断
- }
- }
- if(temp2==0)return(2);
- }
- void read_key(void)
- {
- uchar key_value,i=1;
- uchar key_high_value,key_low_value;//定义变量暂存高四位和低四位按键的键码
- _nop_();
- key_port=0xf0;//置按键控制口为1,准备读入数据
- key_value=key_port & 0xf0;
- if(key_value!=0xf0)//如果没有按键按下,则相等
- {
- delay_50us(200);//延时10ms去除抖动
- key_port=0xf0;//再次讲IO置1,准备读入去除抖动后的数据
- key_value=key_port & 0xf0;
- switch (key_value)
- { case 0xf0:break;//经软件延时后,判断为误判,则退出函数,返回
- case 0x70:key_high_value=0x70;break;
- case 0xb0:key_high_value=0xb0;break;
- case 0xd0:key_high_value=0xd0;break;
- case 0xe0:key_high_value=0xe0;break;
- default:key_high_value=0x00;break;//多个按键同时按下,显示0,然后退出
- }
- key_port=0x0f;//交换高四位和低四位的状态,再次读取键码
- key_value=key_port & 0x0f;
- switch(key_value)
- {
- case 0x07:key_low_value=0x07;break;
- case 0x0b:key_low_value=0x0b;break;
- case 0x0d:key_low_value=0x0d;break;
- case 0x0e:key_low_value=0x0e;break;
- default:key_low_value=0x00;break;
- }
- key_value=key_high_value | key_low_value;
- for(i=0;i<16;i++)
- {
- if(key_value==tabl2[i])
- {
- keybord_value=i;
- flag1=1;//置按键码读取成功标志位1;
- if(flag6==1 && keybord_value==14)
- {
- flag1=0;
- }
- break;
- }
- }
- beek_c=0;//开蜂鸣器
- TR0=1;
- while(flag4<=1);//按按键的时候蜂鸣器响0.1秒
- flag4=0;
- TR0=0;//关闭定时器
- beek_c=1;//关闭蜂鸣器
- if(flag6!=0)
- {
- if(keybord_value==14)
- {
- TR0=1;
- key_port=0xf0;//置按键控制口为1,准备读入数据
- while((key_port & 0xf0 )!=0xf0 && flag5==0)
- {
-
- if(flag4>=30)
- {
- TR0=0;
- flag4=0;
- flag2=5;
- flag5=1;
- flag7=0;
- }
- }
- }
- }
- key_port=0xf0;//置按键控制口为1,准备读入数据
- while((key_port & 0xf0 )!=0xf0 && keybord_value!=14 ); //等待按键释放,按键释放后才往下执行程序
- }
- }
- //定时器0中断服务程序
- void timer0() interrupt 1
- {
- TH0=(65536-50000)/256;
- TL0=(65536-50000)%256;//定时器设置为定时50MS
- t0_crycle++;
- if(t0_crycle==2)// 0.1秒
- {
- t0_crycle=0;
- flag4++; //定时器开启后,FLAG4每0.1秒自加1
- if(flag6!=0 && flag4>=40)//输入密码正确开锁后超过2分钟后不能进入修改开锁密码的界面
- {
- flag6=0;
- flag4=0;
- TR0=0;
- }
- }
- }
- //********************************************************************************************
- void init_t0()
- {
- _nop_();
- TMOD=0x01;//设定定时器工作方式1,定时器定时50毫秒
- TH0=(65536-50000)/256;
- TL0=(65536-50000)%256;
- EA=1;//开总中断
- ET0=1;//允许定时器0中断
- t0_crycle=0;//定时器中断次数计数单元
- }
- /*====================================================================
- 按指定位置显示一串字符:第 X 行,第 y列
- 注意:字符串不能长于16个字符
- ======================================================================*/
- void DisplayListChar(uchar X,uchar Y,uchar ms, uchar code *DData)
- {
- uchar ListLength;
- _nop_();
- ListLength = 0;
- X &= 0x1;
- Y &= 0xF; //限制X不能大于15,Y不能大于1
- while (DData[ListLength]!='\0') //若到达字串尾则退出
- {
- if (Y <= 0xF) //X坐标应小于0xF
- {
- DisplayOneChar(X, Y, DData[ListLength]); //显示单个字符
- ListLength++;
- Y++;
- delayms(ms);//延时显示字符串
- }
- else
- break;//跳出循环体
- }
- }
- /*======================================================================
- LCM初始化
- ======================================================================*/
- void LCMInit(void)
- {
- LCM_Data = 0;
- _nop_();
- WriteCommandLCM(0x38,0); //三次显示模式设置,不检测忙信号
- delayms(5);
- WriteCommandLCM(0x38,0);
- delayms(5);
- WriteCommandLCM(0x38,0);
- delayms(5);
- WriteCommandLCM(0x38,1); //显示模式设置,开始要求每次检测忙信号
- WriteCommandLCM(0x08,1); //关闭显示
- WriteCommandLCM(0x01,1); //显示清屏
- WriteCommandLCM(0x06,1); // 显示光标移动设置
- WriteCommandLCM(0x0C,1); // 显示开及光标设置
- delayms(100);
- }
- //==============================LCD1602显示子程序================================================
- // 写数据函数: E =高脉冲 RS=1 RW=0
- //======================================================================*/
- void WriteDataLCM(uchar WDLCM)
- {
- ReadStatusLCM(); //检测忙
- _nop_();
- LCM_Data = WDLCM;
- LCM_RS = 1;
- LCM_RW = 0;
- LCM_E = 0; //若晶振速度太高可以在这后加小的延时
- LCM_E = 0; //延时
- LCM_E = 1;
- }
- /*====================================================================
- 写指令函数: E=高脉冲 RS=0 RW=0
- ======================================================================*/
- void WriteCommandLCM(uchar WCLCM,BuysC) //BuysC为0时忽略忙检测
- {
- if (BuysC) ReadStatusLCM(); //根据需要检测忙
- LCM_Data = WCLCM;
- LCM_RS = 0;
- LCM_RW = 0;
- LCM_E = 0;
- LCM_E = 0;
- LCM_E = 1;
- }
- /*====================================================================
- 正常读写操作之前必须检测LCD控制器状态:E=1 RS=0 RW=1;
- DB7: 0 LCD控制器空闲,1 LCD控制器忙。
- 读状态
- ======================================================================*/
- uchar ReadStatusLCM(void)
- {
- LCM_Data = 0xFF;
- LCM_RS = 0;
- LCM_RW = 1;
- LCM_E = 0;
- LCM_E = 0;
- LCM_E = 1;
- while (LCM_Data & Busy); //检测忙信号
- return(LCM_Data);
- }
- /*======================================================================
- 功 能: 在1602 指定位置显示一个字符:第一行位置0~15,第二行16~31
- 说 明: 第 X 行,第 y 列 注意:字符串不能长于16个字符
- ======================================================================*/
- void DisplayOneChar( uchar X, uchar Y, uchar ASCII)
- {
- X &= 0x1;
- Y &= 0xF; //限制Y不能大于15,X不能大于1
- if (X) Y |= 0x40; //当要显示第二行时地址码+0x40;
- Y |= 0x80; // 算出指令码
- WriteCommandLCM(Y, 0); //这里不检测忙信号,发送地址码
- WriteDataLCM(ASCII);
- }
- /*====================================================================
- 设定延时时间:x*1ms
- ====================================================================*/
- void delayms(uint Ms)
- {
- uint i,TempCyc;
- for(i=0;i<Ms;i++)
- {
- TempCyc =70;
- while(TempCyc--);
- }
- }
- //***********************************************************************
- //函数名称:void delay_50US(unsigned int t)
- //功能: 延时50*t(us)
- void delay_50us(unsigned int t)
- {
- unsigned char j;
- for(;t>0;t--)
- {
- for(j=19;j>0;j--);
- }
- }
- //******************************************************************************************
- //***********************************************
- //3微秒延时程序
- void delay_3us()
- {
- ;
- ;
- }
- //*************************************************
- //功能:发送非应答信号
- void nack_24c16()
- {
- sda_24c16=1;
- delay_3us();
- delay_3us();
- scl_24c16=1;
- delay_3us();
- delay_3us();
- scl_24c16=0;
- sda_24c16=0;
- }
- //*************************************************
- //功能:发送IIC停止信号
- void stop_24c16()
- {
- sda_24c16=0;
- scl_24c16=1;
- delay_3us();
- delay_3us();
- sda_24c16=1;
- delay_3us();
- delay_3us();
- scl_24c16=0;
- }
- //*************************************************
- //功能:发送启动通讯的信号
- void star_24c16()
- {
- sda_24c16=1;
- scl_24c16=1;
- delay_3us();
- delay_3us();
- sda_24c16=0;
- delay_3us();
- delay_3us();
- scl_24c16=0;
- }
- //****************************************************
- //功能:判断应答或非应答
- //说明:通讯出错时标志为1,否则为0
- void cack_24c16()
- {
- scl_24c16=0;
- sda_24c16=1;
- delay_3us();
- scl_24c16=1;
- flag12=0;//清除错误标志
- if(sda_24c16)flag12=1;
- scl_24c16=0;
- }
- //****************************************************
- //功能:发送应答信号
- void mack_24c16()
- {
- sda_24c16=0;
- scl_24c16=1;
- delay_3us();
- delay_3us();
- scl_24c16=0;
- sda_24c16=1;
- }
- //*************************************************
- //功能:向24C16写入一字节的数据
- void w1byte_24c16(uchar byte1)
- {
- uchar i=8;
- while(i--)
- {
- delay_3us();
- delay_3us();
- delay_3us();
- if(byte1 & 0x80)
- {sda_24c16=1;}
- else
- {sda_24c16=0;}
- delay_3us();
- delay_3us();
- delay_3us();
- scl_24c16=1;
- delay_3us();
- delay_3us();
- delay_3us();
- scl_24c16=0;
- byte1<<=1;
- }
- }
- //*******************************
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
下载:
rar (1)rr.rar
(1.95 MB, 下载次数: 46)
|
|