高通台灣AI黑客松|競賽說明會
|

【Maker電子學】Arduino 上溫度測量一哥:DS18B20

   

作者:Bird

上回我們介紹了兩種類型的溫度測量 IC:類比輸出數位介面,也各舉了一顆作為範例,但文章刊出後,我突然想到漏掉了 Arduino 平台上最常使用的溫度測量 IC — DS18B20,因此這次我們就來補充介紹這顆在 Arduino 平台上很常見的溫度測量 IC 吧!

Arduino 上的溫度測量一哥

提起 Arduino 平台上最常使用的溫度測量 IC,非 DS18B20 莫屬。很多人的第一個 Arduino 專案,除了閃閃 LED、讀讀按鈕開關之外,真正跟環境有連結的實作,大概就是用 DS18B20 測量溫度了,因為它的接線簡單(全部只要三根線)、又有現成的 Arduino library 可直接使用,只要把零件插上去,再寫個五六行程式,就能看到溫度,非常有成就感。

Maker愛用的DS18B20溫度感測器

但如果只是這樣,其實不值得我寫一篇文章來介紹它,因此這回我們要以比較深入的角度來看 DS18B20 這顆溫度測量 IC 在 Arduino 平台上的應用。

DS18B20 是 Maxim Integrated 的產品,事實上這個產品來自於 2001 年 Maxim 收購的 Dallas Semiconductor,從它的編號是 DS 開頭就可略窺一二。年紀稍長的讀者可能對 Dallas Semiconductor 還有印象,因為在 PC 的主機板還需要安裝單獨時鐘 IC 的那個年代,Dallas 的內建電池、SRAM 的整合式 RTC(Real-time Clock)產品算是市場上的一時之選;但隨著半導體製程進步,耗電越來越低,在現今這個年代,RTC 多半只是複雜 SoC 裡的一個附屬小功能,而利用超級電容或是我們之前提過的鈕扣型一次性鋰電池,也能推動 RTC 達數年之久,因此這類的 RTC 產品早已式微。

但溫度測量 IC 仍舊屹立不搖,因為熱管理始終是電子產品設計中,非常重要的課題之一,市場上也出現許多多樣化的產品。

DS18B20 的接線非常簡單,只有三隻腳:電源、接地、資料線,如下圖所示:

DS18B20 接線圖(圖片來源:Bird 提供)

因此在 Arduino 或任何其它的 MCU/MPU 上,只需要用到一隻 I/O 接腳就能和它溝通,比起上回介紹的 I2C 溫度測量 IC 還要省一隻腳。事實上,DS18B20 還支援一種從資料接腳偷電的「寄生供電模式」(Parasite Power Mode),在這種模式下,連電源接腳都不用接了,不過這個模式有一些使用上的限制,我們後面會再詳細說明。

DS18B20 可以測量的溫度範圍是 -55 °C 到 +125 °C,這對於大部分的消費性電子產品來說已經非常夠用,而且它在 -10 °C 到 +85 °C 這個範圍內保證 ±0.5°C 的精確度。它的溫度讀數已經在內部校正為攝氏刻度(也和絕對溫度的比例相同),因此如果你需要華氏的溫度讀數,就必須自己轉換(除以 5 乘以 9 再加 32),但除以 5 這個動作對於沒有浮點計算單元的 MCU 而言,會需要用到非整數的數學計算程式庫,在寫程式時就要特別注意變數的宣告與計算時的型別轉換。

在某些開發平台上,你可能會發現因為用了一個非整數的除法指令,導致編譯出來的程式碼大小暴增,因為 compiler 為了這一個非整數的計算得載入、連結一整個非整數的數學函式庫,因此在某些環境中,查表可能是比較好的做法。

只要一根線的 1–Wire Bus

