Forum: Mikrocontroller und Digitale Elektronik Temperatursteuerung DS18S20 und Arduino


von Sven L. (eet)


Lesenswert?

Guten Tag,
ich programmiere eine Temperatursteuerung mit einem DS18S20 und einem 
Arduino Uno. Für das von mir geschriebene Programm habe ich ein Programm 
aus dem Internet gefunden und angepasst. Das Programm funktioniert. Nur 
habe ich ein kleines Verständnisproblem mit der Auslesung der Temperatur 
und der Konvertierung in die notwendige Kommazahl.

Den betreffenden Quellcode habe ich eingefügt. Der Quellcode setzt sich 
aus drei Quelltexten zusammen. Quelltext 1 und 3 sind für mich zu 100% 
nachvollziehbar. Nur die Berechnung der Temperatur aus Quelltext 2 ist 
für mich problematisch.


Quelltext 1
1
void loop(void) {
2
  byte i;
3
  byte present = 0;
4
  byte type_s;
5
  byte data[12];
6
  byte addr[8];
7
  float celsius, fahrenheit;
8
9
  if ( !ds.search(addr)) {
10
    Serial.println("Keine weiteren Adressen.");
11
    Serial.println();
12
    ds.reset_search();
13
    delay(250);
14
    return;
15
  }
16
  
17
  Serial.print("ROM =");
18
  for ( i = 0; i < 8; i++) {
19
    Serial.print(' ');
20
    Serial.print(addr[i], HEX);
21
  }
22
  if (OneWire::crc8(addr, 7) != addr[7]) {
23
    Serial.println("CRC nicht gültig!!");
24
    return;
25
  }
26
  Serial.println();
27
  switch (addr[0]) { // the first ROM byte indicates which chip
28
    case 0x10:
29
      Serial.println("Gerät ist aus der DS18S20 Familie.");  
30
      type_s = 1;
31
      break;
32
    case 0x28:
33
      Serial.println("Gerät ist aus der DS18B20 Familie.");
34
      type_s = 0;
35
      break;  
36
    default:
37
      Serial.println("Gerätefamilie nicht erkannt");
38
      return;
39
  }
40
  ds.reset();
41
  ds.select(addr);
42
  ds.write(0x44, 1); // startet Konvertierung, mit power-on am Ende
43
  delay(1000);       // 750ms sollten ausreichen
44
  // man sollte ein ds.depower() hier machen, aber ein reset tut das auch
45
  present = ds.reset();
46
  ds.select(addr);
47
  ds.write(0xBE);         // Wert lesen
48
  Serial.print("  Data = ");
49
  Serial.print(present, HEX);
50
  Serial.print(" ");
51
  for ( i = 0; i < 9; i++) { //  9 Bytes
52
    data[i] = ds.read();
53
    Serial.print(data[i], HEX);
54
    Serial.print(" ");
55
  }
56
  Serial.print(" CRC=");
57
  Serial.print(OneWire::crc8(data, 8), HEX);
58
  Serial.println();

Also verstanden habe ich, dass sich die Temperatur aus zwei Bytes 
zusammen setzt. Das ist das Byte 0 und das Byte 1, welches der 
Microcontroller vom Zwischenspeicher (scratchpad) des DS18S20 erhalten 
hat. Byte 1 ist zuständig für das Vorzeichen (MSB) und Byte 0 stellt die 
Zahl an sich dar(LSB). Aber welchen Einfluss haben Byte 6 (COUNT REMAIN) 
und Byte 7 (COUNT PER °C) und warum? Warum ist der Quellcode so gewählt, 
wie er unten aufgeführt ist? Speziell interessiert mich der Bereich bis 
zur Verwendung der Variablen "cfg", da dieser den DS18S20 beschreibt. 
Der untere Bereich ist für den DS18B20 zuständig. Es wäre nett, wenn ihr 
mir den oberen erklären könntet. Über den unteren freue ich mich 
ebenfalls. Ich bin nicht so der Programmierer, also es wäre 
wahrscheinlich hilfreich, wenn ihr es so einfach wie möglich erklärt.

Quelltext 2
1
  // Convert the data to actual temperature
2
  // because the result is a 16 bit signed integer, it should
3
  // be stored to an "int16_t" type, which is always 16 bits
4
  // even when compiled on a 32 bit processor.
5
  int16_t raw = (data[1] << 8) | data[0];
6
  if (type_s) {
7
    raw = raw << 3; // 9 bit resolution default
8
    if (data[7] == 0x10) {
9
      // "count remain" gives full 12 bit resolution
10
      raw = (raw & 0xFFF0) + 12 - data[6];
11
    }
12
  } else {
13
    byte cfg = (data[4] & 0x60);
14
    // at lower res, the low bits are undefined, so let's zero them
15
    if (cfg == 0x00) raw = raw & ~7;      // 9 bit resolution, 93.75 ms
16
    else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
17
    else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
18
    // default is 12 bit resolution, 750 ms conversion time
19
  }
20
  celsius = (float)raw / 16.0;


Quelltext 3
1
  //fahrenheit = celsius * 1.8 + 32.0;
2
  Serial.print("  Temperatur = ");
3
  Serial.print(celsius);
4
  Serial.print(" Celsius, ");
5
  //Serial.print(fahrenheit);
6
  //Serial.println(" Fahrenheit");
7
  lcd.setCursor(0, 1);        // LCD Display ausgeben
8
  lcd.print("                ");
9
  lcd.setCursor(0, 1);
10
  lcd.print(celsius);
11
  lcd.print((char)223);       // Grad Symbol ausgeben
12
  lcd.print("C  ");
13
  //lcd.print(fahrenheit);
14
  //lcd.print((char)223);
15
  //lcd.print("F");
16
}

