DIY高精度時(shí)鐘、溫度顯示器Ds3231+12864+ds18b20+89c51 硬件: 程序: #include<reg52.h> #include<intrins.h> #include<stdlib.h>
#define uchar unsigned char #define uint unsigned int /*端口定義*/ sbit LCD_RS=P2^6; sbit LCD_RW=P2^5; sbit LCD_EN=P2^4; sbit LCD_PSB=P2^3; sbit DQ=P3^7; //18b20 sbit SDA=P1^4; //ds32321 //模擬I2C數(shù)據(jù)傳送位SDA sbit SCL=P1^3; //模擬I2C時(shí)鐘控制位SCL //***按鍵功能****// ////***K1停止時(shí)間顯示****// ////***K2選擇修改位置****// ////***K3進(jìn)行加1的修改****// ////***K4將修改寫入ds3231,同時(shí)啟動(dòng)時(shí)間顯示****// sbit K1=P3^2; sbit K2=P3^3; sbit K3=P3^4; sbit K4=P3^5; //定義變量 uchar numbr[10]="0123456789"; //字模
uchar dis4[]=" "; // 第四行顯示 自己添加 uchar t[]=" . ℃" ; //18b20 uint sdata,xiaoshu1,xiaoshu2; //整數(shù)、小數(shù)1位、小數(shù)2位 bit fg=1; //溫度正負(fù)標(biāo)志 uchar tempL=0,tempH=0; // 變量 uchar year,month,date,hour,min,sec; // ds3231 uchar a[]="2011年22月33日"; uchar b[]="11時(shí)22分33秒"; ///函數(shù) //******************延時(shí)子程序 *******************************
//這個(gè)延時(shí)程序的具體延時(shí)時(shí)間是time=i*8+10,適用于小于2ms的延時(shí)
//************************************************************ void delay(unsigned char i) { for(i;i>0;i--); } //*********************************************************** // 延時(shí)子程序 //************************************************************ void delay1ms(uchar j) { while(j!=0) {uchar i; for(i=124;i>0;i--); //延時(shí)124*8+10=1002us j--; } } /**************************12864部分*************************************/ /**************************12864部分*************************************/ /*寫指令數(shù)據(jù)到LCD RS=L——表示DB0-DB7為顯示指令數(shù)據(jù) RW=L——表示DB0-DB7數(shù)據(jù)被write(當(dāng)E=“H-L”,指令數(shù)據(jù)被寫到IR或DR) E=高脈沖 此時(shí)DB0-DB7=指令碼 */ void write_cmd(uchar cmd) { LCD_RS=0; LCD_RW=0; LCD_EN=0; P0=cmd; delay1ms(5); LCD_EN=1; delay1ms(5); LCD_EN=0; } /*設(shè)定顯示位置*/ void lcd_pos(uchar X, uchar Y) { ucharpos;
if(X== 0) { X= 0x80; } elseif(X == 1) { X= 0x90; } elseif(X == 2) { X= 0x88; } elseif(X == 3) { X= 0x98; } pos= X + Y; write_cmd(pos); //顯示地址 }
/*寫顯示數(shù)據(jù)到LCD*/ /* RS=H——表示DB0-DB7為顯示數(shù)據(jù) RW=L——RW=L,E='H-L',DB0-DB7數(shù)據(jù)被寫到IR或DR E=高脈沖 DB0-DB7=顯示數(shù)據(jù) */ void write_dat(uchar dat) { LCD_RS=1; LCD_RW=0; LCD_EN=0; P0=dat; delay1ms(5); LCD_EN=1; delay1ms(5); LCD_EN=0; } /*LCD初始化*/ void lcd_init() { uinti;
LCD_PSB=1; //并口方式 write_cmd(0x30); //基本操作指令 delay1ms(5); write_cmd(0x0c); //打開顯示,光標(biāo)關(guān)閉 delay1ms(5); write_cmd(0x01); //清除LCD顯示類容 delay1ms(5);
lcd_pos(3,0); i=0; while(dis4[ i]!='\0') { delay1ms(1); write_dat(dis4); delay1ms(1); i++; } } /**********************************18b20************************************************/ /**********************************18b20************************************************/ void Init_DS18B20(void) //初始化 { uchar x=0; DQ=1; //DQ先置高 delay(8); //稍延時(shí) DQ=0; //發(fā)送復(fù)位脈沖 delay(80); //延時(shí)(>480us) DQ=1; //拉高數(shù)據(jù)線 delay(5); //等待(15~60us) x=DQ; //用X的值來判斷初始化有沒有成功,18B20存在的話X=0,否則X=1 delay(20); } //**********讀一個(gè)字節(jié)************// ReadOneChar(void) //主機(jī)數(shù)據(jù)線先從高拉至低電平1us以上,再使數(shù)據(jù)線升為高電平,從而產(chǎn)生讀信號(hào) { unsigned char i=0; //每個(gè)讀周期最短的持續(xù)時(shí)間為60us,各個(gè)讀周期之間必須有1us以上的高電平恢復(fù)期 unsigned char dat=0; for (i=8;i>0;i--) //一個(gè)字節(jié)有8位 { DQ=1; delay(1); DQ=0; dat>>=1; DQ=1; if(DQ) dat|=0x80; delay(4); } return(dat); } //*********************** **寫一個(gè)字節(jié)**************************// void WriteOneChar(unsigned char dat) { unsigned char i=0; //數(shù)據(jù)線從高電平拉至低電平,產(chǎn)生寫起始信號(hào)。15us之內(nèi)將所需寫的位送到數(shù)據(jù)線上, for(i=8;i>0;i--) //在15~60us之間對(duì)數(shù)據(jù)線進(jìn)行采樣,如果是高電平就寫1,低寫0發(fā)生。 { DQ=0; //在開始另一個(gè)寫周期前必須有1us以上的高電平恢復(fù)期。 DQ=dat&0x01; delay(5); DQ=1; dat>>=1; } delay(4); } void ReadTemperature(void) //讀溫度值(低位放tempL;高位放tempH;)// { Init_DS18B20(); //初始化 WriteOneChar(0xcc); //跳過讀序列號(hào)的操作 WriteOneChar(0x44); //啟動(dòng)溫度轉(zhuǎn)換 delay(125); //轉(zhuǎn)換需要一點(diǎn)時(shí)間,延時(shí) Init_DS18B20(); //初始化 WriteOneChar(0xcc); //跳過讀序列號(hào)的操作 WriteOneChar(0xbe); //讀溫度寄存器(頭兩個(gè)值分別為溫度的低位和高位) tempL=ReadOneChar(); //讀出溫度的低位LSB tempH=ReadOneChar(); //讀出溫度的高位MSB if(tempH>0x7f) //最高位為1時(shí)溫度是負(fù) { tempL=~tempL; //補(bǔ)碼轉(zhuǎn)換,取反加一 tempH=~tempH+1; fg=0; //讀取溫度為負(fù)時(shí)fg=0 } sdata= tempL/16+tempH*16; //整數(shù)部分 xiaoshu1= (tempL&0x0f)*10/16; //小數(shù)第一位 xiaoshu2= (tempL&0x0f)*100/16%10;//小數(shù)第二位 t[0]=numbr[sdata/10]; t[1]=numbr[sdata%10]; t[3]=numbr[xiaoshu1]; t[4]=numbr[xiaoshu2]; } /*****************************************ds3231********************************************/ #define ADDRTW 0xD0 //器件寫地址 #define ADDRTD 0xD1 //器件讀地址 #define DS3231_SEC 0x00 //秒 #define DS3231_MIN 0x01 //分 #define DS3231_HOUR 0x02 //時(shí) #define DS3231_DAY 0x03 //星期 #define DS3231_DATE 0x04 //日 #define DS3231_MONTH 0x05 //月 #define DS3231_YEAR 0x06 //年 //鬧鈴1 #define DS3231_Al1SEC 0x07 //秒 #define DS3231_AL1MIN 0x08 //分 #define DS3231_AL1HOUR 0x09 //時(shí) #define DS3231_AL1DAY 0x0A //星期/日 //鬧鈴2 #define DS3231_AL2MIN 0x0b //分 #define DS3231_AL2HOUR 0x0c //時(shí) #define DS3231_AL2DAY 0x0d //星期/日 #define DS3231_CONTROL 0x0e //控制寄存器 #define DS3231_STATUS 0x0f //狀態(tài)寄存器 bit ack; uchar BCD2HEX(uchar val) //BCD轉(zhuǎn)換為Byte { uchari; i= val&0x0f; val >>= 4; val &= 0x0f; val *= 10; i+= val; return i; } uchar HEX2BCD(uchar val)//B碼轉(zhuǎn)換為BCD碼 { uchari,j,k; i=val/10; j=val%10; k=j+(i<<4); return k; } void Start() { SDA=1; //發(fā)送起始條件的數(shù)據(jù)信號(hào) delay(1); SCL=1; delay(5); //起始條件建立時(shí)間大于4.7us,延時(shí) SDA=0; //發(fā)送起始信號(hào) delay(5); // 起始條件鎖定時(shí)間大于4μs SCL=0; //鉗住I2C總線,準(zhǔn)備發(fā)送或接收數(shù)據(jù) delay(2); } void Stop() { SDA=0; //發(fā)送結(jié)束條件的數(shù)據(jù)信號(hào) delay(1); //發(fā)送結(jié)束條件的時(shí)鐘信號(hào) SCL=1; //結(jié)束條件建立時(shí)間大于4us delay(5); SDA=1; //發(fā)送I2C總線結(jié)束信號(hào) delay(4); } /********************************************************/ /******************************************************************* 字節(jié)數(shù)據(jù)發(fā)送函數(shù) 函數(shù)原型: void SendByte(uchar Dat); 功能: 將數(shù)據(jù)c發(fā)送出去,可以是地址,也可以是數(shù)據(jù),發(fā)完后等待應(yīng)答,并對(duì) 此狀態(tài)位進(jìn)行操作.(不應(yīng)答或非應(yīng)答都使ack=0) ack=1 發(fā)送數(shù)據(jù)正常, ack=0 被控器無(wú)應(yīng)答或損壞。 ********************************************************************/ void SendByte(uchar Dat) { uchar BitCnt; for(BitCnt=0;BitCnt<8;BitCnt++) //要傳送的數(shù)據(jù)長(zhǎng)度為8位 { if((Dat<<BitCnt)&0x80) SDA=1; //判斷發(fā)送位 else SDA=0; delay(1); SCL=1; //置時(shí)鐘線為高,通知被控器開始接收數(shù)據(jù)位 delay(5); //保證時(shí)鐘高電平周期大于4μs SCL=0; } delay(2); SDA=1; //8位發(fā)送完后釋放數(shù)據(jù)線,準(zhǔn)備接收應(yīng)答位 delay(2); SCL=1; delay(3); if(SDA==1) ack=0; else ack=1; //判斷是否接收到應(yīng)答信號(hào) SCL=0; delay(2); } uchar RcvByte() //功能: 用來接收從器件傳來的數(shù)據(jù),并判斷總線錯(cuò)誤(不發(fā)應(yīng)答信號(hào)),發(fā)完后請(qǐng)用應(yīng)答函數(shù)應(yīng)答從機(jī)。 { uchar retc; uchar BitCnt; retc=0; SDA=1; //置數(shù)據(jù)線為輸入方式 for(BitCnt=0;BitCnt<8;BitCnt++) { delay(1); SCL=0; //置時(shí)鐘線為低,準(zhǔn)備接收數(shù)據(jù)位 delay(5); //時(shí)鐘低電平周期大于4.7μs SCL=1; //置時(shí)鐘線為高使數(shù)據(jù)線上數(shù)據(jù)有效 delay(3); retc=retc<<1; if(SDA==1) retc=retc+1; //讀數(shù)據(jù)位,接收的數(shù)據(jù)位放入retc中 delay(2); } SCL=0; delay(2); return(retc); } void I2CACK(bit a) // 功能: 主控器進(jìn)行應(yīng)答信號(hào)(可以是應(yīng)答或非應(yīng)答信號(hào),由位參數(shù)a決定) { if(a==0) SDA=0; //在此發(fā)出應(yīng)答或非應(yīng)答信號(hào) else SDA=1; delay(3); SCL=1; delay(5); //時(shí)鐘低電平周期大于4μs SCL=0; //清時(shí)鐘線,鉗住I2C總線以便繼續(xù)接收 delay(2); } uchar I2CRead() /************從DS3231當(dāng)前地址讀一個(gè)字節(jié)************/ { uchar read_data; Start(); SendByte(ADDRTD); if(ack==0) { return(0); } read_data = RcvByte(); I2CACK(1); Stop(); return read_data; } uchar I2CReadAdd(uchar addr) /************從DS3231指定地址讀一個(gè)字節(jié)************/ { Start(); SendByte(ADDRTW); if(ack==0) { return(0); } SendByte(addr); if(ack==0) { return(0); } return(I2CRead()); } void Readtime() /*********************讀取時(shí)間**********************/ { uchar temp; temp=I2CReadAdd(DS3231_SEC);//秒 sec=BCD2HEX(temp); temp=I2CReadAdd(DS3231_MIN);//分 min=BCD2HEX(temp); temp=I2CReadAdd(DS3231_HOUR); //時(shí) hour=BCD2HEX(temp); temp=I2CReadAdd(DS3231_DATE); //日 date=BCD2HEX(temp); temp=I2CReadAdd(DS3231_MONTH); //月 month=BCD2HEX(temp); temp=I2CReadAdd(DS3231_YEAR); //年 year=BCD2HEX(temp); } void InitDS3231() //ds3231初始化 {SCL=1; delay(5); SDA=1; delay(5); } void TimeDisplay(uchar Dhour,ucharDmin,uchar Dsec) //時(shí)分秒數(shù)組賦值 { b[0]=numbr[Dhour / 10]; // 時(shí)十位 b[1]=numbr[Dhour % 10]; // 時(shí)個(gè)位 b[4]=numbr[Dmin / 10]; // 分十位 b[5]=numbr[Dmin % 10]; // 分個(gè)位 b[8]=numbr[Dsec / 10]; // 秒十位 b[9]=numbr[Dsec % 10]; // 秒個(gè)位 } void DateDisplay(uchar Dyear,ucharDmonth,uchar Dday) //年月天數(shù)組賦值 { a[2]=numbr[Dyear / 10]; // 年十位 a[3]=numbr[Dyear % 10]; // 年個(gè)位 a[6]=numbr[Dmonth / 10]; // 月十位 a[7]=numbr[Dmonth % 10]; // 月個(gè)位 a[10]=numbr[Dday / 10]; // 天十位 a[11]=numbr[Dday % 10]; // 天個(gè)位 } void Start_I2C() { SDA=1; //發(fā)送起始條件的數(shù)據(jù)信號(hào) delay(1); SCL=1; delay(5); //起始條件建立時(shí)間大于4.7us,延時(shí) SDA=0; //發(fā)送起始信號(hào) delay(5); // 起始條件鎖定時(shí)間大于4μs SCL=0; //鉗住I2C總線,準(zhǔn)備發(fā)送或接收數(shù)據(jù) delay(2); } void Stop_I2C() { SDA=0; //發(fā)送結(jié)束條件的數(shù)據(jù)信號(hào) delay(1); //發(fā)送結(jié)束條件的時(shí)鐘信號(hào) SCL=1; //結(jié)束條件建立時(shí)間大于4us delay(5); SDA=1; //發(fā)送I2C總線結(jié)束信號(hào) delay(4); } uchar write_byte(uchar addr, ucharwrite_data) { Start_I2C(); SendByte(ADDRTW); //////*******************************************************************/////////// if (ack == 0) return 0; SendByte(addr); if (ack == 0) return 0; SendByte(write_data); if (ack == 0) return 0; Stop_I2C(); delay1ms(10); return 1; } void ModifyTime(uchar yea,uchar mon,ucharda,uchar hou,uchar min,uchar sec) { uchar temp=0; temp=HEX2BCD(yea); write_byte(DS3231_YEAR,temp); //修改年 temp=HEX2BCD(mon); write_byte(DS3231_MONTH,temp); //修改月 temp=HEX2BCD(da); ///////////////////// write_byte(DS3231_DATE,temp); //修改日 temp=HEX2BCD(hou); write_byte(DS3231_HOUR,temp); //修改時(shí) temp=HEX2BCD(min); write_byte(DS3231_MIN,temp); //修改分 temp=HEX2BCD(sec); write_byte(DS3231_SEC,temp); //修改秒 } void xianshi(void) { {uint i; TimeDisplay(hour,min,sec); lcd_pos(1,1); //時(shí)間 i=0; while(b[ i]!='\0') { delay1ms(1); write_dat(b); delay1ms(1); i++; }
DateDisplay(year,month,date); //顯示日期 delay1ms(1); lcd_pos(0,0); i=0; while(a[ i]!='\0') { delay1ms(1); write_dat(a); delay1ms(1); i++; } ReadTemperature(); //顯示溫度 delay1ms(1); lcd_pos(2,1); delay1ms(1); i=0; while(t[ i]!='\0') { delay1ms(1); write_dat(t); delay1ms(2); i++; } } }
void shuaxin(void) { uint i; TimeDisplay(hour,min,sec); lcd_pos(1,1); //時(shí)間 i=0; while(b[ i]!='\0') { delay1ms(1); write_dat(b); delay1ms(1); i++; }
DateDisplay(year,month,date); //顯示日期 delay1ms(1); lcd_pos(0,0); i=0; while(a[ i]!='\0') { delay1ms(1); write_dat(a); delay1ms(1); i++; } }
void sotp(void) { uinti;
while(1) { duan1: if(K1==0) { delay1ms(100); if(K1==0) { Readtime(); shuaxin(); i=0; gotoduan2;
} } else {gotoduan5;}
duan2: if(K2==0) { delay1ms(100); if(K2==0) { i++; if(i>6){i=0;} shuaxin(); } } switch(i) { case 0: if(K3==0){delay1ms(100);if(K3==0){year=year+1;} if(year>=100){year=0;} shuaxin();}break; case 1: if(K3==0){delay1ms(100);if(K3==0){month=month+1;} if(month>=13){month=0;} shuaxin();}break; case 2: if(K3==0){delay1ms(100);if(K3==0){date=date+1;} if(date>=32){date=0;} shuaxin();}break; case 3: if(K3==0){delay1ms(100);if(K3==0){hour=hour+1;} if(hour>=25){hour=0;} shuaxin();}break; case 4: if(K3==0){delay1ms(100);if(K3==0){min=min+1;} if(min>=61){min=0;} shuaxin();}break; case 5: if(K3==0){delay1ms(100);if(K3==0){sec=sec+1;} if(sec>=61){sec=0;} shuaxin();} break; }
duan4: if(K4==0) { delay1ms(100); if(K4==0) { ModifyTime(year,month,date,hour,min,sec); gotoduan5; ///////////////////*******************////////// } }
else {gotoduan2;}
duan5: Readtime(); xianshi(); }
}
main() /*主程序*/ {
lcd_init(); delay1ms(5); InitDS3231(); delay1ms(5); delay1ms(5); sotp(); } |