1、 項目介紹(設(shè)計內(nèi)容)
基于AT89S52單片機的多功能電子萬年歷的硬件結(jié)構(gòu)和軟硬件設(shè)計方法。本設(shè)計由數(shù)據(jù)顯示模塊、溫度采集模塊、時間處理模塊和調(diào)整設(shè)置模塊四個模塊組成。系統(tǒng)以AT89S52單片機為控制器,以串行時鐘日歷芯片DS1302記錄日歷和時間,它可以對年、月、日、時、分、秒進行計時,還具有閏年補償?shù)榷喾N功能。溫度采集選用DS18B20芯片,萬年歷采用直觀的數(shù)字顯示,數(shù)據(jù)顯示采用1602液晶顯示模塊,可以在LCD1602上同時顯示年、月、日、周日、時、分、秒,還具有時間校準等功能。
2、 項目的總體設(shè)計
系統(tǒng)的功能往往決定了系統(tǒng)采用的結(jié)構(gòu),經(jīng)過成本,性能,功耗等多方面的考慮決定用三個8位74LS164串行接口外接LED顯示器,RESPACK-8對單片機AT89S52進行供電,時間芯片DS1302連接單片機AT89S52。從而實現(xiàn)電子萬年歷的功能。
按照系統(tǒng)設(shè)計的要求,初步確定系統(tǒng)由電源模塊、時鐘模塊、顯示模塊、鍵盤接口模塊、溫度測量模塊和鬧鐘模塊共六個模塊組成,電路系統(tǒng)構(gòu)成框圖如圖1所示。
圖1 硬件電路框圖
圖2 主程序流程圖
3、 設(shè)計思路方法及實現(xiàn)步驟(包括硬件設(shè)計和軟件設(shè)計兩個部分)
3.1 Proteus仿真圖
單片機電子萬年歷的制作有多種方法,可供選擇的器件和運用的技術(shù)也有很多種。所以,系統(tǒng)的總體設(shè)計方案應(yīng)在滿足系統(tǒng)功能的前提下,充分考慮系統(tǒng)使用的環(huán)境,所選的結(jié)構(gòu)要簡單使用、易于實現(xiàn),器件的選用著眼于合適的參數(shù)、穩(wěn)定的性能、較低的功耗以及低廉的成本。
系統(tǒng)的功能往往決定了系統(tǒng)采用的結(jié)構(gòu),經(jīng)過成本,性能,功耗等多方面的考慮決定用三個8位74LS164串行接口外接LED顯示器,RESPACK-8對單片機AT89S52進行供電,時間芯片DS1302連接單片機AT89S52。從而實現(xiàn)電子萬年歷的功能。
按照系統(tǒng)設(shè)計的要求,初步確定系統(tǒng)由電源模塊、時鐘模塊、顯示模塊、鍵盤接口模塊、溫度測量模塊和鬧鐘模塊共六個模塊組成,電路系統(tǒng)構(gòu)成框圖如圖1所示。
圖3 硬件電路框圖
3.2 DS1302讀寫程序設(shè)計
本系統(tǒng)的時間讀取主要來源于單片機對DS1302的操作,在硬件上時鐘芯片DS1302與單片機的連接需要三條線,即SCLK(7)、I/O(6)、RST(5),具體連接圖見系統(tǒng)硬件設(shè)計原理圖。讀取寫程序設(shè)計如下:
函 數(shù) 名:RTInputByte()
功 能:實時時鐘寫入一字節(jié)
說 明:往DS1302寫入1Byte數(shù)據(jù) (內(nèi)部函數(shù))
入口參數(shù):d 寫入的數(shù)據(jù)
返 回 值:無
void RTInputByte(uchar d)
{
uchar i;
ACC = d;
for(i=8; i>0; i--)
{
T_IO = ACC0; /*相當于匯編中的 RRC */
T_CLK = 1;
T_CLK = 0;
ACC = ACC >> 1;
}
函 數(shù) 名:RTOutputByte()
功 能:實時時鐘讀取一字節(jié)
說 明:從DS1302讀取1Byte數(shù)據(jù) (內(nèi)部函數(shù))
入口參數(shù):無
返 回 值:ACC
uchar RTOutputByte(void)
{
uchar i;
for(i=8; i>0; i--)
{
ACC = ACC >>1; /*相當于匯編中的 RRC */
ACC7 = T_IO;
T_CLK = 1;
T_CLK = 0;
}
return(ACC);
}
函 數(shù) 名:W1302()
功 能:往DS1302寫入數(shù)據(jù)
說 明:先寫地址,后寫命令/數(shù)據(jù) (內(nèi)部函數(shù))
調(diào) 用:RTInputByte() , RTOutputByte()
入口參數(shù):ucAddr: DS1302地址, ucData: 要寫的數(shù)據(jù)
返 回 值:無
void W1302(uchar ucAddr, uchar ucDa)
{
T_RST = 0;
T_CLK = 0;
T_RST = 1;
RTInputByte(ucAddr); /* 地址,命令 */
RTInputByte(ucDa); /* 寫1Byte數(shù)據(jù)*/
T_CLK = 1;
T_RST = 0;
}
函 數(shù) 名:R1302()
功 能:讀取DS1302某地址的數(shù)據(jù)
說 明:先寫地址,后讀命令/數(shù)據(jù) (內(nèi)部函數(shù))
調(diào) 用:RTInputByte() , RTOutputByte()
入口參數(shù):ucAddr: DS1302地址
返 回 值:ucData :讀取的數(shù)據(jù)
uchar R1302(uchar ucAddr)
{
uchar ucData;
T_RST = 0;
T_CLK = 0;
T_RST = 1;
RTInputByte(ucAddr); /* 地址,命令 */
ucData = RTOutputByte(); /* 讀1Byte數(shù)據(jù) */
T_CLK = 1;
T_RST = 0;
return(ucData);
}
DS1302與微處理器進行數(shù)據(jù)交換時,首先由微處理器向電路發(fā)送命令字節(jié),命令字節(jié)最高位MSB(D7)必須為邏輯 1,如果D7=0,則禁止寫DS1302,即寫保護;D6=0,指定時鐘數(shù)據(jù),D6=1,指定RAM數(shù)據(jù);D5~D1指定輸入或輸出的特定寄存器;最低位LSB(D0)為邏輯0,指定寫操作(輸入),D0=1,指定讀操作(輸出) 。
3.3 完整代碼
#include<reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
char a,miao,shi,fen,ri,yue,nian,keynum;
int temp;//,year1,month1,day1;
#define h1 0x80 //LCD第一行的初始化位置
#define h2 0x80+0x40 //LCD第二行初始化位置
//定義1602相關(guān)管腳
sbit rs=P1^2;
sbit en=P1^0;
sbit rw=P1^1;
//DS1302芯片的管腳定義
sbit DSIO=P1^5;
sbit SCLK=P1^4;
sbit RST=P1^6;
sbit ACC0=ACC^0;//設(shè)置累加器
sbit ACC7=ACC^7;
//按鍵
sbit key1=P3^2;
sbit key2=P3^3;
sbit key3=P3^4;
void delay2(uint s)//延時,用于溫度程序部分
{
while(s--);//區(qū)分i,用s表示
}
void delay(uint z)//延時函數(shù)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void writecom(uchar com)//寫入指令函數(shù)
{
rs=0;
rw=0;
P0=com;
delay2(1);
en=1;
delay2(1);
en=0;
}
void writedata(uchar dat)//寫入數(shù)據(jù)函數(shù)
{
rs=1;
rw=0;
P0=dat;
delay2(1);
en=1;
delay2(1);
en=0;
}
void print(uchar a3,uchar *str)//寫字符串函數(shù)
{
writecom(a3|0x80);
while(*str!='\0')
{
//delay(100);
writedata(*str++);
}
*str=0;
}
void lcdinit()
{
writecom(0x38);//設(shè)置為兩行顯示,8位顯示
writecom(0x0c);//開顯示,不顯示光標
writecom(0x06);//光標右移
writecom(0x01);//清屏
}
void write_1302(uchar addr, uchar dat)
{
uchar n;
RST = 0;
_nop_();
SCLK = 0;//先將SCLK置低電平。
_nop_();
RST = 1; //然后將RST(CE)置高電平。
_nop_();
for (n=0; n<8; n++)//開始傳送八位地址命令
{
DSIO = addr & 0x01;//數(shù)據(jù)從低位開始傳送
addr >>= 1;
SCLK = 1;//數(shù)據(jù)在上升沿時,DS1302讀取數(shù)據(jù)
_nop_();
SCLK = 0;
_nop_();
}
for (n=0; n<8; n++)//寫入8位數(shù)據(jù)
{
DSIO = dat & 0x01;
dat >>= 1;
SCLK = 1;//數(shù)據(jù)在上升沿時,DS1302讀取數(shù)據(jù)
_nop_();
SCLK = 0;
_nop_();
}
RST = 0;//傳送數(shù)據(jù)結(jié)束
_nop_();
}
uchar read_1302(uchar addr )//從1302讀數(shù)據(jù)函數(shù),指定讀取數(shù)據(jù)來源地址
{
uchar n,dat,dat1;
RST = 0;
_nop_();
SCLK = 0;//先將SCLK置低電平。
_nop_();
RST = 1;
_nop_();
for(n=0; n<8; n++)//開始傳送八位地址命令
{
DSIO = addr & 0x01;
addr >>= 1;
SCLK = 1;
_nop_();
SCLK = 0;
_nop_();
}
_nop_();
for(n=0; n<8; n++)//讀取8位數(shù)據(jù)
{
dat1 = DSIO;
dat = (dat>>1) | (dat1<<7);
SCLK = 1;
_nop_();
SCLK = 0;
_nop_();
}
RST = 0;
_nop_();
SCLK = 1;
_nop_();
DSIO = 0;
_nop_();
DSIO = 1;
_nop_();
return dat;
}
uchar turnBCD(uchar bcd)//BCD碼轉(zhuǎn)換為十進制函數(shù)
{
return((bcd>>4)*10+(bcd&0x0F));
}
void ds1302_init()//1302時鐘芯片初始化函數(shù)
{
RST=0;
SCLK=0;
write_1302(0x8e,0x00);//允許寫
write_1302(0x8e,0x80);//打開保護
}
//時分秒顯示函數(shù)
void writetime(uchar add,uchar dat)//寫入時分秒
{
uchar gw,sw;
gw=dat%10;//取得個位數(shù)
sw=dat/10;//取得十位數(shù)
writecom(h2+add);//第二行顯示
writedata(0x30+sw);//顯示該數(shù)字
writedata(0x30+gw);
}
//年月日顯示函數(shù)
void writeday(uchar add,uchar dat)//寫入年月日函數(shù)
{
uchar gw,sw;
gw=dat%10;//取得個位數(shù)字
sw=dat/10;//取得十位數(shù)字
writecom(h1+add);//在第一行顯示
writedata(0x30+sw);
writedata(0x30+gw);//顯示
}
//按鍵掃描函數(shù)
void keyscan()
{
if(key1==0)
{
delay(5);
if(key1==0)
{
while(!key1);
keynum++;
if(keynum>=8)
keynum=1;
switch(keynum)
{
case 1:TR0=0;
writecom(h2+0x0b);//秒的位置
writecom(0x0f);//設(shè)置為光標閃爍
temp=(miao)/10*16+(miao)%10;//秒化為bcd碼
write_1302(0x8e,0x00);
write_1302(0x80,0x80|temp);//秒數(shù)據(jù)寫入
write_1302(0x8e,0x80);
break;
case 2:writecom(h2+8);
break;
case 3:writecom(h2+5);
break;
case 4:writecom(h1+0x0c);
break;
case 5:writecom(h1+0x09);
break;
case 6:writecom(h1+0x06);
break;
case 7:writecom(0x0c);
TR0=1;//重新打開定時器
temp=(miao)/10*16+(miao)%10;
write_1302(0x8e,0x00);
write_1302(0x80,0x00|temp);//寫入秒
write_1302(0x8e,0x80);
break;
}
}
}
if(keynum!=0)//當設(shè)置鍵按下時才能操作
{
if(key2==0)
{
delay(5);
if(key2==0)
{
while(!key2);
switch(keynum)
{
case 1:miao++;//
if(miao>=60) miao=0;
writetime(0x0a,miao);
temp=(miao)/10*16+(miao)%10;//轉(zhuǎn)換為bcd碼
write_1302(0x8e,0x00);//允許寫
write_1302(0x80,temp);// 寫入秒
write_1302(0x8e,0x80);//打開保護
writecom(h2+0x0b);//液晶模式為寫入后自動右移,在此返回原來位置
break;
case 2:fen++;
if(fen>=60) fen=0;
writetime(0x07,fen);
temp=(fen)/10*16+(fen)%10;
write_1302(0x8e,0x00);
write_1302(0x82,temp);
write_1302(0x8e,0x80);
writecom(h2+0x08);
break;
case 3:shi++;
if(shi>=24) shi=0;
writetime(0x04,shi);
temp=(shi)/10*16+(shi)%10;
write_1302(0x8e,0x00);
write_1302(0x84,temp);
write_1302(0x8e,0x80);
writecom(h2+0x05);
break;
case 4:ri++;
if(ri>=32) ri=1;
writeday(0x0b,ri);
temp=(ri)/10*16+(ri)%10;
write_1302(0x8e,0x00);
write_1302(0x86,temp);
write_1302(0x8e,0x80);
writecom(h1+0x0c);
break;
case 5:yue++;
if(yue>=13) yue=1;
writeday(0x08,yue);
temp=(yue)/10*16+(yue)%10;
write_1302(0x8e,0x00);
write_1302(0x88,temp);
write_1302(0x8e,0x80);
writecom(h1+0x09);
break;
case 6:nian++;
if(nian>=100) nian=0;
writeday(0x05,nian);
temp=(int)((nian)/10*16+(nian)%10);
write_1302(0x8e,0x00);
write_1302(0x8c,temp);
write_1302(0x8e,0x80);
writecom(h1+0x06);
break;
}
}
}
//以下是減的函數(shù)
if(key3==0)
{
delay(5);//消除抖動
if(key3==0)
{
while(!key3);
switch(keynum)
{
case 1:miao--;
if(miao<0) miao=59;//減到-1返回59
writetime(0x0a,miao);//在十位數(shù)寫入
temp=(miao)/10*16+(miao)%10;//轉(zhuǎn)換為bcd碼
write_1302(0x8e,0x00);//允許寫
write_1302(0x80,temp);//寫入秒
write_1302(0x8e,0x80);//打開保護
writecom(h2+0x0b);//返回個位位置
break;
case 2:fen--;
if(fen<0) fen=59;
writetime(0x07,fen);
temp=(fen)/10*16+(fen)%10;
write_1302(0x8e,0x00);
write_1302(0x82,temp);
write_1302(0x8e,0x80);
writecom(h2+8);
break;
case 3:shi--;
if(shi<0) shi=23;
writetime(0x04,shi);
temp=(shi)/10*16+(shi)%10;
write_1302(0x8e,0x00);
write_1302(0x84,temp);
write_1302(0x8e,0x80);
writecom(h2+0x05);
break;
case 4:ri--;
if(ri<1) ri=31;
writeday(0x0b,ri);
temp=(ri)/10*16+(ri)%10;
write_1302(0x8e,0x00);
write_1302(0x86,temp);
write_1302(0x8e,0x80);
writecom(h1+0x0c);
break;
case 5:yue--;
if(yue<1) yue=12;
writeday(0x08,yue);
temp=(yue)/10*16+(yue)%10;
write_1302(0x8e,0x00);
write_1302(0x88,temp);
write_1302(0x8e,0x80);
writecom(h1+0x09);
break;
case 6:nian--;
if(nian<0) nian=99;
writeday(0x05,nian);
temp=(int)((nian)/10*16+(nian)%10);
write_1302(0x8e,0x00);
write_1302(0x8c,temp);
write_1302(0x8e,0x80);
writecom(h1+0x06);
break;
}
}
}
}
}
void init()
{
TMOD=0x01;
TH0=(65536-60000)/256;//10毫秒
TL0=(65536-60000)%256;
EA=1;
ET0=1;//允許T0中斷
TR0=1;//啟動中斷
}
void main()
{
lcdinit();
ds1302_init();
init();//定時器初始化函數(shù)
while(1)
{
keyscan();
}
}
void timer0() interrupt 1
{
TH0=(65536-60000)/256;
TL0=(65536-60000)%256;
// TR0=0;
//讀取數(shù)據(jù)
miao=turnBCD(read_1302(0x81));
fen=turnBCD(read_1302(0x83));
shi=turnBCD(read_1302(0x85));
ri=turnBCD(read_1302(0x87));
yue=turnBCD(read_1302(0x89));
nian=turnBCD(read_1302(0x8d));
//顯示數(shù)據(jù)
print(0x80+3,"20");
print(0x80+7,"/");
print(0x80+10,"/");
writeday(0x0b,ri);//顯示日
writeday(0x08,yue);//顯示月
writeday(0x05,nian);//顯示年
print(0x40+6,":");
print(0x40+9,":");
writetime(0x0a,miao);//顯示出秒
writetime(0x07,fen);//顯示出分
writetime(0x04,shi);//顯示出時,第二行第一個開始
}
4、 運行結(jié)果或者測試結(jié)果
①次電路主要是檢測格其引腳電壓是否正常,晶振和電源是否接好,檢測硬件電路是否有短路、斷路、虛焊等,以確保設(shè)計的可靠性和電器元件的性能。而電路中的電源電路、晶體振蕩電路、按鍵接口電路及復(fù)位電路、鬧鐘電路等都是采用基礎(chǔ)的電路設(shè)計,除了基礎(chǔ)電路硬件調(diào)試外我們還可以通過軟件來測試硬件,如通過下載口寫入其它一個比較簡單的程序,以便測試。
②首先由USB電源插口接入5V的直流電壓供給系統(tǒng)使用。在這里接上一個發(fā)光二級管作為指示,單輸入電壓正常時,二極管亮,LCD同時顯示正常。系統(tǒng)在正常工作時,LCD液晶上第一行顯示時分秒和溫度,第二行顯示年月日和星期,如果想要對時間進行調(diào)整,可以通過調(diào)整設(shè)置模塊來實現(xiàn)。當按下設(shè)置鍵P3.0鍵時可調(diào)節(jié)主頁面的時分秒、年月日的調(diào)節(jié),P3.1為調(diào)整加按鍵,P3.2為調(diào)整減按鍵,P3.3按下時可進入另一種模式。第二種模式可顯示閏年,第三種模式可設(shè)置鬧鐘時間。如果想要退出該模式就在按一下P3.3即可。
③在硬件調(diào)試過程中,當接通電源的時候,我們發(fā)現(xiàn)液晶顯示器沒有工作,背光燈有亮但沒有數(shù)據(jù)出來。但電源指示燈已亮,說明電源輸入正常,待我們用萬用表電路中各電壓時發(fā)現(xiàn),單片機各引腳電壓也正常,顯示器的各引腳也正常。經(jīng)過同學(xué)與老師的幫助,發(fā)現(xiàn)程序出錯,改后再接電源,電路一切正常。
5、 遇到的問題及解決的方法
出現(xiàn)電子數(shù)碼萬年歷死機的現(xiàn)象:
此故障多為電壓不穩(wěn)和其他干擾造成的,首先更換記憶電池,可排除多數(shù)情況下的故障;仍不工作,拔下電源與主板連線,再次插上,部分死機故障可以恢復(fù)正常;若還不工作,可以在不通電的情況下,用鑷子短路主板上的所有濾波電容,通電看是否正常工作,最后檢查晶振。
出現(xiàn)走時不準現(xiàn)象:
5V電壓低,記憶電池欠壓,晶振性能不良,修理電源使其恢復(fù)正常,更換電池,更換晶振。
再次通電時間和日期出錯:
這是很長見得故障,很可能是線路板自帶的圓形電子沒電了,長時間不用很可能會耗盡其自帶的電量,那樣的話就失去了其記憶功能。
6、總 結(jié)
在整個設(shè)計過程中,硬件方面主要設(shè)計了AT89S52單片機的最小系統(tǒng)、DS1302接口電路、DS18B20接口電路、鬧鐘及LCD顯示;軟件方面借助各個渠道的資料,主要設(shè)計了陽歷數(shù)據(jù)讀取程序、陽歷轉(zhuǎn)陰歷程序、溫度采集程序、鬧鈴程序以及LCD顯示程序;系統(tǒng)的調(diào)試主要是通過一塊AT89S52開發(fā)板,再借助于Keil、STC以及少許自己搭建的外圍電路實現(xiàn)的;再此過程中,分步調(diào)試時顯示出了陽歷的日期及時間,還有實時溫度,集中調(diào)試時沒有達到預(yù)期效果。此萬年歷具有讀顯示直觀、功能多樣、電路簡潔、成本低廉等諸多優(yōu)點,符合電子儀器儀表的發(fā)展趨勢,具有廣闊的市場前景。
在整個設(shè)計過程中學(xué)到了許多沒學(xué)到的知識,對電路的設(shè)計、布局要先有一個好的構(gòu)思,才顯得電路板美觀、大方。程序編寫中,由于思路不清晰,開始時遇到了很多的問題,經(jīng)過靜下心來思考,理清了思路,反而得心應(yīng)手。在此次設(shè)計中,知道了做事要有一顆平常的心,不要想著走捷徑,一步一腳印。也練就了我們的耐心,做什么事都要有耐心。在本次設(shè)計中學(xué)到了很多很多東西,這是最重要的文章來源:http://www.zghlxwxcb.cn/news/detail-767867.html
7、源碼獲取
萬年歷文章來源地址http://www.zghlxwxcb.cn/news/detail-767867.html
到了這里,關(guān)于基于AT89S52單片機的多功能電子萬年歷的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!