2013年4月6日 星期六

Arduino 溫度傳感器DS18B20

源自http://www.naozhendang.com/2011/06/15/learning-arduino-7-ds18b20-and-1-wire-protocol/








DS18B20是比較常見的溫度傳感器之一,如何用arduino連接DS18B20並讀取數據。當然也有必要瞭解一下DS18201-Wire


DS18x201-Wire

DS18x20
系列數字溫度傳感器主要有DS18S20DS18B20(DS18S20只有9位一種工作模式,分辨率只到0.5攝氏度,DS18B209101112位四種工作可編程控制的模式,分辨率最高為0.0625攝氏度。),都是由美國Dallas半導體公司(現在改名叫Maxim)生產的。這個系列最大的特點就是採用了Maxim的專利技術1-Wire

顧名思義,1-Wire就是採用單一信號線,但可像I2CSPI一樣,同時傳輸時鐘(clock)又傳輸數據(data),而且數據傳輸是雙向的。1-Wire 使用較低的數據傳輸速率,通常是用來溝通小型device,如數位溫度計。通過1-Wire技術可以在單一信號線的基礎上構成傳感器網絡,Maxim起名”MicroLan”
DS18x20的供電主要有兩種模式:
Parasite power mode/寄生供電



所謂的寄生供電是指DS18x20只需要兩根接線一根數據線一根接地線數據線上還要接一個4.7k上拉電阻連電源數據線同時也提供了電能。DS18x20內置了電容,高電平期時把電能儲存在內部電容裏,低電平期內消耗內部電容裏的能量工作,直到下次高電平期內再次電容充電。雖然這樣的模式簡化了線路同時也帶來了一些缺陷:
1. 電路的電流一般很小,只有當DS18x20進行溫度轉化或者寫EEPROM時會高達1.5mA,當DS18x20進行上述操作時,數據線必須保持電平拉高狀態直到操作結束,期間master端的Arduino不能做任何操作,DS18x20溫度轉化時這個時間間隔大概是750ms
2.如果要求DS18x20有精確的轉化,數據線在溫度轉化期間必須保證足夠的能量,但當你使用多個DS18x20構成MicroLan進行多點測溫時,單靠4.7k的上拉電阻無法提供足夠的能量,會導致較大的測溫誤差。
Normal (external supply) mode/標準(外部供電)



標準外部供電模式,相比寄生供電模式,每個DS18x20需要多一條獨立的電源線接獨立電源。雖然多用些線,但由於外部供電,保證了每個設備的進精確度和穩定性。而且沒有了上述溫度轉換期間Arduino不能做任何事的問題。
DS18x201-Wire想瞭解更多的可以點這裏:
http://www.arduino.cc/playground/Learning/OneWire
今天嘗試的就是Arduino+單個DS18B20+寄生供電。
採用寄生供電模式接溫度傳感器DS18B20,同時在Serial Monitor顯示溫度數據。

如果DS18B20VDD針腳再接5V的話,就是外部供電模式了。
電路圖


在開始程式之前,我們需要瞭解一下DS18x20的資料構成,每個DS18x20系列產品ROM裏存放著一個64位字符串,包含8位的family code(0×10代表DS18S20, 0×28代表DS18B20 , 0×22代表DS1822)48位序列碼、8位的CRC(Cyclical Redundancy Check/循環冗餘校驗碼,就是一串用於數據校驗的數字)。這樣ROM code就讓每個DS18x20都有個獨立IDmaster端可以通過MATCH ROM來確定其身份。

怎麼獲得溫度數據呢?當master端發出CONVERT指令時,指定的slave device就會開始溫度轉化過程。這個過程挺慢,前面提到過,大概需要750ms,會產生2位的數據,被儲存在RAM裏一個叫Scratchpad的地方,這個數據可以隨意讀寫。我們可以通過READ SCRATCHPAD指令讀取溫度數據,返回的是一個9位的數據(數據構成如圖所示)





溫度的話我們還需要用以下公式進行轉換:
Temp = ((HighByte << 8 ) + LowByte ) *0.0625




#include <OneWire.h>

// DS18S20 Temperature chip i/o
OneWire ds(10);  // on pin 10

void setup(void) {
  // initialize inputs/outputs
  // start serial port
  Serial.begin(9600);
}

void loop(void) {
  byte i;
  byte present = 0;
  byte data[12];
  byte addr[8];

  if ( !ds.search(addr)) {
      Serial.print("No more addresses.\n");
      ds.reset_search();
      return;
  }

  Serial.print("R=");
  for( i = 0; i < 8; i++) {
    Serial.print(addr[i], HEX);
    Serial.print(" ");
  }

  if ( OneWire::crc8( addr, 7) != addr[7]) {
      Serial.print("CRC is not valid!\n");
      return;
  }

  if ( addr[0] == 0x10) {
      Serial.print("Device is a DS18S20 family device.\n");
  }
  else if ( addr[0] == 0x28) {
      Serial.print("Device is a DS18B20 family device.\n");
  }
  else {
      Serial.print("Device family is not recognized: 0x");
      Serial.println(addr[0],HEX);
      return;
  }

  ds.reset();
  ds.select(addr);
  ds.write(0x44,1);         // start conversion, with parasite power on at the end

  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.

  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad

  Serial.print("P=");
  Serial.print(present,HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print( OneWire::crc8( data, 8), HEX);
  Serial.println();
}





雖然我們讀到了Scratchpad的數據但是顯示的是HEX16進制代碼看不懂啊我們還需要轉化成我們能讀的溫度格式。這裏推薦一個叫Dallas Temperature ControlLibrary,大大簡化了這個過程。




// Load Libraries for DS1820 and OneWire
#include <OneWire.h>
#include <DallasTemperature.h>

// Variables for temperature readings
float myTemp;
float myHighTemp;
float myLowTemp = 50;

// DS1820 Data wire is plugged into pin 4 on the Arduino
#define ONE_WIRE_BUS 4

// Setup oneWire instance to communicate with devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

void setup()
{
  // start serial port
  Serial.begin(9600);
  Serial.println("Dallas Temperature IC Control Library Demo");

   // Start the OneWire library
   sensors.begin();
}

void loop()
{

  // Read the temperature
  readtemp();
  // Write the Results to the serial Monitor
  serialPrint();

}

void readtemp()
{
  // call sensors.requestTemperatures() to issue a global temperature
  // request to all devices on the bus
  sensors.requestTemperatures(); // Send the command to get temperatures
  myTemp = (sensors.getTempCByIndex(0));

// Set High or Low Temp
  if (myTemp < myLowTemp) {     myLowTemp = myTemp;   }   if (myTemp > myHighTemp) {
    myHighTemp = myTemp;
  }
}

void serialPrint()
{
Serial.print("Current Temp: ");
Serial.print(myTemp);
Serial.print("C");
Serial.print("  Lowest Temp: ");
Serial.print(myLowTemp);
Serial.print("C");
Serial.print("  Highest Temp: ");
Serial.print(myHighTemp);
Serial.println("C");
delay (500);
Serial.print("Or if you prefer : ");
Serial.print(DallasTemperature::toFahrenheit(myTemp));
Serial.print("F / ");
Serial.print(DallasTemperature::toFahrenheit(myLowTemp));
Serial.print("F / ");
Serial.print(DallasTemperature::toFahrenheit(myHighTemp));
Serial.println("F ");
Serial.println();
delay (500);
}



沒有留言:

張貼留言

Messaging API作為替代方案

  LINE超好用功能要沒了!LINE Notify明年3月底終止服務,有什麼替代方案? LINE Notify將於2025年3月31日結束服務,官方建議改用Messaging API作為替代方案。 //CHANNEL_ACCESS_TOKEN = 'Messaging ...