【51单片机快速入门指南】6.3:DS18B20 单总线数字温度计的多路读取
目錄
- 硬知識
- DS18B20介紹
- 時序
- 初始化時序
- 寫時序
- 讀時序
- 命令
- ROM 操作命令
- ROM 搜索舉例
- 存貯器操作命令
- 示例程序
- DS18B20.c
- DS18B20.h
- 測試程序
- 定時器中斷服務函數
- 單傳感器時ID的獲取 main.c
- 單傳感器讀取溫度和讀取特定ID傳感器的溫度
- 多路傳感器讀取
普中51-單核-A2
STC89C52
Keil uVision V5.29.0.0
PK51 Prof.Developers Kit Version:9.60.0.0
硬知識
???????摘自《普中 51 單片機開發攻略》、《DS18B20 單總線數字溫度計》
DS18B20介紹
???????DS18B20 是由 DALLAS 半導體公司推出的一種的“一線總線(單總線)”接 口的溫度傳感器。與傳統的熱敏電阻等測溫元件相比,它是一種新型的體積小、 適用電壓寬、與微處理器接口簡單的數字化溫度傳感器。
???????DS18B20 溫度傳感器具有如下特點:
???????ROM 中的 64 位序列號是出廠前被光刻好的,它可以看作是該 DS18B20 的地址序列號。64 位光刻 ROM 的排列是:
???????開始 8 位(28H)是產品類型標號,
???????接著的 48 位是該 DS18B20 自身的序列號,
???????最后 8 位是前面 56 位的循環冗余校驗碼。
???????光刻 ROM 的作用是使每一個 DS18B20 都各不相同,這樣就可以實現一根總線上掛接多個 DS18B20 的目的。
???????DS18B20 溫度傳感器的內部存儲器包括一個高速的暫存器 RAM 和一個非易失性的可電擦除的 EEPROM,后者存放高溫度和低溫度觸發器 TH、TL 和配置寄存器。配置寄存器是配置不同的位數來確定溫度和數字的轉化,配置寄存器結構如下:
???????低五位一直都是"1",TM 是測試模式位,用于設置 DS18B20 在工作模式還 是在測試模式。在 DS18B20 出廠時該位被設置為 0,用戶不需要去改動。R1 和 R0 用來設置 DS18B20 的精度(分辨率),可設置為 9,10,11 或 12 位,對 應的分辨率溫度是 0.5℃,0.25℃,0.125℃和 0.0625℃。R0 和 R1 配置如下圖:
???????在初始狀態下默認的精度是 12 位,即 R0=1、R1=1。高速暫存存儲器由 9 個 字節組成,其分配如下:
???????當溫度轉換命令(44H)發布后,經轉換所得的溫度值以二字節補碼形式存 放在高速暫存存儲器的第 0 和第 1 個字節。存儲的兩個字節,高字節的前 5 位 是符號位 S,單片機可通過單線接口讀到該數據,讀取時低位在前,高位在后, 數據格式如下:
???????如果測得的溫度大于 0,這 5 位為‘ 0’,只要將測到的數值乘以 0.0625 (默認精度是 12 位)即可得到實際溫度;如果溫度小于 0,這 5 位為‘ 1’, 測到的數值需要取反加 1 再乘以 0.0625 即可得到實際溫度。溫度與數據對應關系如下:
時序
???????比如我們要計算+85 度,數據輸出十六進制是 0X0550,因為高字節的高 5 位為 0,表明檢測的溫度是正溫度,0X0550 對應的十進制為 1360,將這個值乘 以 12 位精度 0.0625,所以可以得到+85 度。 知道了怎么計算溫度,接下來我們就來看看如何讀取溫度數據,由于 DS18B20 是單總線器件,所有的單總線器件都要求采用嚴格的信號時序,以保證 數據的 完整性。DS18B20 時序包括如下幾種:初始化時序、寫(0 和 1)時序、 讀(0 和 1)時序。 DS18B20 發送所有的命令和數據都是字節的低位在前。這里我們簡單介紹這幾個信號的時序:
初始化時序
???????單總線上的所有通信都是以初始化序列開始。主機輸出低電平,保持低電平時間至少 480us(該時間的時間范圍可以從 480 到 960 微妙),以產生復位脈沖。接著主機釋放總線,外部的上拉電阻將單總線拉高,延時 15~60 us,并進 入接收模式。接著 DS18B20 拉低總線 60~240 us,以產生低電平應答脈沖,若為低電平,還要做延時,其延時的時間從外部上拉電阻將單總線拉高算起最少要 480 微妙。初始化時序圖如下:
???????邏輯分析儀實測如下
寫時序
???????寫時序包括寫 0 時序和寫 1 時序。所有寫時序至少需要 60us,且在 2 次 獨立的寫時序之間至少需要 1us 的恢復時間,兩種寫時序均起始于主機拉低總線。寫 1 時序:主機輸出低電平,延時 2us,然后釋放總線,延時 60us。寫 0 時序:主機輸出低電平,延時 60us,然后釋放總線,延時 2us。寫時序圖如下:
實操如下
握手后寫入0xcc
讀時序
???????單總線器件僅在主機發出讀時序時,才向主機傳輸數據,所以,在主機發出 讀數據命令后,必須馬上產生讀時序,以便從機能夠傳輸數據。所有讀時序至少 需要 60us,且在 2 次獨立的讀時序之間至少需要 1us 的恢復時間。每個讀時序都由主機發起,至少拉低總線 1us。主機在讀時序期間必須釋放總線,并且在 時序起始后的 15us 之內采樣總線狀態。讀時序圖如下:
???????典型的讀時序過程為:主機輸出低電平延時 2us,然后主機轉入輸入模式延 時 12us,然后讀取單總線當前的電平,然后延時 50us。
???????邏輯分析儀實測如下
讀0時寬度為29us
讀1時:
命令
ROM 操作命令
???????一旦總線主機檢測到從屬器件的存在,它便可以發出器件 ROM 操作命令之一。所有 ROM 操作命令均為 8 位長,這些命令列表如下:
Read ROM(讀 ROM) [33h]
???????此命令允許總線主機讀DS1820的8位產品系列編碼,唯一的48位序列號,以及8位的CRC此命令只能在總線上僅有一個DS1820的情況下可以使用。如果總線上存在多于一個的從屬器件,那么當所有從片企圖同時發送時將發生數據沖突的現象(漏極開路會產生“線與”的結果)。
Match ROM("符合"ROM)[55h]
???????“符合”ROM命令。后繼以64位的ROM數據序列,允許總線主機對多點總線上特定的DS1820尋址。只有與64位ROM序列嚴格相符的DS 1820才能對后繼的存貯器操作命令作出響應。所有與64位ROM序列不符的從片將等待復位脈沖。此命令在總線上有單個或多個器件的情況下均可使用。
Skip ROM(“跳過"ROM)[CCh]
???????在單點總線系統中,此命令通過允許總線主機不提供64位ROM編碼而訪問存儲器操作來節省時間。如果在總線上存在多于一個的從屬器件而且在Skip ROM命令之后發出讀命令,那么由于多個從片同時發送數據,會在總線上發生數據沖突(漏極開路下拉會產生“線與”的效果)。
Search ROM(搜索ROM)[F0h]
???????當系統開始工作時,總線主機可能不知道單線總線上的器件個數或者不知道其64位ROM編碼。
搜索ROM命令允許總線主機使用一種“消去”(elimination)處理來識別總線上所有從片的64位ROM編碼。
Alarm Search(告警搜索)[ECh]
???????此命令的流程與搜索ROM命令相同。但是,僅在最近一次溫度測量出現告警的情況下,DS1820才對此命令作出響應。告警的條件定義為溫度高于TH或低于TL。只要DS1820一上電,告警條件就保持在設置狀態,直到另一次溫度測量顯示出非告警值,或者改變TH或TL的設置使得測量值再一次位于允許的范圍之內。貯存在EEPROM內的觸發器值用于告警。
ROM 搜索舉例
【51單片機快速入門指南】6.3.1:使用1-WIRE搜索算法搜索單總線上所有DS18B20的ID(基于二叉樹)
???????ROM搜索過程是簡單三步過程的重復:讀一位,讀該位的補碼(complement),然后寫所需的那一位的值。總線主機在ROM的每一位上完成這一簡單的三步過程。在全部過程完成之后,總線主機便知道一個器件中ROM的內容。器件中其余的數以及它們的ROM編碼可以由另外一個過程來識別。
???????以下ROM搜索過程的例子假設四個不同的器件連接到同一條單線總線上。四個器件的ROM數據如下所示:
ROM1 00110101……
ROM2 10101010……
ROM3 11110101……
ROM4 00010001……
搜索過程如下:
???????從三步過程的兩次讀中可獲得的數據具有以下的解釋:
???????00:有器件連接著,在此數據位上它們的值發生沖突。
???????01:有器件連接著,在此數據位上它們的值均為0。
???????10:有器件連接著,在此數據位上它們的值均為1
???????11:沒有器件與單線總線相連。
存貯器操作命令
注:
讀暫存存儲器(Read Scratchpad)[BEh]
???????此命令讀暫存存儲器的內容。讀開始于字節0,并繼續經過暫存存儲器,直至第九個字節(字節8,CRC)被讀出為止。如果不是所有位置均可讀,那么主機可以在任何時候發出一復位以中止讀操作。
復制暫存存儲器(Copy Scratchpad)[48h]
???????此命令把暫存存儲器復制入DS1820的E2E^2E2存儲器,把溫度觸發器字節存貯入非易失性存儲器,如果總線主機在此命令之后發出讀時間片,那么只要DS1820正忙于把暫存存儲器復制入E2E^2E2,它就會在總線上輸出"0",當復制過程完成之后,它將反回"1",如果由寄生電源供電,總線主機在發出此命令之后必須能立即強制上拉至少10mS.
溫度變換(Convert T)[44h]
???????此命令開始溫度變換。不需要另外的數據。溫度變換將被執行,接著DS1820便保持在空閑狀態。如果總線主機在此命令之后發出讀時間片,那么只要DS1820正忙于進行溫度變換,它將在總線上輸出“0",當溫度變換完成時,它便返回“1",如果由寄生電源供電,那么總線主機在發出此命令之后必須立即強制上拉至少2秒。
重新調出E2(Recall E2)[B8h]
???????此命令把貯存在E2中溫度觸發器的值重新調至暫存存儲器,這種重新調出的操作在對DS1820上電時也自動發生,因此只要器件一接電,暫存存儲器內就有有效的數據可供使用。在此命令發出之后,對于所發出的第一個讀數據時間片,器件都將輸出其忙的標志"0"=忙,“1”=準備就緒。
讀電源(Read Power Supply)[B4h]
???????對于在此命令送至DS1820之后所發出的第一讀出數據的時間片,器件都會給出其電源方式的信號:“0”=寄生電源供電,“1”=外部電源供電。
示例程序
DS18B20.c
#include "DS18B20.h"void delay_10us(uint8_t n);void DS18B20_Delay() {}void DS18B20_Pin_H() {DS18B20_Pin = 1; }void DS18B20_Pin_L() {DS18B20_Pin = 0; }uint8_t DS18B20_Pin_Read() {return DS18B20_Pin; }/******************************************************************************* * 函 數 名 : DS18B20_reset * 函數功能 : 復位DS18B20 * 輸 入 : 無 * 輸 出 : 無 *******************************************************************************/ void DS18B20_reset(void) {DS18B20_Pin_L(); //拉低IOdelay_10us(72); //480~960usDS18B20_Pin_H(); //釋放IO }/******************************************************************************* * 函 數 名 : DS18B20_check * 函數功能 : 檢測DS18B20是否存在 * 輸 入 : 無 * 輸 出 : 1:未檢測到DS18B20的存在,0:存在 *******************************************************************************/ uint8_t DS18B20_check(void) {uint8_t time_temp = 0;while (DS18B20_Pin_Read() && time_temp < 6) //等待DQ為低電平 實測29us{time_temp++;delay_10us(1);}if (time_temp >= 6)return 1; //如果超時則強制返回1elsetime_temp = 0;while ((!DS18B20_Pin_Read()) && time_temp < 25) //等待DQ為高電平 實測109us{time_temp++;delay_10us(1);}if (time_temp >= 25)return 1; //如果超時則強制返回1return 0; }/******************************************************************************* * 函 數 名 : DS18B20_read_bit * 函數功能 : 從DS18B20讀取一個位 * 輸 入 : 無 * 輸 出 : 1/0 *******************************************************************************/ uint8_t DS18B20_read_bit(void) {uint8_t dat = 0;DS18B20_Pin_L();DS18B20_Delay();DS18B20_Pin_H();delay_10us(1);if (DS18B20_Pin_Read())dat = 1; //如果總線上為1則數據dat為1,否則為0else dat = 0;delay_10us(5);return dat; }/******************************************************************************* * 函 數 名 : DS18B20_read_byte * 函數功能 : 從DS18B20讀取一個字節 * 輸 入 : 無 * 輸 出 : 一個字節數據 *******************************************************************************/ uint8_t DS18B20_read_byte(void) {uint8_t i = 0;uint8_t dat = 0;uint8_t temp = 0;for (i = 0; i<8; i++)//循環8次,每次讀取一位,且先讀低位再讀高位{temp = DS18B20_read_bit();dat = (temp << 7) | (dat >> 1);}return dat; }/******************************************************************************* * 函 數 名 : DS18B20_write_bit * 函數功能 : 寫一個位到DS18B20 * 輸 入 : dat:要寫入的位 * 輸 出 : 無 *******************************************************************************/ void DS18B20_write_bit(uint8_t Bit) {if (Bit){DS18B20_Pin_L();DS18B20_Delay();DS18B20_Pin_H();delay_10us(6);}else{DS18B20_Pin_L();delay_10us(6);DS18B20_Pin_H();DS18B20_Delay();} }/******************************************************************************* * 函 數 名 : DS18B20_write_byte * 函數功能 : 寫一個字節到DS18B20 * 輸 入 : dat:要寫入的字節 * 輸 出 : 無 *******************************************************************************/ void DS18B20_write_byte(uint8_t dat) {uint8_t i = 0;uint8_t temp = 0;for (i = 0; i<8; i++)//循環8次,每次寫一位,且先寫低位再寫高位{temp = dat & 0x01;//選擇低位準備寫入dat >>= 1;//將次高位移到低位DS18B20_write_bit(temp);} }/******************************************************************************* * 函 數 名 : DS18B20_init * 函數功能 : 初始化DS18B20的IO口 DQ 同時檢測DS的存在 * 輸 入 : 無 * 輸 出 : 1:不存在,0:存在 *******************************************************************************/ uint8_t DS18B20_init(void) {DS18B20_reset();return DS18B20_check(); }void DS18B20_Read_ROM(uint8_t * pROM) {uint8_t i = 0;if(DS18B20_init())return;DS18B20_write_byte(DS18B20_READ_ROM);for(i = 0; i < 8; ++i)*pROM++ = DS18B20_read_byte(); }void DS18B20_Connect(uint8_t * pROM) {uint8_t i = 0;if(DS18B20_init())return;DS18B20_write_byte(DS18B20_MATCH_ROM);for(i = 0; i < 8; ++i)DS18B20_write_byte(*pROM++); }float DS18B20_Read_Temperture() {uint8_t dath = 0;uint8_t datl = 0;uint16_t value = 0;DS18B20_write_byte(DS18B20_Read_Scratchpad);//讀存儲器datl = DS18B20_read_byte();//低字節dath = DS18B20_read_byte();//高字節value = (dath << 8) + datl;//合并為16位數據if ((value & 0xf800) == 0xf800)//判斷符號位,負溫度{value = (~value) + 1; //數據取反再加1return value*(-0.0625);//乘以精度 }else //正溫度return value*0.0625; }void DS18B20_Single(void) {if(DS18B20_init())return;DS18B20_write_byte(DS18B20_SKIP_ROM);//SKIP ROM }DS18B20.h
#ifndef _DS18B20_H #define _DS18B20_H#include <STC89C5xRC.H> #include "stdint.h"//管腳定義 sbit DS18B20_Pin=P3^7; //DS18B20數據口定義//ROM 操作命令 #define DS18B20_READ_ROM 0x33 #define DS18B20_MATCH_ROM 0x55 #define DS18B20_SKIP_ROM 0xcc #define DS18B20_SEARCH_ROM 0xf0 #define DS18B20_ALARM_ROM 0xec//DS1820 命令集 #define DS18B20_Read_Scratchpad 0xbe #define DS18B20_Write_Scratchpad 0x4e #define DS18B20_Copy_Scratchpad 0x48 #define DS18B20_Convert_T 0x44 #define DS18B20_Recall_E2 0xB8 #define DS18B20_Read_Power_Supply 0xB4uint8_t DS18B20_init(void);//函數聲明 void DS18B20_write_byte(uint8_t dat); uint8_t DS18B20_read_bit(void); void DS18B20_write_bit(uint8_t Bit); void DS18B20_Read_ROM(uint8_t * pROM); void DS18B20_Single(void); void DS18B20_Connect(uint8_t * pROM); float DS18B20_Read_Temperture();#endif測試程序
???????stdint.h見【51單片機快速入門指南】1:基礎知識和工程創建
???????串口部分見【51單片機快速入門指南】3.3:USART 串口通信
???????定時器的介紹和配置源碼見【51單片機快速入門指南】3.2:定時器/計數器
定時器中斷服務函數
void TIM0_Callback() interrupt 1 //定時器0中斷函數 { extern uint8_t TIM0_Counter;--TIM0_Counter; }單傳感器時ID的獲取 main.c
#include <STC89C5xRC.H> #include "intrins.h" #include "stdint.h" #include "TIM.h" #include "USART.h" #include "DS18B20.h"void Delay1ms() //@22.1184MHz {unsigned char i, j;_nop_();i = 4;j = 146;do{while (--j);} while (--i); }void delay_ms(uint8_t ms) {while(ms --)Delay1ms(); }uint8_t TIM0_Counter = 0; void delay_10us(uint8_t n) {TL0 = TH0;TIM0_Counter = n;while(TIM0_Counter > 1); }uint8_t DS18B20_0[8]; code uint8_t DS18B20_1[8] = {0x28, 0x38, 0x66, 0x16, 0xa8, 0x01, 0x3c, 0xe4};void main(void) {uint8_t i;Timer_Init(TIMER_0, TIMER_MODE_2, GATE_DISABLE, CLK_Internal, 22118400, 10, STC_TIM_Priority_Highest); //定時器配置為10us中斷一次,8位重裝載USART_Init(USART_MODE_1, Rx_ENABLE, STC_USART_Priority_Lowest, 22118400, 115200, DOUBLE_BAUD_DISABLE, USART_TIMER_2);DS18B20_Read_ROM(&DS18B20_0);while(1){ for(i = 0; i < 7; ++i)printf("0x%x, ", (int16_t)DS18B20_0[i]);printf("0x%x\r\n", (int16_t)DS18B20_0[i]);delay_ms(100);} }單傳感器讀取溫度和讀取特定ID傳感器的溫度
code uint8_t DS18B20_0[8] = {0x28, 0x38, 0x66, 0x16, 0xa8, 0x01, 0x3c, 0xe4};void main(void) {Timer_Init(TIMER_0, TIMER_MODE_2, GATE_DISABLE, CLK_Internal, 22118400, 10, STC_TIM_Priority_Highest); //定時器配置為10us中斷一次,8位重裝載USART_Init(USART_MODE_1, Rx_ENABLE, STC_USART_Priority_Lowest, 22118400, 115200, DOUBLE_BAUD_DISABLE, USART_TIMER_2);while(1){ DS18B20_Single(); DS18B20_write_byte(DS18B20_Convert_T); //轉換命令 DS18B20_Single();printf("%f, ", DS18B20_Read_Temperture());DS18B20_Connect(&DS18B20_0);DS18B20_write_byte(DS18B20_Convert_T); //轉換命令DS18B20_Connect(&DS18B20_0);printf("%f\r\n", DS18B20_Read_Temperture());delay_ms(100);} }多路傳感器讀取
code uint8_t DS18B20_0[8] = {0x28, 0x30, 0xc5, 0xb8, 0x0, 0x0, 0x0, 0x8e}; code uint8_t DS18B20_1[8] = {0x28, 0x38, 0x66, 0x16, 0x0, 0x0, 0x0, 0xee};void main(void) {Timer_Init(TIMER_0, TIMER_MODE_2, GATE_DISABLE, CLK_Internal, 22118400, 10, STC_TIM_Priority_Highest); //定時器配置為10us中斷一次,8位重裝載USART_Init(USART_MODE_1, Rx_ENABLE, STC_USART_Priority_Lowest, 22118400, 57600, DOUBLE_BAUD_DISABLE, USART_TIMER_1);DS18B20_Read_ROM(&DS18B20_1);while(1){ DS18B20_Connect(&DS18B20_0);DS18B20_write_byte(DS18B20_Convert_T); //轉換命令DS18B20_Connect(&DS18B20_0);printf("%f, ", DS18B20_Read_Temperture());DS18B20_Connect(&DS18B20_1);DS18B20_write_byte(DS18B20_Convert_T); //轉換命令DS18B20_Connect(&DS18B20_1);printf("%f\r\n", DS18B20_Read_Temperture());delay_ms(100);} }總結
以上是生活随笔為你收集整理的【51单片机快速入门指南】6.3:DS18B20 单总线数字温度计的多路读取的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 搭建基于虚拟账号的电子邮件系统
- 下一篇: Fibonacci Knapsack