單總線接口DS18B20溫度傳感器


   在單片機與器件的有線通信中,並行最簡單了,比方說1602液晶屏。但是代價就是連線多,占用單片機較多的IO口。一般的話,都是使用串行通信方式,其中有UART、SPI、IIC,還有一個我一直以來都比較頭疼的達拉斯公司(現被美信收購)的單總線。單總線,故名思意,就是用1條連線進行通信,比起兩條線的IIC(時鍾線、數據線),單總線在操作上比較困難,對於時序要求比較高。沒有了時鍾線的幫助,它依靠電平持續時間判斷信號,操作難度可想而知。

         在這之前,個人曾經購買過兩個價格不菲(主要是價格貴,傳感器又是小得不起眼)的DS18B20。這兩塊都在我手上經過了相同的經歷:起初用用還行,勉強能夠用單片機驅動,可以讀出溫度值來。一段時間沒有用過后,重新拿出來后就出現各色各樣的問題(要不就原來的程序壓根驅動不了,要不就是有溫度,當是讀出來的溫度是芯片內部默認的85度)。就這樣子,毀掉了。所以,對於單總線,我有一種莫名的恐懼。

         不過現在,被我花了大半天時間,從閱讀手冊開始,重新開始掌握單總線的編程。當然,也花了幾個鍾頭調試(在protues上面仿真),結果,是看到了成功。現在將其中的一點點心得記載下來。

        DS18B20的硬件連接就不多說了,分為外部電源供電與寄生電源供電方式。

其內部方框圖如下圖,64位的ROM(只讀),可以理解為DS18B20的ID。一個暫存器(掉電丟失數據)有8byte:從低字節開始是溫度傳感器的低字節、高字節、上限觸發(用於設置溫度報警上限)、下限觸發、配置寄存器(用於設置溫度傳感器轉化位數,默認12位)、(3字節保留位)、CRC。另外有一個EEPROM(掉電不丟失數據),用來保存暫存器相應的上限觸發、下限觸發、配置寄存器、CRC。

                       

現在,我們需要跟傳感器通信,通信的話一般先要先要有一個初始化,所謂初始化,說白了就是問一下DS18B20在不在,然后,如果DS18B20還“活“着的話,它會給你回話,當然這里的回話很簡單,把原來高電平拉至低電平。(跟IIC差不多,平常狀態是高電平,低電平的話就是非常態)

初始化的時序如下圖:

 

仔細看這張圖就知道,主控器先將電平拉低,最少拉低480us,然后將總線釋放掉(回到高電平)。然后,DS18B20為了表示存在,會在60-240us這段時間之間把總線拉低。這樣,你可以在將總線釋放掉后大約70us時候,讀取一下總線電平狀態,就可以知道了。

打完了招呼之后,就需要聊天。沒錯,就是向其發送命令或者數據。但你得知道怎么在一條總線上發一位數據。

 

上圖就是單片機向總線發送1位數據的時序圖。先拉低總線(持續時間要大於1us),然后在15us之內,你要發送你的電平(如果是高電平,釋放掉總線;如果是低電平,就持續拉低電平),在接下來的45us之內,DS18B20會采樣總線電平值。最后你還要把總線釋放掉,便會常態。通常需要一次發個8bit,在每比特之間,要記得至少有1us的延時,當然,延時沒上限。

學會了發送數據,也要學會接受數據。

 

從總線上面接受數據也跟發送數據一樣,仔細看時序圖,就可以解決了。

通常,我們會寫成發一個字節的函數(而不是一比特)。有了通信的手段,再去看看通信的內容。就像人類可以看懂a,b,c一樣,DS18B20可以看懂的就比較少,而且你得按照規則發送,不然它也看不懂。

在數據手冊上,分為ROM指令:包括MATCH ROM[0x55]、SKIP ROM[0xCC]等。當然SKIP ROM這幾個單詞是給人類看的,真正發送給DS18B20的是0xcc(十六進制)。還有功能指令:包括CONVERT T[0x44]等。

總結來看,你得這樣跟DS18B20交流:

步驟1:初始化(打招呼)

步驟2:ROM操作指令(就是發送指令)

步驟3:DS18B20功能指令(還是發送指令)

當然,可能需要讀取溫度數據,就跟在發送完讀取指令之后。需要提醒一下:各個步驟之間不必沒有空隙,而是需要有個延時(總線一直處於高電平),這個延時很長也不要緊,就是不要太短,不然的話,DS18B20就工作不正常。

 

下面貼出我寫的代碼:

頭文件部分:(通常頭文件寫常數、變量、宏定義、函數原型,C文件寫函數實體)

 

#ifndef __hal_ds18b20_h__

#define __hal_ds18b20_h__

 

