隨著電子技術的發展,人類不斷研究,不斷創新紀錄,萬年歷目前已經不再局限于以書本形式出現,以電腦軟體或者電子產品形式出現的萬年歷被稱為電子萬年歷,與傳統書本形式的萬年歷相比,電子萬年歷得到了越來越廣泛的應用,采用電子時鐘作為時間顯示已經成為一種時尚,目前市場上各式各樣的電子時鐘數不勝數,但多數是只針對時間顯示,功能單一不能滿足人們日常生活需求,
本設計將制作一種基于單片機控制的帶實時溫度顯示、具有定時功能的電子萬年歷,傳統的電子日歷大都體積大,功耗大,顯示不準確等特點,為了縮小體積,減小功耗,使其變得小巧靈敏,本設計加入了時鐘芯片DS1302,可對時間進行準確記時,同時可設定定時時間,實作定時功能,另外本設計具有顯示實時溫度的功能,傳統的溫度傳感器系統大都采用放大、調理、A/D轉換,轉換后的數字信號送入計算機處理,處理電路復雜、可靠性相對較差,占用計算機的資源比較多,本設計將采用DS18B20一線制數字溫度傳感器,可將溫度信號直接轉換成數字信號送給微處理器,電路簡單,成本低,實作了時間溫度同時顯示的效果,最后,溫度和時間都將通過12864液晶顯示幕進行顯示,測驗表明系統達到了設計要求的各項功能,各部分作業正常,
文章目錄
- 1 概論
- 1.1 萬年歷發展背景
- 1.2 國內外現狀、發展
- 2 系統基本方案選擇和論證
- 2.1 單片機芯片的選擇方案和論證
- 2.2 顯示模塊的選擇方案和論證
- 2.3 時鐘芯片的選擇方案和論證
- 2.4 溫度傳感器的選擇方案和論證
- 2.5 電路設計最終方案確定
- 3 系統硬體電路設計
- 3.1 系統功能模塊劃分及整體電路圖
- 3.2 各單元模塊功能分析及模塊電路設計
- 3.2.1 時鐘模塊
- 3.2.2 溫度模塊
- 3.2.3 顯示模塊
- 3.2.4 獨立鍵盤模塊
- 3.2.5 蜂鳴器模塊
- 4 系統軟體設計
- 4.1 萬年歷軟體系統的流程圖
- 4.2 萬年歷軟體系統的程式部分
- 5 測驗結果
- 致 謝
- 參考文獻
# 摘 要
本設計將制作一種基于單片機控制的帶實時溫度顯示、具有定時功能的電子萬年歷,傳統的電子日歷大都體積大,功耗大,顯示不準確等特點,為了縮小體積,減小功耗,使其變得小巧靈敏,本設計加入了時鐘芯片DS1302,可對時間進行準確記時,同時可設定定時時間,實作定時功能,另外本設計具有顯示實時溫度的功能,傳統的溫度傳感器系統大都采用放大、調理、A/D轉換,轉換后的數字信號送入計算機處理,處理電路復雜、可靠性相對較差,占用計算機的資源比較多,本設計將采用DS18B20一線制數字溫度傳感器,可將溫度信號直接轉換成數字信號送給微處理器,電路簡單,成本低,實作了時間溫度同時顯示的效果,最后,溫度和時間都將通過12864液晶顯示幕進行顯示,測驗表明系統達到了設計要求的各項功能,各部分作業正常,
關鍵詞 : 時鐘 溫度檢測 單片機 溫度 LCD12864 DS1302
1 概論
1.1 萬年歷發展背景
隨著電子技術的發展,人類不斷研究,不斷創新紀錄,萬年歷目前已經不再局限于以書本形式出現,以電腦軟體或者電子產品形式出現的萬年歷被稱為電子萬年歷,與傳統書本形式的萬年歷相比,電子萬年歷得到了越來越廣泛的應用,采用電子時鐘作為時間顯示已經成為一種時尚,目前市場上各式各樣的電子時鐘數不勝數,但多數是只針對時間顯示,功能單一不能滿足人們日常生活需求,
1.2 國內外現狀、發展
隨著電子技術的迅速發展,特別是隨大規模集成電路出現,給人類生活帶來了根本性的改變,尤其是單片機技術的應用產品已經走進了千家萬戶,電子萬年歷的出現給人們的生活帶來的諸多方便,
萬年歷中使用的LCD的應用很廣泛,如手表上的液晶顯示屏,儀表儀器上的液晶顯示幕或者是電腦筆記本上的液晶顯示幕,都使用了LCD,在一般的辦公設備上也很常見,如傳真機,復印機,以及一些娛樂器材玩具等也常常見到LCD的足跡,字符型液晶顯示模塊是一種專門用于顯示字母,數字,符號等的點陣式液晶顯示模塊,在顯示幕件上的設計,它是由若干個5×7或5×11等點陣符位組成,每一個點陣字符位都可以顯示一個字符,點陣字符位之間有一空點距的間隔起到了字符間距和行距的作用,目前市面上常用的有16字×1行,16字×2行,20字×2行和40字×2行等的字符模塊組,這些LCD雖然顯示字數各不相同,但是都具有相同的輸入輸出界面,
隨著單片機的發展,電子萬年歷呈現了微型化 ,功能豐富化的趨勢,而且價格在不斷下降,考慮到資源問題,現在的設計設計的萬年歷都采用了節能設計方案,萬年歷對人們的生活有著十分重要的作用,所以電子萬年歷還是有很大的發展前景的,
2 系統基本方案選擇和論證
2.1 單片機芯片的選擇方案和論證
方案一:
采用89C51芯片作為硬體核心,89C51是一種帶4K位元組閃爍可編程可擦除只讀存盤器,采用Flash ROM,內部具有4KB ROM 存盤空間,能于3V的超低壓作業,而且與MCS-51系列單片機完全兼容,與工業標準的MCS-51指令集和輸出管腳相兼容,由于將多功能8位CPU和閃爍存盤器組合在單個芯片中,89C51是一種高效微控制器,51單片機為很多嵌入式控制系統提供了一種靈活性高且價廉的方案但是運用于電路設計中時由于不具備在線編程(ISP)技術,當在對電路進行除錯時,由于程式的錯誤修改或對程式的新增功能需要燒入程式時,對芯片的多次拔插可能對芯片造成一定的損壞,目前該型號芯片已經停產,
方案二:
采用AT89S52單片機,AT89S52單片機是ATMEL生產的單片機,是新一代8051單片機,指令代碼完全兼容傳統8051,內部集成看門狗電路,AT89S52單片機內部有8KB的程式Flash存盤器,由于我們設計的萬年歷燒寫檔案大概在7KB左右 ,而AT89S52單片機的程式Flash為8KB,不用再外接程式存盤器了,
經過綜合比較最終選擇方案二,即選擇AT89S52作為主控制器,
2.2 顯示模塊的選擇方案和論證
方案一:
LCD12864液晶是一種具有8位并行介面方式的點陣圖形液晶顯示模塊;其顯示解析度為128×64,利用該模塊靈活的介面方式和簡單、方便的操作指令,可構成全中文人機互動圖形界面,可以顯示16×16點陣的漢字,也可完成圖形顯示,低電壓低功耗是其又一顯著特點,由該模塊構成的液晶顯示方案與同型別的圖形點陣液晶顯示模塊相比,不論硬體電路結構或顯示程式都要簡潔得多,萬年歷要求顯示年月日、時分秒、星期、和農歷,LCD12864液晶可以完成設計的要求 ,
方案二:
系統采用LED顯示,LED應用可分為兩大類:一是LED單管應用,包括背光源LED,紅外線LED等;另外就是LED顯示屏,目前,中國在LED基礎材料制造方面與國際還存在著一定的差距,但就LED顯示屏而言,中國的設計和生產技術水平基本與國際同步,LED顯示屏是由發光二極管排列組成的顯示幕件,它采用低電壓掃描驅動,具有:耗電少、使用壽命長、成本低、亮度高、故障少、視角大、可視距離遠等特點,采用LED數碼管動態掃描.價格上比較經濟實惠,但不能顯示文字,性價比不是很高,操作起來比較液晶顯示來說略顯繁瑣,所以也不用此種作為顯示,
經過綜合比較最終選擇方案一,即選擇LCD12864液晶顯示屏,
2.3 時鐘芯片的選擇方案和論證
方案一:
采用單片機定時,單片機集成度高、功能強、可靠性高、體積小、功耗低、使用方便、價格低廉等一系列優點,單片機的應用領域已從面向工業控制、通訊、交通、智能儀表等迅速發展到家用消費產品、辦公自動化、汽車電子、PC機外圍以及網路通訊等廣大領域,
直接采用單片機定時計數器提供秒信號,計數的脈沖由外部提供,定時的脈沖由外部晶振提供,定時加1的周期為一個機器周期;定時時間與初值和晶振頻率有關,使用程式實作年、月、日、星期、時、分、秒計數,采用此種方案減少芯片的使用,節約成本,但程式復雜度較高,
方案二:
采用DS1302時鐘芯片,DS1302 是美國DALLAS公司推出的一種高性能、低功耗、帶RAM的實時時鐘電路,它可以對年、月、日、星期、時、分、秒進行計時,具有閏年補償功能,作業電壓為2.5V~5.5V,采用雙電源供電(主電源和備用電源),可設定備用電源充電方式,提供了對后背電源進行涓細電流充電的能力,DS1302用于資料記錄,特別是對某些具有特殊意義的資料點的記錄上,能實作資料與出現該資料的時間同時記錄,因此廣泛應用于測量系統中,采用三線介面與CPU進行同步通信,并可采用突發方式一次傳送多個位元組的時鐘信號或RAM資料,DS1302內部有一個31×8的用于臨時性存放資料的RAM暫存器,采用DS1302只需要寫出驅動程式,呼叫程式讀出暫存器內資料經過簡單的變換就可以輸出萬年歷的資料,
經過綜合比較最終選擇方案二,即采用DS1302時鐘芯片,
2.4 溫度傳感器的選擇方案和論證
方案一:
采用熱敏電阻作為溫度傳感器,熱敏電阻是開發早、種類多、發展較成熟的敏感元器件,熱敏電阻由半導體陶瓷材料組成,利用的原理是溫度引起電阻變化,熱敏電阻的主要特點是:靈敏度較高,其電阻溫度系數要比金屬大10~100倍以上;作業溫度范圍寬,常溫器件適用于-55℃~315℃,高溫器件適用溫度高于315℃(目前最高可達到2000℃)低溫器件適用于-273℃~55℃;體積小,能夠測量其他溫度計無法測量的空隙、腔體及生物體內血管的溫度;使用方便,電阻值可在0.1~100kΩ間任意選擇;易加工成復雜的形狀,可大批量生產;穩定性好、過載能力強,由于半導體熱敏電阻有獨特的性能,所以在應用方面它不僅可以作為測量元件(如測量溫度、流量、液位等),還可以作為控制元件(如熱敏開關、限流器)和電路補償元件,熱敏電阻廣泛用于家用電器、電力工業、通訊、軍事科學、宇航等各個領域,發展前景極其廣闊,
使用熱敏電阻作為傳感器,用熱敏電阻與一個相應阻值電阻相串聯分壓,利用熱敏電阻阻值隨溫度變化而變化的特性,采集這兩個電阻變化的分壓值,并進行A/D轉換,此設計方案需用A/D轉換電路,增加硬體成本而且熱敏電阻的感溫特性曲線并不是嚴格線性的,會產生較大的測量誤差,
方案二:
采用DS18B20溫度傳感器,在應用與高精度、高可靠性的場合時DALLAS(達拉斯)公司生產的DS18B20溫度傳感器當仁不讓,超小的體積,超低的硬體開消,抗干擾能力強,精度高,附加功能強,使得DS18B20更受歡迎,對于我們普通的電子愛好者來說,DS18B20的優勢更是我們學習單片機技術和開發溫度相關的小產品的不二選擇,這是世界上第一片支持“一線總線”介面的溫度傳感器,DS18B20數字溫度計提供9位(二進制)溫度讀數,指示器件的溫度,資訊經過單線介面送入DS18B20或從DS18B20送出,因此從單片機到DS18B20僅需一條線連接即可,它可在1秒鐘(典型值)內把溫度變換成數字
經過綜合比較最終選擇方案二,即采用采用DS18B20溫度傳感器,
2.5 電路設計最終方案確定
最終選擇單片機AT89S52作為主控制器;選擇LCD12864型液晶作為顯示模塊,此模塊可以顯示字母、數字符號、中文字型及圖形,具有繪圖及文字畫面混合顯示功能,可實作設計對“年”、“月”、“日”、“室內溫度”、“度”五個詞語的顯示要求;選擇采用DS1302時鐘芯片,使程式實作年、月、日、星期、時、分、秒的顯示,采用DS18B20溫度傳感器,可以對溫度做出比較精確的測量,而且和單片機通訊只要一個IO,連接方便,
3 系統硬體電路設計
3.1 系統功能模塊劃分及整體電路圖
根據系統功能要求,可大致畫出系統所需硬體結構框圖如圖3-1-1所示:

圖3-1-1 系統功能模塊圖
主控模塊采用性價比較高的AT89S52單片機芯片,在其內部燒寫好程式,可通程序式的運行控制測溫模塊進行測溫;測溫模塊主要是由DS18B20構成,將其與所測物件進行接觸即可獲取被測物件的溫度資料,而所測得的溫度和時鐘芯片測得的實時日歷將通過顯示模塊的液晶顯示幕以數字形式顯示;單片機呼叫程式,讀取DS1302內暫存器,可以得到萬年歷的時間資料,經程序式處理就可以輸出在LCD上;鍵盤電路可對實時日歷進行調整;蜂鳴器可以在鬧鐘定時中,作為聲音提醒,
作品整體電路圖如圖3-1-2所示

圖3-1-2 整體電路圖
3.2 各單元模塊功能分析及模塊電路設計
3.2.1 時鐘模塊
DS1302的作業原理和單片機的介面:
DS1302為美國DALLAS公司的一種實時時鐘芯片,主要特點是采用串行資料傳輸,可為掉電保護電源提供可編程的充電功能,并且可以關閉充電功能,采用32.768Hz晶振,它可以對年、月、日、星期、時、分、秒進行計時,且具有閏年補償等多種功能,DS1302 用于資料記錄,特別是對某些具有特殊意義的資料點的記錄上,能實作資料與出現該資料的時間同時記錄,這種記錄對長時間的連續測控系統結果的分析以及對例外資料出現的原因的查找有重要意義,在本設計中,它的實際電路圖如圖3-2所示:

圖3-2 DS1302與單片機的連接
DS1302需要外接32.768K的晶振,1號引腳接主電源VCC(5V)電源,8號引腳接備用電池(3V),當主電源掉電后,備用電源為DS1302提供電源,維持DS1302內資料不丟失,這正是時鐘芯片所必須的特性,
3.2.2 溫度模塊
傳統的溫度傳感器系統大都采用放大、調理、A/D轉換,轉換后的數字信號送入計算機處理,處理電路復雜、可靠性相對較差,占用計算機的資源比較多,本設計測溫模塊采用一線制總線數字溫度傳感器DS18B20,可將溫度信號直接轉換成數字信號送給微處理器,電路簡單,成本低,其電路原理圖如圖3-3所示:

圖3-3 DS18B20溫度模塊
從圖中可看出,將溫度傳感器的一線制總線通過埠2與本設計主控芯片STC12C5A6S2的埠標號為DS18B20的相連即可實作相互之間的通信,設計中的測溫元件采用的是DS18B20測溫元件,DS18B20是由DALLAS(達拉斯)公司生產的一種溫度傳感器,超小的體積,超低的硬體開消,抗干擾能力強,精度高,附加功能強,使得DS18B20很受歡迎,這是世界上第一片支持“一線總線”介面的溫度傳感器,DS18B20數字溫度計提供9位(二進制)溫度讀數,指示器件的溫度,資訊經過單線介面送入DS18B20或從DS18B20送出,因此從單片機到DS18B20僅需一條線連接即可,它可在1秒鐘(典型值)內把溫度變換成數字,
3.2.3 顯示模塊
本設計顯示模塊主要采用LCD12864液晶顯示幕,其電路原理圖如下:

圖3-5 LCD12864模塊
LCD12864液晶顯示幕通過資料埠也即埠7~14與主控芯片AT89S52的I/O埠P3相連接實作資料與指令的傳輸,再通過控制埠RS、RW、EN也即埠4~6與主控芯片P1.5,P1.6,P1.7埠相接實作對資料和指令傳輸的控制 ,顯示模塊采用12864液晶顯示幕可實作對溫度和時間的直接顯示,清晰明了,
3.2.4 獨立鍵盤模塊
鍵盤是人與萬年歷實作資訊互動的介面,本設計中,我們采用3個獨立鍵盤,電路原理如下圖3-9:

圖3-9 獨立鍵盤
當按鍵按下,與主控芯片連接的埠被降為低電平,按鍵松開則也升為高電平,按鍵采用的是Tack Switch按鈕開關,它具有自動恢復(彈回)的功能,當我們按下按鈕時,其中的接點接通(或切斷),放開按鈕后,接點恢復為切斷(或接通),按照尺寸區分,電子電路或微型計算機所使用的Tack Swith可分為8mm、10mm、12mm等,雖然Tack Switch有4個引腳,但實際上,其內部只有一對a接點,即其中兩個引腳是內部相連通的,而另外兩個引腳內部也是相連通的,3個按鍵實作了開機模式選擇,日期調節等功能,獨立按鍵的引入使得體現了本設計的人性化,智能化,功能的強大,
3.2.5 蜂鳴器模塊
蜂鳴器模塊是本設計中體現人機互動的又一大設計亮點,其電路原理圖如下

圖3-10 蜂鳴器模塊
本設計里,我們采用有源蜂鳴器,由于蜂鳴器的作業電流一般比較大,以至于單片機的I/O 口是無法直接驅動的,所以要利用放大電路來驅動,我們使用三極管來放大電流,驅動蜂鳴器,此模塊只要通過BELL(連接到到單片機P2.7)輸入的PWM波既可以使蜂鳴器分出聲音,我們設計的這款萬年歷可以在鬧鐘定時中作為聲音提醒信號,
4 系統軟體設計
4.1 萬年歷軟體系統的流程圖

