本系統(tǒng)采用以單片機為核心元件的控制方案。由于單片機種類繁多,各種型號都有其一定的應用環(huán)境,因此在選用時要多加比較,合理選擇,以獲得最佳的性價比。一般來說在選取單片機時從下面幾個方面考慮:性能、存儲器、運行速度、I/O口、定時/計數(shù)器、串行接口、模擬電路功能、工作電壓、功耗、封裝形式、抗干擾性、保密性,除了以上的一些方面外,還有一些最基本的條件,比如:中斷源的數(shù)量和優(yōu)先級、工作溫度范圍、有沒有低電壓檢測功能、單片機內(nèi)有無時鐘振蕩器、有無上電復位功能等。在開發(fā)過程中還要考慮開發(fā)工具、編程器、開發(fā)成本、開發(fā)人員的適應性、技術(shù)支持和服務等因素。基于以上因素本設計選用單片機STC89C516作為本設計的核心元件,利用單片機靈活的編程設計和豐富的I/O端口,及其控制的準確性,實現(xiàn)基本的密碼鎖功能。在單片機的外圍電路外接輸入鍵盤用于密碼的輸入和一些功能的控制,外接AT24C02芯片用于密碼的存儲,外接LCD1602顯示器用于顯示作用。
二、 硬件設計硬件連接如下: P1 -->矩陣鍵盤 P20--> EEPROM模塊SDA P21--> EEPROM模塊SCL P22-->繼電器模塊J15 其他管腳說明: P0 LCD1602_DATAPINS P2^7 LCD1602_E; P2^5 LCD1602_RW; P2^6 LCD1602_RS;
P1 矩陣鍵盤
P2^1; 存儲模塊 24C02 SCL P2^0 存儲模塊 24C02 SDA 2.1 主控芯片2.2存儲模塊24c02存儲模塊的設計是把WP引腳接到GND上因為要讓器件進行正常的讀/寫操作,把SDA串行數(shù)據(jù)/地址與單片機的P2.0引腳使AT24C02與單片機進行所有數(shù)據(jù)的發(fā)送或接收,把SCL串行時鐘引腳與單片機的P2.1引腳相連接,讓單片機產(chǎn)生一個AT24C02工作的時鐘,使其正常的工作。存儲模塊電路如圖所示。 AT24C02的特點:數(shù)據(jù)線看門狗定時器;可編程復位門級;高數(shù)據(jù)傳輸速率400kHz速率和I2C總線;2.7V至7V電壓;低功耗CMOS工藝兼容;16 bespate寫緩沖區(qū);反撇寫保護芯片;高低電平復位信號輸出;100萬擦除周期;保存長達100年。  2.3顯示模塊LCD1602如果想要了解系統(tǒng)的運行與工作狀態(tài)顯示器是不可缺少的,顯示器是一個典型的輸出設備并且它的實際應用也是極為廣泛的,幾乎所有的電子產(chǎn)品都會使用到顯示器其差別僅在于顯示器的結(jié)構(gòu)類型不同而已。最簡單的顯示器可以是LED發(fā)光二極管或者數(shù)碼管,它可以給出一個簡單的開關(guān)信息或者數(shù)字顯示,而需要顯示復雜的完整的信息上述的器件就不能勝任,而液晶的出現(xiàn)就很好的解決了這一問題,它不僅顯示內(nèi)容豐富而且好節(jié)約了單片機的管腳資源。本設計采用的是液晶顯示LCD1602。LCD1602內(nèi)部的字符已經(jīng)儲存了不同的字符形式,每一個字符都有一個固定的代碼,其代碼與標準的ASCII字符代碼一致。因此只要寫入顯示字符的ASCII碼即可,這種標準化的設計給使用帶來很大的方便。比如英文字母“C”的ASCII代碼是01000011(43H),顯示時單片機往液晶模塊寫入顯示指令,模塊就會把地址為43H中的點陣字符圖形識別出,并會在液晶屏相應位置上看到字母“C”。 目前市場字符液晶絕大多數(shù)是基于HD44780的液晶芯片,控制原理是完全相同的,因此基于HD44780的寫控制程序可以很容易地應用到市場上大部分的字符液晶。LCD1602液晶的幾個特性:+5V電壓、對比度可調(diào)、內(nèi)含復位電路;提供各種控制命令,如:清屏、字符閃爍、光標閃爍、顯示移位等多種功能;有80字節(jié)顯示數(shù)據(jù)存儲器DDRAM;內(nèi)建有160個5X7點陣的字型的字符發(fā)生器CGROM 8個可由用戶自定義的5X7的字符發(fā)生器CGRAM。1602的引腳如下所示: LCD1602采用標準的16腳接口,其中VSS為地電源,VDD接5V正電源,VEE為液晶顯示器。RS為寄存器選擇,高電平時選擇數(shù)據(jù)寄存器、低電平時選擇指令寄存器。RW為讀寫信號線,高電平1時進行讀操作,低電平0時進行寫操作。E端為使能端。D0~D7為8位雙向數(shù)據(jù)線。 2.4鍵盤輸入模塊鍵盤是單片機十分重要的輸入設備,是實現(xiàn)人機對話的紐帶。鍵盤是由一組規(guī)則排列的按鍵組成,一個按鍵實際上就是一個開關(guān)元件,即鍵盤是一組規(guī)則排列的開關(guān)。根據(jù)按鍵與單片機的連接方式不同,按鍵主要分為獨立式按鍵和矩陣式按鍵 ,有了這些按鍵,對單片機的控制就方便多了。 本設計按鍵數(shù)量較多,所以采用矩陣式按鍵以節(jié)省I/O口線。將16個按鍵分為4排4列排列好,下圖是矩陣鍵盤硬件結(jié)構(gòu)。當有一個鍵按下時,通過某一邊引腳賦低電平,掃描全部引腳看是否與最初的賦值一樣,不一樣則根據(jù)相應的算法(通過改變后的值與初始值相或,根據(jù)結(jié)果賦值)確定是哪個鍵按下。
2.5開鎖電路開鎖電路的功能是當輸入正確的密碼后密碼鎖將被鎖定,既開鎖。當單片機P2.2引腳發(fā)出信號經(jīng)三極管放大后,觸動電磁閥即會把鎖打開。一旦輸入密碼,單片機便會與初始密碼進行比對,如果密碼輸入3次都與原始密碼不相符即會報警。其原理圖如圖3-11所示 
四、 實驗現(xiàn)象將線連接好并將程序下載后,可以看到1602上顯示: 此時可以按下任意鍵繼續(xù) 在這里我們可以“1”或“9”選擇開鎖或者是修改密碼。如果這里選擇的開鎖,按下確定鍵,此時會提示輸入密碼,初始密碼默認為888888,輸入密碼 然后按下確定鍵,如果密碼正確則會顯示:并且可以看到繼電器模塊指示燈亮并可以聽到繼電器閉合的聲音。 如果密碼錯誤則會提示:Error.
如果需要修改密碼,則選擇Change password 
這里會提示輸入密碼: 
輸入原密碼 
按下確認鍵,如果密碼正確會提示輸入新密碼, 輸入密碼后按下確認鍵,會提示OK并返回到待機畫面。
三、 軟件設計
3.1 按鍵掃描
按鍵掃描程序如下:
#include "key.h"
u8 KeyValue=0;
void delay(u16 i)
{
while(i--);
}
u8 KeyDown(void)
{
char a=0;
GPIO_KEY=0x0f;
if(GPIO_KEY!=0x0f)//有按鍵按下
{
delay(1000);//延時10ms去抖
if(GPIO_KEY!=0x0f)//有按鍵按下
{
GPIO_KEY=0X0F;
switch(GPIO_KEY)
{
case(0X07): KeyValue=0;break;
case(0X0b): KeyValue=1;break;
case(0X0d): KeyValue=2;break;
case(0X0e): KeyValue=3;break;
}
GPIO_KEY=0XF0;
switch(GPIO_KEY)
{
case(0X70): KeyValue=KeyValue;break;
case(0Xb0): KeyValue=KeyValue+4;break;
case(0Xd0): KeyValue=KeyValue+8;break;
case(0Xe0): KeyValue=KeyValue+12;break;
}
while((a<50)&&(GPIO_KEY!=0xf0))
{
delay(1000);
a++;
}
}
}
else
{
KeyValue=0xff; //無按鍵按下
}
return KeyValue; //返回KeyValue
}
從前面硬件電路中可以看到,P1口的低四位接的4*4矩陣鍵盤的行,高四位接的矩陣鍵盤的列。檢測矩陣鍵盤是否有按鍵按下時:先將P1端口的低四位置1,高四位清零,檢測P1端口的狀態(tài),如果高四位不為零,則表示有按鍵按下,并且可以知道是x0-x3哪一列有按鍵按下,比如P1=0x1f;則第一列有按鍵按下。此時我們在將P1口第四位清零,高四位置1;檢測P1的狀態(tài),就知道y0-y3哪一行有按鍵按下。結(jié)合xy就可以知道具體是哪個按鍵按下。
3.2 1602初始化
16002初始化程序如下:
#include "lcd.h"
uchar i;
/*******************************************************************************
* 函 數(shù) 名 : Lcd1602_Delay1ms
* 函數(shù)功能 : 延時函數(shù),延時1ms
* 輸 入 : c
* 輸 出 : 無
* 說 名 : 該函數(shù)是在12MHZ晶振下,12分頻單片機的延時。
*******************************************************************************/
void Lcd1602_Delay1ms(uint c) //誤差 0us
{
uchar a,b;
for (; c>0; c--)
{
for (b=199;b>0;b--)
{
for(a=1;a>0;a--);
}
}
}
/*******************************************************************************
* 函 數(shù) 名 : LcdWriteCom
* 函數(shù)功能 : 向LCD寫入一個字節(jié)的命令
* 輸 入 : com
* 輸 出 : 無
*******************************************************************************/
#ifndef LCD1602_4PINS //當沒有定義這個LCD1602_4PINS時
void LcdWriteCom(uchar com) //寫入命令
{
LCD1602_E = 0; //使能
LCD1602_RS = 0; //選擇發(fā)送命令
LCD1602_RW = 0; //選擇寫入
LCD1602_DATAPINS = com; //放入命令
Lcd1602_Delay1ms(1); //等待數(shù)據(jù)穩(wěn)定
LCD1602_E = 1; //寫入時序
Lcd1602_Delay1ms(5); //保持時間
LCD1602_E = 0;
}
#else
void LcdWriteCom(uchar com) //寫入命令
{
LCD1602_E = 0; //使能清零
LCD1602_RS = 0; //選擇寫入命令
LCD1602_RW = 0; //選擇寫入
LCD1602_DATAPINS = com; //由于4位的接線是接到P0口的高四位,所以傳送高四位不用改
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //寫入時序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
LCD1602_DATAPINS = com << 4; //發(fā)送低四位
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //寫入時序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 數(shù) 名 : LcdWriteData
* 函數(shù)功能 : 向LCD寫入一個字節(jié)的數(shù)據(jù)
* 輸 入 : dat
* 輸 出 : 無
*******************************************************************************/
#ifndef LCD1602_4PINS
void LcdWriteData(uchar dat) //寫入數(shù)據(jù)
{
LCD1602_E = 0; //使能清零
LCD1602_RS = 1; //選擇輸入數(shù)據(jù)
LCD1602_RW = 0; //選擇寫入
LCD1602_DATAPINS = dat; //寫入數(shù)據(jù)
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //寫入時序
Lcd1602_Delay1ms(5); //保持時間
LCD1602_E = 0;
}
#else
void LcdWriteData(uchar dat) //寫入數(shù)據(jù)
{
LCD1602_E = 0; //使能清零
LCD1602_RS = 1; //選擇寫入數(shù)據(jù)
LCD1602_RW = 0; //選擇寫入
LCD1602_DATAPINS = dat; //由于4位的接線是接到P0口的高四位,所以傳送高四位不用改
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //寫入時序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
LCD1602_DATAPINS = dat << 4; //寫入低四位
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //寫入時序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
}
#endif
/**********************************************************************
***********************************************************************/
/*****************************************************************************
函數(shù)名 :
函數(shù)功能 :顯示*
**********************************************************************************/
/******************************************************************************/
/****************************************************************************/
/**************************************************
字符串顯示函數(shù)
Coordinate 高四位顯示坐標的X,第四位顯示坐標的Y
如在第一行從第一位開始顯示 hello: ShowString (0x01,“hello”);
************************************************/
void ShowString (unsigned char Coordinate,char *ptr)
{
if(Coordinate<=0x0f) //高四位為0,顯示第一行
{
LcdWriteCom((Coordinate&0x0f)+0x80);
while(*ptr!='\0')
{
LcdWriteData(*ptr);
ptr++;
}
}
else //高四位為1,顯示第2行
{
LcdWriteCom((Coordinate&0x0f)+0xc0);
while(*ptr!='\0')
{
LcdWriteData(*ptr);
ptr++;
}
}
}
/**************************************************************************/
/*******************************************************************************
* 函 數(shù) 名 : LcdInit()
* 函數(shù)功能 : 初始化LCD屏
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
#ifndef LCD1602_4PINS
void LcdInit() //LCD初始化子程序
{
LcdWriteCom(0x38); //開顯示
LcdWriteCom(0x0c); //開顯示不顯示光標
LcdWriteCom(0x06); //寫一個指針加1
LcdWriteCom(0x01); //清屏
LcdWriteCom(0x80); //設置數(shù)據(jù)指針起點
}
#else
void LcdInit() //LCD初始化子程序
{
LcdWriteCom(0x32); //將8位總線轉(zhuǎn)為4位總線
LcdWriteCom(0x28); //在四位線下的初始化
LcdWriteCom(0x0c); //開顯示不顯示光標
LcdWriteCom(0x06); //寫一個指針加1
LcdWriteCom(0x01); //清屏
LcdWriteCom(0x80); //設置數(shù)據(jù)指針起點
}
#endif
1602的初始化函數(shù)比較長,但其中大部分是1602驅(qū)動的底層協(xié)議,初學者不必深究,只需要學會怎么使用即可。這里簡單介紹幾個常用的函數(shù)。
LcdWriteCom()
1602的寫命令函數(shù),通過此函數(shù)向1602寫命令。比如:清屏LcdWriteCom(0x01); LcdWriteCom(0x80); //設置數(shù)據(jù)指針起點
LcdWriteData()
當需要在1602上顯示時,就需要用到LcdWriteData(),不過在寫數(shù)據(jù)之前需要通過LcdWriteCom()函數(shù)告訴要寫數(shù)據(jù)的地址,1602的第一行的16個顯示位地址是0x80到0x8f;第二行的地址是0xc0到0xcf。比如在1602的第一行第一位顯示數(shù)字8:LcdWriteCom(0x80);
LcdWriteData(‘8’);
ShowString (unsigned char Coordinate,char *ptr)
ShowString (首地址,字符串)函數(shù)在需要顯示字符串時使用。比如在第二行第3位開始顯示hello:
ShowString (0x13,”hello”);
其中首地址的高四位為0則表示在第一排顯示,為“1”則在第二排顯示。低四位為0則在第0位顯示。
3.3 24C02初始化
24C02的初始化如下:
#include"i2c.h"
/*******************************************************************************
* 函數(shù)名 : Delay10us()
* 函數(shù)功能 : 延時10us
* 輸入 : 無
* 輸出 : 無
*******************************************************************************/
void Delay10us()
{
unsigned char a,b;
for(b=1;b>0;b--)
for(a=2;a>0;a--);
}
/*******************************************************************************
* 函數(shù)名 : I2cStart()
* 函數(shù)功能 : 起始信號:在SCL時鐘信號在高電平期間SDA信號產(chǎn)生一個下降沿
* 輸入 : 無
* 輸出 : 無
* 備注 : 起始之后SDA和SCL都為0
*******************************************************************************/
void I2cStart()
{
SDA=1;
Delay10us();
SCL=1;
Delay10us();//建立時間是SDA保持時間>4.7us
SDA=0;
Delay10us();//保持時間是>4us
SCL=0;
Delay10us();
}
/*******************************************************************************
* 函數(shù)名 : I2cStop()
* 函數(shù)功能 : 終止信號:在SCL時鐘信號高電平期間SDA信號產(chǎn)生一個上升沿
* 輸入 : 無
* 輸出 : 無
* 備注 : 結(jié)束之后保持SDA和SCL都為1;表示總線空閑
*******************************************************************************/
void I2cStop()
{
SDA=0;
Delay10us();
SCL=1;
Delay10us();//建立時間大于4.7us
SDA=1;
Delay10us();
}
/*******************************************************************************
* 函數(shù)名 : I2cSendByte(unsigned char dat)
* 函數(shù)功能 : 通過I2C發(fā)送一個字節(jié)。在SCL時鐘信號高電平期間,保持發(fā)送信號SDA保持穩(wěn)定
* 輸入 : num
* 輸出 : 0或1。發(fā)送成功返回1,發(fā)送失敗返回0
* 備注 : 發(fā)送完一個字節(jié)SCL=0,SDA=1
*******************************************************************************/
unsigned char I2cSendByte(unsigned char dat)
{
unsigned char a=0,b=0;//最大255,一個機器周期為1us,最大延時255us。
for(a=0;a<8;a++)//要發(fā)送8位,從最高位開始
{
SDA=dat>>7; //起始信號之后SCL=0,所以可以直接改變SDA信號
dat=dat<<1;
Delay10us();
SCL=1;
Delay10us();//建立時間>4.7us
SCL=0;
Delay10us();//時間大于4us
}
SDA=1;
Delay10us();
SCL=1;
while(SDA)//等待應答,也就是等待從設備把SDA拉低
{
b++;
if(b>200) //如果超過2000us沒有應答發(fā)送失敗,或者為非應答,表示接收結(jié)束
{
SCL=0;
Delay10us();
return 0;
}
}
SCL=0;
Delay10us();
return 1;
}
/*******************************************************************************
* 函數(shù)名 : I2cReadByte()
* 函數(shù)功能 : 使用I2c讀取一個字節(jié)
* 輸入 : 無
* 輸出 : dat
* 備注 : 接收完一個字節(jié)SCL=0,SDA=1.
*******************************************************************************/
unsigned char I2cReadByte()
{
unsigned char a=0,dat=0;
SDA=1; //起始和發(fā)送一個字節(jié)之后SCL都是0
Delay10us();
for(a=0;a<8;a++)//接收8個字節(jié)
{
SCL=1;
Delay10us();
dat<<=1;
dat|=SDA;
Delay10us();
SCL=0;
Delay10us();
}
return dat;
}
/*******************************************************************************
* 函數(shù)名 : void At24c02Write(unsigned char addr,unsigned char dat)
* 函數(shù)功能 : 往24c02的一個地址寫入一個數(shù)據(jù)
* 輸入 : 無
* 輸出 : 無
*******************************************************************************/
void At24c02Write(unsigned char addr,unsigned char dat)
{
I2cStart();
I2cSendByte(0xa0);//發(fā)送寫器件地址
I2cSendByte(addr);//發(fā)送要寫入內(nèi)存地址
I2cSendByte(dat); //發(fā)送數(shù)據(jù)
I2cStop();
}
/*******************************************************************************
* 函數(shù)名 : unsigned char At24c02Read(unsigned char addr)
* 函數(shù)功能 : 讀取24c02的一個地址的一個數(shù)據(jù)
* 輸入 : 無
* 輸出 : 無
*******************************************************************************/
unsigned char At24c02Read(unsigned char addr)
{
unsigned char num;
I2cStart();
I2cSendByte(0xa0); //發(fā)送寫器件地址
I2cSendByte(addr); //發(fā)送要讀取的地址
I2cStart();
I2cSendByte(0xa1); //發(fā)送讀器件地址
num=I2cReadByte(); //讀取數(shù)據(jù)
I2cStop();
return num;
}
24c02采用IIC與單片機通信,其詳細通信時序參考24C02數(shù)據(jù)手冊。我們只需使用At24c02Read(unsigned char addr)和At24c02Write(unsigned char addr,unsigned char dat)即可。
Keil代碼工程文件下載:
電子密碼鎖Keil代碼.7z
(1.93 MB, 下載次數(shù): 123)
2021-10-8 03:01 上傳
點擊文件名下載附件
|