溫度傳感器是各種傳感器中最常用的一種,早期使用的是模擬溫度傳感器,如熱敏電阻,隨著環(huán)境溫度的變化,它的阻值也發(fā)生線性變化,用處理器采集電阻兩端的電壓,然后根據(jù)某個(gè)公式就可以計(jì)算出當(dāng)前環(huán)境溫度。美國(guó)DALLAS半導(dǎo)體公司推出的數(shù)字化溫度傳感器DS18B20采用單總線協(xié)議,即與單片機(jī)接口僅需占用一個(gè)I/O端口,無(wú)需任何外部元件,直接將環(huán)境溫度轉(zhuǎn)化成數(shù)字信號(hào),以數(shù)字碼方式串行輸出,從而大大簡(jiǎn)化了傳感器與處理器的接口。 DS18B20的三種封裝: DS18B20的內(nèi)部結(jié)構(gòu): 它采用單條信號(hào)線,既可傳輸時(shí)鐘,又可傳輸數(shù)據(jù),而且數(shù)據(jù)傳輸是雙向的。如果要控制多個(gè)DS18B20進(jìn)行溫度采集,只要將所有的DS18B20的I/O口全部連接到一起就可以了。在具體操作時(shí),通過(guò)讀取每個(gè)DS18B20內(nèi)部芯片的序列號(hào)來(lái)識(shí)別。64位光刻ROM中的序列號(hào)是出場(chǎng)前被光刻好的,他可以看做該DS18B20的地址序列碼。 DS18B20的復(fù)位時(shí)序: DS18B20復(fù)位,確定其存在: #include <reg52.h> #define uint unsigned int uint i; sbit DQ = P3^3; sbit bell = P3^4; void reset(){ DQ = 1; //開(kāi)始的時(shí)候是高脈沖 DQ=0; //然后是低脈沖 i=103; while(i>0)i--; //低脈沖需要延遲一會(huì)兒 DQ=1; //數(shù)據(jù)線拉高,系統(tǒng)將總線放開(kāi),并進(jìn)入接受狀態(tài) i=4; while(i>0)i--; //延時(shí)等待,若初始化成功則在15"60ms內(nèi)產(chǎn)生一個(gè)由 if(DQ == 0){ //DS18B20在檢測(cè)到總線的上升沿后,等待15"60ms,接著 while(DQ == 0); //在T2時(shí)刻發(fā)出存在脈沖(低電平) bell = 0; } else bell = 1; } void main(){ reset(); while(1); } DS18B20的寫(xiě)0和寫(xiě)1時(shí)序: DS18B20的讀數(shù)據(jù)時(shí)序: 讀出光刻ROM中的ID號(hào),在LCD上顯示: #include <reg52.h> #include<intrins.h> sbit DQ = P3^3; sbit RS = P1^0; sbit RW = P1^1; sbit E = P1^2; sbit bell = P3^4; #define uchar unsigned char #define uint unsigned int #define nop() _nop_() uint i; uchar value; uchar DS[8]; uchar Time_Data[]={'0','1','2','3','4','5','6','7', '8','9','A','B','C','D','E','F'}; void delay(uchar t){ while(--t); } void lcd_com(uchar s){ RS = 0; //低電平,寫(xiě)指令 P2 = s; //傳數(shù)據(jù) delay(14); //看時(shí)序圖,數(shù)據(jù)需要穩(wěn)定一段時(shí)間 E = 1; //給一個(gè)高脈沖,發(fā)送命令 delay(14); //如圖,高脈沖延時(shí)一段時(shí)間,確保命令發(fā)送 E = 0; //發(fā)送結(jié)束E置為低電平 } void lcd_data(uchar s){ RS = 1; P2 = s; delay(14); E = 1; delay(14); E = 0; } void init_lcd(){ RS = 1; //先發(fā)指令,在初始時(shí)刻RS是高,E和RW是低 E = 0; RW = 0; lcd_com(0x38); //設(shè)置為16*2顯示,5*7點(diǎn)陣,8位數(shù)據(jù)接口 lcd_com(0x0f); //開(kāi)顯示,顯示光標(biāo),光標(biāo)閃爍 lcd_com(0x06); //讀寫(xiě)一個(gè)字符后地址指針加一 lcd_com(0x01); } void Display_lcd(uchar y, uchar x, uchar value){ if(y) lcd_com(0x80+0x40+x); //如果y為1,寫(xiě)在第二行 else lcd_com(0x80+x); lcd_data(value); //寫(xiě)到LCD602上 } void DS18B20_reset(){ DQ = 1; //開(kāi)始的時(shí)候是高脈沖 DQ=0; //然后是低脈沖 i=103; while(i>0)i--; //低脈沖需要延遲一會(huì)兒 DQ=1; //數(shù)據(jù)線拉高 i=4; while(i>0)i--; //延時(shí)等待,若初始化成功則在15"60ms內(nèi)產(chǎn)生一個(gè)由 if(DQ == 0){ //DS18B20返回的低電平 while(DQ == 0); // bell = 0; } else bell = 1; } uchar DS18B20_read(void) { uchar i = 0; uchar Value = 0; for(i = 0; i < 8; i ++) { DQ = 1; DQ = 0; delay(1); DQ = 1; //在T1時(shí)刻將總線拉高,產(chǎn)生讀時(shí)間隙 delay(1); //讀時(shí)隙在T1和T2之間有效 if(DQ) { Value |= 0x01 << i; } delay(17); //必須在T3時(shí)刻之間主機(jī)完成讀操作 DQ = 1; nop(); } return Value; } void DS18B20_write(uchar Value){ for(i = 0; i < 8; i++){ DQ = 1; DQ = 0; //當(dāng)t0從高拉低產(chǎn)生寫(xiě)時(shí)隙, delay(5); DQ = Value & 0x01; //必須在t0開(kāi)始的15us內(nèi)將數(shù)據(jù)送到總線上 delay(20); //DS18B20在t0后的15us"60us內(nèi)對(duì)總線采樣 DQ = 1; //如果采到低電平則寫(xiě)入0,高電平寫(xiě)入1 Value >>= 1; delay(2); } } void DS18B20_ID_read(){ DS18B20_reset(); DS18B20_write(0x33); for(i = 0; i < 8; i++) DS[ i] = DS18B20_read(); } void main(){ init_lcd(); while(1){ DS18B20_ID_read(); Display_lcd(0,0,'>'); Display_lcd(0,1,':'); Display_lcd(0,2,Time_Data[DS[0]/16]); Display_lcd(0,3,Time_Data[DS[0]%16]); Display_lcd(0,4,'>'); Display_lcd(0,5,':'); Display_lcd(0,6,Time_Data[DS[1]/16]); Display_lcd(0,7,Time_Data[DS[1]%16]); Display_lcd(0,8,'>'); Display_lcd(0,9,':'); Display_lcd(0,10,Time_Data[DS[2]/16]); Display_lcd(0,11,Time_Data[DS[2]%16]); Display_lcd(0,12,'>'); Display_lcd(0,13,':'); Display_lcd(0,14,Time_Data[DS[3]/16]); Display_lcd(0,15,Time_Data[DS[3]%16]); Display_lcd(1,0,'>'); Display_lcd(1,1,':'); Display_lcd(1,2,Time_Data[DS[4]/16]); Display_lcd(1,3,Time_Data[DS[4]%16]); Display_lcd(1,4,'>'); Display_lcd(1,5,':'); Display_lcd(1,6,Time_Data[DS[5]/16]); Display_lcd(1,7,Time_Data[DS[5]%16]); Display_lcd(1,8,'>'); Display_lcd(1,9,':'); Display_lcd(1,10,Time_Data[DS[6]/16]); Display_lcd(1,11,Time_Data[DS[6]%16]); Display_lcd(1,12,'>'); Display_lcd(1,13,':'); Display_lcd(1,14,Time_Data[DS[7]/16]); Display_lcd(1,15,Time_Data[DS[7]%16]); } } 單只DS18B20工作流程: 2只DS18B20并聯(lián)工作流程: DS18B20溫度存儲(chǔ)格式: DS18B20暫存器的分布: DS18B20內(nèi)部ROM指令: DS18B20內(nèi)部RAM指令: 讀出當(dāng)前溫度,在LCD上顯示,溫度超過(guò)一定時(shí),報(bào)警。 #include <reg52.h> #include<intrins.h> sbit DQ = P3^3; sbit RS = P1^0; sbit RW = P1^1; sbit E = P1^2; sbit bell = P3^4; #define uchar unsigned char #define uint unsigned int #define nop() _nop_() uint i; uchar value; uchar DS[8]; uchar Time_Data[]={'0','1','2','3','4','5','6','7', '8','9','A','B','C','D','E','F'}; void delay(uchar t){ while(--t); } void Lcd_Com(uchar s){ RS = 0; //低電平,寫(xiě)指令 P2 = s; //傳數(shù)據(jù) delay(14); //看時(shí)序圖,數(shù)據(jù)需要穩(wěn)定一段時(shí)間 E = 1; //給一個(gè)高脈沖,發(fā)送命令 delay(14); //如圖,高脈沖延時(shí)一段時(shí)間,確保命令發(fā)送 E = 0; //發(fā)送結(jié)束E置為低電平 } void Lcd_Data(uchar s){ RS = 1; P2 = s; delay(14); E = 1; delay(14); E = 0; } void Init_Lcd(){ RS = 1; //先發(fā)指令,在初始時(shí)刻RS是高,E和RW是低 E = 0; RW = 0; Lcd_Com(0x38); //設(shè)置為16*2顯示,5*7點(diǎn)陣,8位數(shù)據(jù)接口 Lcd_Com(0x0f); //開(kāi)顯示,顯示光標(biāo),光標(biāo)閃爍 Lcd_Com(0x06); //讀寫(xiě)一個(gè)字符后地址指針加一 Lcd_Com(0x01); } void Display_Lcd(uchar y, uchar x, uchar value){ if(y) Lcd_Com(0x80+0x40+x); //如果y為1,寫(xiě)在第二行 else Lcd_Com(0x80+x); Lcd_Data(value); //寫(xiě)到LCD602上 } void DS18B20_Reset(){ DQ = 1; //開(kāi)始的時(shí)候是高脈沖 DQ=0; //然后是低脈沖 i=103; while(i>0)i--; //低脈沖需要延遲一會(huì)兒 DQ=1; //數(shù)據(jù)線拉高 i=4; while(i>0)i--; //延時(shí)等待,若初始化成功則在15"60ms內(nèi)產(chǎn)生一個(gè)由 if(DQ == 0){ //DS18B20返回的低電平 while(DQ == 0); // bell = 0; } else bell = 1; } uchar DS18B20_Read(void) { uchar i = 0; uchar Value = 0; for(i = 0; i < 8; i ++) { DQ = 1; DQ = 0; delay(1); DQ = 1; delay(1); if(DQ) { Value |= 0x01 << i; } delay(17); DQ = 1; nop(); } return Value; } void DS18B20_Write(uchar Value){ for(i = 0; i < 8; i++){ DQ = 1; DQ = 0; delay(5); DQ = Value & 0x01; delay(20); DQ = 1; Value >>= 1; delay(2); } } uchar DS18B20_Temp_Read(){ uchar temp_h,temp_l,temp; DS18B20_Reset(); //復(fù)位 DS18B20_Write(0x0cc); //跳過(guò)ROM,只有一個(gè)所以跳過(guò) DS18B20_Write(0x44); //開(kāi)始溫度轉(zhuǎn)換 DS18B20_Reset(); //復(fù)位 DS18B20_Write(0x0cc); //跳過(guò)ROM DS18B20_Write(0x0be); //讀暫存器 temp_l = DS18B20_Read(); //讀出溫度低8位 temp_h = DS18B20_Read(); //讀出溫度高8位 temp_l >>= 4; //去掉4位小數(shù)位 temp_h <<= 4; //去掉4位符號(hào)位 temp = temp_h | temp_l; temp = temp & 0x7f; //最高位是符號(hào)位 return temp; } void main(){ uchar temp; Init_Lcd(); while(1){ temp = DS18B20_Temp_Read(); if(temp > 25) //如果溫度大于25報(bào)警 bell = 0; else bell = 1; Display_Lcd(0,0,'T'); Display_Lcd(0,1,'E'); Display_Lcd(0,2,'M'); Display_Lcd(0,3,'P'); Display_Lcd(0,4,':'); Display_Lcd(0,5,Time_Data[temp/100]); Display_Lcd(0,6,Time_Data[temp%100/10]); Display_Lcd(0,7,Time_Data[temp%10]); } } 李萬(wàn)鵬 |