DS18B20 在唯一一根 data 接腳上所使用的通訊協定叫做 1–Wire bus,這是來自 Dallas Semiconductor 的技術,它的概念和 I2C Bus 很類似,但速度較慢、只需要一根線。1–Wire 的標準通訊速度是 16 Kbps 左右(I2C 則有 100 Kbps、400 Kbps、甚至最新的 1 Mbps),較新的裝置則支援新的 10 倍速 overdrive 模式。

既然 1–Wire bus 叫做 Bus,表示它裡面可以有不只一個裝置(對!1–wire bus 理論上對同一個 Bus 上的裝置數量沒有限制)。Bus 上的裝置都必須用 open–drain 的方式驅動 Bus,且大家是共用一個 pull–up 電阻,這種驅動結構讓 Bus 上的所有裝置都可以把 Bus 拉下來(稱為「Driving the Bus Low」),也可以不拉 Bus(稱為「Releasing the Bus」),但是當你不拉 Bus 時,不代表別人不會拉,因此 Bus 上只要有人拉 Bus,Bus 就會呈現 low 的狀態,而必須沒有人拉 Bus,Bus 才會讓 pull–up 電阻拉起來,呈現 high 的狀態。

透過巧妙的通訊協定設計,我們就能用這樣的架構讓同一個 Bus 上有很多裝置一起通訊,因此,要將 Arduino 與 1–wire bus 的裝置連接,除了裝置本身以外,還要幫它們加一個 pull–up 電阻。下圖是 Arduino 連接 DS18B20 最簡單的電路圖:

Arduino 連接 DS18B20 的基本電路圖(圖片來源:Bird 提供)

圖中 4.7 KΩ 的電阻就是 1–Wire Bus 的 pull–up 電阻。這個電阻要放多大,取決於 Bus 的長度、Bus 接線的長度、以及資料的傳輸速率,每當 Bus 上的裝置從 「Drving the Bus」 的狀態切換到 「Releasing the Bus」時,整條 1–Wire bus 就靠這個 pull–up 電阻將它拉到 high 的狀態。

但並不是裝置們放掉 bus 的那一瞬間,這個 pull–up 電阻就「咻!」瞬間把 Bus 拉到 high 的狀態,沒有這麼美好。

RC 時間常數

對這個 pull–up 電阻來說,整條 1–Wire Bus 是一個電容性的負載,換句話說就是一個電容器,當它要把 Bus 從 low 拉到 high 時,其實是透過這個 pull–up 電阻對這個電容器充電(定電壓透過電阻對電容器充電是一個很常見的電路模型),電容器上的電壓可以表示為:

