Forum: Compiler & IDEs DS18B20 Temperaturauswertung für LCD-Anzeige ATmega32


von Rick (Gast)


Lesenswert?

Hi!

Bastle derzeit an einem C-Programm zur Anzeige der Temperatur an einem 
LCD Display (4x20 Zeichen und HD Controller).
Ich schnall einfach nícht, wie man das LSB und MSB (Temparatur vom 
Scratchpad)so verarbeitet, daß eine integerzahl in diesem Format 
rauskommt:

-58 entspricht -5,8°C
506 entspricht 50,6°C

Wie macht ihr das?

Ich hab hier schon die Beiträge im Forum gelesen und auch die App.Note: 
162 von Maxim (Interfacing the DS18B20 in a Microcontroller 
Environment), hat mir aber leider nicht wirklich geholfen.

Gibts da nen besseren Weg, als meine Idee/Vorschlag?

Grüsse Rick

von T. H. (pumpkin) Benutzerseite


Lesenswert?

So wie das aussieht kannst du den String in einen Integer wandeln und
dann mit 10 abdividieren?

Oder ist das kein String sondern ein LSByte und MSByte? Dann doch 
einfach durch 10 teilen...

von Rick (Gast)


Lesenswert?

Hi!

Ausgelesen wird die Temparatur in 2Byte. Zuerst das LSB dann das MSB.

Wertigkeit der Bits:

LSB: 2^3  2^2  2^1  2^0  2^(-1)  2^(-2)  2^(-3)  2^(-4)

MSB: S    S    S    S    S       2^6     2^5     2^4

wobei S für Sign steht und alle 1 bei einer neg. Zahl sind.

Bsp:

Digital output:              Temparatur:

0000 0000 0000 1000           +0,5°C

1111 1111 1111 1000           -0,5°C

Grüße Rick

von Markus _. (markush)


Lesenswert?

Servus Rick,

Die oberen 5 Stellen im MSB zeigen einen negativen oder positiven Wert 
an. Die restlichen Bits stellen die Temperatur dar. Du kannst diesen 
Wert einfach durch 16 teilen dann hast du die Temperatur. Im Datenblatt 
des DS18B20 ist das auf Seite 4 sehr schön dargestellt.

Gruß - Markus

von Markus _. (markush)


Lesenswert?

Ups, hat sich überschnitten mit deinem Beitrag...

von Rick (Gast)


Lesenswert?

Hi Markus!

Bei meinem Datenbaltt steh auf Seite 4 nix darüber.
Da sind nur Anschlußschaltungen.

Auf Seite 5 steht mein obiger post. (Operation - Measuring Temperature)

Das mit dem durch 16 teilen versteh ich nicht. Sorry

Gruß Rick

von T. H. (pumpkin) Benutzerseite


Lesenswert?

Durch 16 deswegen, weil du in deinem Integer (MSByte und LSbyte
zusammen) Nachkommastellen hast, die dort nicht hingehören (2^-1 ..
2^-4). Also "schiebt" man die nach rechts um die Stelle 2^0 (=1) auf das
LSB (LSBit) zu bekommen. Und zwar um vier Stellen nach rechts, was einer
Division durch 16 gleicht.

1
    Wert >> 4 = Wert : 2 : 2 : 2 : 2 
2
              = Wert : (2*2*2*2)
3
              = Wert : (2^4)
4
              = Wert * (2^-4)    <-- Oha! 
5
              = Wert : 16


¿Comprende!

von Rick (Gast)


Lesenswert?

ja ich verstehen, also ein links-shift von 4 :-)

Dann hab ich die Temparatur ohne Nachkommastellen ;-) die ich aber 
brauche.
Was soll ich dann mit den Nachkommastellen machen?

Die Version mit den Nachkommastellen rausschieben hab ich auch schon 
geschnallt und auch mehrfach gesehen.

von Rick (Gast)


Lesenswert?

sorry rechts shift

von T. H. (pumpkin) Benutzerseite


Lesenswert?

Machst du:
1
// ...
2
3
float neuerWert;
4
5
neuerWert = Wert / 16;
6
7
// ...

von Falk B. (falk)


Lesenswert?

@  Rick (Gast)

>Dann hab ich die Temparatur ohne Nachkommastellen ;-) die ich aber
>brauche.
>Was soll ich dann mit den Nachkommastellen machen?

Festkommaarithmetik betreiben.

MFg
Falk

von Rick (Gast)


Lesenswert?

Hi!

Sorry, aber ich steh total auf der Leitung!

wenn ich einen integer-wert zurückliefern will, der der Temparatur mit 
EINER Nachkommastlle entspricht:

Nochmal das Bsp:

403  entspricht 40,3°C
-508 entspricht -50,8°C

so kann ich durch einen rechtsshift von 4 die Nachkommastellen 
entfernen.
ja, soweit alles klar.
Die Auswertung, ob der Wert negativ ist mit einer Maske.
Zahl umdrehen, irgendwie mit ~temp+1
aber weiter nix comprende!

Gruß Rick

von Falk B. (falk)


Lesenswert?

@ Rick (Gast)

>Sorry, aber ich steh total auf der Leitung!

Dann mach mal nen Schritt vorwärts ;-)

>wenn ich einen integer-wert zurückliefern will, der der Temparatur mit
>EINER Nachkommastlle entspricht:

Definiere "eine Nachkommastelle". Binär oder Dezimal?

Dein Sensor hat eine Auflösung von 1/16 Grad. Rechne das wie im Artikel 
beschrieben auf 1/100 um und alle ist paletti.

>so kann ich durch einen rechtsshift von 4 die Nachkommastellen
>entfernen.

Das willst du aber nicht!

>Die Auswertung, ob der Wert negativ ist mit einer Maske.

Wozu? Jeder Compiler rechnet wunderbar mit vorzeichenbehafteten Zahlen. 
Lass den Murks.

>aber weiter nix comprende!

Tief durchatmen und nochmal lesen.

MFG
Falk

von Rick (Gast)


Lesenswert?

Hi Falk!

Also bevor jemand mit Dingen nach mir wirf, wer ich mir erstmal den 
Artikel über Festkommaarithmetik ausdrucken und durcharbeiten!
Danke für den "link"

...und anschleißend meld ich mich wieder.

Gruß Rick

von T. H. (pumpkin) Benutzerseite


Lesenswert?

Beispiel:

1
Binärer Wert (ohne Signbits):   11110000111 (bin)
2
3
Als Integer:                    1927 (dec)
4
5
Korrigiert:                     1927 : 16 = 120,4375
6
7
Kontrolle:                      2^6  * 1 = 64
8
                                2^5  * 1 = 32
9
                                2^4  * 1 = 16
10
                                2^3  * 1 = 8
11
                                2^2  * 0 = 0
12
                                2^1  * 0 = 0
13
                                2^0  * 0 = 0
14
                                2^-1 * 0 = 0
15
                                2^-2 * 1 = 0,25
16
                                2^-3 * 1 = 0,125
17
                                2^-4 * 1 = 0,0625
18
                                =================
19
                                Summe    120,4375  <-- Alles Banane!

Doofes Beispiel, aber so wirds gerechnet.

von Rick M. (rick00)


Lesenswert?

Hi!

Nach dem durcharbeiten des Artikels Festkommaarithmetik ist mir 
folgender Gedanke gekommen, der hoffentlich richtig ist:

Der DS18B20 hat bei 12bit eine Auflösung von 1/16°C = 0,0625°C
Multipliziert man 0,0625 mit 10000 so erhält man den Korrekturfaktor= 
625 und eine neue Auflösung vo 1/10000°C.

Folgende Vorgehensweise:

1.) Man untersucht das MSB der Temparatur, ob die Temparatur neg. ist 
oder nicht:

if((MSB & 0xF8) == 0xF8);   // ist Temparatur negativ?

sssssxxx
11111xxx   MSB
11111000   0xF8     bitweise und
=================
11111000   0xF8


1.1) Wenn Temparatur neg. ist:

markiere die Temparatur als negativ in Hinsicht auf eine 16bit signed 
integer Variable => 15. bit auf "1" setzten =>   sign = 1

int16_t temp;

MSB &= 0x87;

11111xxx    MSB
10000111    0x87   bitweise und
=================
10000xxx   MSB neu

temp= ((MSB<<8) | LSB);  // MSB und LSB in einer signed integer Variable
                            zusammensetzen => man erhält die neg. Temp.


1.2 Wenn Temparatur positiv ist:

int16_t temp;

temp = ((MSB<<8) | LSB);  // MSB und LSB in einer signed integer 
Variable
                            zusammensetzen => man erhält die pos. Temp.


2.) Temparatur mit Korrekturfaktor multiplizieren:

int16_t temp;
int32_t temp1;

temp1 = temp*625;
return temp1;

sollte der Korrekturfaktor 625 zu groß sein?, so kann mann auch mit 62 
multiplizieren. Man erhält dann eine neue Auflösung von 1/1000°C und 
einen vertretbaren Umrechnungsfehler.

temp1 kann dann mit der im Artikel beschriebenen Weise, mit itoa, 
weiterverarbeitet werden und am LCD-Display ausgegeben werden.

Grüsse Rick

von T. H. (pumpkin) Benutzerseite


Lesenswert?

¿Comprende!   ;^)


Ich würde es etwas anders machen, aber ähnlich.
1
// alle 's' maskieren, außer oberstes
2
3
int16_t value = ((MSB << 8) | (LSB)) & 0x87FF;

Signed oder unsigned ergibt sich daraus automatisch. Aufpassen bei 2er 
Komplementdarstellung, keine Ahnung wie das bei den uint16_t Typ ist. 
Falk? Danach kannst du den Wert weiter nach gutdünken verwursten. Prima 
Rick!

von Karl H. (kbuchegg)


Lesenswert?

Erik M. wrote:
>
> 1.) Man untersucht das MSB der Temparatur, ob die Temparatur neg. ist
> oder nicht:
>
> if((MSB & 0xF8) == 0xF8);   // ist Temparatur negativ?

Lass doch den Quatsch.
Setze ganz einfach die beiden Bytes zu einem 16-Bit int
zusammen und dau hast deine 1/16 Grad fix fertig.

uint8_t LowByte;
uint8_t HiByte;

int16_t Total = (int16_t) ( (uint16_t)HiByte << 8 ) | LowByte;

Fertig ist die komplette Zahl (in 1/16 Grad Einheiten) in Total
und kann verwurschtet werden.

> temp1 = temp*625;
> return temp1;
>
> sollte der Korrekturfaktor 625 zu groß sein?,

Höchstwahrscfheinlich. In einen int16_t passen nun mal nur
Zahlen von -32768 bis +32767.
Das heist bei 32 Grad wäre bei dir Schluss.

> so kann mann auch mit 62
> multiplizieren. Man erhält dann eine neue Auflösung von 1/1000°C und
> einen vertretbaren Umrechnungsfehler.

Und wer bitte misst auf 1/1000 Grad genau?
Einen Wert auf 5 Nachkommastellen ausrechnen ist eine Sache.
Aber das auch messen können ist eine ganz andere Sache.

von Karl H. (kbuchegg)


Lesenswert?

T. H. wrote:
> ¿Comprende!   ;^)
>
>
> Ich würde es etwas anders machen, aber ähnlich.
>
>
1
> // alle 's' maskieren, außer oberstes
2
> 
3
> int16_t value = ((MSB << 8) | (LSB)) & 0x87FF;
4
>

Du darfst nicht UNDen.
Es ist für die 2-er Komplementdarstellung wichtig, dass alle 1
an den Sign Bit Stellen erhalten bleiben (so dort 1en vorhanden sind)

von Karl H. (kbuchegg)


Lesenswert?

> temp1 kann dann mit der im Artikel beschriebenen Weise, mit itoa,
> weiterverarbeitet werden und am LCD-Display ausgegeben werden.

Wozu willst dann da kompliziert mit Korrekturfaktoren etc.
rummachen.
Gib die Zerlegung in Vorkomma und Nachkomma doch gleich in
die Ausgaberoutine.


Du hast eine Zahl vom DS bekommen

  0000000010100100 = dezimal 164

Das dividierst du durch 16, weil 16 die Basiseinheit des Sensors ist.
Aus 0000000010100100 wird so 0000000000001010 oder dezimal 10.
Damit hast du den Vorkommaanteil, der kann schon ausgegeben werden.
Danach kommt aufs Display ein '.'
und der Nachkommaanteil. Den erhältst duz indem du die untersten
4 Bits abtrennst.

  int Nachkomma = Total & 0x000F;

Aufpassen musst du nur, dass du das Vorzeichen auch richtig
mitnimmst. Wenn das MSB in Total gesetzt ist, dann mussen auch
hier wieder die restlichen Bits auf 1 (damit das wieder eine
korrekte Negative Zahl ist) und anschliessend Positiv machen.

  if( Total & 0x8000 ) {
    Nachkomma |= 0xFFF0;
    Nachkomma = -Nachkomma;
  }

Damit steht in Nachkomma eine Zahl von 0 bis +15. Wobei die
Einheit davon 1/16 Grad ist. Willst du 2 Nachkommastellen?
Nichts einfacher als das

   Nachkomma * 100 / 16;

(Und lass den µC das auch wirklich rechnen. Keine Sorge, der COmpiler
wird die / 16 durch einen Shift ersetzen)

Und schon hast du in Nachkomma eine Zahl zwischn 0 und 99 die
du so ausgeben kannst. Auf eventuelle führende Nullen nicht vergessen.

In obigen Beispiel:
Das kam vom Sensor

    0000000010100100

die untersten 4 Bit abtrennen

    0000000000000100

da das MSB eine 0 war, muss an der Bitdarstellung nichts
weiter gemacht werden.

    0000000000000100 = dezimal 4

    4 * 100 = 400 / 16 = 25

Also lautet deine komplette Temperatur zu   10.25 Grad

Anstelle der komplizirten Maskiererei im Nachkommanteil muesste
es auch gehen, wenn man das den Compiler machen lässt:

  Vorkomma  = Total / 16;
  Nachkomma = Total % 16;
  Nachkomma = Nachkomma * 100 / 16;

 Vorkomma direkt ausgeben, dann einen Punkt und den Nachkommaanteil
wobei nicht auf die führenden Nullen vergessen werden darf.

von T. H. (pumpkin) Benutzerseite


Lesenswert?

Karl heinz Buchegger wrote:

> Du darfst nicht UNDen.

Das entscheide immernoch ich!  ;^)   Es ginge, aber dann nehme ich 
uint16_t.

von Karl H. (kbuchegg)


Lesenswert?

T. H. wrote:
> Karl heinz Buchegger wrote:
>
>> Du darfst nicht UNDen.
>
> Das entscheide immernoch ich!  ;^)   Es ginge, aber dann nehme ich
> uint16_t.

Tschuldigung. Du bist der Boss.
Mach das ruhig, wenn du dich selbst um das Vorzeichen kümmern
willst :-)

von Rick M. (rick00)


Lesenswert?

Karl heinz Buchegger wrote:
> Erik M. wrote:
>
>> sollte der Korrekturfaktor 625 zu groß sein?,
>
> Höchstwahrscfheinlich. In einen int16_t passen nun mal nur
> Zahlen von -32768 bis +32767.
> Das heist bei 32 Grad wäre bei dir Schluss.

deswegen hab ich ja auch int32_t genommen und nicht int16_t

gugst du hier:

int16_t temp;
int32_t temp1;    <<<<<<<<<<<<=================

temp1 = temp*625;
return temp1;


Den Rest von Dir muß ich mir mal genauer unter die Lupe nehmen.
Ich wollt eigentlich nur mal wissen ob meine Gedanken richtig waren und 
dies auch anwendbar wäre.
...hab nie behauptet, daß dies der einzig richtige Weg wäre!

Grüsse Rick

von Rick M. (rick00)


Lesenswert?

Morgen!

Zuerst mal zu "meiner" Variante:

Signed Integer undterscheiden sich von unsigned Integer insofern, daß 
bei den signed Variablen das höchstwertigste Bit für das Vorzeichen 
reserviert ist.
Daraus ergibt sich auch der kleinere Zahlenbereich.

bei 16 bit:

Sxxx xxxx xxxx xxxx    int16_t

xxxx xxxx xxxx xxxx    uint16_t

Als ich das Datenblatt des DS18B20 studierte und ich folgendes sah:

SSSS Sxxx xxxx xxxx    dachte ich mir, was soll denn das 5 
Vorzeichenbits,
                       so ein blödsinn. Weg mit den 4 überflüssigen s.

Deswegen auch meine Maske, die schlichtweg falsch ist!

Die Einsen gehören schon dort hin, nur haben sie nichts mit dem 
Vorzeichen zu tun, sondern entstehen durch das 2er-Komplement.

Bsp.: Temparatur vom DS18B20 von +10,125°C

0000 0000 1010 0010    +10,125°C
1111 1111 0101 1101    Einserkomplement
0000 0000 0000 0001    +1
--------------------------------------------
1111 1111 0101 1110    -10,125°C wie im Datenblatt nachzulesen.

Karl Heinz hatte also recht: wenn da Einsen sind, dann müssen die da 
auch bleiben!

Jetzt zu "meinem" Vorgehen:

int16_t temp;

temp= ((MSB<<8) | LSB);         // Temparatur zusammensetzen

Diese Temparatur ist vorzeichenbehaftet und korrekt, also keine Maske.

int16_t temp1;

temp1 = (temp*625)/1000;
return temp1;

und schon haben wir die Temparatur mit Vorzeichen und auf 1/10°C genau.
Naja genau ist relatuv, denn ich runde ja nicht korrekt, was bei einer 
Genauigkeit von +/- 0,5°C auch wirklich wurscht ist.

temp1 zB.: 225 entspricht 22,5°C
           -55 entspricht -5,5°C

Der Rückgabewert ist jetzt genau so, wie ich ihn haben wollte.
Der Rückgabewert kann:

1.) leicht im Hauptprogramm verglichen werden
    if(temp>600) bedeutet: wenn Temparatur größer 60,0°C

2.) leicht am UART oder LCD ausgegeben werden. Siehe Artikel
    Festkommaarithmetik (ich hoffe nur, daß itoa auch mit neg. Zahlen
    arbeitet?!)


------------------------------------------------------------------------ 
---
jetzt zu Variante 2 (wie Karl Heinz beschrieben):

analog zu meinem Bsp => -10,125°C vom Sensor:

1111 1111 0101 1110     -10,125°C

bei Division durch 16 kapiert der Compiler hoffentlich, daß es sich um 
eine neg. Zahl handelt, denn ansonsten mach er daraus nur nen 
rechtsshift von 4 und das wäre falsch.
Ansonsten macht er das 2er-Komplement, anschl. die Division und anschl. 
wieder das 2er-Komplement.
Bei letzteren haben wir schon mal die Zahl: -10

int16_t zahl

zahl = temp/16;




jetzt zu Nachkomma:
-----------------------------

Nachkomma holen:

int16_t nachkomma;

nachkomma = temp  0x000F;

1111 1111 0101 1110    -10,125°C
0000 0000 0000 1111    0x000F
------------------------------------- bitweise und
0000 0000 0000 1110  Nachkomma, aber immer noch "negativ" dargestellt

hier brauchen wir die Untersuchung, ob unsere temp negativ ist:

if(temp & 0x8000)

1111 1111 0101 1110
1000 0000 0000 0000    0X8000
--------------------------------- bitweise und
1000 0000 0000 0000    ungleich null, also wahr


In diesem Fall brauchen wir das 2er-Komplement der nachkommastellen

nachkomma = (~nachkomma)+1;


0000 0000 0000 1110    Nachkommastellen
1111 1111 1111 0001    Einserkomplement
0000 0000 0000 0001    +1
------------------------------------------
1111 1111 1111 0010

jetzt haben wir aber wieder lauter Einsen, die da nciht hingehören, also 
weg damit

nachkomma = nachkomma & 0x000F;

1111 1111 1111 0010
0000 0000 0000 1111  0x000F
----------------------------------------
0000 0000 0000 00010  Dezimal 2

nachkomma = (nachkomma*100)/16;

nachkomma ist jetzt dezimal 12


Die Temp lautet zusammengesetzt also -10,12°C


Grüße Rick

von Rick (Gast)


Lesenswert?

Hi!

Stimmt des oder ned?

Grüsse Rick

von Falk B. (falk)


Lesenswert?

@  Karl heinz Buchegger (kbuchegg)

>Und wer bitte misst auf 1/1000 Grad genau?

Niemand, ist auch vollkommen egal. Es geht um Festkommaarithmetik.

>Einen Wert auf 5 Nachkommastellen ausrechnen ist eine Sache.
>Aber das auch messen können ist eine ganz andere Sache.

Die Rechnung ist aber notwendig, um Nachkommastellen ausgeben zu können. 
Siehe Link.

MFG
Falk

von Rick M. (rick00)


Lesenswert?

Hi!

Sorry, wenn ich mich wiederhole: Stimmen meine Gedankengänge oder nicht?

Grüsse Rick

von Karl H. (kbuchegg)


Lesenswert?

Warum probierst du's nicht einfach aus?
Das ist doch das Schönste an der ganzen Programmiererei:
Sich am Papier ein Verfahren ausdenken, es umsetzen
und nachsehen obs auch wirklich funktioniert.

von Falk B. (falk)


Lesenswert?

@ Erik M. (rick00)

>Signed Integer undterscheiden sich von unsigned Integer insofern, daß
>bei den signed Variablen das höchstwertigste Bit für das Vorzeichen
>reserviert ist.

Nö, die meisten signed Variabeln verwenden das Zweikomplement. Das ist 
ein wsentlicher Unterschied.

Daraus ergibt sich auch der kleinere Zahlenbereich.


>Die Einsen gehören schon dort hin, nur haben sie nichts mit dem
>Vorzeichen zu tun, sondern entstehen durch das 2er-Komplement.

WEIL es ein Zweikomplement ist, gehören sie zum Vorzeichen.

>Karl Heinz hatte also recht: wenn da Einsen sind, dann müssen die da
>auch bleiben!

Sieh mal einer an ;-)


>temp= ((MSB<<8) | LSB);         // Temparatur zusammensetzen
>Diese Temparatur ist vorzeichenbehaftet und korrekt, also keine Maske.

Ja.


>int16_t temp1;
>temp1 = (temp*625)/1000;
>return temp1;

>und schon haben wir die Temparatur mit Vorzeichen und auf 1/10°C genau.

Ja.

>    Festkommaarithmetik (ich hoffe nur, daß itoa auch mit neg. Zahlen
>    arbeitet?!)

Steht im Artikel!


>bei Division durch 16 kapiert der Compiler hoffentlich, daß es sich um
>eine neg. Zahl handelt, denn ansonsten mach er daraus nur nen
>rechtsshift von 4 und das wäre falsch.

Der Compiler ist schon schlau genug, mit vorzeichenbehafteten Zahlen 
umzugehen ;-)

>Ansonsten macht er das 2er-Komplement, anschl. die Division und anschl.
>wieder das 2er-Komplement.

???

>Die Temp lautet zusammengesetzt also -10,12°C

Voiala!

MFG
Falk

von Rick (Gast)


Lesenswert?

@ Karl heinz Buchegger

Warum probierst du's nicht einfach aus?

Weil ich noch immer auf meine Temparatursensoren warte und vielleicht 
andere auch dieses Thema lesen.

Gruß Rick

von Rick M. (rick00)


Lesenswert?

Falk Brunner wrote:
> @ Erik M. (rick00)
>
>>Signed Integer undterscheiden sich von unsigned Integer insofern, daß
>>bei den signed Variablen das höchstwertigste Bit für das Vorzeichen
>>reserviert ist.
>
> Nö, die meisten signed Variabeln verwenden das Zweikomplement. Das ist
> ein wsentlicher Unterschied.
>
> Daraus ergibt sich auch der kleinere Zahlenbereich.
>
>

nimm uint8_t  Zahlenbereich von 0 bis 255

int8_t Zahlenbereich von -127 bis +128 genau 1 Bit weniger, weil fürs 
Vorzeichen verwendet wird.

Gruß Rick

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.