Vielen Dank im Voraus und ich wünsche ein frohes neues Jahr! :)

von Martin B. (Gast)


Lesenswert?

Das ist in der Formel zur Berechnung der Temperatur aus dem Datenblatt 
begründet.

Die heisst:

TEMPERATURE=TEMP_READ-0,25+(COUNT_PER_C-COUNT_REMAIN)/COUNT_PER_C

COUNT_PER_C ist laut Datenblatt immer 16, also:

TEMPERATURE=TEMP_READ - 0,25 + (16 - COUNT_REMAIN) / 16

Um ganzzahlig rechnen zu können multipliziert man beide Seiten  mit 16:

16 * TEMPERATURE=16*TEMP_READ – 4 + 16 - COUNT_REMAIN

Jetzt muss man noch berücksichtigen, dass das letzte rechte Bit von 
TEMP_READ

die Wertigkeit 2 hoch -1 hat. Dann ist raw << 3 gleich TEMP_READ * 16

und mit (raw & 0xFFF0) + 12 - data[6] erhält man 16 * TEMPERATURE.

Die Temperatur als float-Wert ist dann celsius = (float)raw / 16.0;

von Sven L. (eet)


Lesenswert?

Vielen Dank für deine Antwort. Ich konnte die Berechnung nachvollziehen.

Nur was ich immer noch nicht so ganz verstehe ist für was sind COUNT 
REMAIN,
COUNT PER °C  zuständig und was hat es mit der Bedingung
(data[7] == 0x10)auf sich?

von Martin B. (Gast)


Lesenswert?

OK, ich dachte, es geht nur um die Formel.

Zunächst zur Abfrage if (data[7] == 0x10): Die ist nach meinem 
Verständnis für DS18S20 und  DS18B20 überflüssig, denn der Wert ist bei 
beiden immer 0x10. Vielleicht, ist die Abfrage für einen alten DS1820 
gedacht. In dessen Datenblatt ist  kein 0x10 festgelegt. Die 
Unterscheidung zwischen DS18S20 und DS18B20 wird im Programm an Hand des 
Rom Codes gemacht und die Variable type_s entsprechend gesetzt.  Die 
wird im „Quelltext 2“ auch abgefragt und dann auf die Berechnung für 
DS18S20 oder DS18B20 verzweigt.

COUNT_PER_C gibt die Auflösung der in COUNT_REMAIN enthaltenen Grad 
Bruchteile an. Der Wert ist fix 16, also handelt es sich um eine 
Auflösung von 1/16  = 0,0625 (Grad Celsius). Wenn man also einen höher 
aufgelösten Wert als im  Temperaturregister (der ist gerundet) haben 
will, dann muss man COUNT_REMAIN  verwenden. Man löscht das gerundete 
letzte Bit (das war nach dem Shift die Operation (raw & 0xFFF0)  ) und 
berechnet den Temperaturwert nach der Formel aus dem Datenblatt.

Fragt man sich jetzt noch, wieso werden nicht wie beim DS18B20 alle 11 
Datenbit in das insgesamt 16 Bit lange Temperaturregister geschrieben?
Das denke ich, ist historisch begründet. Der DS18S20 ist der Ersatz für 
den DS1820. Er muss also auch softwarekompatibel sein und liefert die 
1/16 Grad in COUNT_REMAIN und eine 16 in COUNT_PER_C.

Das Messverfahren ist in älteren Dallas Application Notes beschrieben. 
Z. B. in Application Note Nr. 68 und 128.

Aber: Das alles betrifft nur eine (mögliche) Auflösung. Die Genauigkeit 
wird mit +-0,5 Grad angegeben.

von Sven L. (eet)


Lesenswert?

Verstehe ich das jetzt richtig für den DS18S20:

Der Wert Temp_Read bestehend aus Byte 0 und 1 besitzt eine Auflösung von 
0,5 Grad Celsius. Verwende ich hingegen die in Quelltext 2 verwendete 
Formel für die Temperatur erhalte ich einen Temperaturwert mit der 
Auflösung von 0,0625 Grad Celsius.

Oder warum hat die 0,0625 ansonsten für den DS18S20 eine wichtige Rolle? 
Für das auf- oder abrunden auf 0,5 Grad Celsius benötige ich ja keine 
0,0625 Grad Celsius Auflösung.

von Sven L. (eet)


Lesenswert?

Sven L. schrieb:
>
> Der Wert Temp_Read bestehend aus Byte 0 und 1 besitzt eine Auflösung von
> 0,5 Grad Celsius. Verwende ich hingegen die in Quelltext 2 verwendete
> Formel für die Temperatur erhalte ich einen Temperaturwert mit der
> Auflösung von 0,0625 Grad Celsius.
>

Ich habe gerade einen Blick in das Datenblatt geworfen und es stimmt so 
wie ich es oben beschrieben hatte. Meine Frage erübrigt sich also.

Martin B. vielen Dank, dass du alle meine Fragen beantwortet hast.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.