Vc = V*(1 – e(-t/RC)

這是一個指數曲線,為了計算方便,我們常會定義這種 RC 充電電路的時間常數為 T:

T=R*C

大致上來說,T 就是這個電路將電容器充電到電源電壓的 63 % 所需要的時間。由於流經電阻的電流與它上面的電壓差有關,因此當電容器的電壓越來越高,電阻上的壓差就越來越小,充電的速度就會減緩,因此電容的電壓上升速度也會跟著減緩,整個充電過程的電壓就會呈現這樣的曲線:

(圖片來源:Bird 提供)

根據 datasheet,DS18B20 在 DQ 接腳上的電容負載是 25 pF。我們先忽略線路的寄生電容不計,計算看看用 4.7 KΩ 的 pull–up 電阻來充 DQ 上的這個電容器需要多少時間,時間常數 T 是:

T = R*C = 4.7*103(ohm)*25 * 10-12(Farad)= 117.5*10-9(seconds)

這個算式計算起來很簡單,但是單位往往是很多人計算時的障礙。這邊電阻的單位是歐姆沒有問題,但電容的單位是我們比較不熟悉的 Farad(法拉),在我們常用的零件數值中,幾乎沒有機會用到 Farad 這麼大的單位,多半是 uF(百萬分之一 Farad)或 pF(10 的負 12 次方 Farad,或是一兆分之一 Farad)。當電阻的單位是 Ohm、電容的單位是 Farad 時,算出來的時間單位是秒,因此上面的算式算出來的時間常數就是 117.5 乘以 10 的負 9 次方秒,或是 117.5 pS、0.1175 uS。

根據上面的電壓波形圖,電容充滿所需要的時間大概是時間常數的 5 倍,因此我們可以說 4.7 KΩ 的 pull–up 電阻需要 0.1175*5 = 0.5875(uS)。

以 1–Wire bus 的資料速率 16 Kbps 來計算,一個 bit 的時間是:

1 S / 16000 = 0.00000625 S = 62.5 uS

所以波形上升時間佔整個 bit 時間的比例很小,大概比 1 % 小一點,算是很安全的。

這裡要強調一個觀念,雖然我們在數位電路裡常常討論方波,但世界上是不存在完美方波的,所有看起來好像很方的波形在放大之後看起來一定都圓圓、斜斜的,這就是因為以上所說的這種 RC 充電效應所造成的,因此方波夠不夠方、上升或下降得夠不夠快,在實務上仍然要看你使用它的時機來決定。

下圖就是一個很典型的、「不夠方」的方波(取自某個設計失敗的 I2C Bus):

(圖片來源:Bird 提供)

當 Bus 上的負載電容太大(通常是因為線接太長、線上的裝置數量太多、放了太大的 pull–up 電阻),就會造成這樣的波形,而這樣的波形容易造成資料傳輸的錯誤,因此,根據 Bus 上的負載選擇正確的 pull–up 電阻,是這種 open–drain 驅動的 Bus 設計上很關鍵的一步。

從資料線偷電 — 寄生供電模式

前面提過,DS18B20 有個從 DQ 線上偷電的寄生供電模式(Parasitie Power Mode)。其實這個供電方式的原理很簡單,趁 DQ 線是 high 的時候,除了可以利用它對 IC 供電外,同時也可利用它上面的電流去充一個小小的電容器,當 DQ 為 low 的時候,就利用這個小電容器裡儲存的電荷對 IC 內部持續供電。

DS18B20 的 datasheet 上有一張這樣的方塊圖,說明了這個機制的工作原理:

(圖片來源:Bird 提供)

不管是來自 DQ 還是 VDD 的電流,都會透過二極體對 Cpp 這顆小電容器充電,因此當 VDD 沒有供電而 DQ 為 low 時,這個小電容器就能持續對 IC 內部供電;但這麼做有一個限制,當 DS18B20 在寫入內部的 EEPROM 或是進行溫度轉換時,它的耗電會超過 1.5 mA,這個電流事實上已大過 Cpp 儲存電荷所能供應的電流,而 1.5 mA 在 4.7 KΩ 的 pull–up 電阻上會造成

V= I*R=0.0015*4700= 7.05(V)

這麼大的壓降,也就是說 4.7 KΩ 的 pull–up 電阻根本無法供應 1.5 mA 這麼大的電流,在這樣的狀況下, DS18B20 會因為無法得到足夠的供電進行工作,造成 IC 內部電路的不穩定甚至無法工作。

解決這個問題的方法,就是利用另一個 MOSFET 製造一個「strong pull-up」。當我們預期 DS18B20 要進行比較耗電的工作時,就打開這個 MOSFET,讓 DQ 腳上能收到足夠的電源,在 datasheet 上有這麼一個範例電路:

(圖片來源:Bird 提供)

當我們對 DS18B20 下了溫度轉換(Convert T)或寫入 EEPROM(Copy Scratchpad)的指令後,要在 10 uS 之內開啟圖中那個 MOSFET,讓 DQ 能經由 MOSFET 得到一個 strong 的 pull–up 供電(供電的時間至少要維持 EEPROM 的寫入時間,或是溫度轉換的時間)。Datasheet 上也有說明這兩個時間:

(圖片來源:Bird 提供)

EEPROM 的寫入時間最長是 10 mS。

(圖片來源:Bird 提供)

而溫度轉換所需要的時間依精度的變化而有不同,在最高的 12–bit 精度下做一次轉換需要最長 750 mS。

由於 1–Wire Bus 所有的讀寫都是由 MCU/MPU 發動,因此雖然 strong pull–up 在動作的期間,Bus 上沒辦法有任何的 Driving Bus 活動,但這並不會影響到整體的通訊,因為 MCU 自己知道它在控制 strong pull–up,當 MCU 需要對 DS18B20 進行讀寫、傳輸資料時,它就會把 strong pull-up 關掉。

用 Arduino 驅動 DS18B20

雖然 1–Wire Bus 的協定相當複雜,但 Arduino 上已經有許多前人寫好的 library 可以供我們使用,因此我們不用自己去設計驅動 1–Wire Bus 的程式。要在 Arduino 上使用 DS18B20 需要兩個 library:OneWireDallasTemperature(前者顧名,而後者則包含了針對 DS18B20 設計的各種功能)。

一個最簡單的 DS18B20 程式長這樣:

#include <OneWire.h>

#include <DallasTemperature.h>



#define ONE_WIRE_BUS 2    // 告訴 OneWire library DQ 接在那隻腳上



OneWire oneWire(ONE_WIRE_BUS); // 建立 OneWire 物件

DallasTemperature DS18B20(&oneWire); // 建立 DS18B20 物件



void setup(void)

{

DS18B20.begin();

}



void loop(void)

{

float temperature; //注意,溫度讀值帶小數,要用 float

DS18B20.requestTemperatures();  //下指令開始轉換

temperature = DS18B20.getTempCByIndex(0));  //讀取第一顆 DS18B20 的溫度

Delay(1000);

}

