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
Ups, hat sich überschnitten mit deinem Beitrag...
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.
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!
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.
Machst du:
1 | // ...
|
2 | |
3 | float neuerWert; |
4 | |
5 | neuerWert = Wert / 16; |
6 | |
7 | // ...
|
@ 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
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.
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_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!
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_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)
> 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.
Karl heinz Buchegger wrote:
> Du darfst nicht UNDen.
Das entscheide immernoch ich! ;^) Es ginge, aber dann nehme ich
uint16_t.
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
Hi! Sorry, wenn ich mich wiederhole: Stimmen meine Gedankengänge oder nicht? Grüsse Rick
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.