熱度 1|
說明:需要按程序中定義的接口。按自己接口,只要在程序中修改接口定義即可。
1、DS1302引腳排列:如下圖
引腳說明:
1)Vcc1:后備電源,VCC2:主電源。在主電源關(guān)閉的情況下,也能保持時鐘的連續(xù)運(yùn)行。DS1302由Vcc1或Vcc2兩者中的較大者供電。當(dāng)Vcc2大于Vcc1+0.2V時,Vcc2給DS1302供電。當(dāng)Vcc2小于Vcc1時,DS1302由Vcc1供電。2)X1、X2:振蕩源,外接32.768kHz晶振。
3)RST:復(fù)位/片選線,通過把RST輸入驅(qū)動置高電平來啟動所有的數(shù)據(jù)傳送。RST輸入有兩種功能:首先,RST接通控制邏輯,允許地址/命令序列送入移位寄存器;其次,RST提供終止單字節(jié)或多字節(jié)數(shù)據(jù)的傳送手段。當(dāng)RST為高電平時,所有的數(shù)據(jù)傳送被初始化,允許對DS1302進(jìn)行操作。如果在傳送過程中RST置為低電平,則會終止此次數(shù)據(jù)傳送,I/O引腳變?yōu)楦咦钁B(tài)。上電運(yùn)行時,在Vcc>2.0V之前,RST必須保持低電平。只有在SCLK為低電平時,才能將RST置為高電平。
4)I/O為串行數(shù)據(jù)輸入輸出端(雙向)。
5)SCLK為時鐘輸入端。
2、 DS1302的控制字節(jié)
DS1302 的控制字如下圖所示。
控制字節(jié)的最高有效位(位7)必須是邏輯1,如果它為0,則不能把數(shù)據(jù)寫入DS1302中,位6如果為0,則表示存取日歷時鐘數(shù)據(jù),為1表示存取RAM數(shù)據(jù);位5至位1指示操作單元的地址;最低有效位(位0)如為0表示要進(jìn)行寫操作,為1表示進(jìn)行讀操作,控制字節(jié)總是從最低位開始輸出。
3、數(shù)據(jù)輸入輸出(I/O)
在控制指令字輸入后的下一個SCLK時鐘的上升沿時,數(shù)據(jù)被寫入DS1302,數(shù)據(jù)輸入從低位即位0開始。同樣,在緊跟8位的控制指令字后的下一個SCLK脈沖的下降沿讀出DS1302的數(shù)據(jù),讀出數(shù)據(jù)時從低位0位到高位7。
DS1302的寄存器
DS1302有12個寄存器,其中有7個寄存器與日歷、時鐘相關(guān),存放的數(shù)據(jù)位為BCD碼形式,其日歷、時間寄存器及其控制字見數(shù)據(jù)手冊。
此外,DS1302 還有年份寄存器、控制寄存器、充電寄存器、時鐘突發(fā)寄存器及與RAM相關(guān)的寄存器等。時鐘突發(fā)寄存器可一次性順序讀寫除充電寄存器外的所有寄存器內(nèi)容。 DS1302與RAM相關(guān)的寄存器分為兩類:一類是單個RAM單元,共31個,每個單元組態(tài)為一個8位的字節(jié),其命令控制字為C0H~FDH,其中奇數(shù)為讀操作,偶數(shù)為寫操作;另一類為突發(fā)方式下的RAM寄存器,此方式下可一次性讀寫所有的RAM的31個字節(jié),命令控制字為FEH(寫)、FFH(讀)。
溫度傳感器的種類眾多,在應(yīng)用與高精度、高可靠性的場合時 DALLAS(達(dá)拉斯)公司生產(chǎn)的 DS18B20 溫度傳感器當(dāng)仁不讓。超小的體積,超低的硬件開消,抗干擾能力強(qiáng),精度高,附加功能強(qiáng),使得 DS18B20 更受歡迎。對于我們普通的電子愛好者來說,DS18B20 的優(yōu)勢更是我們學(xué)習(xí)單片機(jī)技術(shù)和開發(fā)溫度相關(guān)的小產(chǎn)品的不二選擇。了解其工作原理和應(yīng)用可以拓寬您對單片機(jī)開發(fā)的思路。 如果要更全的資料請搜索“完整的ds18b20中文資料.pdf “。
DS18B20 的主要特征:
„ 全數(shù)字溫度轉(zhuǎn)換及輸出。
„ 先進(jìn)的單總線數(shù)據(jù)通信。
„ 最高 12 位分辨率,精度可達(dá)土 0.5攝氏度。
„ 12 位分辨率時的最大工作周期為 750 毫秒。
„ 可選擇寄生工作方式。
„ 檢測溫度范圍為–55°C ~+125°C (–67°F ~+257°F)
„ 內(nèi)置 EEPROM,限溫報警功能。
„ 64 位光刻 ROM,內(nèi)置產(chǎn)品序列號,方便多機(jī)掛接。
„ 多樣封裝形式,適應(yīng)不同硬件系統(tǒng)。
DS18B20
引腳功能:
·GND 電壓地 ·DQ 單數(shù)據(jù)總線
·VDD 電源電壓 ·NC 空引腳
DS18B20
工作原理及應(yīng)用:
DS18B20 的溫度檢測與數(shù)字?jǐn)?shù)據(jù)輸出全集成于一個芯片之上,從而抗干擾力更強(qiáng)。其一個工作周期可分為兩個部分,即溫度檢測和數(shù)據(jù)處理。在講解其工作流程之前我們有必要了解 18B20的內(nèi)部存儲器資源。18B20 共有2種形態(tài)的存儲器資源,它們分別是:
1> ROM 只讀存儲器,用于存放
DS18B20ID 編碼,其前 8 位是單線系列編碼(DS18B20
的編碼是19H) ,后面48 位是芯片唯一的序列號,最后 8位是以上 56的位的 CRC碼(冗余校驗)。數(shù)據(jù)在出產(chǎn)時設(shè)置不由用戶更改。DS18B20 共 64 位 ROM。
2> RAM 數(shù)據(jù)暫存器,用于內(nèi)部計算和數(shù)據(jù)存取,數(shù)據(jù)在掉電后丟失,DS18B20 共9 個字節(jié) RAM,每個字節(jié)為 8 位。第1、2 個字節(jié)是溫度轉(zhuǎn)換后的數(shù)據(jù)值信息,第 3、4 個字節(jié)是用戶 EEPROM(常用于溫度報警值儲存)的鏡像。在上電復(fù)位時其值將被刷新。第 5 個字節(jié)則是用戶第 3 個
EEPROM的鏡像。第 6、7、8 個字節(jié)為計數(shù)寄存器,是為了讓用戶得到更高的溫度分辨率而設(shè)計的,同樣也是內(nèi)部溫度轉(zhuǎn)換、計算的暫存單元。第 9 個字節(jié)為前 8個字節(jié)的 CRC碼。EEPROM 非易失性記憶體,用于存放長期需要保存的數(shù)據(jù),上下限溫度報警值和校驗數(shù)據(jù), DS18B20共3位EEPROM,并在 RAM 都存在鏡像,以方便用戶操作。
下面是其C程序(獨(dú)家專售):
#include<reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define LCDIO P2
sbit DQ=P1^3;//ds18b20與單片機(jī)連接口
sbit rs=P1^0;
sbit rd=P1^1;
sbit lcden=P1^2;
sbit acc0=ACC^0; //移位時的第0位
sbit acc7=ACC^7; //移位時用的第7位
uchar second,minute,hour,day,month,year,week,count=0;
uchar ReadValue,num,time;
uint tvalue;//溫度值
uchar tflag;
uchar code table[]={" 2010-11-29 MON"};
uchar code table1[]={" 15:45:00 000.0C"};
uchar code table2[]= "THUFRISATSUNMONTUEWES";
uchar data disdata[5];
sbit DATA=P0^1; //時鐘數(shù)據(jù)接口
sbit RST=P0^2;
sbit SCLK=P0^0;
sbit menu=P3^5; //菜單
sbit add=P3^6; //加一
sbit dec=P3^7; //減一
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void delay1(uint z)
{
for(;z>0;z--);
}
void write_com(uchar com)
{
rs=0;
rd=0;
lcden=0;
P2=com;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void write_date(uchar date)
{
rs=1;
rd=0;
lcden=0;
P2=date;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void init()
{
uchar num;
lcden=0;
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
write_com(0x80);
delay(5);
write_com(0x80);
for(num=0;num<15;num++)
{
write_date(table[num]);
delay(5);
}
write_com(0x80+0x40);
for(num=0;num<15;num++)
{
write_date(table1[num]);
delay(5);
}
}
void Write1302(uchar dat)
{
uchar i;
SCLK=0; //拉低SCLK,為脈沖上升沿寫入數(shù)據(jù)做好準(zhǔn)備
delay1(2); //稍微等待,使硬件做好準(zhǔn)備
for(i=0;i<8;i++) //連續(xù)寫8個二進(jìn)制位數(shù)據(jù)
{
DATA=dat&0x01; //取出dat的第0位數(shù)據(jù)寫入1302
delay(2); //稍微等待,使硬件做好準(zhǔn)備
SCLK=1; //上升沿寫入數(shù)據(jù)
delay1(2); //稍微等待,使硬件做好準(zhǔn)備
SCLK=0; //重新拉低SCLK,形成脈沖
dat>>=1; //將dat的各數(shù)據(jù)位右移1位,準(zhǔn)備寫入下一個數(shù)據(jù)位
}
}
void WriteSet1302(uchar Cmd,uchar dat)
{
RST=0; //禁止數(shù)據(jù)傳遞
SCLK=0; //確保寫數(shù)居前SCLK被拉低
RST=1; //啟動數(shù)據(jù)傳輸
delay1(2); //稍微等待,使硬件做好準(zhǔn)備
Write1302(Cmd); //寫入命令字
Write1302(dat); //寫數(shù)據(jù)
SCLK=1; //將時鐘電平置于已知狀態(tài)
RST=0; //禁止數(shù)據(jù)傳遞
}
uchar Read1302(void)
{
uchar i,dat;
delay(2); //稍微等待,使硬件做好準(zhǔn)備
for(i=0;i<8;i++) //連續(xù)讀8個二進(jìn)制位數(shù)據(jù)
{
dat>>=1; //將dat的各數(shù)據(jù)位右移1位,因為先讀出的是字節(jié)的最低位
if(DATA==1) //如果讀出的數(shù)據(jù)是1
dat|=0x80; //將1取出,寫在dat的最高位
SCLK=1; //將SCLK置于高電平,為下降沿讀出
delay1(2); //稍微等待
SCLK=0; //拉低SCLK,形成脈沖下降沿
delay1(2); //稍微等待
}
return dat; //將讀出的數(shù)據(jù)返回
}
uchar ReadSet1302(uchar Cmd)
{
uchar dat;
RST=0; //拉低RST
SCLK=0; //確保寫數(shù)居前SCLK被拉低
RST=1; //啟動數(shù)據(jù)傳輸
Write1302(Cmd); //寫入命令字
dat=Read1302(); //讀出數(shù)據(jù)
SCLK=1; //將時鐘電平置于已知狀態(tài)
RST=0; //禁止數(shù)據(jù)傳遞
return dat; //將讀出的數(shù)據(jù)返回
}
void Init_DS1302(void)
{
WriteSet1302(0x8E,0x00); //根據(jù)寫狀態(tài)寄存器命令字,寫入不保護(hù)指令
WriteSet1302(0x80,((0/10)<<4|(0%10))); //根據(jù)寫秒寄存器命令字,寫入秒的初始值
WriteSet1302(0x82,((45/10)<<4|(45%10))); //根據(jù)寫分寄存器命令字,寫入分的初始值
WriteSet1302(0x84,((15/10)<<4|(15%10))); //根據(jù)寫小時寄存器命令字,寫入小時的初始值
WriteSet1302(0x86,((29/10)<<4|(29%10))); //根據(jù)寫日寄存器命令字,寫入日的初始值
WriteSet1302(0x88,((11/10)<<4|(11%10))); //根據(jù)寫月寄存器命令字,寫入月的初始值
WriteSet1302(0x8c,((10/10)<<4|(10%10))); //nian
WriteSet1302(0x8a,((4/10)<<4|(4%10)));
}
void DisplaySecond(uchar x)
{
uchar i,j;
i=x/10;
j=x%10;
write_com(0xc7);
write_date(0x30+i);
write_date(0x30+j);
}
void DisplayMinute(uchar x)
{
uchar i,j;
i=x/10;
j=x%10;
write_com(0xc4);
write_date(0x30+i);
write_date(0x30+j);
}
void DisplayHour(uchar x)
{
uchar i,j;
i=x/10;
j=x%10;
write_com(0xc1);
write_date(0x30+i);
write_date(0x30+j);
}
void DisplayDay(uchar x)
{
uchar i,j;
i=x/10;
j=x%10;
write_com(0x89);
write_date(0x30+i);
write_date(0x30+j);
}
void DisplayMonth(uchar x)
{
uchar i,j;
i=x/10;
j=x%10;
write_com(0x86);
write_date(0x30+i);
write_date(0x30+j);
}
void DisplayYear(uchar x)
{
uchar i,j;
i=x/10;
j=x%10;
write_com(0x83);
write_date(0x30+i);
write_date(0x30+j);
}
void DisplayWeek(uchar x)
{ uchar i;
x=x*3;
write_com(0x8c);
for(i=0;i<3;i++)
{
write_date(table2[x]);
x++;
}
}
void read_date(void)
{
ReadValue = ReadSet1302(0x81);
second=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue = ReadSet1302(0x83);
minute=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue = ReadSet1302(0x85);
hour=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue = ReadSet1302(0x87);
day=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue = ReadSet1302(0x89);
month=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue = ReadSet1302(0x8d);
year=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue=ReadSet1302(0x8b); //讀星期
week=ReadValue&0x07;
DisplaySecond(second);
DisplayMinute(minute);
DisplayHour(hour);
DisplayDay(day);
DisplayMonth(month);
DisplayYear(year);
DisplayWeek(week);
}
void turn_val(char newval,uchar flag,uchar newaddr,uchar s1num)
{
newval=ReadSet1302(newaddr); //讀取當(dāng)前時間
newval=((newval&0x70)>>4)*10+(newval&0x0f); //將bcd碼轉(zhuǎn)換成十進(jìn)制
if(flag) //判斷是加一還是減一
{
newval++;
switch(s1num)
{ case 1: if(newval>99) newval=0;
DisplayYear(newval);
break;
case 2: if(newval>12) newval=1;
DisplayMonth(newval);
break;
case 3: if(newval>31) newval=1;
DisplayDay(newval);
break;
case 4: if(newval>6) newval=0;
DisplayWeek(newval);
break;
case 5: if(newval>23) newval=0;
DisplayHour(newval);
break;
case 6: if(newval>59) newval=0;
DisplayMinute(newval);
break;
case 7: if(newval>59) newval=0;
DisplaySecond(newval);
break;
default:break;
}
}
else
{
newval--;
switch(s1num)
{ case 1: if(newval==0) newval=99;
DisplayYear(newval);
break;
case 2: if(newval==0) newval=12;
DisplayMonth(newval);
break;
case 3: if(newval==0) newval=31;
DisplayDay(newval);
break;
case 4: if(newval<0) newval=6;
DisplayWeek(newval);
break;
case 5: if(newval<0) newval=23;
DisplayHour(newval);
break;
case 6: if(newval<0) newval=59;
DisplayMinute(newval);
break;
case 7: if(newval<0) newval=59;
DisplaySecond(newval);
break;
default:break;
}
}
WriteSet1302((newaddr-1),((newval/10)<<4)|(newval%10)); //將新數(shù)據(jù)寫入寄存器
}
//鍵盤掃描程序
//*******************************************
void key_scan(void)
{ uchar miao,s1num=0;
if(menu==0)
{
delay(5);
if(menu==0)
{
while(!menu);
s1num++;
while(1)
{
if(menu==0)
{
delay(5);
if(menu==0)
{
while(!menu);
s1num++;
}
}
rd=0;
miao=ReadSet1302(0x81);
second=miao;
WriteSet1302(0x80,miao|0x80);
write_com(0x0f);//光標(biāo)閃射
if(s1num==1)
{ year=ReadSet1302(0x8d);
write_com(0x80+4); //年光標(biāo)
if(add==0)
{
delay(3);
if(add==0)
{ while(!add);
turn_val(year,1,0x8d,1);
}
}
if(dec==0)
{
delay(3);
if(dec==0)
{ while(!dec);
turn_val(year,0,0x8d,1);
}
}
}
if(s1num==2)
{
month=ReadSet1302(0x89);
write_com(0x80+7); //月光標(biāo)
if(add==0)
{
delay(3);
if(add==0)
{ while(!add);
turn_val(month,1,0x89,2);
}
}
if(dec==0)
{
delay(3);
if(dec==0)
{ while(!dec);
turn_val(month,0,0x89,2);
}
}
}
if(s1num==3)
{ day=ReadSet1302(0x87);
write_com(0x80+10);//日光標(biāo)
if(add==0)
{
delay(3);
if(add==0)
{ while(!add);
turn_val(day,1,0x87,3);
}
}
if(dec==0)
{
delay(3);
if(dec==0)
{ while(!dec);
turn_val(day,0,0x87,3); //寫入日寄存器
}
}
}
if(s1num==4)
{ week=ReadSet1302(0x8b);
write_com(0x80+14); //星期光標(biāo)
if(add==0)
{
delay(3);
if(add==0)
{ while(!add);
turn_val(week,1,0x8b,4);
}
}
if(dec==0)
{
delay(3);
if(dec==0)
{ while(!dec);
turn_val(week,0,0x8b,4);
}
}
}
if(s1num==5)
{ hour=ReadSet1302(0x85);
write_com(0x80+0x40+2); //時光標(biāo)
if(add==0)
{
delay(3);
if(add==0)
{ while(!add);
turn_val(hour,1,0x85,5);
}
}
if(dec==0)
{
delay(3);
if(dec==0)
{ while(!dec);
turn_val(hour,0,0x85,5);
}
}
}
if(s1num==6)//調(diào)時間分
{ minute=ReadSet1302(0x83);
write_com(0x80+0x40+5);
if(add==0)
{
delay(5);
if(add==0)
{ while(!add);
turn_val(minute,1,0x83,6); //寫入分寄存器
}
}
if(dec==0)
{
delay(3);
if(dec==0)
{ while(!dec);
turn_val(minute,0,0x83,6); //寫入分寄存器
}
}
}
if(s1num==7)//調(diào)時間秒
{ second=ReadSet1302(0x81);
write_com(0x80+0x40+8);//秒光標(biāo)
if(add==0)
{
delay(3);
if(add==0)
{ while(!add);
if(second==0x60)
second=0x00;
turn_val(second,1,0x81,7);
}
}
if(dec==0)
{
delay(3);
if(dec==0)
{ while(!dec);
turn_val(second,0,0x81,7);
}
}
}
if(s1num==8)
{ miao=ReadSet1302(0x81);
second=miao;
WriteSet1302(0x80,second&0x7f);
s1num=0;//s1num清零//
write_com(0x0c);//光標(biāo)不閃爍//
break;
}
}
}
}
}
void delay_18B20(unsigned int i)//延時1微秒
{
while(i--);
}
void ds1820rst()/*ds1820復(fù)位*/
{ unsigned char x=0;
DQ = 1; //DQ復(fù)位
delay_18B20(4); //延時
DQ = 0; //DQ拉低
delay_18B20(100); //精確延時大于480us
DQ = 1; //拉高
delay_18B20(40);
}
uchar ds1820rd()/*讀數(shù)據(jù)*/
{ unsigned char i=0;
unsigned char dat = 0;
for (i=8;i>0;i--)
{ DQ = 0; //給脈沖信號
dat>>=1;
DQ = 1; //給脈沖信號
if(DQ)
dat|=0x80;
delay_18B20(10);
}
return(dat);
}
void ds1820wr(uchar wdata)/*寫數(shù)據(jù)*/
{unsigned char i=0;
for (i=8; i>0; i--)
{ DQ = 0;
DQ = wdata&0x01;
delay_18B20(10);
DQ = 1;
wdata>>=1;
}
}
read_temp()/*讀取溫度值并轉(zhuǎn)換*/
{uchar a,b;
ds1820rst();
ds1820wr(0xcc);//*跳過讀序列號*/
ds1820wr(0x44);//*啟動溫度轉(zhuǎn)換*/
ds1820rst();
ds1820wr(0xcc);//*跳過讀序列號*/
ds1820wr(0xbe);//*讀取溫度*/
a=ds1820rd();
b=ds1820rd();
tvalue=b;
tvalue<<=8;
tvalue=tvalue|a;
if(tvalue<0x0fff)
tflag=0;
else
{tvalue=~tvalue+1;
tflag=1;
}
tvalue=tvalue*(0.425);//溫度值擴(kuò)大10倍,精確到1位小數(shù)
return(tvalue);
}
void ds1820disp()//溫度值顯示
{ uchar flagdat;
disdata[0]=tvalue/1000+0x30;//百位數(shù)
disdata[1]=tvalue%1000/100+0x30;//十位數(shù)
disdata[2]=tvalue%100/10+0x30;//個位數(shù)
disdata[3]=tvalue%10+0x30;//小數(shù)位
if(tflag==0)
flagdat=0x20;//正溫度不顯示符號
else
flagdat=0x2d;//負(fù)溫度顯示負(fù)號:-
if(disdata[0]==0x30)
{disdata[0]=0x20;//如果百位為0,不顯示
if(disdata[1]==0x30)
{disdata[1]=0x20;//如果百位為0,十位為0也不顯示
}
}
write_com(0xc9);
write_date(flagdat);//顯示符號位
write_com(0xca);
write_date(disdata[0]);//顯示百位
write_com(0xcb);
write_date(disdata[1]);//顯示十位
write_com(0xcc);
write_date(disdata[2]);//顯示個位
write_com(0xcd);
write_date(0x2e);//顯示小數(shù)點
write_com(0xce);
write_date(disdata[3]);//顯示小數(shù)位
write_com(0xcf);
write_date('C');
}
void main()
{
init();
Init_DS1302(); //將1302初始化
read_temp();//讀取溫度
ds1820disp();//顯示
while(1)
{
read_date();
key_scan();
read_temp();//讀取溫度
ds1820disp();//顯示
}
}
絕對正確!
Powered by 單片機(jī)教程網(wǎng)