由於 1–Wire Bus 上可以有超過一顆以上的 DS18B20,每一顆 DS18B20 在出廠前都會用雷射在晶片上設定一個獨一無二的序號,因此接在同一個 Bus 上的 DS18B20 還是可以被 MCU 識別為不同的 IC,只要改變讀取指令中的 index,就能讀取不同顆 DS18B20 的溫度。

三根線

DallasTemperature 這個 library 並不支援寄生供電模式,因此 Arduino 與 DS18B20 之間還是要接三根線,但三根線其實已經非常方便了,除了常見的這種 TO-92 包裝:

TO-92(圖片來源:Bird 提供)

市場上還買得到一種封在防水不鏽鋼套管中的 DS18B20:

(圖片來源:Bird 提供)

這種封裝的 DS18B20 使用起來非常方便,因為 DS18B20 本身位在不鏽鋼探頭的最頂端,而整個探頭又是防水的,它就可以直接伸進液體中測量溫度,或是測量距離系統電路較遠區域的溫度,但使用這種封裝的 DS18B20 時,要特別注意接線的長度,線越長,線上的寄生電容就越大,pull–up 電阻的數值就必須相對應減小,以避免發生前面提及的「不夠方的方波」問題。

小結

這次我們對 Arduino 上最常用的溫度測量 IC:DS18B20 做了比較深入的介紹,並稍微聊了一下像 1–Wire Bus 這類以 open–drain 驅動的 Bus,在 pull–up 電阻上的選擇,讓讀者們能更了解這種 Bus 的工作原理,希望大家對 Arduino 上的溫度測量 IC 能有更多的認識,並能更巧妙地應用在你的 maker 專案中。

(責任編輯:賴佩萱)

Liang Bird

訂閱MakerPRO知識充電報

與40000位開發者一同掌握科技創新的技術資訊!

Author: Liang Bird

在外商圈電子業中闖蕩多年,經歷過 NXP、Sony、Crossmatch 等企業,從事無線通訊、影像系統、手機、液晶面板、半導體、生物辨識等不同領域產品開發。熱愛學習新事物,協助新創團隊解決技術問題。台大農機系、台科大電子所畢業,熱愛賞鳥、演奏管風琴、大提琴、法國號,亦是不折不扣的熱血 maker。

Share This Post On
468 ad

2 Comments

  1. Arduino電路圖錯誤
    1為GND
    3為VCC

    Post a Reply

發佈回覆給「ray」的留言 取消回覆

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *