Forum: Mikrocontroller und Digitale Elektronik DS18B20: Negatives Vorzeichen bei -0.0625 bis -0.9375 ohne Float


von Matthias Q. (zaphod_beeblebrox)


Lesenswert?

Hallo,
ich baue gerade einen kleinen Temp-Logger für ein Abwasserrohr, das mir 
vor ein paar Tagen eingefroren ist.

Dabei nutze ich den DS18B20. Es funktioniert soweit auch alles ganz gut.
Ich splitte Vorkommastelle und Nachkommastelle:
1
temp = 0x12AB; // Beispiel
2
vorkomma  = temp / 16;
3
nachkomma = temp % 16;
4
5
// Bei negativer Temperatur das Vorzeichen der Nachkommastelle korrigieren (ist sonst 0.-50 °C)
6
if (temp & 0xF000) {
7
   nachkomma |= 0xFFF0;
8
   nachkomma  = -nachkomma;
9
}
10
nachkomma = nachkomma * 100 / 16;

Ich hab jetzt nur das Problem, dass ich Werte negative Temperaturen, die 
vorne eine 0 haben (also z.B. -0.5°C) so nicht darstellen kann, da die 0 
weder positiv noch negativ ist, ergo auch kein Vorzeichen hat.

Ich bau mir gerade ne double-krücke, aber so ganz elegant finde ich das 
nicht. Gibt es eurer Meinung nach noch einen schöneren Weg? Ich setz 
mich morgen sonst mal ran und bau ggf. einen eigenen Converter, der das 
berücksichtigt und trotzdem schön ausrichtet (so, dass die Kommastellen 
untereinander beleiben, wie es sprintf macht).

Für Tipps bin ich sehr dankbar & viele Grüße,
Matthias

von GG (Gast)


Lesenswert?

Servus,

nach dem Datenblatt wird bei Minus-Temperaturen
das Ergebnis negiert.

+25.0°C 0000 0000 0011 0010 0032h
+0.5°C 0000 0000 0000 0001 0001h
0°C 0000 0000 0000 0000 0000h
-0.5°C 1111 1111 1111 1111 FFFFh
-25.0°C 1111 1111 1100 1110 FFCEh

if(temp >= 0xFC91) // Minus
{
~temp  // weiter mit negierten Temp rechnen
...

printf(" - " %d, xxxx)

}

Gruß GG

von Wolfgang-G (Gast)


Lesenswert?

@GG (Gast)
bist Du sicher, dass Du das richtige Datenblatt verwendest?
MfG

von GG (Gast)


Lesenswert?

Servus Matthias,

ich verwende den Code mit wechselnden Prozessoren( vom Atxmega bis 
Atmega128) laufend. Über Null Grad (temp) unter Null Grad (~temp). Was 
für eine Routine ich nehme, entscheidet die Frage >= oder <= 0xFC91. 
Diesen Wert hab ich errechnet,getestet und auch irgend wo mal gelesen. 
Das Vorzeichen "+" oder "-" übernehme ich direkt aus der if(..) Abfrage. 
Ein kleiner Anzeigefehler besteht aber dennoch, da zum Beispiel bei 
exakt 0 Grad immer ein Vorzeichen (+/-) erscheint. Das verschwindet 
aber, wenn sich die Temp. um 0,5 Grad ändert! Das war's

Datenblatt ok!

Gruß GG

von 900ss (900ss)


Lesenswert?

GG schrieb:
> nach dem Datenblatt wird bei Minus-Temperaturen
> das Ergebnis negiert.

Das ist falsch. Laut Datenblatt wird nicht negiert, sondern das 2-er 
Komplement verwendet, also eine übliche Darstellung der negativen Werte.

Zitat:
The temperature data is stored as a 16-bit sign-extended
two’s complement number in the temperature register (see  Figure 2).
Zitat Ende

>
> +25.0°C 0000 0000 0011 0010 0032h
> +0.5°C 0000 0000 0000 0001 0001h
> 0°C 0000 0000 0000 0000 0000h
> -0.5°C 1111 1111 1111 1111 FFFFh
> -25.0°C 1111 1111 1100 1110 FFCEh
>

Die oben angegeben Werte stehen so nicht im Datenblatt des DS18B20.

> if(temp >= 0xFC91) // Minus
> {
> ~temp  // weiter mit negierten Temp rechnen
> ...

Damit ergibt sich ein Fehler, da nicht das 2-er Komplement verwendet 
wird.

von 900ss (900ss)


Lesenswert?

So würde es auch funktionieren:

1
  int temp16;
2
  unsigned char sign;
3
4
  temp16 = ReadTemperature();
5
6
  if( temp16 < 0 )
7
  {
8
    sign = 1;          // merke neg. vorzeichen
9
    temp16 = -temp16;  // temperatur positiv machen
10
  }
11
  else
12
    sign = 0;          // merke pos. vorzeichen
13
14
  // ab hier weiter mit pos. Temperaturwerten

von Florian T. (florian_t)


Lesenswert?

Also ich hole nur ungern diesen alten Thread wieder hoch, aber da ich 
gestern eine gefühlte ewigkeit nach der korrekten Lösung gesucht habe, 
hier nun meine Lösung:

- DS18B20 speichert negative werte als Zweierkomplement.
- Einfaches negieren des Wertes reicht da nicht.

So gehts:

//-----------------------------------
int16_t decimal;
uint8_t digit;

// Wert vom scratchpad holen
temp = temperature[0];
temp |= temperature[1] << 8;

if(temperature[1] > 7) // Sign bits gesetzt
{
  temp = ~(temp - 1); // Rückwandeln vom zweierkomplement
  decimal = (temp / 16) * -1; // Vorzeichen setzen
}
else
{
  decimal = temp / 16;
}

*digit = temp % 16;
//-----------------------------------

Soweit ich es bisher beurteilen konnte, lieferte der sensor bisher mit 
dieser Umrechnung nur glaubhafte Werte.

von Markus B. (up_marki)


Lesenswert?

Florian T. schrieb:
> Also ich hole nur ungern diesen alten Thread wieder hoch, aber da ich
> gestern eine gefühlte ewigkeit nach der korrekten Lösung gesucht habe,
> hier nun meine Lösung:

Ui - auch ich hole den Beitrag nochmals hoch nachdem ich auch schon eine 
Weile google und nach der Lösung suche.

In der Lösung von Florian ist mir folgende Zeile ein Rätsel:

> *digit = temp % 16;

Was hat der "*" zu bedeuten?
Mein Compiler (Atmel Studio 7) kommt mit der Zeile nicht klar. Stern 
weg, dann klappt es. Aber ich erhalte nicht den Nachkommawert den ich 
erwarte.

Im Nachkommateil "digit" steht ja so der Modulo-Wert zwischen 0 und 15.
Oder verstehe ich das auch falsch?
Dann müsste ich den Modulo-Wert mit 0.0625 multiplizieren?

Wie wird die Temperatur, korrekt mit Kommastelle ausgegeben?

Mein Versuch:
sprintf(s, "%4d.%01dC",decimal, digit);

Vielen Dank schon mal
Grüsse
up_marki

von Falk B. (falk)


Lesenswert?

@Markus B. (up_marki)

>In der Lösung von Florian ist mir folgende Zeile ein Rätsel:

>> *digit = temp % 16;

>Was hat der "*" zu bedeuten?

Eine Dereferenzierung eines Pointers.

Schreibe das Ergebnis von temp modulo 16 an den Ort, an den digit zeigt.
Steht in jedem C-Buch, das ist das kleine 1x1. digit ist hier ein 
Pointer auf eine Variable. Der unvollständige Ausschnitt des Codes ist 
Teil einer Funktion, digit ist ein Funktionsargument.

>Mein Compiler (Atmel Studio 7) kommt mit der Zeile nicht klar.

Kaum ;-)

>Im Nachkommateil "digit" steht ja so der Modulo-Wert zwischen 0 und 15.

Ja.

>Dann müsste ich den Modulo-Wert mit 0.0625 multiplizieren?

Nein.

>Wie wird die Temperatur, korrekt mit Kommastelle ausgegeben?

So.

Beitrag "Re: Onewire + DS18x20 Library"

von Markus B. (up_marki)


Lesenswert?

Hallo Falk

Vielen Dank für die Antworten.
Werde Deinen Code mal genauer ansehen, hab ihn kurz überflogen.

>> Mein Compiler (Atmel Studio 7) kommt mit der Zeile nicht klar.

> Kaum ;-)
Doch, doch er kommt damit nicht klar - weil *digit demzufolge bei mir 
nicht richtig deklariert ist :-)

Bei mir liegt es nur noch an den Nachkomastellen, deswegen möchte ich 
vorerst nicht komplett alles ändern.

Trotzdem verstehe ich nicht ganz, wie der Nachkommateil bei Florian 
funktioniert. Mann müsste wohl den ganzen Code kennen?!

von Falk B. (falk)


Lesenswert?

@ Markus B. (up_marki)

>Doch, doch er kommt damit nicht klar - weil *digit demzufolge bei mir
>nicht richtig deklariert ist :-)

Was wohl eher weniger am Compiler liegt . . . 8-0

>Bei mir liegt es nur noch an den Nachkomastellen, deswegen möchte ich
>vorerst nicht komplett alles ändern.

Musst du auch gar nicht. Wenn digit eine normale Variable ist, musst du 
nur den Stern weglassen.

>Trotzdem verstehe ich nicht ganz, wie der Nachkommateil bei Florian
>funktioniert. Mann müsste wohl den ganzen Code kennen?!

Nicht wirklich. Aber da ist viel unnötiger Hackerkram drin. Das kann man 
DEUTLICH einfacher und geradliniger hinschreiben.

[c]

int digit, decimal, temp;

// Wert vom scratchpad holen und zusammenfügen
temp = ((int)temperature[1] << 8) | temperature[0];

decimal = temp / 16;
digit = abs(temp % 16);

[c]

Siehe Festkommaarithmetik

von Falk B. (falk)


Lesenswert?

Hmm, war wohl gestern zu spät. Wenn gleich die Rechnung oben stimmt, so 
will man die Nachkommastellen eher nicht 1/16 Bruchteile anzeigen, eher 
dezimal als 1/10. Also muss man auf 1/10 Auflösung umrechnen, so wie es 
die verlinkte Software tut.
1
int digit, decimal, temp;
2
3
// Wert vom scratchpad holen und zusammenfügen
4
temp = ((int)temperature[1] << 8) | temperature[0];
5
temp = temp * 10 / 16;  // in 1/10 Grad umrechnen
6
7
decimal = temp / 10;         // Vorkomma mit Vorzeichen
8
digit = abs(temp % 10);      // Nachkomma ohne Vorzeichen

: Bearbeitet durch User
von Markus B. (up_marki)


Lesenswert?

Hallo Falk
Habe ich so probiert, aber jetz wären wir beim Titel dieses 
ursprünglichen Thread's. Von 0 bis -1°C erhalte ich ein positives 
Vorzeichen.

Da es mir aber gerade an Zeit mangelt, versuche ich es später nochmal.

Beim gestrigen überfliegen Deines Cods konnte ich nicht auf Anhieb 
finden, wie negative Temperaturen behandelt werden. Der Sensor DS18B20 
liefert ja bei negativen Temperaturen das Zweierkomplement.

Gruss
up_marki

von Falk B. (falk)


Lesenswert?

@Markus B. (up_marki)

>ursprünglichen Thread's. Von 0 bis -1°C erhalte ich ein positives
>Vorzeichen.

Wirklich? Wie merkst du das?
Ach so, ich weiß warum. Mein Fehler ;-)

Man muss das Vorzeichen prüfen und bei der Ausgabe berücksichtigen!

Etwa so.
1
int digit, decimal, temp;
2
3
// Wert vom scratchpad holen und zusammenfügen
4
temp = ((int)temperature[1] << 8) | temperature[0];
5
temp = temp * 10 / 16;  // in 1/10 Grad umrechnen
6
7
decimal = temp / 10;         // Vorkomma mit Vorzeichen
8
digit = abs(temp % 10);      // Nachkomma ohne Vorzeichen
9
10
if (temp < 0) {
11
  sprintf(s, "-%4d.%01dC",decimal, digit);
12
} else {
13
  sprintf(s, " %4d.%01dC",decimal, digit);
14
}

von Stefan (Gast)


Lesenswert?

Ich löse das meistens so:

if (temp2write and 0x8000) then
      text[0] = "-"
      temp2write = not temp2write + 1
    end if

temp2write Ist der Wert der vom Sensor kommt.

von Falk B. (falk)


Lesenswert?

@ Stefan (Gast)

>if (temp2write and 0x8000) then
>      text[0] = "-"
>      temp2write = not temp2write + 1
>    end if

Ist das cool oder einfach nur Hackermist? Man kann eine 
vorzeichenbehaftete Zahl einfach als das handhaben, was sie ist. 
Vorzeichenbehaftet. Man muss nicht beweisen, daß man früher mal ein 
cooler Assemblerfreak war und die Definition des Zweierkomplements 
verstanden hat.
1
if (temp2write < 0) then
2
  text[0] = "-"
3
  temp2write = -temp2write
4
end if

Das ist deutlich verständlicher.

von Peter D. (peda)


Lesenswert?


von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Falk B. schrieb:
> Ist das cool oder einfach nur Hackermist?