#include<reg52.h>

#include"datatype.h"

#include"hal.h"

#include"delay.h"

 

sbit dq=P1^2;

 

#define DQ dq

 

//DS18B20 ROM指令

#define SEARCH_ROM 0xf0

#define READ_ROM 0x33

#define MATH_ROM 0x55

#define SKIP_ROM 0xcc//使用該指令跳過ROM指令

#define ALARM_SEARCH 0xec

 

//DS18B20 功能指令

#define CONVERT_T 0x44//使用該指令開始轉換溫度

#define WRITE_SCRATCHPAD 0x4e

#define READ_SCRATCHPAD 0xbe//使用該指令讀取溫度值

#define COPY_SCRATCHPAD 0x48

#define RECALL_E2 0xb8

#define READ_POWER_SUPPLY 0xb4

 

 

bit hal_ds18b20_init();

void hal_ds18b20_bit_write(bit val);

bit hal_ds18b20_bit_read();

void hal_ds18b20_byte_write(uchar val);

uchar hal_ds18b20_byte_read();

uint hal_ds18b20_get_temp(bit length,uchar * flag);

 

#endif

 

 

C文件部分:

 

#include"hal_ds18b20.h"

 

//返回1:表示初始化成功

bit hal_ds18b20_init()

{

         //DQ高電平

         DQ=0;//先拉低

         delay_480us();//等待480us

         DQ=1;//再釋放總線,進入等待狀態

         delay_70us();//等待70us

         return !DQ;

        //延時一段時間后,DQ高電平

}

 

void hal_ds18b20_bit_write(bit val)

{

         //DQ高電平

         DQ=0;//拉低

         delay_8us();//延時8us

         if(val)

         {

                   DQ=1;//寫1的話,需要在15us之內拉高

         }

         //如果寫0,則DQ依舊是低電平

         delay_52us();

         DQ=1;//經過60us,然后釋放總線

         //DQ高電平

}

 

bit hal_ds18b20_bit_read()

{

         bit tmp=0;

         //DQ高電平

         DQ=0;

         _nop_();_nop_();//拉低電平2us

         DQ=1;//釋放總線

         delay_10us();//時間到此有12us

         tmp=DQ;//對總線進行采樣

         delay_48us();//時間到此有60us

         return tmp;

         //此時,DQ被ds18b20釋放

         //DQ高電平

}

 

//單總線要求從最低有效位開始傳送

void hal_ds18b20_byte_write(uchar val)

{

         uchar i;

         for(i=0;i<8;i++)

         { 

                   _nop_();_nop_();//每傳送一位,期間至少間隔1us

                   if(val&(0x01<<i))

                            hal_ds18b20_bit_write(1);

                   else

                            hal_ds18b20_bit_write(0);

         }       

}

 

uchar hal_ds18b20_byte_read()

{

         uchar tmp=0,i;

         for(i=0;i<8;i++)

         {

                   _nop_();_nop_();//每傳送一位,期間至少間隔1us

                   if(hal_ds18b20_bit_read())

                            tmp=tmp|(0x01<<i);

         }

         return tmp;

}

 

//參數length=0:返回精確度為1位小數,此時返回值擴大了10倍

//參數length=1:返回精確度為2位小數,此時返回值擴大了100倍

//參數*flag,只是作為溫度正負值標志傳出用。0:正;1:負

//注意接收返回值變量需要是int型的

uint hal_ds18b20_get_temp(bit length,uchar * flag)

{

         uint val=0;

         uchar tmp1,tmp2;

         while(!hal_ds18b20_init())

                   DELAY_500MS();

         delay_ms(1);//發指令期間延時比較重要

         hal_ds18b20_byte_write(SKIP_ROM);

         delay_ms(1);

         hal_ds18b20_byte_write(CONVERT_T);

         DELAY_1S();//12bit精度情況下,需要750ms轉換溫度時間

         while(!hal_ds18b20_init())

                   DELAY_500MS();

         delay_ms(1);

         hal_ds18b20_byte_write(SKIP_ROM);

         delay_ms(1);

         hal_ds18b20_byte_write(READ_SCRATCHPAD);

         delay_ms(1);

         tmp1=hal_ds18b20_byte_read();//一共可以讀取9字節

         tmp2=hal_ds18b20_byte_read();//這里來只讀取前兩個字節,溫度值

         //tmp1低字節         tmp2高字節

         *flag=(tmp2&0x80)?1:0;//傳回溫度正負

         val=tmp1|tmp2<<8;//組成16位

         if(*flag)//溫度值是負數,需要取反加一

                   val=(~val)+1;

         //按照12bit測溫精度來算,擴大了100倍

         return ((uint)(val*6.25+(length?0.5:50)))/(length?1:10);

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM