Forum: Mikrocontroller und Digitale Elektronik DS18S20 Temperatur lesen 2 Complement


von W. P. (wopi)


Lesenswert?

Hallo,

Im Datenblatt vom DS18S20 steht, das fur den Wert 0xFFFF ich eine 
Temperatur von -0.5C bekommen sollte.

Erstmal was funktioniert: 0xFF92 soll -55.0 sein:

0xFF92 = 0b1111111110010010

LSB = 0b10010010
MSB = 0b11111111

Bit 0 von LSB ist 0, also hat der Wert eine 0 nach dem komma.

Vom MSB geht hevor das es ein negativer Wert ist.

Ich invertiere die bits und bekomme 0b01101101, da es sich aber um einen 
7bit Wert handelt, ist der wert 0b00110110, dazu +1 und wir haben 
0b00110111 was decimal 55 entspricht. Funktioniert also prima.


Jetzt das was ich nicht verstehe:

0xFFFF = 0b1111111111111111

LSB = 0b11111111
MSB = 0b11111111

Bit 0 von LSB ist 1, also hat der Wert ein 5 nach dem komma.

Sign bit ist 1, also ist es ein negativer Wert.

Ich invertiere die bits und bekomme 0b00000000, da es sich aber um einen 
7bit wert handelt, ist der wert 0b0000000, dazu +1 und wir haben 
0b0000001 was decimal 1 entspricht, dazu 0.5, ergibt zusammen 1.5.

Wieso steht im Datenblatt 0.5 ? Kann ich nicht richtig zahlen ?

Grus an Alle

von michael (Gast)


Lesenswert?

Hallo,

es gibt noch eine andere Möglichkeint, eine Zahl im zweierkomplement in 
eine Zahl im Zehnersystem umzuwandeln:
Das niederwertigste Bit in MS Byte hat die Wertigkeit -2^7, das 
höchstwertigste Bit in LS Byte hat die Wertigkeit 2^6, das nächste Bit 
die Wertigkeit 2^5 ... das niederwertigste Bit in LS Byte hat die 
Wertigkeit 2^(-1) = 1/2.

0xFFFF kann man dann schreiben als (die 7 höchstwertigsten Bits in MS 
Byte kann man weglassen, das ist sowieso nur das Vorzeichenbit):

11111111.1 = -128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 + 0,5 = -0,5, was auch 
im Datenblatt steht

vgl. auch
http://de.wikipedia.org/wiki/Zweierkomplement#Umwandlung_ins_Dezimalsystem
im Abschnitt "Umwandlung ins Dezimalsystem" unten.

Wo dein Denkfehler ist weiß ich gerade nicht, vielleicht sieht das ja 
jemand anderes...

Viele Grüße
Michael

von michael (Gast)


Lesenswert?

Scheinbar funktioniert die Hilfsregel "invertieren + 1 addieren" nur, 
wenn es  sich um eine Zahl aus Z (also keine Nachkommastellen) handelt

Viele Grüße
Michael

von mue-c (Gast)


Lesenswert?

Hallo Michael,
ich kenne jetzt deine Hilfsregel nicht, aber vollständig wäre die 
Anweisung ja:
invertieren -> rechts shiften -> +1
Wenn die Reihenfolge tatsächlich so wäre:
invertieren -> +1 -> rechts shiften
würde sie immer stimmen. Scheint mir auch logischer zu sein.

von Karl H. (kbuchegg)


Lesenswert?

W. P. schrieb:

> 0b0000001 was decimal 1 entspricht, dazu 0.5, ergibt zusammen 1.5.
                                      ****

Deine eigentliche Zahl ist ja negativ. Also 0.5 wegzählen. Nicht dazu.


Du kannst es auch so sehen:
0xFFFF ist die erste negative Zahl, die überhaupt möglich ist. Da bei 
dir die 'Zahlauflösung' 0.5 Einheiten beträgt, ist die erste negative 
Zahl dann eben -0.5  ( -1 * 0.5 -> -0.5 )

von Karl H. (kbuchegg)


Lesenswert?

> Bit 0 von LSB ist 1, also hat der Wert ein 5 nach dem komma.

Hier ist dein Denkfehler.
Du verwechselst da 2 Operationen.
Die eine Operation ist: An die Zahl eine Stelle anhängen (und sei es nur
                        eine Nachkommastelle)
Die andere ist: Zu einer Zahl ein ander (eben 0.5) vorzeichenrichtig
                addieren.

Bei positiven Zahlen liefern beide Operationen ein identisches Ergebnis. 
Nur bei negativen eben nicht.

 -x + 0.5
ist eben nicht dasselbe wie
 -( x + 0.5)

von W. P. (wopi)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Deine eigentliche Zahl ist ja negativ. Also 0.5 wegzählen. Nicht dazu.

Mann bin ich blod... hast recht, ein Kerl will was mit uCs machen, kann 
aber nicht bis zwei zahlen :)

Tausend Mal danke :)

von Karl H. (kbuchegg)


Lesenswert?

W. P. schrieb:
> Karl Heinz Buchegger schrieb:
>> Deine eigentliche Zahl ist ja negativ. Also 0.5 wegzählen. Nicht dazu.
>
> Mann bin ich blod... hast recht, ein Kerl will was mit uCs machen, kann
> aber nicht bis zwei zahlen :)
>
> Tausend Mal danke :)

Und ich hoffe, das das nur eine theoretische Überlegung war. In einer 
Programmiersprache (zb C), die mit 2-er Komplement umgehen kann, 
brauchst du dich nicht darum kümmern.
Ber der Ausgabe stellst du einfach fest, ob die Zahl negativ ist oder 
nicht und malst dann einfach ein '-' auf die Anzeige. In diesem Fall 
nimmst du dann auch das nagative der Zahl (um sie positiv zu machen) und 
gibst die einfach aus. Für den Vorkommaanteil dividierst du die Zahl 
durch 2, dann malst du einen Punkt hin und wenn der Moduls der (jetzt 
positiven) Zahl ungleich 0 ist malst du noch eine 5 hinten drann.

  if( zahl < 0 )
  {
    Ausgabe( '-' )
    Zahl = -Zahl
  }

  AusgabeInt( Zahl / 2 )
  Ausgabe( '.' )
  if( Zahl % 2 == 0 )
    Ausgabe( '0' )
  else
    Ausgabe( '5' )

von W. P. (wopi)


Lesenswert?

Danke Euch allen, funtzt prima:
1
#include <stdio.h>
2
#include <stdint.h>
3
4
float raw_to_celsius(msb, lsb){
5
  float celsius = 0;
6
  celsius =  ((lsb & 1) == 1) ? 0.5 : 0;
7
  celsius =  ((msb & 1 << 7) == 1 << 7) ? celsius - 128 : celsius;
8
  celsius += (lsb >> 1);
9
  return celsius;
10
}
11
12
int main(){
13
14
  printf("% 5.1f\n", raw_to_celsius(0x00, 0xAA));
15
  printf("% 5.1f\n", raw_to_celsius(0x00, 0x32));
16
  printf("% 5.1f\n", raw_to_celsius(0x00, 0x01));
17
  printf("% 5.1f\n", raw_to_celsius(0x00, 0x00));
18
  printf("% 5.1f\n", raw_to_celsius(0xFF, 0xFF));
19
  printf("% 5.1f\n", raw_to_celsius(0xFF, 0xCE));
20
  printf("% 5.1f\n", raw_to_celsius(0xFF, 0x92));
21
22
}

Das Resultat:
1
 85.0
2
 25.0
3
  0.5
4
  0.0
5
 -0.5
6
-25.0
7
-55.0

Danke nochmal und Grus an Alle.

von Karl H. (kbuchegg)


Lesenswert?

>
1
> float raw_to_celsius(msb, lsb){
2
>   float celsius = 0;
3
>   celsius =  ((lsb & 1) == 1) ? 0.5 : 0;
4
>   celsius =  ((msb & 1 << 7) == 1 << 7) ? celsius - 128 : celsius;
5
>   celsius += (lsb >> 1);
6
>   return celsius;
7
> }

Ich habs befürchtet. Ich habs befürchtet.
Die Welt kann manchmal so grausam sein

Wenn schon float
1
float raw_to_celsius(uint8_t msb, uint8_t lsb)
2
{
3
  int16_t Temperature = ( msb << 8 ) + lsb;  // Beide bytes zu einem signed int zusammensetzen
4
  return Temperature / 2.0;              // Der Chip liefert immer das Doppelte der tatsächlichen Temperatur,
5
                                         // weil 1 Bit die Nachkommastelle darstellt.
6
}

von W. P. (wopi)


Lesenswert?

@kbuchegg,

Das Beispiel von Dir gibt mir:
1
 85.0
2
 25.0
3
  0.5
4
  0.0
5
 32767.5
6
 32743.0
7
 32713.0

Was mache if falsch ?

von W. P. (wopi)


Lesenswert?

Ok, int16_t anstatt vom int.

Funtzt jest, danke :)

von Karl H. (kbuchegg)


Lesenswert?

W. P. schrieb:
> Ok, int16_t anstatt vom int.

Welchen µC benutzt du?

Ich hatte die int16_t nachgebessert um das etwas klarer zu machen. Auf 
einem AVR ist int16_t und int allerdings identisch.

von W. P. (wopi)


Lesenswert?

Den Quellcode habe ich auf meinem Laptop per gcc am laufen.

Prototyping befor ich das auf meinem ATMEGA8 schmeisse.

Die LCD routinen fur mein Projekt habe ich schon, und laufen auf dem AVR 
ok.

Jetzt noch eine Routine fur zwei DS18S20 an zwei Pins basteln und 
fertig.

Ich weiss, ist Pinverschwendung, aber ich brauch nichts anderes 
anzuschliesen auser noch einen Relais und es macht das auslesen / 
austauschen der Sensoren einfacher.

von Karl H. (kbuchegg)


Lesenswert?

W. P. schrieb:
> Den Quellcode habe ich auf meinem Laptop per gcc am laufen.
>
> Prototyping befor ich das auf meinem ATMEGA8 schmeisse.

Dann ist alles klar.
Am PC ist ein int heutzutage 32 Bit gross.
Daher: Bei solchen Sachen explizit sein (hätt ich gleich tun sollen. 
Sorry)

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.