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
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...
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
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
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
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.
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.
@ 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
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
@ 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
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
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
¿Comprende! ;^)
Ich würde es etwas anders machen, aber ähnlich.
1
// alle 's' maskieren, außer oberstes
2
3
int16_tvalue=((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!
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.
T. H. wrote:
> ¿Comprende! ;^)>>> Ich würde es etwas anders machen, aber ähnlich.>>
1
>// alle 's' maskieren, außer oberstes
2
>
3
>int16_tvalue=((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)
> 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.
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 :-)
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
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
@ 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
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.
@ 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
@ 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
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