圖4-1 系統軟體流程圖
當接通電源開始作業后,單片機中的程式開始運行,將對DS18B20進行初始化,以便和單片機芯片達成通信協議,完成初始化后,由于本系統只有一個測溫元件,單片機會向其發出跳過RAM指令,接下來便可向其發送操作指令,啟動測溫程式,測溫程序完成后,發出溫度轉換指令,從而便可將溫度轉化成數字模式進行顯示讀取;同時DS1302將讀取時分秒星期以及年月日暫存器然后通過液晶顯示實時時間、星期及日期;鍵盤電路中按鍵可對實時日歷時鐘進行調整,
4.2 萬年歷軟體系統的程式部分
DS1302程式代碼
/*
* DS1302 時鐘芯片
*/
#include "DS1302.h" //包含頭檔案
/*
* 寫一個位元組
*/
void write_ds1302_byte(uint8 dat)
{
uint8 i;
for (i=0;i<8;i++) //回圈8次
{ SDA = dat & 0x01;
SCK = 1; // SCK埠置1
dat >>= 1;
SCK = 0; // SCK埠置0
}
}
/*
* 讀一個位元組
*/
uint8 read_ds1302_byte(void)
{
uint8 i, dat=0;
for (i=0;i<8;i++)//回圈8次
{
dat >>= 1;
if (SDA)
dat |= 0x80;
SCK = 1;// SCK埠置1
SCK = 0;// SCK埠置0
}
return dat;
}
void reset_ds1302(void)
{
RST = 0;// RST埠置0
SCK = 0;// SCK埠置0
RST = 1;// RST埠置1
}
/*
* 清除寫保護
*/
void clear_ds1302_WP(void)
{
reset_ds1302();
RST = 1;// RST埠置1
write_ds1302_byte(0x8E);
write_ds1302_byte(0);
SDA = 0;// SDA埠置0
RST = 0;// RST埠置0
}
/*
* 設定寫保護
*/
void set_ds1302_WP(void)
{
reset_ds1302();//復位1302
RST = 1;// RST埠置1
write_ds1302_byte(0x8E);
write_ds1302_byte(0x80);
SDA = 0;// SDA埠置0
RST = 0;// RST埠置0
}
/*
* 設定時鐘資料 (秒分時日月周年)
*/
void set_time(uint8 *timedata)
{
uint8 i, tmp, tmps[7];
for (i=0; i<7; i++) // 轉化為BCD格式
{
tmp = timedata[i] / 10;
tmps[i] = timedata[i] % 10;
tmps[i] = tmps[i] + tmp*16;
}
clear_ds1302_WP();//取消寫保護
reset_ds1302();//復位芯片
RST = 1;// RST埠置1
write_ds1302_byte(DS1302_W_ADDR);
for (i=0; i<7; i++)//回圈7次
{
write_ds1302_byte(tmps[i]);
delay(10);//延時
}
write_ds1302_byte(0);
SDA = 0;// SDA埠置0
RST = 0;// RST埠置0
set_ds1302_WP();//設定寫保護
}
/*
* 讀時鐘資料(秒分時日月周年)
*/
void read_time(uint8 *timedata)
{
uint8 i, tmp;
clear_ds1302_WP();//取消寫保護
reset_ds1302(); //復位芯片
RST = 1; //ST埠置1
write_ds1302_byte(DS1302_R_ADDR);
for (i=0; i<7; i++)//回圈7次
{
timedata[i] = read_ds1302_byte();
delay(10); //延時
}
SDA = 0;// SDA埠置0
RST = 0;// RST埠置0
set_ds1302_WP();//設定寫保護
for (i=0; i<7; i++)//回圈7次
{
tmp = timedata[i];
timedata[i] = (tmp/16%10)*10;
timedata[i] += (tmp%16);
}
}
LCD12864程式代碼
/*
* LCD128*64
*/
#include "LCD.h"
#include "word.h"
#define Page_Add 0xb8
#define Col_Add 0x40
#define Disp_On 0x3f
#define Disp_Off 0x3e
#define Start_Line 0xc0
/*
* 12864判忙
*/
void chekbusy12864(void)
{
uint8 dat;
RS = 0; //指令模式
RW = 1; //讀資料
do{
P3 = 0;
E = 1; //E埠置1
_nop_(); //延時1us
dat = P3 & 0x80;
E = 0; //E埠置0
}while (dat != 0);
}
/*
* 12864片選
* i:0是左屏,1是右屏,2是雙屏
*/
void choose12864(uint8 i)
{
switch (i)
{
case 0: CS1 = 0; CS2 = 1; break;//片選左屏
case 1: CS1 = 1; CS2 = 0; break;//片選右屏
// case 2: CS1 = 0; CS2 = 0; break;
default: break;//退出
}
}
/*
* 寫命令
*/
void cmd_w12864(uint8 cmd)
{
chekbusy12864();
RS = 0;//RS埠置0
RW = 0;//RW埠置0
_nop_();//延時1us
E = 1;//E埠置1
_nop_();//延時1us
P3 = cmd;
_nop_();//延時1us
E = 0;//E埠置0
}
/*
* 寫資料
*/
void dat_w12864(uint8 dat)
{
chekbusy12864();
RS = 1;//RS埠置1
RW = 0;//RW埠置0
_nop_();//延時1us
E = 1;//E埠置1
_nop_();//延時1us
P3 = dat;
_nop_();//延時1us
E = 0;//E埠置0
}
/*
* 清屏
*/
void clear12864(void)
{
uint8 page,row;
choose12864(0);
for(page=0; page<8; page++)
{
cmd_w12864(0xb8+page);
cmd_w12864(0x40);
for(row=0; row<64; row++)
{
dat_w12864(0x00);//寫資料0
}
}
choose12864(1);
for(page=0; page<8; page++)
{
cmd_w12864(0xb8+page);
cmd_w12864(0x40);
for(row=0; row<64; row++)
{
dat_w12864(0x00);//寫資料0
}
}
}
/*
* LCD初始化
*/
void LCD_init(void)
{
chekbusy12864();
cmd_w12864(0xc0); //從第0行開始顯示
cmd_w12864(0x3f); //LCD顯示RAM中的內容
clear12864();
}
/*
* 8x16字符的顯示
*/
void play8(uint8 x, uint8 y, uint8 *addr)
{
uint8 i;
if (x > 63)
{
choose12864(1);
x = x-64;
}
else
{
choose12864(0);
}
cmd_w12864(0x40|x);
cmd_w12864(0xb8|(y++));
if ((y & 0x80) == 0)
for (i=0;i<8;i++)
dat_w12864(*addr++);
else
for (i=0;i<8;i++)
dat_w12864(0xFF - *addr++);
cmd_w12864(0x40|x);
cmd_w12864(0xb8|y);
if ((y & 0x80) == 0)
for (i=0;i<8;i++)
dat_w12864(*addr++);
else
for (i=0;i<8;i++)
dat_w12864(0xFF - *addr++);
}
/*
* 16x16顯示
*/
void play16(uint8 x, uint8 y, uint8 *addr)
{
uint8 i;
if (x > 63)
{
choose12864(1);
x = x-64;
}
else
{
choose12864(0);
}
cmd_w12864(0x40|x);
cmd_w12864(0xb8|(y++));
if ((y & 0x80) == 0)
for (i=0; i<16; i++)
dat_w12864(*addr++);
else
for (i=0; i<16; i++)
dat_w12864(0xFF - *addr++);
cmd_w12864(0x40|x);
cmd_w12864(0xb8|y);
if ((y & 0x80) == 0)
for (i=0; i<16; i++)
dat_w12864(*addr++);
else
for (i=0; i<16; i++)
dat_w12864(0xFF - *addr++);
}
/*
* 16*32 字符顯示
*/
void play32(uint8 x, uint8 y, uint8 num)
{
uint8 i, j, *addr;
addr = Num + num*64;
if (x > 63)
{
choose12864(1);
x = x-64;
}
else
{
choose12864(0);
}
for (j=0; j<4; j++)
{
cmd_w12864(0x40|x);
cmd_w12864(0xb8|(y++));
if ((y & 0x80) == 0)
for (i=0; i<16; i++)
dat_w12864(*addr++);
else
for (i=0; i<16; i++)
dat_w12864(0xFF - *addr++);
}
}
/*
* 8x16數字的顯示
*/
void play8_num(uint8 x, uint8 y, uint8 num)
{
play8(x, y, &S_num[16*(num/10%10)]);
play8(x+8, y, &S_num[16*(num%10)]);
}
/*
* 16x32BCD數字的顯示
*/
void play32_num(uint8 x, uint8 y, uint8 num)
{
play32(x, y, num/10%10);
play32(x+16, y, num%10);
}
void play_week(uint8 x, uint8 y, uint8 num)
{
switch (num)
{
case 1: play16(x+32, y, zhou_yi); break;
case 2: play16(x+32, y, zhou_er); break;
case 3: play16(x+32, y, zhou_san); break;
case 4: play16(x+32, y, zhou_si); break;
case 5: play16(x+32, y, zhou_wu); break;
case 6: play16(x+32, y, zhou_liu); break;
case 7: play16(x+32, y, zhou_qi); break;
default : break;
}
}
//************************************************************************/
// 函式: LCD_Delay()
// 描述: 延時t ms函式
// 引數: t
// 回傳: 無
// 備注: 11.0592MHZ t=0延時時間約13us
// 版本: 2011/01/01 First version
//************************************************************************/
void LCD_Delay_us(unsigned int t)
{
while(t--);
}
//************************************************************************/
// 函式: LCD_Delay()
// 描述: 延時t ms函式
// 引數: t
// 回傳: 無
// 備注: 11.0592MHZ t=1延時時間約1ms
// 版本: 2011/01/01 First version
//************************************************************************/
void LCD_Delay_ms(unsigned int t)
{
unsigned int i,j;
for(i=0;i<t;i++)
for(j=0;j<113;j++)
;
}
主界面框架顯示及設定頁面
/*
* 主界面框架
*/
void main_frame(void)
{
play32(80, 2, 10); //顯示數
play32(32, 2, 10); //顯示數
play16(17, 0, hanzi_nian);//顯示斜線
play16(49, 0, hanzi_yue);//顯示斜線
play16(80, 0, hanzi_ri);//顯示斜線
play16(0, 6 ,hanzi_si);
play16(16, 6 ,hanzi_nei);
play16(32, 6 ,hanzi_wen);
play16(48, 6 ,hanzi_du);
play16(112, 6,hanzi_du);//顯示度
}
/*
* 主界面
*/
void main_show(bit refresh)
{
// uint8 lunar[2];
if (refresh)
read_time((uint8 *)&time);// 讀時間函式// 時間
if (refresh || (time.sec != tmp_time.sec)) // 秒更新
{
tmp_time.sec = time.sec; //讀取秒資料
play8_num(96, 6,zhengshu); //溫度顯示
play32_num(96, 2, time.sec); //顯示秒
}
if (refresh)
main_frame();//重繪界面
if (refresh || (time.min != tmp_time.min)) // 分更新
{
if (!refresh)
flag = 0;
tmp_time.min = time.min;//讀取分
play32_num(48, 2, time.min); //顯示分
}
if (refresh || (time.hour != tmp_time.hour)) // 時更新
{
if ((!refresh)&&(Clock_flag))
alarm_sound();
tmp_time.hour = time.hour; //讀取時
play32_num(0, 2, time.hour); //顯示時
}
if (refresh || (time.day != tmp_time.day)) //日更新
{
tmp_time.day = time.day; //讀取日
play8_num(64, 0, time.day); //顯示日
// 農歷
turn_lunar_calendar(&time, lunar);
play_lunar_calendar(0, 6, lunar[0], lunar[1]);
}
if (refresh || (time.week != tmp_time.week)) // 周更新
{
tmp_time.week = time.week; //讀取周
play_week(140, 0, time.week); //顯示周
}
if (refresh || (time.mon != tmp_time.mon)) // 月更新
{
tmp_time.mon = time.mon; //讀取月
play8_num(32, 0, time.mon); //顯示月
// 農歷
turn_lunar_calendar(&time, lunar); //轉換農歷年
play_lunar_calendar(0, 6, lunar[0], lunar[1]); //顯示農歷年
}
if (refresh || (time.year != tmp_time.year)) // 年更新
{
tmp_time.year = time.year; //讀取年資料
play8_num(0, 0, time.year); //顯示年
// 農歷
turn_lunar_calendar(&time, lunar); //轉換農歷年
play_lunar_calendar(0, 6, lunar[0], lunar[1]); //顯示農歷年
}
}
/*
* 主機界面設定
*/
void main_set(void)
{
int8 key_val, state=1; //變數
play32_num(96, 2|0x80, time.sec); //顯示秒
while (1)
{ key_val = scan_key();//鍵盤掃描
if (key_val == 1) // 設定
{
if (state >= 7)
state = 0;
else
state++; //位置狀態加1
set_time((uint8 *)&time); //設定時間
main_show(1); //顯示主界面
switch (state)
{ case 0: set_time((uint8 *)&time); break;//設定時間
case 1: play32_num(96, 2|0x80, time.sec); break;//顯示秒
case 2: play32_num(48, 2|0x80, time.min); break;//顯示分
case 3: play32_num(0, 2|0x80, time.hour); break;//顯示時
case 4: play_week(140, 0|0x80, time.week); break;//顯示周
case 5: play8_num(64, 0|0x80, time.day); break; //顯示日
case 6: play8_num(32, 0|0x80, time.mon); break; //顯示月
case 7: play8_num(0, 0|0x80, time.year); break; //顯示年
default: break; //退出回圈
}
}
else if (key_val > 1)//按鍵值大于1
{
if (key_val == 4)
{
state = 0;
clear12864(); //清螢屏
main_show(1); //主界面
}
if (state == 1)//位置1設定秒
{
if (key_val == 3)//加按下?
time.sec++;//秒加1
else if(key_val == 2)
time.sec--; //秒減1
if (time.sec >= 60)
time.sec = 0;
else if (time.sec < 0)
time.sec = 59;
play32_num(96, 2|0x80, time.sec);//顯示秒
}
else if (state == 2) //位置2設定分
{
if (key_val == 3) //加按下?
time.min++; //加1
else if(key_val == 2)
time.min--; //減1
if (time.min >= 60)
time.min = 0;
else if (time.min < 0)
time.min = 59;
play32_num(48, 2|0x80, time.min);//顯示分
}
else if (state == 3) //位置3設定時
{
if (key_val == 3) //加按下?
time.hour++; //加1
else if(key_val == 2)
time.hour--; //減1
if (time.hour >= 24)
time.hour = 0;
else if (time.hour < 0)
time.hour = 23;
play32_num(0, 2|0x80, time.hour);//顯示時
}
else if (state == 4) //位置4設定周
{
if (key_val == 3) //加按下?
time.week++; //加1
else if(key_val == 2)
time.week--; //減1
if (time.week >= 8)
time.week = 1;
else if (time.week < 1)
time.week = 7;
play_week(140, 0|0x80, time.week);//顯示周
}
else if (state == 5)//位置5設定日
{
if (key_val == 3) //加按下?
time.day++; //加1
else if(key_val == 2)
time.day--; //減1
if (time.day >= 32)
time.day = 1;
else if (time.day < 1)
time.day = 31;
play8_num(64, 0|0x80, time.day);//顯示日
}
else if (state == 6) //位置6設定月
{
if (key_val == 3) //加按下?
time.mon++; //加1
else if(key_val == 2)
time.mon--; //減1
if (time.mon >= 13)
time.mon = 1;
else if (time.mon < 1)
time.mon = 12;
play8_num(32, 0|0x80, time.mon);//顯示月
}
else if (state == 7) //位置7設定年
{
if (key_val == 3) //加按下?
time.year++; //加1
else if(key_val == 2)
time.year--; //減1
if (time.year >= 100)
time.year = 0; //0年
else if (time.year < 0)
time.year = 99; //99年
play8_num(0, 0|0x80, time.year);//顯示年
}
else
{
break; //退出回圈
}
}
if (state == 0)
break; //退出回圈
}
}
鬧鐘界面顯示
/*
* 鬧鐘界面顯示
*/
void alarm_show(void)
{
int8 key_val, state=1;
uint32 t=0;
play16(16, 0, nao); //顯示 鬧
play16(32, 0, zhong); //鐘
play16(48, 0, hanzi_she);
play16(64, 0, hanzi_ding);
play16(80, 0, hanzi_ye);
play16(96, 0, hanzi_mian);
play16(0, 2, nao); //顯示 鬧
play16(16, 2, zhong); //鐘
play16(32, 2, maohao); //冒號:
if (Alarm_flag)
play16(48, 2, kai); //開
else
play16(48, 2, guan);//關
play8_num(80, 2, alarm.hour); //時
play8(96, 2, maohao1); //冒號
play8_num(104, 2, alarm.min); //分
play16(0, 4, zheng); //顯示 整
play16(16, 4, dian); //顯示 點
play16(32, 4, bao); //顯示 報
play16(48, 4, shi); //顯示 時
play16(64, 4, maohao); //顯示 冒號
play16(0, 6, hanzi_she); //顯示 整
play16(16, 6, hanzi_ding); //顯示 點
play16(32, 6, hanzi_wen); //顯示 報
play16(48, 6, hanzi_du); //顯示 時
play16(64, 6, maohao); //顯示 冒號
play8_num(80, 6,shedingwendu);
play16(96, 6, hanzi_du); //顯示 度
if (Clock_flag)
play16(80, 4, kai); //顯示 開
else
play16(80, 4, guan); //顯示 關
for (t=0; t<30000; t++)
{
key_val = scan_key(); //鍵盤掃描 獲取鍵值
if (key_val > 1) //判斷資料
break;
if (key_val ==4) //判斷資料
{
clear12864(); //清螢屏
main_show(1); //主界面
}
else if (key_val == 1) //判斷資料
{
if (Alarm_flag)
play16(48, 2|0x80, kai);//顯示 開
else
play16(48, 2|0x80, guan);//關
while (1)
{
key_val = scan_key();//鍵盤掃描 獲取鍵值
if (key_val == 1) // 完成設定
{
if (state >= 5) //判斷資料
state = 0;
else
state++;
if (Alarm_flag)
play16(48, 2, kai); //顯示 開
else
play16(48, 2, guan); //顯示 關
play8_num(80, 2, alarm.hour); //鬧鐘 時 顯示
play8_num(104, 2, alarm.min); //鬧鐘 分 顯示
if (Clock_flag)
play16(80, 4, kai); //顯示 開
else
play16(80, 4, guan); //顯示 關
switch (state) //判斷資料
{
case 1:
if (Alarm_flag)//判斷資料
play16(48, 2|0x80, kai); //顯示 開
else
play16(48, 2|0x80, guan); //顯示 關
break;
case 2:
play8_num(104, 2|0x80, alarm.min);//鬧鐘 分 顯示
break;
case 3:
play8_num(80, 2|0x80, alarm.hour);//鬧鐘 時 顯示
break;
case 4:
if (Clock_flag)//判斷資料
play16(80, 4|0x80, kai);//顯示 開
else
play16(80, 4|0x80, guan);//顯示 關
break;
case 5:
play8_num(80, 6|0x80, shedingwendu);
default: break;
}
}
else if (key_val > 1)//判斷資料
{
if (state == 1)//判斷資料
{
Alarm_flag = ~Alarm_flag;
if (Alarm_flag)
play16(48, 2|0x80, kai);//顯示 開
else
play16(48, 2|0x80, guan);//顯示 關
}
else if (state == 2)//判斷資料
{
if (key_val == 3)//判斷資料
alarm.min++;//加1
else if(key_val == 2)
alarm.min--;//減1
if (alarm.min >= 60)//判斷資料
alarm.min = 0;
else if (alarm.min < 0)//判斷資料
alarm.min = 59;
play8_num(104, 2|0x80, alarm.min);//鬧鐘 分 顯示
}
else if (state == 3)//判斷資料
{
if (key_val == 3)//判斷資料
alarm.hour++;//加1
else if(key_val == 2)
alarm.hour--;//減1
if (alarm.hour >= 24)//判斷資料
alarm.hour = 0;
else if (alarm.hour < 0)//判斷資料
alarm.hour = 23;
play8_num(80, 2|0x80, alarm.hour);//鬧鐘 時 顯示
}
else if (state == 4) //判斷資料
{
Clock_flag = ~Clock_flag;
if (Clock_flag) //判斷資料
play16(80, 4|0x80, kai);//顯示 開
else
play16(80, 4|0x80, guan);//顯示 關
}
else if (state == 5) //判斷資料
{
if (key_val == 3)//判斷資料
shedingwendu++;//加1
else if(key_val == 2)
shedingwendu--;//減1
if (shedingwendu >= 99)//判斷資料
shedingwendu = 99;
else if (shedingwendu < 0)//判斷資料
shedingwendu = 99;
play8_num(80, 6|0x80, shedingwendu);
}
else
{
break; //退出
}
}
if (state == 0) //狀態為0退出
break; //狀態為0退出
}
if (state == 0) //狀態為0退出
break; //狀態為0退出
}
}
}
主函式
void main()
{
uint8 key_val;
read_18B20(); //初始DS18B20
Delay_nms(1000);//延時1S,等待18B20作業正常
LCD_init(); //初始化液晶
clear12864(); //清螢屏
information();
delayms(3000);
LCD_init(); //初始化液晶
clear12864(); //清螢屏
main_frame(); //顯示主界面框架
main_show(1); //重繪1次
read_18B20(); //讀溫度
play8_num(96, 6,zhengshu); //顯示溫度
while(1)
{
key_val = scan_key();
if (key_val == 1) //K1?
{
main_set(); //設定主界面
}
else if (key_val == 3) //K3?
{
clear12864(); //清螢屏
alarm_show(); //鬧鐘畫面
clear12864(); //清螢屏
main_show(1); //主界面
}
else
{
read_time((uint8 *)&time); //讀取時間
main_show(0); //顯示主界面
if((time.sec%2)==0){read_18B20();} //每隔2S采集一次
}
/*********************鬧鐘*********************/
if (Alarm_flag)//如果鬧鐘標志有 執行下面的
{
if ((flag == 0) && (alarm.hour == time.hour) && (alarm.min == time.min))//判斷條件是否滿足
{
flag = 1;
clear12864(); //清螢屏
alarm_show(); //鬧鐘
PlayMusic(); //播放音樂
PlayMusic(); //播放音樂
clear12864(); //清螢屏
main_show(1); //顯示主界面
}
}
if(zhengshu>shedingwendu)
{
BEEP = ~BEEP;
}
}
}
5 測驗結果
在硬體電路焊接和軟體程式設計分別完成的基礎之上,進行軟硬體的結合與除錯,通過下載將在電腦上已完成的程式下載到單片機芯片中,在除錯中發現軟體中存在的問題,及時解決問題,確保系統能正常作業并達到設計要求,通過反復的除錯與實驗,可以證明該系統能夠較好地完成設計所需的基本要求,即能夠正確的顯示萬年歷,
在完成軟體系統時,剛開始我是用的是12M的晶振,所有器件正常,后來我換了11.0592M的晶振,結果溫度就不正常了,經過認真排查才發現是由于DS18B20在資料讀取時,對時間要求很精確,由于晶振的不同造成了讀資料的錯誤,經過這次除錯,讓我更清晰的認識到了時序對元器件的重要性,在設計中,因為考慮到鬧鐘定時功能,我們希望我們設定的鬧鐘時刻不會因為系統的掉電而丟失,考慮到DS1302是有鋰電池作為電源的,不會因為主系統掉電丟失內部資料,所以我們將鬧鐘的定時時刻放到了DS1302內的空余暫存器里面,像這些靈活的技巧就需要我們認真的閱讀元件的資料手冊,從中索取對自己有用的資訊,
經測驗,本作品完成設計所有要求,經過萬年歷的設計,讓我學到了很多,讓我認識到了學習基礎知識的重要性,當設計完整的系統時,要考慮到硬體和軟體兩者的結合,有時硬體的不足,我們可以用軟體程式來彌補,從而節約硬體成本,在設計軟體程式時要模塊化,可以提高程式的可讀性,
致 謝
在論文即將完成之際,我要特別感謝我的指導老師對我的熱情關懷和細心指導,在我做畢業設計的整個程序中,老師都以她最大的可能來幫助我,教導我,跟著老師做畢業設計,我學會了好多東西,這些都對我未來的作業和生活產生重大的影響,她不僅僅是我們學術上的良師,更是生活中的益友,她以一個教育作業者熱忱的心胸不厭其煩地指導著我們,教育者我們,使我們不僅學到了扎實的專業知識,更學到了做人的道理,她孜孜不倦悉心細致的教誨和嚴謹治學一絲不茍的作業作風使我永遠都不能忘記,在此,特向她表示真誠的感謝,祝老師身體健康,桃李滿天下,
同時,在我三年的大學生活中,也得到了很多老師、同學、朋友的支持和幫助,在此一并表示感謝,正是由于你們,才使我的大學生活更加豐富多彩,感謝你們,
參考文獻
[1] 李群芳,肖看.單片機原理、介面及應用.北京:清華大學出版社,2007
[2] 譚浩強.C語言程式設計.北京:清華大學出版社,2006
[3] 張義和,王敏男,許宏昌等.例說51單片機.北京:人民郵電出版社,2008
[4] 劉坤,宋戈,趙紅波等.51單片機C語言應用技術開發技術大全.北京:人民郵電出版社,2008
[5] 白延敏.51單片機典型系統開發實體精講.北京:電子工業出版社,2009
[6] 周麗娜.Protel99SE電路設計技術.北京:中國鐵道出版社,2009
[7] 王為青,程國鋼.單片機Keil C×51應用開發技術.北京:人民郵電出版社,2007
[8] 江志紅.51單片機技術與應用系統開發案例精選.北京:清華大學出版社,2009
[9] Muhammad Ali Mazidi,Janice Gillispie,Rolin Mckinlay.The 8051 Microcontroller and Embedded Systems:Using Assembly and C,Second Edition.Pearson Education,2006
[10] U. Tietze Ch. Schenk. Electronic Circuits. Handbook for Design and Application, Berlin, New York: Springer-Verlag,2005
創作不易,分享更需勇氣! 喜歡的可以給點個 ?贊? 嗎?歡迎評論區留言交流~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/229948.html
標籤:其他
上一篇:物聯網 掌控板搶答器實驗
下一篇:學習STM32mini版
