Arduino Playground is read-only starting December 31st, 2018. For more info please look at this Forum Post

1-Wire Protocol von Dallas Semiconductors

(Übersetzung aus dem englischen Wiki)

Dallas Semiconductors (jetzt zugehörig zu Maxim) produziert eine Familie von Schaltkreisen welche über ein proprietäres Protokoll angesteuert werden 1-wire Protokoll. Es sind keine Lizenzkosten für Implementierungen von Schaltungen mit 1-Wire notwendig.

In einem 1-Wire network, welches Dallas "MicroLan" (tm) getauft hat, kommuniziert ein einzelner "Master" mit einem oder mehreren "Slaves" über eine einzelne Datenleitung, welche ausserdem zur Spannungszuführung verwendet werden kann. (wenn die Slaves über 1-wire ihre Spannung bezeiehen, so wird dies parasitäre Spannungsversorgung genannt.)

Tom Boyd's guide to 1-Wire (englisch) könnte sehr interessant sein und bringt weitreichende Informationen mit sich.

Die 1-wire Temperatursensoren sind teilweise recht populär geworden, da sie recht einfach einzusetzen und verhältnismässig preiswert sind, sie bieten Temperaturen von kalibrierten Sensoren die direkt ausgelesen werden können. Eine ideale Möglichkeit um Sensoren an einen Arduino zu koppeln, auch über lange Wege. Der nachfolgende Beispiel-Code zeigt wie ein 1-wire Schaltkreis mit Jim Studt's OneWire Arduino library, eingebunden wird, mit einem DS18S20 Temperatursensor als Beispiel. Viele 1-Wire Schaltkreise können in beiden Betriebsarten parasitär and normal angesteuert werden.

MicroLans können direkt von einem Arduino mit Hilfe der Arduino 1-Wire library angesteuert werden. Alternativ können sie auch von einem Interface welches Extrakosten bedeuten, jedoch die CPU-Last auf dem Arduino reduziert. Die Kosten für so ein Interface machen ca. $8 als Bausatz (2/2010) aus. Hier ist eine Anleitung für den Einsatz von einem Drittanbieter.

Spannunsgversorgung für einen DS18x20

Der Schaltkreis kann auf zwei Arten mit Spannung versorgt werden. Zum Einen (die "parasitäre" Variante) was bedeutet, dass nur zwei Leitungen zum Chip geführt werden müssen. Die andere Variante, kann in manchen Fällen, eine stabilere Kommunikation bieten (parasitär arbeitet meistens recht gut), Da eine extra Ader ausschliesslich die Spannungsversorgung für den Chip bietet. Für die ersten Schritte, gerade wenn der Arduino weniger als 6m vom Sensor entfernt ist, dann sollte die parasitäre Betriebsart vollkommen ausreichen. Der Beispiel-Code arbeitet natürlich für beide Varianten.

Parasitäre Betriebsart
Wenn die parasitäre Betriebsart gewählt wird, sind nur zwei Adern erforderlich: Eine Datenleitung und eine Masseleitung. Beim Master, ist ein 4.7k pull-up Widerstand an den 1-wire bus anzuschliessen. Wenn die Leitung im "high" status ist, kann ein interner Kondensator im Chip aufgeladen werden.

Die Stromaufnahme ist normalerweise sehr gering, sie kann aber bis auf 1.5 mA ansteigen, wenn eine Temperaturumrechnung stattfindet oder der interne EEPROM beschrieben wird. Wenn ein Slave diese Operationen durchführt, muss der Master die Datenleitung so lange auf High setzen, bis diese Operationen beendet sind; eine Verzögerung von 750ms ist für eine DS18S20 Temperaturkonversion notwendig. Der Master kann in dieser Zeit keine weiteren Operationen durchführen, z.B: Kommandos ausführen, oder weitere slaves abfragen bis diese Operation beendet ist. Um dies zu ermöglichen, ist die OneWire library so entwickelt, dass es eine Möglichkeit gibt die Datenleitung dauerhaft auf High zu setzen.

Normale Betriebsart (externe Spannungszufuhr)
Mit einer externen Spannungszuführung, sind insgesamt drei Leitungen erforderlich: die Datenleitung, Masse und 5V. Der 4.7k pull-up Widerstand auf der Busleitung ist trotzdem erforderlich . Dadurch dass der Datenbus voll für die Kommunikation zur Verfügung steht, kann der Microcontroller ununuterbrochen die Temperatur abrufen. Auf diese Weise wird der wert sofort geschickt wenn die Konvertierung beendet ist, im Gegensatz zu 750ms Wartezeit in "parasitärer" Betriebsart.

Hinweis zu den Wirderständen: Für größere Netzwerke sollten kleiner Widerstände ausprobiert werden. Das ATmega328/168 Datenblatt empfiehlt bis zu 1k6 runter zu gehen andere Nutzer berichteten von guten Ergebnissen auch mit kleineren Werten.

Addressierung von einem DS18x20

Jeder 1-Wire Chip enthält eine 64-bit Seriennummer im 'rom', bestehend aus einer 8-bit Kennung für den Schaltkreistyp, einer 48-bit Seriennummer, und eine 8-bit CRC. Diese CRC wird verwendet um die Integrität der Daten sicherzustellen. Der nachfolgende Beispielcode stellt sicher dass das angesprochene Gerät ein DS18S20 ist in dem der Gerätecode 0x10 getestet wird. (Wenn Sie einen neueren DS18B20 sensor verwenden, so muss auf den Gerätetyp 0x28 getestet werden, für einen DS1822 muss auf 0x22 getestet werden.)

Bevor ein Kommando an ein Slave gesendet wird, muss der Slave per Rom-Adresse ausgewählt werden. Es kann ebenfalls ein Kommando an alle slave's mit dem 'skip rom' Kommando gesendet werden. Dies ist nur zuverlässig für Kommandos welche keine Antwort von allen "Slave's" erzeugt - eine Datenkollision würde entstehen, wenn mehr als ein Slave antworten.

Siehe auch DS18S20 oder DS18B20 Datenblätter für mehr Informationen.

Library

Durch Jim Studts OneWire library (siehe nachfolgende links) ist es nun möglich sämtliche Dallas ICs mit einer einfachen library von Miles Burton. Dallas Temperature Control Library

Beispielcode

  1. #include <OneWire.h>
  2.  
  3. // DS18S20 Temperaturchip i/o
  4. OneWire ds(10);  // an pin 10
  5.  
  6. void setup(void) {
  7.   // inputs/outputs initialisieren
  8.   // seriellen port starten
  9.   Serial.begin(9600);
  10. }
  11.  
  12. void loop(void) {
  13.   byte i;
  14.   byte present = 0;
  15.   byte data[12];
  16.   byte addr[8];
  17.  
  18.   if ( !ds.search(addr)) {
  19.       Serial.print("Keine weiteren Addressen.\n");
  20.       ds.reset_search();
  21.       return;
  22.   }
  23.  
  24.   Serial.print("R=");
  25.   for( i = 0; i < 8; i++) {
  26.     Serial.print(addr[i], HEX);
  27.     Serial.print(" ");
  28.   }
  29.  
  30.   if ( OneWire::crc8( addr, 7) != addr[7]) {
  31.       Serial.print("CRC nicht gültig!\n");
  32.       return;
  33.   }
  34.  
  35.   if ( addr[0] == 0x10) {
  36.       Serial.print("Gerät ist aus der DS18S20 Familie.\n");
  37.   }
  38.   else if ( addr[0] == 0x28) {
  39.       Serial.print("Gerät ist aus der DS18B20 Familie.\n");
  40.   }
  41.   else {
  42.       Serial.print("Gerätefamilie nicht erkannt : 0x");
  43.       Serial.println(addr[0],HEX);
  44.       return;
  45.   }
  46.  
  47.   ds.reset();
  48.   ds.select(addr);
  49.   ds.write(0x44,1);         // start Konvertierung, mit power-on am Ende
  50.  
  51.   delay(1000);     // 750ms sollten ausreichen
  52.   // man sollte ein ds.depower() hier machen, aber ein reset tut das auch
  53.  
  54.   present = ds.reset();
  55.   ds.select(addr);    
  56.   ds.write(0xBE);         // Wert lesen
  57.  
  58.   Serial.print("P=");
  59.   Serial.print(present,HEX);
  60.   Serial.print(" ");
  61.   for ( i = 0; i < 9; i++) {           // 9 bytes
  62.     data[i] = ds.read();
  63.     Serial.print(data[i], HEX);
  64.     Serial.print(" ");
  65.   }
  66.   Serial.print(" CRC=");
  67.   Serial.print( OneWire::crc8( data, 8), HEX);
  68.   Serial.println();
  69. }

HEX Werte in sinnvolle Werte konvertieren (Temperature)

Um einen HEX Wert in einen Analogwert zu konvertieren muss als erstes geklärt werden ob es sich um einen DS18S20, oder DS18B20 handelt. Der Code um die Temperatur zu errechnen, muss geringfügig für den DS18B20 (und DS1822) angepasst werden,weil dieser einen 12-bit Temperaturwert (0.0625 °C Auflösung) liefert, während der DS18S20 und der DS1820 einen 9-bit Wert liefern (0.5 °C Auflösung).

Als erstes müssen Variablen angelegt werden, (direkt nach loop() einfügen)


  1. int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;

Dann für DS18B20 den folgenden Code vor Serial.println(); einfügen.


  1.  
  2.  LowByte = data[0];
  3.   HighByte = data[1];
  4.   TReading = (HighByte << 8) + LowByte;
  5.   SignBit = TReading & 0x8000;  // test most sig bit
  6.   if (SignBit) // negative
  7.   {
  8.     TReading = (TReading ^ 0xffff) + 1; // 2's comp
  9.   }
  10.   Tc_100 = (6 * TReading) + TReading / 4;    // mal (100 * 0.0625) oder 6.25
  11. /* Für DS18S20 folgendes verwenden Tc_100 = (TReading*100/2);    */
  12.  
  13.  
  14.   Whole = Tc_100 / 100;  // Ganzzahlen und Brüche trennen
  15.   Fract = Tc_100 % 100;
  16.  
  17.  
  18.   if (SignBit) // negative Werte ermitteln
  19.   {
  20.      Serial.print("-");
  21.   }
  22.   Serial.print(Whole);
  23.   Serial.print(".");
  24.   if (Fract < 10)
  25.   {
  26.      Serial.print("0");
  27.   }
  28.   Serial.print(Fract);
  29.  
  30.   Serial.print("\n");

