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
voidloop(void){
2
bytei;
3
bytepresent=0;
4
bytetype_s;
5
bytedata[12];
6
byteaddr[8];
7
floatcelsius,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
case0x10:
29
Serial.println("Gerät ist aus der DS18S20 Familie.");
30
type_s=1;
31
break;
32
case0x28:
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_traw=(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
bytecfg=(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
elseif(cfg==0x20)raw=raw&~3;// 10 bit res, 187.5 ms
17
elseif(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! :)
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;
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?
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.
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.
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.