Vll. cooler Hackermist?
Was für ein Problem gibt es damit, daß man den Sensorwert, laut 
Datenblatt, zurück rechnet?
Man braucht nicht für jeden Mückenschiss float mit zig Nachkommastellen, 
nur weil ja noch Platz im µC ist.
Auch kann man, wenn man kann, selber entscheiden, ob das höchste Bit 
hier ein Minus darstellen soll, oder ob das Gelump 'nur' eine Zahl ist.
Aus dem Grund, möglichst wenige Platz sinnlos zu verschwenden, wäre 
meine Minus-Prüfung VOR der Ausgabe des Wert, da ich die Ausgabe nur 1x 
benötige, nämlich, nachdem ich '-' oder ' ' ausgegeben habe (wo ich 
direkt das 2er Komplement bilde, damit der Wert auch wieder passt).

In diesem Sinne, ab in die Rinne ...

PS: gibt's dafür nicht irgendwo eine LIB, daß man sich überhaupt gar 
keinen Kopf mehr machen muß? Kann doch nicht sein, daß man selber 
(nach-)denken muß ...

von Markus B. (up_marki)


Lesenswert?

Hallo zusammen

Danke für alle Beiträge.

Seid doch ein bisschen brav zu einander. Jede der Meinungen ist denke 
ich berechtigt.
Zum einen will man gut lesbaren Code - macht Sinn, ebenso ist es doch 
sinnvoll etwas ressourcen optimiert zu coden.

Sehe mir bei Gelegenheit auch noch mal eure verlinkten Beiträge an.
Dann entscheide ich mich für eine Variante.

Frohes Schaffen oder davon Erholen :-)

up_marki

: Bearbeitet durch User
von Markus B. (up_marki)


Lesenswert?

Hallo zusammen

Mich freut es immer, wenn ich in den Beiträgen die definitive Lösung 
finde. Deshalb hier meine funktionierende Lösung, welche auf eueren 
Beiträgen basiert. Danke nochmal.

Sollte jemand wesentliche Verbesserungen haben - nur zu...

Code:
1
uint16_t    temp;
2
int8_t      sign;
3
int16_t      decimal;
4
uint8_t      digit;
5
6
temp = ((int)temperature[1] << 8) | temperature[0];
7
sign = 0;                // Vorzeichen positiv
8
if(temp > 0xF000)          // Temperatur negativ
9
{
10
  temp = ~(temp - 1);        // Rückwandeln vom Zweierkomplement
11
  sign = 1;              // Vorzeichen negativ
12
}
13
14
temp = temp * 10 / 16;        // in 1/10 Grad umrechnen
15
decimal = temp / 10;        // Vorkommawert mit Vorzeichen
16
digit = temp % 10;            // Nachkommawert 
17
18
if(sign == 1)               // Negative Zahl
19
{
20
  sprintf(s, "-%3d.%01dC",decimal, digit);
21
}
22
else
23
{
24
  sprintf(s, " %3d.%01dC",decimal, digit);
25
}
26
      usart_send_string(s);

up_marki

von Falk B. (falk)


Lesenswert?

@ Markus B. (up_marki)

>if(temp > 0xF000)          // Temperatur negativ

Herzlichen Glückwunsch, du hat nichts verstanden!

von Markus B. (up_marki)


Lesenswert?

Falk B. schrieb:

>>if(temp > 0xF000)          // Temperatur negativ
>
> Herzlichen Glückwunsch, du hat nichts verstanden!

Vielen Danke für die Glückwünsche! Freut mich.
...und was habe ich nicht verstanden ?
Wenn ich nichts verstanden hätte würde es gar nicht funktionieren :-)


gefiele Dir folgendes besser?
1
  if(temp and 0x8000)          // Temperatur negativ

gem. Datenblatt sind im MSB Bit 4 - 8 als "Sign" deklariert.

Oder wo ist das Problem????

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?


von Markus B. (up_marki)


Lesenswert?

Falk B. schrieb:
> @ Markus B. (up_marki)
>
>>if(temp > 0xF000)          // Temperatur negativ
>
> Herzlichen Glückwunsch, du hat nichts verstanden!

Mag sein...

Ich habe nichts gegen vorzeichenbehaftete Zahlen und diese so weiter zu 
Verarbeiten. Nur was ich bei deinem Beitrag: 
Beitrag "Re: DS18B20: Negatives Vorzeichen bei -0.0625 bis -0.9375 ohne Float" 
nicht verstehe - die negative Temperatur im ScratchPad ist nicht direkt 
negativ um so weiter zu verarbeiten???

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Die Temperatur besteht aus mehreren Bytes, nicht mehr, nicht weniger.

Es geht darum, wie Du diese Bytes interpretierst.

Bei 'uint' hat das Byte die Wertigkeit 0...255, bei 'vorzeichenbehaftet' 
(ka, wie Das declariert wird) -128...127, obwohl genau die gleichen Bits 
gesetzt sind.
Ist einzig die Art, wie Du mit den Zahlen weiter rechnen willst.

MfG

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.