Dieser Code-Block konvertiert die Temperature nach °C und gibt sie seriell aus.

Ein Beispielprogramm für einen DS 1820 mit 0.5 °C Auflösung

Das Programmbeispiel zuvor ist für die B-Typen of des DS1820. HHier kommt ein Beispielprogramm welches mit dem DS1820 arbeitet, es werden mehrere Sensoren abgefragt und auf LCD angezeigt. . Das Beispiel wurde für Arduino pin 9 geschrieben. Es kann auf jeden anderen erdenklichen Port geändert werden. Pin 1 und 3 des DS1820 müssen auf Masse gelegt werden! Im Beispiel ein 5k wurde von Pin 2 des DS1820 auf Vcc (+5V) gelegt. Siehe LiquidCrystal dokumentation für den Anschluss von LCD an einen Arduino.

  1.  
  2. [@
  3. #include <OneWire.h>
  4. #include <LiquidCrystal.h>
  5. // LCD=======================================================
  6. //Bibliothek initialisieren
  7. LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
  8. #define LCD_WIDTH 20
  9. #define LCD_HEIGHT 2
  10.  
  11. /* DS18S20 Temperaturchip i/o */
  12.  
  13. OneWire  ds(9);  // on pin 9
  14. #define MAX_DS1820_SENSORS 2
  15. byte addr[MAX_DS1820_SENSORS][8];
  16. void setup(void)
  17. {
  18.   lcd.begin(LCD_WIDTH, LCD_HEIGHT,1);
  19.   lcd.setCursor(0,0);
  20.   lcd.print("DS1820 Test");
  21.   if (!ds.search(addr[0]))
  22.   {
  23.     lcd.setCursor(0,0);
  24.     lcd.print("No more addresses.");
  25.     ds.reset_search();
  26.     delay(250);
  27.     return;
  28.   }
  29.   if ( !ds.search(addr[1]))
  30.   {
  31.     lcd.setCursor(0,0);
  32.     lcd.print("No more addresses.");
  33.     ds.reset_search();
  34.     delay(250);
  35.     return;
  36.   }
  37. }
  38. int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;
  39. char buf[20];
  40.  
  41. void loop(void)
  42. {
  43.   byte i, sensor;
  44.   byte present = 0;
  45.   byte data[12];
  46.  
  47.   for (sensor=0;sensor<MAX_DS1820_SENSORS;sensor++)
  48.   {
  49.     if ( OneWire::crc8( addr[sensor], 7) != addr[sensor][7])
  50.     {
  51.       lcd.setCursor(0,0);
  52.       lcd.print("CRC is not valid");
  53.       return;
  54.     }
  55.  
  56.     if ( addr[sensor][0] != 0x10)
  57.     {
  58.       lcd.setCursor(0,0);
  59.       lcd.print("Device is not a DS18S20 family device.");
  60.       return;
  61.     }
  62.  
  63.     ds.reset();
  64.     ds.select(addr[sensor]);
  65.     ds.write(0x44,1);         // start conversion, with parasite power on at the end
  66.  
  67.     delay(1000);     // maybe 750ms is enough, maybe not
  68.     // we might do a ds.depower() here, but the reset will take care of it.
  69.  
  70.     present = ds.reset();
  71.     ds.select(addr[sensor]);    
  72.     ds.write(0xBE);         // Read Scratchpad
  73.  
  74.     for ( i = 0; i < 9; i++)
  75.     {           // we need 9 bytes
  76.       data[i] = ds.read();
  77.     }
  78.  
  79.     LowByte = data[0];
  80.     HighByte = data[1];
  81.     TReading = (HighByte << 8) + LowByte;
  82.     SignBit = TReading & 0x8000;  // test most sig bit
  83.     if (SignBit) // negative
  84.     {
  85.       TReading = (TReading ^ 0xffff) + 1; // 2's comp
  86.     }
  87.     Tc_100 = (TReading*100/2);    
  88.  
  89.     Whole = Tc_100 / 100;  // separate off the whole and fractional portions
  90.     Fract = Tc_100 % 100;
  91.  
  92.     sprintf(buf, "%d:%c%d.%d\337C     ",sensor,SignBit ? '-' : '+', Whole, Fract < 10 ? 0 : Fract);
  93.  
  94.     lcd.setCursor(0,sensor%LCD_HEIGHT);
  95.     lcd.print(buf);
  96.   }
  97. }
  98. @]


weitere 1-Wire Bibliotheken