《基于單片機的萬年歷設(shè)計.doc》由會員分享,可在線閱讀,更多相關(guān)《基于單片機的萬年歷設(shè)計.doc(29頁珍藏版)》請在裝配圖網(wǎng)上搜索。
基于單片機的萬年歷設(shè)計
二、實驗要求
設(shè)計一個萬年歷,將時鐘顯示在LCD1602的顯示屏上并且可以進行年、月、日以及時、分、秒的設(shè)置。此外還可以通過按鍵進行鬧鐘設(shè)置以及事件提醒功能,用蜂鳴器進行鬧鈴提醒。最后附加一個溫濕度檢測的功能,用溫濕度傳感器檢測室內(nèi)的溫濕度并將溫濕度數(shù)據(jù)在顯示屏上顯示出來。
三、實驗設(shè)備和儀器
1.用 STC89C52芯片作為系統(tǒng)板的主控芯片
2.DHT11溫濕度傳感器
3.DS1302時鐘芯片
4.LCD1602顯示屏
四、實驗各模塊原理介紹
4.1 STC89C52單片機
STC89C52是STC公司生產(chǎn)的一種低功耗、高性能CMOS8位微控制器,具有8K字節(jié)系統(tǒng)可編程Flash存儲器。STC89C52使用經(jīng)典的MCS-51內(nèi)核,具有傳統(tǒng)51單片機不具備的功能。在單芯片上,擁有靈巧的8 位CPU 和在系統(tǒng)可編程Flash,使得STC89C52為眾多嵌入式控制應(yīng)用系統(tǒng)提供高靈活、超有效的解決方案。
(1)主要特性
8K字節(jié)程序存儲空間;
512字節(jié)數(shù)據(jù)存儲空間;
內(nèi)帶4K字節(jié)EEPROM存儲空間;
可直接使用串口下載;
(2)器件參數(shù)
1. 增強型8051單片機,6時鐘/機器周期和12時鐘/機器周期可以任意選擇,指令代碼完全兼容傳統(tǒng)8051。
2. 工作電壓:5.5V~3.3V(5V單片機)/3.8V~2.0V(3V 單片機)
3.工作頻率范圍:0~40MHz,相當(dāng)于普通8051的0~80MHz,實際工作頻率可達48MHz
4. 用戶應(yīng)用程序空間為8K字節(jié)
5. 片上集成512字節(jié)RAM
6. 通用I/O 口(32個),復(fù)位后為:P1/P2/P3 是準(zhǔn)雙向口/弱上拉,P0口是漏極開路輸出,作為總線擴展用時,不用加上拉電阻,作為I/O口用時,需加上拉電阻。
7. ISP(在系統(tǒng)可編程)/IAP(在應(yīng)用可編程),無需專用編程器,無需專用仿真器,可通過串口(RXD/P3.0,TXD/P3.1)直接下載用戶程序,數(shù)秒即可完成一片。
8. 具有EEPROM 功能
9. 共3個16位定時器/計數(shù)器。即定時器T0、T1、T2。
10.外部中斷4 路,下降沿中斷或低電平觸發(fā)電路,Power Down 模式可由外部中斷低電平觸發(fā)中斷方式喚醒。
11. 通用異步串行口(UART),還可用定時器軟件實現(xiàn)多個UART。
12. 工作溫度范圍:-40~+85℃(工業(yè)級)/0~75℃(商業(yè)級)
13. PDIP封裝
1、STC89C52單片機引腳圖
圖4.1 STC89C52單片機引腳圖
① 主電源引腳(2根)
VCC(Pin40):電源輸入,接+5V電源
GND(Pin20):接地線
②外接晶振引腳(2根)
XTAL1(Pin19):片內(nèi)振蕩電路的輸入端
XTAL2(Pin20):片內(nèi)振蕩電路的輸出端
③控制引腳(4根)
RST/VPP(Pin9):復(fù)位引腳,引腳上出現(xiàn)2個機器周期的高電平將使單片機復(fù)位。
ALE/PROG(Pin30):地址鎖存允許信號
PSEN(Pin29):外部存儲器讀選通信號
EA/VPP(Pin31):程序存儲器的內(nèi)外部選通,接低電平從外部程序存儲器讀指令,如果接高電平則從內(nèi)部程序存儲器讀指令。
④可編程輸入/輸出引腳(32根)
STC89C52單片機有4組8位的可編程I/O口,分別位P0、P1、P2、P3口,每個口有8位(8根引腳),共32根。
PO口(Pin39~Pin32):8位雙向I/O口線,名稱為P0.0~P0.7 P1口(Pin1~Pin8):8位準(zhǔn)雙向I/O口線,名稱為P1.0~P1.7 P2口(Pin21~Pin28):8位準(zhǔn)雙向I/O口線,名稱為P2.0~P2.7 P3口(Pin10~Pin17):8位準(zhǔn)雙向I/O口線,名稱為P3.0~P3.7。
2、STC89C52單片機最小系統(tǒng)
STC89C52單片機最小系統(tǒng)主要由復(fù)位電路和時鐘電路組成。復(fù)位功能通過外部電路來實現(xiàn),按下按鍵K1控制單片機的復(fù)位。時鐘電路主要通過晶振來實現(xiàn),為單片機提供運行時鐘。根據(jù)不同的需求選用的晶振頻率會有差異,在本次課程設(shè)計中采用11.0592MHZ的晶振頻率。單片機復(fù)位電路如圖4.2所示,晶振電路如圖4.3所示。
圖4.2 復(fù)位電路
圖4.3晶振電路
4.2 DS1302芯片
DS1302是DALLAS公司出的一款實時時鐘芯片,它廣泛應(yīng)用于電話、傳真、便攜式儀器等產(chǎn)品領(lǐng)域,主要性能指標(biāo)如下:
1、DS1302是一個實時時鐘芯片,可以提供秒、分、小時、日期、月、年等信息,具有軟件自動調(diào)整的能力,可以通過配置AM/PM來決定采用24小時格式還是12小時格式。
2、擁有31字節(jié)數(shù)據(jù)存儲RAM。
3、串行I/O通信方式。
4、DS1302的工作電壓比較寬,在2.0V~5.5V范圍內(nèi)均可正常工作。采用雙電源供電,當(dāng)主電源比備用電源高0.2V時,由主電源供電,否則采用備用電源,一般是一個紐扣電池。
5、DS1302實時時鐘芯片的功耗很低,當(dāng)工作電壓為2.0V時,工作電流小于300nA。
6、DS1302共有8個引腳,有兩種封裝形式,一種是DIP-8封裝,芯片寬度(不含引腳)是300mil,一種是SOP-8封裝,有兩種寬度,一種是150mil,一種是208mil。
(1) DS1302引腳及其功能
圖 4.4 DS1302芯片引腳圖
(1)Vcc2:主電源引腳,當(dāng)Vcc2比Vcc1高0.2v以上時,DS1302由Vcc2供電,當(dāng)Vcc2低于Vcc1時,由Vcc1供電。
(2、3)X1、X2:這兩個引腳需要接一個32.768k的晶振,為了給DS1302提供一個基準(zhǔn)。但是該晶振的引腳負載電容必須為6pF,若使用有源晶振,接到X1上即可,X2則懸空。
(4)GND:接地
(5)CE:DS1302的輸入引腳。該引腳內(nèi)部有一個40k的下拉電阻,當(dāng)該引腳為高電平,對DS1302進行讀寫。
(6)I/O:該引腳是一個雙向通信引腳,且內(nèi)部含有一個40k的下拉電阻,可進行數(shù)據(jù)的讀寫。
(7)SCLK:該引腳是輸入引腳,且內(nèi)部含有一個40k的下拉電阻,作為通信的時鐘信號。
(8)Vcc1:備用電源引腳。
(2) DS1302與單片機接線圖
DS1302引腳與單片機接線圖如圖4.5所示,從圖4.5可看出,引腳X1與X2之間接一個32.768K的晶體正振蕩器,Vcc1通過一個紐扣能電池再接地。
圖4.5 DS1302與單片機連接圖
4.3 LCD1602液晶顯示屏
LCD1602液晶屏能夠能夠同時顯示32個字符,價格便宜,編程簡單而且穩(wěn)定可靠。LCD1602液晶屏是一種圖形點陣顯示器,顯示原理簡單易懂,都是液晶屏內(nèi)部的液晶材料變化而顯示不同的字符,因為液晶是具有流動特性的物質(zhì),所以只需外加很微小的力量即可使液晶分子運動。
(1)LCD1602引腳
1602LCD采用標(biāo)準(zhǔn)的14腳(無背光)或16腳(帶背光)接口,各引腳接口圖如圖4.6所示:
圖4.6 LCD1602引腳圖
(a) 各個引腳說明
1腳:VSS為地電源
2腳:VDD接5V正電源
3腳:V0為液晶顯示器對比度調(diào)整端,接正電源時對比度最弱,接地時對比
對比度過高時會產(chǎn)生“鬼影”,使用時可以通過一個10K的電位器調(diào)整。
4腳:RS為寄存器選擇,高電平時選擇數(shù)據(jù)寄存器、低電平時選擇指令寄存器
5腳:R/W為讀寫信號線,高電平時進行讀操作,低電平時進行寫操作。當(dāng)RS
R/W共同為低電平時可以寫入指令或者顯示地址,當(dāng)RS為低電平,R/W為高電平;
RS為高電平,R/W為低電平時可以寫入數(shù)據(jù)。
6腳:E端為使能端,當(dāng)E端由高電平跳變成低電平時,液晶模塊執(zhí)行命令。
7~14腳:D0~D7為8位雙向數(shù)據(jù)線。
15腳:背光源正極
16腳:背光源負極
(b)LCD1602主要技術(shù)參數(shù):
顯示容量:32個字符;
芯片工作電壓:4.5-5.5V;
工作電流:2.0mA(5.0V);
模塊最佳工作電壓:5.0V;
(2)LCD1602與單片機接線圖
圖4.7 LCD1602與單片機接線圖
LCD1602與單片機接線圖如4.7所示,背光源正極(5)與VDD(2)均接電源,將液晶顯示器接地保證對比度最強,可使用一個10K的電位器進行調(diào)整。
4.4 溫濕度傳感器DHT11
DHT11數(shù)字溫濕度傳感器是一款含有已校準(zhǔn)數(shù)字信號輸出的溫濕度復(fù)合傳感器。它采用專用的數(shù)字模塊采集技術(shù)、溫濕度傳感技術(shù),確保產(chǎn)品的可靠性、長期穩(wěn)定性。傳感器包括一個電阻式感濕元件和一個NTC測溫元件,并與一個高性能8位單片機相連接。因此,該產(chǎn)品具有品質(zhì)卓越、超快響應(yīng)、抗干擾能力強、性價比極高等優(yōu)點。
每個DHT11傳感器都在極為精確的濕度校驗室中進行校準(zhǔn)。校準(zhǔn)系數(shù)以程序的形式儲存在OTP內(nèi)存中,傳感器內(nèi)部在檢測信號的處理過程中要調(diào)用這些校準(zhǔn)系數(shù)。單線制串行接口,使系統(tǒng)集成變得簡易快捷。超小的體積、極低的功耗,信號傳輸距離可達20米以上,使其成為各類應(yīng)用甚至最為苛刻的應(yīng)用場合的最佳選則。產(chǎn)品為4針單排引腳封裝,連接方便,特殊封裝形式可根據(jù)用戶需求而提供。
(1)DHT11引腳及其功能
DHT11的引腳圖如圖4.8所示,一般情況下它有四個引腳,分別為:VCC、DOUT、NC、GND。各引腳以及功能如表4-1所示。
圖4.8 DHT11引腳圖
表4-1 DHT11引腳及其功能
pin
名稱
注釋
1
VDD
供電3-5.5VDC
2
DATA
串行數(shù)據(jù),單總線
3
NC
空腳,請懸空
4
GND
接地,電源負極
1.電源引腳
DHT11的供電電壓為3-5.5V。傳感器上電后,要等待 1s 以越過不穩(wěn)定狀態(tài)在此期間無需發(fā)送任何指令。電源引腳(VDD,GND)之間可增加一個100nF 的電容,作用在于去耦濾波。
2.串行接口(單線雙向)
DATA是用于微處理器與DHT11之間的通信和同步的串行雙向接口,采用單總線數(shù)據(jù)格式。每次通信都是以高位先出的順序傳輸40位數(shù)據(jù),用時約為4 ms。
數(shù)據(jù)格式為:
8位濕度整數(shù)數(shù)據(jù)+8位濕度小數(shù)數(shù)據(jù)+8位溫度整數(shù)數(shù)據(jù)+8位溫度小數(shù)數(shù)據(jù)+8位校驗和數(shù)據(jù)。數(shù)據(jù)分小數(shù)部分和整數(shù)部分,當(dāng)前小數(shù)部分用于以后擴展,現(xiàn)讀出為零。數(shù)據(jù)傳送正確時,校驗和數(shù)據(jù)等于“8位濕度整數(shù)數(shù)據(jù)+8位濕度小數(shù)數(shù)據(jù)+8位溫度整數(shù)數(shù)據(jù)+8位溫度小數(shù)數(shù)據(jù)”所得結(jié)果的末8位。
(2)DHT11與單片機接線圖
DHT11與單片機的接線圖如圖4.9所示,由圖4.9中可看出,DHT11第三個引腳接單片機的P2.2引腳,第一個引腳接電阻和發(fā)光二極管。
圖4.9 DHT11與單片機接線圖
4.5 蜂鳴器
蜂鳴器是一種一體化結(jié)構(gòu)的電子訊響器,采用直流電壓供電,廣泛應(yīng)用于計算機、打印機、復(fù)印機、報警器、電子玩具、汽車電子設(shè)備、電話機、定時器等電子產(chǎn)品中作發(fā)聲器件。蜂鳴器可分為無源蜂鳴器和有源蜂鳴器兩種,此處我們用到的是有源蜂鳴器。有源蜂鳴器工作的理想信號是直流電,通常標(biāo)示為VDC、VDD等。因為蜂鳴器內(nèi)部有一簡單的振蕩電路,能將恒定的直流電轉(zhuǎn)化成一定頻率的脈沖信號,從而帶動鉬片振動發(fā)音。
有源蜂鳴器主要靠壓電效應(yīng)的原理來發(fā)聲的,且內(nèi)部帶有多諧振蕩器,可產(chǎn)生1.5-2.5kHZ 的電壓信號。由此有源蜂鳴器才能發(fā)聲。有源蜂鳴器在單片機中的電路圖較為簡單,圖4.10為蜂鳴器與單片機的接線圖。
圖4.10 蜂鳴器與單片機的接線圖
有源蜂鳴器直接接上額定電源就可連續(xù)發(fā)聲,I/O口輸出后接一個1K的電阻,目的在于基極限流,以低電平方式,啟動蜂鳴器發(fā)聲。
五、流程圖
(1) 主程序流程圖
圖5.1是系統(tǒng)主程序流程圖,系統(tǒng)開始時先進行初始化,然后再調(diào)整時間和鬧鐘,再通過串口屏將時間顯示出來。
圖5.1 主程序流程圖
(2)子程序流程圖
系統(tǒng)從主程序開始執(zhí)行,分別調(diào)用各個子程序,其中子程序包括DS1302子程序,DHT11子程序和LCD1602子程序等。
(1)DS1302芯片子程序流程圖
DS1302為實時時鐘芯片,可以提供秒、分、時、日、月、年及星期等信息,具有軟件自動調(diào)整的能力,可以通過配置AM/PM來決定采用24小時格式還是12小時格式。從DS1302中可以讀出年、月、日、時、分、秒等信息。圖5.2為DS1302芯片的子程序流程圖:
圖5.2 DS1302子程序流程圖
(2)DHT11子程序流程圖
DHT11為溫濕度傳感器,可以實時顯示環(huán)境中的溫濕度。其數(shù)據(jù)由8位濕度整數(shù)數(shù)據(jù),8位濕度小數(shù)數(shù)據(jù),8位溫度整數(shù)數(shù)據(jù)和8位溫度小數(shù)數(shù)據(jù)以及8位校驗和數(shù)據(jù)。DHT11子程序流程圖如5.3所示:
圖5.3 DHT11子程序流程圖
(3)LCD1602子程序流程圖
LCD1602顯示時,也要先進行初始化,緊接著讀指令,讀完之后寫指令,再對數(shù)據(jù)進行讀寫,然后顯示出來,其流程圖如圖5.4所示。
圖5.4 LCD1602顯示屏子程序流程圖
六、實驗?zāi)康?
1.學(xué)會并掌握可keil軟件的使用;
2.學(xué)會并掌握Altium Designer軟件的使用;
3.實現(xiàn)萬年歷的基本功能;
4.通過實驗鞏固單片機相關(guān)知識和檢驗自身動手能力
七、實驗內(nèi)容
本次實驗使用STC89C52單片機作為主控芯片,使用DS1302芯片作為時鐘芯片,在LCD1602顯示屏上顯示,并可以設(shè)置鬧鐘和事件提醒功能,此外還可以進行室內(nèi)溫濕度的檢測和顯示,最終完成萬年歷的設(shè)計。
八、實驗步驟
1.查詢資料,確定主控芯片、溫濕度傳感器、時鐘芯片以及LCD1602顯示屏的使用說明。
2.根據(jù)功能要求進行硬件電路的設(shè)計,使用Altium Designer軟件設(shè)計電路。整個電路分為主控芯片控制模塊、LCD1602顯示模塊、按鍵設(shè)置模塊、蜂鳴器電路模塊以及DHT11溫濕度傳感器模塊。硬件電路圖如圖1所示:
圖8.1:硬件電路圖
3.根據(jù)電路圖編寫C語言代碼:
代碼如下:
#include
#define uchar unsigned char
#define uint unsigned int
#define lcddata P0
#define readalarmsecond 0xc7 //定義從 DS1302 中讀鬧鐘秒值的命令字節(jié)
#define writealarmsecond 0xc6 //定義往 DS1302 中寫鬧鐘秒值的命令字節(jié)
#define readalarmminute 0xc5 //定義從 DS1302 中讀鬧鐘分值的命令字節(jié)
#define writealarmminute 0xc4 //定義往 DS1302 中寫鬧鐘分值的命令字節(jié)
#define readalarmhour 0xc3 //定義從 DS1302 中讀鬧鐘小時值命令字節(jié)
#define writealarmhour 0xc2 //定義往 DS1302 中寫鬧鐘小時值命令字節(jié)
#define readeventday 0xd7 //定義從 DS1302 中讀事件提醒日期值的命令字節(jié)
#define writeeventday 0xd6 //定義往 DS1302 中寫事件提醒日期值的命令字節(jié)
#define readeventmonth 0xd5 //定義從 DS1302 中讀事件提醒月份值的命令字節(jié)
#define writeeventmonth 0xd4 //定義往 DS1302 中寫事件提醒月份值的命令字節(jié)
#define readeventyear 0xd3 //定義從 DS1302 中讀事件提醒年份值的命令字節(jié)
#define writeeventyear 0xd2 //定義往 DS1302 中寫事件提醒年份值的命令字節(jié)
sbit lcdrs=P2^7; //LCD 數(shù)據(jù) /命令選擇端 (H/L)
sbit lcdrw=P2^6; //LCD 讀/寫選擇端 (H/L)
sbit lcden=P2^5; //LCD 使能控制
sbit beep=P1^3; //蜂鳴器控制端
sbit DQ = P1^4; //溫濕度傳感器
sbit kmenu=P3^2; //功能鍵
sbit kup=P3^3; //增大鍵
sbit kdown=P3^4; //減小鍵
sbit kalarm=P3^5; //鬧鐘查看鍵 Ring: 顯示鬧鐘需要調(diào)到的時間
sbit kevent=P3^6; //事件查看鍵 Ding: 顯示事件提醒需要調(diào)到的時間
//sbit kmode=P3^7; //切換溫濕度顯示
sbit dsclk=P2^2; //ds1302 的時鐘信號端
sbit dsdata=P2^1; //ds1302 的數(shù)據(jù) I/O 端
sbit dsrst=P2^0; //ds1302 的復(fù)位端
sbit ACC7=ACC^7; //定義累加器的最高和最低位。
sbit ACC0=ACC^0;
bit flagalarm,flagseta,flagset; // 分別定義鬧鐘響標(biāo)志位,鬧鐘設(shè)置標(biāo)志位,時間設(shè)置標(biāo)志位
bit flagevent,flagsseta; // 分別定義事件提醒標(biāo)志位,事件提醒設(shè)置標(biāo)志位
uchar FLAG,k;
uchar temp;
uchar T_data_H,T_data_L,RH_data_H,RH_data_L,checkdata;
uchar T_data_H_temp,T_data_L_temp,RH_data_H_temp,RH_data_L_temp,checkdata_temp;
uchar comdata;
uchar kmenunum,s,m,h,dd,mm,yy; //功能鍵被按次數(shù)的計數(shù)器 .
char second,minute,hour,year,month,day,week;
uchar inittime[7]={0x59,0x59,0x11,0x05,0x07,0x04,0x18}; //初始化后設(shè)置為:
//2018 年 7 月 5 日 星期四, 11 點 59 分 59 秒,
//從左往右的數(shù)據(jù)依次是,秒,分,時,日,月,星期,年。
// 年月日星期: 2018-07-05 THU
// 時間: 11:59:59
uchar code t1[]=" - - "; //液晶固定顯示部分。
uchar code t2[]=" : : ";
//各函數(shù)聲明
void delay(uint z); //毫秒級延時函數(shù)。
void di(); //蜂鳴器發(fā)聲函數(shù)。
void writecom(uchar com); //液晶寫命令函數(shù)。
void writedata(uchar dat); //液晶寫數(shù)據(jù)函數(shù)。
void writetime(uchar add,uchar dat); // 液晶刷新時分秒。
void writenyr(uchar add,uchar dat); // 液晶刷新日期。
void writeweek(uchar week); // 液晶星期幾顯示函數(shù)。
void keyscan(); // 鍵盤掃描函數(shù)。
void dswritebyte(uchar d);// 往 DS1302 寫入一個字節(jié)。
uchar dsreadbyte(); //從 DS1302 讀出一個字節(jié)。
void dswrite(uchar add, uchar dat); // 向 DS1302 的指定地址寫入一個字節(jié)
uchar dsread(uchar add); //從 DS1302 的指定地址讀出一個字節(jié)
void init1302time(uchar *pClock); //DS1302 時間初始化函數(shù)
void init(); //初始化函數(shù)
void Delay_100us(uint j);//延時100us
void Delay_10us(void);//延時10us
void COM(void);
void Read_DHT11(void);
void delay(uint z) //毫秒級延時函數(shù)。
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void di() //蜂鳴器發(fā)聲函數(shù)。
{
beep=0;
delay(100);
beep=1;
}
void writecom(uchar com) //液晶寫命令函數(shù)。
{
lcdrs=0;// 置為寫入命令
lcddata=com;// 送入數(shù)據(jù)
delay(1);
lcden=1;// 拉高使能端
delay(1);
lcden=0;// 完成高脈沖
}
void writedata(uchar dat) //液晶寫數(shù)據(jù)函數(shù)。
{
lcdrs=1;// 置為寫入數(shù)據(jù)
lcddata=dat;// 送入數(shù)據(jù)
delay(1);
lcden=1;
delay(1);
lcden=0;
}
void writetime(uchar add,uchar dat) // 液晶刷新時分秒。
{ //3為時的開始位置,6 為分, 9 為秒。
uchar shi,ge;
shi=dat/10;
ge=dat%10;
writecom(0x80+0x40+add);
writedata(0x30+shi);
writedata(0x30+ge);
}
void writenyr(uchar add,uchar dat) // 液晶刷新日期。
{ //0 為年, 3 為月, 6 為日。
uchar shi,ge;
shi=dat/10;
ge=dat%10;
writecom(0x80+add);
writedata(0x30+shi);
writedata(0x30+ge);
}
void writeweek(uchar week) // 液晶星期幾顯示函數(shù)。
{
writecom(0x80+9); //從第一行隔十三個字符后開始寫星期
switch(week)
{
case 1: writedata(M);
writedata(O);
writedata(N);
break;
case 2: writedata(T);
writedata(U);
writedata(E);
break;
case 3: writedata(W);
writedata(E);
writedata(D);
break;
case 4: writedata(T);
writedata(H);
writedata(U);
break;
case 5: writedata(F);
writedata(R);
writedata(I);
break;
case 6: writedata(S);
writedata(A);
writedata(T);
break;
case 7: writedata(S);
writedata(U);
writedata(N);
break;
}
}
void keyscan() // 鍵盤掃描函數(shù)。
{
if((flagalarm==1)||(flagevent==1)) // 如果鬧鐘在響或者有事件提醒,任意鍵停止鬧鐘響或者取消事件提醒
{
if((kmenu==0)||(kup==0)||(kdown==0)||(kalarm==0)||(kevent==0))
{
delay(5);
if((kmenu==0)||(kup==0)||(kdown==0)||(kalarm==0)||(kevent==0))
{
while(!(kmenu&&kup&&kdown&&kalarm&&kevent));
di();
flagalarm=0; // 清除鬧鐘標(biāo)志。
flagevent=0; // 清除事件提醒標(biāo)志。
}
}
}
if(kmenu==0) //檢測功能鍵。
{
delay(5);
if(kmenu==0)
{
kmenunum++; // 記錄功能鍵按下次數(shù)。
if(flagseta==1) // 檢測是否在進行鬧鐘設(shè)置。
if(kmenunum==4) kmenunum=1; // 因為進行鬧鐘設(shè)置時,只調(diào)節(jié)時分秒,所以, kmenunum 只能等于 1,2,3。
if(flagsseta==1) // 檢測是否在進行事件提醒設(shè)置。
if(kmenunum==8) kmenunum=5; // 因為進行事件提醒設(shè)置時,只調(diào)節(jié)年月日,所以, kmenunum 只能等于 5,6,7
flagset=1; // 設(shè)置標(biāo)志位,表示在進行各種時間設(shè)置。
while(!kmenu);
di();
switch(kmenunum) //定位光標(biāo)閃爍點。
{
case 1: writecom(0x80+0x40+10); // 秒閃爍。
writecom(0x0f); // 開光標(biāo)閃爍。
break;
case 2: writecom(0x80+0x40+7); // 分閃爍。
break;
case 3: writecom(0x80+0x40+4); // 時閃爍。
break;
case 4: writecom(0x80+10); //星期閃爍 .
break;
case 5: writecom(0x80+7); //日閃爍 .
break;
case 6: writecom(0x80+4); //月閃爍 .
break;
case 7: writecom(0x80+1); //年閃爍 .
break;
case 8: kmenunum=0;
writecom(0x0c); // 取消光標(biāo)閃爍。
flagset=0; //取消時間調(diào)節(jié)標(biāo)志。
dswrite(0x8e,0x00); /* 允許寫操作 */
dswrite(0x80,second/10*16+second%10); dswrite(0x82,minute/10*16+minute%10); dswrite(0x84,hour/10*16+hour%10);
dswrite(0x8a,week/10*16+week%10);
dswrite(0x86,day/10*16+day%10);
dswrite(0x88,month/10*16+month%10);
dswrite(0x8c,year/10*16+year%10);
dswrite(0x8e,0x80); /* 禁止寫操作 */
break;
}
}
}
if(kmenunum!=0) //只有當(dāng)功能鍵按下時 ,才檢測增大 ,減小鍵 .
{
if(kup==0)
{
delay(5);
if(kup==0)
{
while(!kup);
di();
switch(kmenunum) //根據(jù)功能鍵被按下的次數(shù),調(diào)節(jié)相應(yīng)數(shù)值。
{
case 1: second++;
if(second==60)
second=0;
writetime(9,second);
writecom(0x80+0x40+10); // 因為,上面送液晶顯示
break; //一次 ,光標(biāo)后移一位 ,所以要將光標(biāo)復(fù)位 .
case 2: minute++;
if(minute==60)
minute=0;
writetime(6,minute);
writecom(0x80+0x40+7);
break;
case 3: hour++;
if(hour==24)
hour=0;
writetime(3,hour);
writecom(0x80+0x40+4);
break;
case 4: week++;
if(week==8)
week=1;
writeweek(week);
writecom(0x80+11);
break;
case 5: day++;
if(day==32)
day=1;
writenyr(6,day);
writecom(0x80+7);
break;
case 6: month++;
if(month==13)
month=1;
writenyr(3,month);
writecom(0x80+4);
break;
case 7: year++;
if(year==100)
year=0;
writenyr(0,year);
writecom(0x80+1);
break;
}
}
}
if(kdown==0)
{
delay(5);
if(kdown==0)
{
while(!kdown);
di();
switch(kmenunum) //根據(jù)功能鍵被按次數(shù)調(diào)節(jié)相應(yīng)數(shù)值 .
{
case 1: second--;
if(second==-1)
second=59;
writetime(9,second);
writecom(0x80+0x40+10);
break;
case 2: minute--;
if(minute==-1)
minute=59;
writetime(6,minute);
writecom(0x80+0x40+7);
break;
case 3: hour--;
if(hour==-1)
hour=23;
writetime(3,hour);
writecom(0x80+0x40+4);
break;
case 4: week--;
if(week==0)
week=7;
writeweek(week);
writecom(0x80+11);
break;
case 5: day--;
if(day==0)
day=31;
writenyr(6,day);
writecom(0x80+7);
break;
case 6: month--;
if(month==0)
month=12;
writenyr(3,month);
writecom(0x80+4);
break;
case 7: year--;
if(year==-1)
year=99;
writenyr(0,year);
writecom(0x80+1);
break;
}
}
}
}
if(kalarm==0) // 檢測鬧鐘調(diào)節(jié)鍵是否按下,此條if 語句與 if(kmenunum!=0)
{ // 在同一個層次。
delay(5);
if(kalarm==0)
{
flagseta=~flagseta;
while(!kalarm);
di();
if(flagseta==0) //此時,退出鬧鐘設(shè)置,保存各相關(guān)數(shù)值。
{
flagset=0; //清除時間設(shè)置標(biāo)志,在 if(kalarm==0) 這個 if 語句writecom(0x80+0x40)中,不用 flagset=1 這條語句來設(shè)置
//flagset 標(biāo)志位,因為如果進行了鬧鐘時間的設(shè)置就會執(zhí)行前面 if(kmenunum==0) 語句中的 flagset=1 這條語句。
writedata( ); // 清除液晶上的 “ Ri鬧鐘調(diào)節(jié)標(biāo)志。 ”
writedata( );
writecom(0x0c); //取消光標(biāo)閃爍
dswrite(0x8e,0x00); /* 允許寫操作 */
dswrite(writealarmsecond,second/10*16+second%10); // 往 DS1302 中保存鬧鐘的時分秒值。
dswrite(writealarmminute,minute/10*16+minute%10);
dswrite(writealarmhour,hour/10*16+hour%10);
dswrite(0x8e,0x80); /* 禁止寫操作 */
}
else //進入鬧鐘設(shè)置。
{
s=dsread(readalarmsecond)/16*10+dsread(readalarmsecond)%16; /* 讀取DS1302 中保存的鬧鐘時分秒原始值,轉(zhuǎn)化為十進制數(shù)存進 s,m,h 保存,用以后面判斷鬧鐘時間是否到達。*/
m=dsread(readalarmminute)/16*10+dsread(readalarmminute)%16;
h=dsread(readalarmhour)/16*10+dsread(readalarmhour)%16;
second=s; //把鬧鐘時間的十進制數(shù)據(jù)傳送給變量 second,minute,hour,用以調(diào)節(jié)其值。
minute=m;
hour=h;
writecom(0x80+0x40);
writedata(R); // 液晶顯示鬧鐘調(diào)節(jié)標(biāo)志。
writedata(i);
writetime(3,hour); // 送液晶顯示鬧鐘時間。
writetime(6,minute);
writetime(9,second);
}
}
}
if(kevent==0) // 檢測事件提醒調(diào)節(jié)鍵是否按下,此條 if 語句與 if(kmenunum!=0)
{ // 在同一個層次。
delay(5);
if(kevent==0)
{
flagsseta=~flagsseta;
while(!kevent);
di();
if(flagsseta==0) //此時,退出事件提醒設(shè)置,保存各相關(guān)數(shù)值。
{
flagset=0; //清除時間設(shè)置標(biāo)志,在 if(kevent==0) 這個 if 語句
writecom(0x80+9);// 中,不用 flagset=1 這條語句來設(shè)置
//flagset 標(biāo)志位,因為 ,如果進行了事件提醒時間的設(shè)置
//,就會執(zhí)行前面 if(kmenunum==0) 語句中的 flagset=1 這條語句。
writedata( ); // 清除液晶上的 “ Ti 事件提醒調(diào)節(jié)標(biāo)志。 ”
writedata( );
writedata( );
writecom(0x0c); //取消光標(biāo)閃爍
dswrite(0x8e,0x00); /* 允許寫操作 */
dswrite(writeeventyear,year/10*16+year%10); // 往 DS1302 中保存事件提醒的年月日值。
dswrite(writeeventmonth,month/10*16+month%10);
dswrite(writeeventday,day/10*16+day%10);
dswrite(0x8e,0x80); /* 禁止寫操作 */
}
else //進入事件提醒設(shè)置。
{
yy=dsread(readeventyear)/16*10+dsread(readeventyear)%16; /* 讀取DS1302 中保存的事件提醒年月日原始值,轉(zhuǎn)化為十進制數(shù)存進 yy,mm,dd 保存,用以后面判斷事件提醒時間是否到達。*/
mm=dsread(readeventmonth)/16*10+dsread(readeventmonth)%16;
dd=dsread(readeventday)/16*10+dsread(readeventday)%16;
year=yy; //把事件提醒時間的十進制數(shù)據(jù)傳送給變量 hour,day,month,week用以調(diào)節(jié)其值。
month=mm;
day=dd;
writecom(0x80+9);
writedata(T); // 液晶顯示事件提醒設(shè)置標(biāo)志。
writedata(i);
writedata( );
writenyr(0,year);// 送液晶顯示事件提醒時間。
writenyr(3,month);
writenyr(6,day);
}
}
}
}
void dswritebyte(uchar d)// 往 DS1302 寫入一個字節(jié)。
{
uchar i;
ACC=d;
for(i=8;i>0;i--)
{
dsdata=ACC0;
dsclk=1; /*為什么這里時鐘先為 1 后為 0,因為 dsclk 初始化為 0,而,每次循環(huán)
最后 ,都將其置 0,這樣就形成一個從 0 開始的上升沿 ,恰好滿足 ds1302 寫數(shù)據(jù)在上升沿, 但是 ,
時鐘必須從 0 開始的要求 */
dsclk=0;
ACC=ACC>>1;
}
}
uchar dsreadbyte() //從 DS1302 讀出一個字節(jié)。
{
uchar i;
for(i=8;i>0;i--)
{
ACC=ACC>>1;
ACC7=dsdata;
dsclk=1; // 讀數(shù)據(jù)也一樣 ,雖然是下降沿 ,但是 ,也要從 0 開始 .這樣設(shè)置 dsclk 剛好滿足要求。
dsclk=0;
}
return ACC;
}
void dswrite(uchar add,uchar dat) // 向 DS1302 的指定地址寫入一個字節(jié)。
{
dsrst=0;
dsclk=0;
dsrst=1;
dswritebyte(add); /* 先寫入地址 ,命令字節(jié) */
dswritebyte(dat); /* 再寫 1Byte 數(shù)據(jù) */
dsclk=1;
dsrst=0;
}
uchar dsread(uchar add) //從 DS1302 的指定地址讀出一個字節(jié) .
{
uchar dat;
dsrst=0;
dsclk=0;
dsrst=1;
dswritebyte(add); /* 先寫入地址 ,命令字節(jié) */
dat=dsreadbyte(); /* 再讀 1Byte 數(shù)據(jù) */
dsclk=1;
dsrst=0;
return dat;
}
void init1302time(uchar *pClock) //DS1302 時間初始化函數(shù) .
{
uchar i;
uchar add=0x80;
dswrite(0x8e,0x00); /* 允許寫操作 */
for(i=7;i>0;i--)
{
dswrite(add,*pClock); /* 依次寫入秒 分 時 日 月 星期 年 */
pClock++;
add+=2; //因為 DS1302 同一個時鐘寄存器占兩個地址,最低位為 0,是
} //用于寫的地址 ,最低位為 1,是用于讀的地址 .所以 add 加 2.
dswrite(0x8e,0x80); /* 禁止寫操作 */
}
void init() //初始化函數(shù)。
{
uchar num;
//變量初始化 .
flagset=0; // 時間設(shè)置標(biāo)志位置 0,表示未進行時間設(shè)置
flagseta=0; //鬧鐘設(shè)置標(biāo)志位置 0.
flagalarm=0;// 鬧鐘標(biāo)志位置 0.
kmenunum=0; //功能鍵被按次數(shù)計數(shù)器置 0.
lcdrw=0;
//DS1302 初始化。
dswrite(0x8e,0x00); /* 允許寫操作 */
dswrite(0x80,0x00);// 打開 DS1302 晶振,使其開始工作。
dswrite(0x84,0x00);// 設(shè)置為 24 小時制。
init1302time(inittime);// 初始化時間為:
//2018 年 7 月 5 日 星期四, 11 點 59 分 59 秒,
//從左往右的數(shù)據(jù)依次是,秒,分,時,日,月,星期,年。
dswrite(0x8e,0x80); /* 禁止寫操作 */
//1602 液晶初始化。
writecom(0x38);//開顯示
writecom(0x0c);//開顯示不顯示光標(biāo)
writecom(0x06);//寫一個指針加1
writecom(0x01);//清屏
writecom(0x80);//設(shè)置數(shù)據(jù)指針起點
for(num=0;num<16;num++) //寫入液晶固定顯示部分。
{
writedata(t1[num]);
delay(1);
}
writecom(0x80+0x40); // 換到液晶第二行。
for(num=0;num<16;num++)
{
writedata(t2[num]);
delay(1);
}
}
void Delay_100us(uint j)//延時100us
{
uchar i;
for(;j>0;j--)
for(i=0;i<27;i++);//STC89C52RC
}
void Delay_10us(void)//延時10us
{
uchar i;
i--;i--;i--;i--;i--;i--;//STC89C52RC
}
void COM(void)
{
uchar i;
for(i=0;i<8;i++)
{
FLAG=2;
while((!DQ)&&FLAG++);
Delay_10us();
Delay_10us();
Delay_10us();
temp=0;
if(DQ)temp=1;
FLAG=2;
while((DQ)&&FLAG++);
//超時則跳出for循環(huán)
if(FLAG==1) break;
//判斷數(shù)據(jù)位是0還是1
// 如果高電平高過預(yù)定0高電平值則數(shù)據(jù)位為 1
comdata<<=1;
comdata|=temp; //0
}//rof
}
//--------------------------------
//-----濕度讀取子程序 ------------
//--------------------------------
//----以下變量均為全局變量--------
//----溫度高8位== T_data_H------
//----溫度低8位== T_data_L------
//----濕度高8位== RH_data_H-----
//----濕度低8位== RH_data_L-----
//----校驗 8位 == checkdata-----
//----調(diào)用相關(guān)子程序如下----------
//---- Delay();, Delay_10us();,COM();
//--------------------------------
鏈接地址:http://www.820124.com/p-6679430.html