2018年1月2日 星期二

DS18B20-讀取溫度值【整合篇】

萬事俱備,整合吧!

 在上一篇中了解了DS18B20的暫存器架構及溫度值的計算後,本篇就是完成讀取溫度值的最後一步:整合!
底下我們從接線方式、程式架構及程式碼等三個方面依序說明!

接線方式

 再次重申,此程式只適用於Arduino與DS18B20於一對一的連接狀況下喲!此外,請注意,於Arduino的接線pin腳是7不是2,若你的接腳有調整時,請調整程式內的設定!


程式架構

底下之讀取溫度架構圖是關鍵函式-getTemp()的架構解析,而數字所標明的區域為前面所寫的工具副程式:

1、重啟程序(綠色):Arduino將電位拉低並維持480μs,DS18B20應簽低電位值既代表重啟程序OK。

2、命令程序(藍色):命令拆解成為位元,再使用寫位元(Write bit)程序使用DS18B20

3、讀取程序(黃色):讀取DS18B20給予之位元值,並組合成一字元(Byte)

4、計算溫度值(紅色):將讀取程序的位元值(LSB及MSB)轉換為溫度值
 除了整合上述四個工具副程式外,其中,要注意的是等待轉換的時間設定,
不同的位元(9-12位元)精密度有不同的轉換時間如下表所示,在這裡直接設定750μs,可確保其轉換時間充足。


程式碼及輸出

 程式碼有點長,但其實就是初始設定、關鍵程式庫(getTemp()-程式行號為17至31行)及工具程式碼的結合!
 

程式碼

const uint8_t  g_dq_pin =7; //Arduino數位腳位7接到DS18B20
#define Skip_ROM    0xcc //用於1對1時,省略每次作ROM序號確認程序
#define Convert_T   0x44 //啟動溫度轉換
#define Read_Scratchpad  0xbe //讀取暫存器值,有9個字元

void setup() {
 Serial.begin(9600);
 Serial.println("<>");
}

void loop() {
 Serial.print("DS18b20's Temperature ->");
 Serial.println(getTempC());
 delay(1000);
}

float getTempC()
{
 while(!CommandReset());       //重啟成功,再進行動作
 SendCommand(Skip_ROM);  //主機下0xcc命令(1對1的省略ROM確認作業)
 SendCommand(Convert_T);  //0x44啟動溫度轉換
 delay(750);
 //再次重啟,因為要作通訊作業
 while(!CommandReset());          //重啟成功,再進行動作
 SendCommand(Skip_ROM);        //主機下0xcc命令(1對1的省略ROM確認作業)
 SendCommand(Read_Scratchpad);   //0xBE溫度暫存器中的訊息(共9個字元)
 //前兩個字元就是溫度的訊息
 uint8_t LSB = ReceiveData();   //第一個讀到的是低位
 uint8_t MSB = ReceiveData();      //第二個讀到的是高位
 return Caculate_Temperature(LSB,MSB);
}

float Caculate_Temperature(uint8_t temp_LSB , uint8_t temp_MSB)
{
 boolean  fp_minus = false;        //温度正負標誌:預設為false,因為通常為零度以上
 if(temp_MSB > 0x7f)  //當temp_MSB>7代表此溫度為負數值時,測到的數值需要先反相再加 1
 {
  fp_minus = true;     //溫度正負指標,負數時fg_Minus=0
  temp_MSB = ~temp_MSB;     //將temp_MSB中的每一位元反相(0、1互換)
  temp_LSB = ~temp_LSB + 1;
  //將temp_LSB中的每一位元反相(0、1互換),要記得加一,才能正確的反應其值
 }
 //以十六位元空箱來結合MSB及LSB
 uint16_t raw_temp =
     (((uint16_t)temp_MSB) << 8) | //將MSB先用空的十六位元左移8個位元,等於乘256
     (((uint16_t)temp_LSB));
 float fp_temp = raw_temp * 0.0625;
 //將十六位元的整數值再乘於0.0625的單位值,既得溫度值
 if(fp_minus) {fp_temp = -fp_temp;} //當fp_minus是1,代表是負數,將溫度加上負號
 return fp_temp;
}

uint8_t ReceiveData()
{
 uint8_t byte_in=0;
 for(uint8_t i = 0; i < 8; i++)
 {
  //此時所測到的電位,就是此位元的資料
  if(ReadSlot()) {
   //看看此時主機線的電位狀況若為高位,就是1
   bitSet(byte_in, i);     //將byte_in第i個位元值,設置為1
  }
  else {
   bitClear(byte_in, i);   //將byte_in第i個位元值,設置為0
  }
 }
 return (byte_in);
}

uint8_t ReadSlot() {
 delayMicroseconds(1);
 pinMode(g_dq_pin, OUTPUT);       //轉為輸出,可達到高電位
 digitalWrite(g_dq_pin, LOW);   //將電位拉低告訴DS18B20,主機已準備好了
 delayMicroseconds(2);
 pinMode(g_dq_pin, INPUT);    //轉為輸入狀態,同時釋放線路
 delayMicroseconds(10);     //加前面的延時,約於12~13us時取樣
 uint8_t fp=digitalRead(g_dq_pin);
 delayMicroseconds(55);    //加上延時過渡此段作業時間60us
 return fp;
}

void SendCommand(uint8_t instruction)
{
 for(uint8_t i = 0; i < 8; i++) {
  WriteSolt(bitRead(instruction, i));
 }
}

void WriteSolt(uint8_t order_bit)
{
 if(order_bit) {        //當值為1時的處理,
  pinMode(g_dq_pin, OUTPUT);      //先將pin腳改為輸出狀態
  digitalWrite(g_dq_pin, LOW);    //將電位拉低,等於通知DS18B20要do something
  delayMicroseconds(10);       //至少要等待1us,但於15us前轉為高電位
  pinMode(g_dq_pin, INPUT);     //將接收轉成INPUT狀態,轉為高電位
  delayMicroseconds(60);      //加前段的延時至少等待60us過此周期
 }
 else {           //當寫入值為'0'時,Tx拉低電位時段60~120us
  pinMode(g_dq_pin, OUTPUT);    //先轉為輸出狀態
  digitalWrite(g_dq_pin, LOW);   //將電位輸出低電位
  delayMicroseconds(65);         //靜靜的等待DS18B20來讀取資料
  pinMode(g_dq_pin, INPUT);      //釋放電位控制轉回輸入狀態
  delayMicroseconds(5);     //等待上拉電阻將電位復位為HIGH
 }
}

uint8_t CommandReset()
{
 pinMode(g_dq_pin, OUTPUT);
 digitalWrite(g_dq_pin, LOW);
 delayMicroseconds(720);
 pinMode(g_dq_pin, INPUT);
 delayMicroseconds(70);
 uint8_t is_exist = !digitalRead(g_dq_pin);
 delayMicroseconds(410);
 return is_exist;
}

程式輸出 



結語

 歷經了前面淺水區的練功期,再加上此階段的暫存器、溫度值轉換,如今,終於看到溫度值了!不過,高興一下就好!

這樣就結束了嗎?當然沒那麼簡單,讀到溫度值只是此階段的甜頭,但不是終點,因為,溫度值雖然出來了,但我們要保證讀取到DS18B20的溫度值是正確無誤,要達到這個功能就必須掌握DS18B20的檢核功能-CRC碼檢驗,只要通過了檢驗,就代表此數值無誤,我們可以安心使用了!

那麼,就讓我們邁入檢核程式階段吧!

沒有留言:

張貼留言