本帖最后由 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.暫時這些,有問題望指
* 如果有不同想法的朋友,歡迎一起交流學習!!
硬件連接圖如下:
硬件連接的詳細接法程序中有,不清楚的也可以直接回帖問我
51hei圖片_20200607182508.jpg (98.41 KB, 下載次數: 52)
下載附件
2020-6-8 08:14 上傳
注意注意!!!!做了幾處修改,下面是優化版
注意注意!!!!做了幾處修改,下面是優化版
注意注意!!!!做了幾處修改,下面是優化版
修改: 1.添加刪除功能
2.優化了幾處錯誤,基本完成所有密碼鎖的功能
如有發現其他錯誤,望不惜賜教!!
此處是優化版的源碼,請下載這個:
優化版.rar
(83.14 KB, 下載次數: 44)
2020-6-10 12:51 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
(有幾處錯誤)單片機源程序如下:
- #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)
2020-6-8 08:15 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
|