提示:文章寫完后,目錄可以自動(dòng)加粗樣式生成,如何生成可參考右邊的幫助文檔
設(shè)計(jì)要求
微機(jī)原理及單片機(jī)應(yīng)用技術(shù)的課程設(shè)計(jì),C51設(shè)計(jì)一個(gè)密碼鎖;
要求:鍵盤上有0-9個(gè)數(shù)字按鍵,功能鍵:確認(rèn)和取消等,可設(shè)置復(fù)合鍵。密碼的位數(shù)及密碼可以任意設(shè)定,,當(dāng)輸入數(shù)字和設(shè)置的密碼相同的時(shí)候,鎖打開,否則無法打開。
基本實(shí)現(xiàn):掉電以后密碼不保存,回復(fù)初始密碼;
提高實(shí)現(xiàn):掉電以后密碼不丟失,可由鍵盤輸入任意密碼
一、系統(tǒng)模型設(shè)計(jì)
Proteus仿真圖
附圖為提高實(shí)現(xiàn)中的仿真,基礎(chǔ)實(shí)現(xiàn)的區(qū)別在右下角少了一個(gè)AT24c02芯片。
二、基礎(chǔ)實(shí)現(xiàn)
2.1 程序結(jié)構(gòu)
程序主要有四個(gè)函數(shù):
void delay() //延時(shí)函數(shù)
void display() //數(shù)碼管顯示函數(shù)
void key_scan() //鍵盤掃描函數(shù)
void main() //
void time_count() interrupt 1 //中斷定時(shí)函數(shù)
初始化代碼:
#include <reg51.h>
#define keyboard P2
unsigned char code table[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
unsigned char code lig[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //P1引腳選位
unsigned int code number[4][3]={{7,8,9},{4,5,6},{1,2,3},{0,0,0}}; //鍵盤查表
unsigned char show[8]={0xbf,0xbf,0xbf,0xbf,0xbf,0xbf,0xbf,0xbf}; //數(shù)碼管只顯示show中的內(nèi)容,數(shù)碼管默認(rèn)顯示‘橫杠’
unsigned char keywords[21] = {1,2,3,4}; //初始密碼
unsigned char input[21] = {0}; //承載輸入密碼
unsigned char a=0,b,c,d,j,k,temp,key,M,N,clc,enter,change,count=0,number_1=60,z,pp = 3; //寄存器的定義 **clc,enter,change 這三個(gè)我將其稱為狀態(tài)寄存器,當(dāng)對(duì)應(yīng)按鍵按下的時(shí)候置1;pp密碼位數(shù),初始密碼更改時(shí)要同步更改
signed int num=-1,lock=1; //lock和上述的clc一樣,都是狀態(tài)寄存器,lock=1時(shí)鎖定,0解鎖;num為密碼輸入時(shí)候的位數(shù)輔助計(jì)數(shù),注意初始為-1
sbit led1=P3^3; //紅燈--鎖定指示燈
sbit led2=P3^4; //綠燈--解鎖指示燈
void delay(unsigned char o)
{
while(o--);
}
void display() //8位數(shù)碼管顯示輸出
{
unsigned int i;
for(i=0;i<8;i++)
{
P1 = lig[i]; //多位數(shù)碼管選位
P0 = show[i]; //數(shù)碼管顯示內(nèi)容
delay(100);
P0 =0xff;
}
}
2.2 4×4鍵盤掃描
鍵盤掃描代碼:
void key_scan() //鍵盤按鍵檢測
{
P2=0x0f;
delay(10);
if(keyboard == 0x0f) //a用作按鈕松手檢測
a=0;
if(a == 0)
{
P2=0x0f;
temp=P2;
temp=temp&0x0f;
if(temp!=0x0f)
{
delay(10);
switch(temp)
{
case 0x0e:N=0;break;
case 0x0d:N=1;break;
case 0x0b:N=2;break;
case 0x07:N=3;break;
}
P2=0xf0;
temp=P2;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(10);
switch(temp)
{
case 0xe0:M=0;a=1;break;
case 0xd0:M=1;a=1;break;
case 0xb0:M=2;a=1;break;
case 0x70:M=3;a=1;break;
}
if(M<3)
{
key=number[N][M]; //查表確定按鍵對(duì)應(yīng)的數(shù)
num++; //輸入位數(shù)統(tǒng)計(jì)
}
else
{
switch(N) //對(duì)應(yīng)最后一列按鍵
{
case 0:change=1;break; //功能按鍵置位對(duì)應(yīng)的狀態(tài)寄存器
case 1:clc=1;break;
case 2:enter=1;break;
case 3:lock=1;break;
}
}
}
}
}
}
2.3 處理函數(shù)(數(shù)據(jù)處理函數(shù))
數(shù)據(jù)處理的內(nèi)容都放置在這個(gè)函數(shù)中
void processing()
{
if(num>-1) //輸入input數(shù)組轉(zhuǎn)換為顯示數(shù)組
{
input[num]=key; //輸入密碼進(jìn)入input數(shù)組中
if(num>-1 && num<7)
{
show[num]=table[key]; //將輸入的密碼實(shí)時(shí)放入show數(shù)組中并顯示在數(shù)碼管上;**注意放入show中的是對(duì)應(yīng)顯示數(shù)字的8位二進(jìn)制數(shù)不是密碼本身
}
if(num>7 && num != b)
{
for(c=0;c<8;c++)
{
show[c] = table[input[num-7+c]]; //在密碼超出8位后數(shù)碼管顯示內(nèi)容跟著滾動(dòng)
}
}
b = num; //實(shí)現(xiàn)每按一次數(shù)碼管內(nèi)容刷新一次
}
if(lock == 1 && enter == 1 ) //在鎖定狀態(tài)下,按確認(rèn)鍵開鎖
{
for(z = 0;z < pp+1;z++)
{
if(input[z] == keywords[z]) //比較輸入的密碼與設(shè)定的是否一致
d = 1;
if(input[z] != keywords[z])
{
d = 0;
break;
}
}
if(pp != num) //判斷密碼位數(shù)是否一樣
d = 0;
if(d) //設(shè)備解鎖
{
lock = 0;
number_1 = 30; //啟動(dòng)30秒倒計(jì)時(shí)鎖定
}
for( z = 0;z<21;z++) //無論解鎖與否都對(duì)input和show復(fù)位
{input[z] = 0;}
for( z = 0;z<8;z++)
{show[z]=0xbf;}
num = -1;
d = 0;
enter = 0; //狀態(tài)寄存器復(fù)位
}
if(clc) //清除鍵按下 //清除鍵
{
for( z = 0;z<21;z++)
{input[z] = 0;}
for( z = 0;z<8;z++)
{show[z]=0xbf;}
num = -1;
clc = 0;
}
if(lock == 0 && change == 1) //在開鎖情況下按更改密碼
{
for(z=0;z<21;z++)
{
keywords[z] = input[z];
}
lock = 1; //更改密碼后設(shè)備自動(dòng)鎖定
for( k = 0;k<8;k++)
{show[k]=0xbf;}
for( k = 0;k<21;k++)
{input[k] = 0;}
pp = num; //更改密碼位數(shù)
num = -1;
change = 0;
}
if(lock == 0 && number_1 == 0) //倒計(jì)時(shí)結(jié)束后鎖定
lock = 1;
}
2.4 主函數(shù)和中斷定時(shí)函數(shù)
void main()
{
EA=1; //系統(tǒng)初始化寄存器
ET0=1;
TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
TR0=1;
while(1)
{
display();
key_scan();
processing();
if(lock == 1) //指示燈
{
led1 = 0;
led2 = 1;}
else
{
led1 = 1;
led2 = 0;}
}
}
void time_count() interrupt 1 //定時(shí)器中斷實(shí)現(xiàn)延時(shí)
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
count++;
if(count==20)
{
count=0;
number_1--;
if(number_1<0)
number_1=59;
}
}
三、提高實(shí)現(xiàn)
說明
提高實(shí)現(xiàn)在基礎(chǔ)實(shí)現(xiàn)的基礎(chǔ)上有兩處的差異。
(1)主函數(shù)中系統(tǒng)啟動(dòng)從AT24C02中讀取密碼放入keywords中;
(2)更改密碼時(shí),多了一個(gè)密碼寫入AT24C02的步驟
(3)讀寫AT24C02的函數(shù)編寫
附上上述代碼
更改密碼部分
if(lock == 0 && change == 1) //在開鎖情況下按更改密碼
{
number_1 = 20;
for(z=0;z<8;z++)
{
keywords[z] = input[z];
}
qq = num;
At24c02Write(0x50, keywords); //密碼寫入24C02
delay(1000);
lock = 1;
for( k = 0;k<8;k++)
{show[k]=0xbf;}
for( k = 0;k<8;k++)
{input[k] = 0;}
num = -1;
change = 0;
}
主函數(shù)部分
void main() //主程序
{
EA=1; //系統(tǒng)初始化寄存器
ET0=1;
IT0=0;
TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
TR0=1;
// At24c02Write(0x50, keys); //keys[]為寫入芯片的初始密碼,應(yīng)當(dāng)在程序開頭定義
// Delay10us(); //用來第一次運(yùn)行的時(shí)候?qū)戇M(jìn)24C02(由于AT24C02芯片第一次沒有密碼數(shù)據(jù)在里面),在第一次運(yùn)行后將這兩行注釋,從新編譯一次就可以正常運(yùn)行
At24c02Read(keywords,0x50); //啟動(dòng)讀取密碼
while(1)
{
display();
key_scan();
processing();
if(lock == 1) //指示燈程序
{
led1 = 0;
led2 = 1;}
else
{
led1 = 1;
led2 = 0;}
}
}
AT24C02讀寫代碼
AT24C02的讀寫89C51沒有直接的函數(shù)調(diào)用,只能自己按照手冊(cè)通過引腳寫通訊函數(shù)。
AT24C02的讀寫下面兩篇博客寫的很好,想要學(xué)習(xí)AT24C02的讀者可以移步至
https://blog.csdn.net/snyanglq/article/details/50408489?
https://blog.csdn.net/zbl12345678910/article/details/106029054
下面附上讀寫代碼
/**********************************
** 函數(shù)名稱 :ACK
** 函數(shù)功能 : 應(yīng)答信號(hào)
**********************************/
void ACK() //發(fā)送應(yīng)答A子函數(shù)ACK
{
unsigned char i;
SCL = 1; //時(shí)鐘線發(fā)出時(shí)鐘脈沖
Delay10us();
while((SDA == 1) && (i < 200))i++ ;
SCL = 0; //與SCL=1組成時(shí)鐘脈沖
Delay10us();
}
/**********************************
** 函數(shù)名稱 :NACK
** 函數(shù)功能 : 非應(yīng)答信號(hào)
**********************************/
void NACK() //發(fā)送應(yīng)答A子函數(shù)NACK,見實(shí)例354
{
SDA = 1; //數(shù)據(jù)線高電平(發(fā)送數(shù)據(jù)1)
SCL = 0;
Delay10us();
SCL = 1; //時(shí)鐘線發(fā)出時(shí)鐘脈沖
Delay10us();
SCL = 0; //與SCL=1組成時(shí)鐘脈沖
SDA = 0; //數(shù)據(jù)線低電平復(fù)位
}
/*******************************************************************************
* 函數(shù)名 : Start_signal(void)
* 函數(shù)功能 : I2C總線起始信號(hào)
* 輸入 : 無
* 輸出 : 無
*******************************************************************************/
void I2C_Start_signal(void)
{
SDA = 1 ;
Delay10us();
SCL = 1 ;
Delay10us();
SDA = 0 ;
Delay10us();
SCL = 0 ;
Delay10us();
}
/*******************************************************************************
* 函數(shù)名 : Start_signal(void)
* 函數(shù)功能 : I2C總線終止信號(hào)
* 輸入 : 無
* 輸出 : 無
*******************************************************************************/
void I2C_Stop_signal(void)
{
SDA = 0 ;
Delay10us();
SCL = 1 ;
Delay10us();
Delay10us();
SDA=1;
Delay10us();
}
/*******************************************************************************
* 函數(shù)名 : I2C_SendByte(unsigned char dat, unsigned char ack)
* 函數(shù)功能 : I2C總線發(fā)送數(shù)據(jù)
* 輸入 : dat,一個(gè)字節(jié)的數(shù)據(jù)
* 輸出 : 發(fā)送成功返回1,發(fā)送失敗返回0
* 備 注 : 發(fā)送完一個(gè)字節(jié)I2C_SCL=0, 需要應(yīng)答則應(yīng)答設(shè)置為1,否則為0
*******************************************************************************/
unsigned char I2C_SendByte(unsigned char dat, unsigned char ack)
{
unsigned char a = 0,b = 0;//最大255,一個(gè)機(jī)器周期為1us,最大延時(shí)255us。
Replay:
b = 0 ;
for(a=0; a<8; a++)//要發(fā)送8位,從最高位開始
{
SDA = dat >> 7; //起始信號(hào)之后I2C_SCL=0,所以可以直接改變I2C_SDA信號(hào)
dat = dat << 1;
Delay10us();
SCL = 1;
Delay10us();//建立時(shí)間>4.7us
SCL = 0;
Delay10us();//時(shí)間大于4us
}
SDA = 1;
Delay10us();
SCL = 1;
while(SDA && (ack == 1))//等待應(yīng)答,也就是等待從設(shè)備把I2C_SDA拉低
{
b++;
if(b > 200) //如果超過200us沒有應(yīng)答發(fā)送失敗,或者為非應(yīng)答,表示接收結(jié)束
{
SCL = 0;
Delay10us();
// return 0;
goto Replay ; //如果超過200us沒有應(yīng)答則發(fā)送失敗,或者為非應(yīng)答,這時(shí)候系統(tǒng)啟動(dòng)重發(fā)機(jī)制
//使用goto語句返回到上面接著發(fā)
}
}
SCL = 0;
Delay10us();
return 1;
}
/*******************************************************************************
* 函數(shù)名 : I2cReadByte()
* 函數(shù)功能 : I2C總線接收數(shù)據(jù)
* 輸入 : 無
* 輸出 : dat,數(shù)據(jù)
*******************************************************************************/
unsigned char I2cReadByte()
{
unsigned char a=0,dat=0;
SDA=1;
Delay10us();
for(a=0;a<8;a++)//接收8個(gè)字節(jié)
{
SCL=1;
Delay10us();
dat<<=1;
dat|=SDA;
Delay10us();
SCL=0;
Delay10us();
}
return dat;
}
/*******************************************************************************
* 函數(shù)名 : Delay10us()
* 函數(shù)功能 : 延時(shí)
* 輸入 : 無
* 輸出 : 無
*******************************************************************************/
void Delay10us()
{
unsigned char a,b;
for(b=1;b>0;b--)
for(a=2;a>0;a--);
}
/*******************************************************************************
* 函 數(shù) 名 : void At24c02Write(unsigned char addr,unsigned char dat)
* 函數(shù)功能 : 往24c02的8個(gè)地址寫入8個(gè)數(shù)據(jù)
* 輸 入 : 地址和數(shù)據(jù)
* 輸 出 : 無
*******************************************************************************/
void At24c02Write(unsigned char addr,unsigned char dat[])
{
unsigned char i;
I2C_Start_signal();
I2C_SendByte(0xa0, 1);//發(fā)送寫器件地址
I2C_SendByte(addr, 1);//發(fā)送要寫入內(nèi)存地址
for(i = 0; i < 8; i++)
I2C_SendByte(dat[i], 0); //發(fā)送數(shù)據(jù)
I2C_Stop_signal();
}
/*******************************************************************************
* 函 數(shù) 名 : unsigned char At24c02Read(unsigned char addr)
* 函數(shù)功能 : 讀取24c02的一個(gè)地址的一個(gè)數(shù)據(jù)
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void At24c02Read(unsigned char b[], unsigned char addr)
{
unsigned char i;
for(i = 0; i < 7; i++)
{
I2C_Start_signal();
I2C_SendByte(0xa0, 1); //發(fā)送寫器件地址 1010 0000
I2C_SendByte(addr + i, 1); //發(fā)送要讀取的地址
I2C_Start_signal();
I2C_SendByte(0xa1, 1); //發(fā)送讀器件地址
b[i] = I2cReadByte(); //讀取數(shù)據(jù)
ACK();
}
I2C_Start_signal();
I2C_SendByte(0xa0, 1); //發(fā)送寫器件地址 1010 0000
I2C_SendByte(addr + i, 1); //發(fā)送要讀取的地址
I2C_Start_signal();
I2C_SendByte(0xa1, 1); //發(fā)送讀器件地址
b[i] = I2cReadByte();
NACK();
I2C_Stop_signal();
Delay10us();
}
*由于AT24C02讀寫函數(shù)一次只讀寫8位,所以提高實(shí)現(xiàn)中的密碼默認(rèn)只設(shè)8位,如果需要更多位密碼可以通過多次讀取拼接簡單實(shí)現(xiàn)更多位密碼設(shè)定。
總結(jié)
上述的課程設(shè)計(jì)完整代碼和仿真文件可以下載,文章中沒有講清除的地方可以結(jié)合程序進(jìn)一步理解,希望對(duì)各位有所幫助。文章來源:http://www.zghlxwxcb.cn/news/detail-409795.html
https://download.csdn.net/download/canmianlaida/85656284文章來源地址http://www.zghlxwxcb.cn/news/detail-409795.html
到了這里,關(guān)于C51單片機(jī)密碼鎖課程設(shè)計(jì)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!