mikrocontroller.net

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


Autor: Matthias Quade (zaphod_beeblebrox)
Datum:

Bewertung
0 lesenswert
nicht 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:
temp = 0x12AB; // Beispiel
vorkomma  = temp / 16;
nachkomma = temp % 16;

// Bei negativer Temperatur das Vorzeichen der Nachkommastelle korrigieren (ist sonst 0.-50 °C)
if (temp & 0xF000) {
   nachkomma |= 0xFFF0;
   nachkomma  = -nachkomma;
}
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

Autor: GG (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Wolfgang-G (Gast)
Datum:

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

Autor: GG (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: 900ss D. (900ss)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: 900ss D. (900ss)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
So würde es auch funktionieren:

  int temp16;
  unsigned char sign;

  temp16 = ReadTemperature();

  if( temp16 < 0 )
  {
    sign = 1;          // merke neg. vorzeichen
    temp16 = -temp16;  // temperatur positiv machen
  }
  else
    sign = 0;          // merke pos. vorzeichen

  // ab hier weiter mit pos. Temperaturwerten

Autor: Florian Trück (florian_t)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Markus B. (up_marki)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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"

Autor: Markus B. (up_marki)
Datum:

Bewertung
0 lesenswert
nicht 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?!

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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.

int digit, decimal, temp;

// Wert vom scratchpad holen und zusammenfügen
temp = ((int)temperature[1] << 8) | temperature[0];
temp = temp * 10 / 16;  // in 1/10 Grad umrechnen

decimal = temp / 10;         // Vorkomma mit Vorzeichen
digit = abs(temp % 10);      // Nachkomma ohne Vorzeichen


: Bearbeitet durch User
Autor: Markus B. (up_marki)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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.
int digit, decimal, temp;

// Wert vom scratchpad holen und zusammenfügen
temp = ((int)temperature[1] << 8) | temperature[0];
temp = temp * 10 / 16;  // in 1/10 Grad umrechnen

decimal = temp / 10;         // Vorkomma mit Vorzeichen
digit = abs(temp % 10);      // Nachkomma ohne Vorzeichen

if (temp < 0) {
  sprintf(s, "-%4d.%01dC",decimal, digit);
} else {
  sprintf(s, " %4d.%01dC",decimal, digit);
}

Autor: Stefan (Gast)
Datum:

Bewertung
1 lesenswert
nicht 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.

Autor: Falk Brunner (falk)
Datum:

Bewertung
-1 lesenswert
nicht 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.
if (temp2write < 0) then
  text[0] = "-"
  temp2write = -temp2write
end if

Das ist deutlich verständlicher.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Patrick J. (ho-bit-hun-ter)
Datum:

Bewertung
0 lesenswert
nicht 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ß ...

Autor: Markus B. (up_marki)
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: Markus B. (up_marki)
Datum:

Bewertung
0 lesenswert
nicht 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:
uint16_t    temp;
int8_t      sign;
int16_t      decimal;
uint8_t      digit;

temp = ((int)temperature[1] << 8) | temperature[0];
sign = 0;                // Vorzeichen positiv
if(temp > 0xF000)          // Temperatur negativ
{
  temp = ~(temp - 1);        // Rückwandeln vom Zweierkomplement
  sign = 1;              // Vorzeichen negativ
}

temp = temp * 10 / 16;        // in 1/10 Grad umrechnen
decimal = temp / 10;        // Vorkommawert mit Vorzeichen
digit = temp % 10;            // Nachkommawert 

if(sign == 1)               // Negative Zahl
{
  sprintf(s, "-%3d.%01dC",decimal, digit);
}
else
{
  sprintf(s, " %3d.%01dC",decimal, digit);
}
      usart_send_string(s);

up_marki

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Markus B. (up_marki)

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

Herzlichen Glückwunsch, du hat nichts verstanden!

Autor: Markus B. (up_marki)
Datum:

Bewertung
0 lesenswert
nicht 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?
  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
Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Markus B. (up_marki)
Datum:

Bewertung
0 lesenswert
nicht 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???

Autor: Patrick J. (ho-bit-hun-ter)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.