Forum: Mikrocontroller und Digitale Elektronik byte zu Zahl in C


von Holger W. (mb_holger)


Lesenswert?

Hallo
Ich brauche einen Denkanstoß. Ich komme bei dem DS18B20 Sensor nicht 
weiter.
Ich habe einen funktionierenden Code in C für den DS1820. Jetzt mein 
Problem
Ich möchte den Code für den DS 1820 an den DS18B20 anpassen und komme 
mit den zwei Byte nicht weiter. Der DS 19B20 gibt die Temperatur und das 
Vorzeichen in 2 Byte aus.

". The temperature data is stored as a 16-bit sign-extended two’s 
complement number in the temperature register The sign bits (S) indicate 
if the temperature is positive or negative: for positive numbers S = 0 
and for negative numbers S = 1. If the DS18B20 is configured for 12-bit
resolution."

Ich habe  dann im Programm die zwei Byte ausgelesen.
und einzeln vorliegen. LS BYTE bit 0 bis 7 und MS BYTE bit 8 bis 15.

bit 11 bis 15 gibt das Vorzeichen aus und bit 0 bis 10 den 
Temperaturwert.

Wie kann ich aus den bit 0 bis 10 verteilt in zwei byte den Wert reelle 
Zahl in einer Variablen  erzeugen und aus den bit 10 bis 15 das 
Vorzeichen ( bit 10 bis 15 ist 0 bei positiv und 1 bei negativ erzeugen.

Ist wahrscheinlich simpel aber ich komme nicht weiter mit den 2 
einzelnen byte und den bits für das Vorzeichen. Viel habe ich noch nicht 
programmiert.
vielen Dank

von Oliver S. (oliverso)


Lesenswert?

Zweierkomplement ist die ganz normale Darstellung von Integern. Du musst 
die beiden Bytes also einfach wieder hintereinander basteln, und hast 
dann einen signed short.

Oliver

von H.Joachim S. (crazyhorse)


Lesenswert?

Hm, du brauchst doch nur die 2 Byte in einen int zu packen und fertig. 
Oder verstehe ich die Frage falsch?

von Codix (Gast)


Lesenswert?

So:
1
***SNIPPET*****
2
3
    if( id[0] == 0x28 || id[0] == 0x10 ){  // temperature sensor
4
      w1_byte_wr( READ );      // read command
5
      temp = w1_byte_rd();      // low byte
6
      temp |= (uint)w1_byte_rd() << 8;    // high byte
7
      if( id[0] == 0x10 )      // 9 -> 12 bit
8
        temp <<= 3;
9
   }
10
  else
11
      return(0);
12
return(temp >> 4); // Divided by 16

von Holger W. (mb_holger)


Lesenswert?

Danke für die Antworten.

Ich habe in dem einen Byte die Vorzeichen bits 11 bis 15, die muss ich 
irgend wie abtrennen sonst erhalte ich falsche werte  und dann die 
restlichen Bits zu dem anderen byte dazutun oder wie ?  Der Wert hat nur 
die 11 bits.

 Grüße Holger

von Christian J. (Gast)


Lesenswert?

Sensordaten sind in data[0] und [1]
1
  /* First two bytes of scratchpad are temperature values */
2
  temperature = data[0] | (data[1] << 8);
3
4
  /* Reset line */
5
  OneWire_Reset(OneWire);
6
  
7
  /* Check if temperature is negative */
8
  if (temperature & 0x8000) {
9
    /* Two's complement, temperature is negative */
10
    temperature = ~temperature + 1;
11
  }
12
}

von Oliver S. (oliverso)


Lesenswert?

Holger W. schrieb:
> Ich habe in dem einen Byte die Vorzeichen bits 11 bis 15, die muss ich
> irgend wie abtrennen sonst erhalte ich falsche werte

Sagt wer?

Schau dir die Definition des Zweierkomplements an.

Oliver

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Ich empfehle dem TO zunächst den Kerninghan & Ritchie in der Ausgabe von 
1980,
wo das Zweierkomplement erklärt ist.

>>Ist wahrscheinlich simpel

Ja, ist es, ein Zweizeiler.

Und nebenbei eine wahnsinnig gute Seite, die es lohnt abgespeichert zu
werden:

https://graphics.stanford.edu/~seander/bithacks.html

Damit ist man geholfen!

von Holger W. (mb_holger)


Lesenswert?

Danke erst mal.
Die zwei oben genannten Beispiele verstehe ich noch nicht. Ich werde 
mich  mit dem Zweierkomplement erst mal schlau machen.

Wie gesagt die zwei byts habe ich als  uint8_t  scratchpad[0] und
scratchpad[1] Ich muß aus den beiden nur noch eine positive oder 
negative reelle Zahl erzeugen.



für das zusammenbasteln  der zwei byts habe ich dass gefunden:

uint16_t u16;
uint8_t u8_low;
uint8_t u8_high;

u16 = u8_high * 256 + u8_low;


------ oder

uint16_t make_u16(uint8_t high_byte, uint8_t low_byte)
{
  return ( ((uint16_t)high_byte)<<8 | low_byte)
}


bei den positiven Werten wird das funktionieren? bei den negativen werde 
ich sehen was passiert und mit den Nachkommastellen muß dann mal sehen.
die bits 0 bis 3 sind die Kommastellen.



Grüße Holger

von H.Joachim S. (crazyhorse)


Lesenswert?

nein, bei negativen wirds natürlich nicht funktionieren.
uint16 ist der falsche Datentyp.

von Sascha (Gast)


Lesenswert?

u16 = u8_high * 256 + u8_low;

ist übrigens das gleiche wie

u16 = (u8_high << 8) + u8_low;

Wurde hier auch schon ein paarmal in diversen Codeschnipseln gezeigt.

Bitshifting muss man bei µC Programmierung halt können, da machste nix.

von Wolfgang A. (Gast)


Lesenswert?

H.Joachim S. schrieb:
> Hm, du brauchst doch nur die 2 Byte in einen int zu packen und fertig.

Aber nur, wenn auf dem Compiler ein int Integer genau zwei Byte lang 
ist. Besser wäre wohl int16_t.

von H.Joachim S. (crazyhorse)


Lesenswert?

Stimmt.

von Holger W. (mb_holger)


Lesenswert?

ja klar uint16_t geht bei 0 los, ich muss int16_t nehmen. Ich werde es 
morgen testen, auch die anderen Vorschläge, währe ja fast zu einfach, 
mal sehen.
Grüße Holger

von Holger W. (mb_holger)


Lesenswert?

Codix schrieb:
> ***SNIPPET*****
>
>     if( id[0] == 0x28 || id[0] == 0x10 ){  // temperature sensor
>       w1_byte_wr( READ );      // read command
>       temp = w1_byte_rd();      // low byte
>       temp |= (uint)w1_byte_rd() << 8;    // high byte
>       if( id[0] == 0x10 )      // 9 -> 12 bit
>         temp <<= 3;
>    }
>   else
>       return(0);
> return(temp >> 4); // Divided by 16

Hallo, mit diesen Code geht es. Danke

noch eine Frage warum am Ende noch durch 16 teilen? Im Datenblatt steht 
das nicht.

von Zeno (Gast)


Lesenswert?

Holger W. schrieb:
> Ich brauche einen Denkanstoß

Ich meine Du hast eine 1-Wire Implementierung realisiert. Ich habe so 
was gemacht, was mit allen !-Wire-Devices von Dallas funktioniert, auch 
mit dem von angesprochenen DS18B20. Wenn Du daran Interesse hast und 
keine kommerziellen Ansätze verfolgst könnten wir ins Geschäft kommen.

Gruß zeno

von Wolfgang A. (Gast)


Lesenswert?

Holger W. schrieb:
> noch eine Frage warum am Ende noch durch 16 teilen? Im Datenblatt steht
> das nicht.

1. Damit "***SNIPPET*****" die Temperatur in Einheiten von °C zurück 
gibt.
2. Datenblatt S.3f

von Holger W. (mb_holger)


Lesenswert?

Hallo, hier ein Auszug aus dem Datenblatt:

The DS18B20 output temperature data is calibrated in
degrees Celsius; for Fahrenheit applications, a lookup
table or conversion routine must be used. The tempera-
ture data is stored as a 16-bit sign-extended two’s comple-
ment number in the temperature register (see Figure 4).


Codix schrieb:
> ***SNIPPET*****
>
>     if( id[0] == 0x28 || id[0] == 0x10 ){  // temperature sensor
>       w1_byte_wr( READ );      // read command
>       temp = w1_byte_rd();      // low byte
>       temp |= (uint)w1_byte_rd() << 8;    // high byte
>       if( id[0] == 0x10 )      // 9 -> 12 bit
>         temp <<= 3;
>    }
>   else
>       return(0);
> return(temp >> 4); // Divided by 16


Die richtige Temperatur wird jetzt bei mir angezeigt aber diesen
Codeteil habe ich noch nicht verstanden. Ich habe mir  erstmal ein Buch 
für c bestellt.

trotzdem noch meine Frage warum am Ende  durch 16 teilen? Im Datenblatt 
steht das nicht und vielleicht kann mir jemand kurz diesen Codeteil 
erkären bis das Buch da ist.

Grüße Holger

von H.Joachim S. (crazyhorse)


Angehängte Dateien:

Lesenswert?

ohne Worte :-)

von Georg (Gast)


Lesenswert?

H.Joachim S. schrieb:
> ohne Worte :-)

mit Worte: die Ausgabe des Sensors ist eine 16bit-Integer-Zahl mit 
Vorzeichen, aber die Einheit ist 1/16 Grad! 4 bit Rechtsschieben 
schmeisst die Grad-Bruchteile einfach weg, wenn du die aber wissen 
willst (wahrscheinlich ist der Sensor aber nicht so genau), musst du 
statt Schieben echt dividieren, und das Ergebnis muss dann natürlich vom 
Typ real sein.

Georg

von Cyblord -. (cyblord)


Lesenswert?

Georg schrieb:
> H.Joachim S. schrieb:
>> ohne Worte :-)
>
> mit Worte: die Ausgabe des Sensors ist eine 16bit-Integer-Zahl mit
> Vorzeichen, aber die Einheit ist 1/16 Grad! 4 bit Rechtsschieben
> schmeisst die Grad-Bruchteile einfach weg, wenn du die aber wissen
> willst (wahrscheinlich ist der Sensor aber nicht so genau), musst du
> statt Schieben echt dividieren, und das Ergebnis muss dann natürlich vom
> Typ real sein.
>
> Georg

So macht man das aber nicht auf kleinen Controllern. Man dividiert 
ganzzahlig (schieben) durch 16 und den Bruchteil erhält man durch eine 
Modulo 16 Operation. Diesen kann man mit ABS vom Vorzeichen befreien und 
dann kann man beides zusammen z.B. auf einem LCD anzeigen. Textlich muss 
noch ein Komma dazwischen.

von Georg (Gast)


Lesenswert?

Cyblord -. schrieb:
> So macht man das aber nicht auf kleinen Controllern

Da hast du im Sinn der Effektivität sicher recht, aber das dürfte den TO 
doch noch überfordern - schliesslich fängt er gerade erst an sich mit 
den Grundlagen von C zu beschäftigen. Und von Assembler hat er sicher 
höchstens mal ganz entfernt was gehört.

Georg

von spess53 (Gast)


Lesenswert?

Hi

> aber das dürfte den TO doch noch überfordern -

Und da fängt man mit 1-Wire an?

>schliesslich fängt er gerade erst an sich mit
>den Grundlagen von C zu beschäftigen.

Dann sollte er auch mit den Grundlagen anfangen.

MfG spess

von Eric B. (beric)


Lesenswert?

Georg schrieb:
> mit Worte: die Ausgabe des Sensors ist eine 16bit-Integer-Zahl mit
> Vorzeichen, aber die Einheit ist 1/16 Grad! 4 bit Rechtsschieben
> schmeisst die Grad-Bruchteile einfach weg, wenn du die aber wissen
> willst (wahrscheinlich ist der Sensor aber nicht so genau), musst du
> statt Schieben echt dividieren, und das Ergebnis muss dann natürlich vom
> Typ real sein.

Ein bisschen bessere Resultate soll mann dann schon bekommen mit
1
 return((temp + 8) >> 4); // Divided by 16
Dann wird aus 0,96°C wenigstens 1°C statt 0°C :-)

von Holger W. (mb_holger)


Lesenswert?

Ja, dass mit dem Teilen durch 16 habe ich verstanden. Integerzahlen in 
Binärschreibweise haben keine Dezimalstellen. Diese werden hier mit der 
Teilung durch 16 erzeugt. Teilung durch 16 weil die letzen 4 bit die 
Dezimalstellen darstellen und  4 bit  1/16 darstellt. Also ist jede 
Werterhöhung um 1 in dieser Integerzahl nur ein 1/16 Grad. Im Falle des 
Schiebens (4 bit Rechtsschieben) und wegschmeißen wird nicht mehr 
dividiert, dann ist die Integerzahl die Gradzahl.

Wenn mir jemand noch etwas zu dieser Zeile sagen kann,habe ich alles 
soweit verstanden

if( id[0] == 0x10 )      // 9 -> 12 bit
temp <<= 3;

Ist das etwas mit der Anzahl der Dezimalstellen ? Wird hier gerundet?
Weil ich in der Ausgabe nur die erste Dezimalstelle erhalte, die andern 
sind Null bei der Teilung durch 16.


vielen Dank Holger

von Holger W. (mb_holger)


Lesenswert?

Bitte sagt mir noch etwas kurz zu dieser Zeile:
Wass macht diese:


if( id[0] == 0x10 )      // 9 -> 12 bit
temp <<= 3;

Grüße Holger

von Holger W. (mb_holger)


Lesenswert?

bitte bitte
Holger

von Holger W. (mb_holger)


Lesenswert?

Ich will nicht nerven, aber bitte redet mit mir. Mein neues Buch kommt 
erst morgen.
Holger

von Oliver S. (oliverso)


Lesenswert?

RTFM

Des Sensors...

Oliver

von Ralf G. (ralg)


Lesenswert?


von Hans M. (Gast)


Lesenswert?

Da wird nur geschaut, welcher Sensortype ( 9 oder 12 Bit, langsam oder 
langsammer ;-) ) es ist.
Steht aber auch im Datenblatt.


Hans

von Murmelchen (Gast)


Lesenswert?

Cyblord -. schrieb:

> So macht man das aber nicht auf kleinen Controllern. Man dividiert
> ganzzahlig (schieben) durch 16 und den Bruchteil erhält man durch eine
> Modulo 16 Operation. Diesen kann man mit ABS vom Vorzeichen befreien und
> dann kann man beides zusammen z.B. auf einem LCD anzeigen. Textlich muss
> noch ein Komma dazwischen.

Modulo 16 liefert Werte von 0 bis 15.

Wie man damit nur durch Integer-Arithmetik entweder auf die 
vierstelligen oder sinnvollerweise auf eine Stelle nach dem Komma 
gerundeten Werte kommen kann, erschließt sich mir jetzt auf Anhieb aber 
noch nicht.

0x07d0 -> 125,0000 °C -> 125,0 °C
  ..          ..           ..
0x000f ->   0,9375 °C ->   0,9 °C
  ..         ..             ..
0x0003 ->   0,1875 °c ->   0,2 °C
0x0002 ->   0,1250 °C ->   0,1 °C
0x0001 ->   0,0625 °C ->   0,1 °C
0x0000 ->   0,0000 °C ->   0,0 °C
0xffff ->  -0,0625 °C ->  -0,1 °C
  ..          ..            ..
0xfc90 -> -55,0000 °C -> -55,0 °C


int16_t temp;
...
printf("%.4f °C", temp / 16.0); bzw.

printf("%.1f °C", temp / 16.0);

liefert hingegen das gewünschte Ergebnis auch ohne Verrenkungen, bläht 
die Sache aber natürlich auf 8-Bit-Rechnern auch deutlich auf.


Mit besten Grüßen

Murmelchen

von Holger W. (mb_holger)


Lesenswert?

also mit eurer Hilfe funktioniert alles so wie es soll und ich habe auch 
alles so weit verstanden.


Jetzt sind auch die DS 1820 angekommen. Ich werde mal die Tolleranz zu 
den DS 18B20 testen. Ich kann ja jetzt beide verwenden. Danke noch mal.

Holger Weiß

von Holger W. (mb_holger)


Lesenswert?

zum Vergleich der Sensoren

DS 18B20   16,1 grad
DS 1820    17,2 grad
DS 1820    17,0 grad

Die beiden 1820 sind gleich aber der 18B20 weicht um ein grad ab !!!
Mir ist noch aufgefallen, dass nur die 1820 nach dem anschließen um 0,5 
grad ansteigen. Ist dass eine eigene Erwärmung nach den anschließen 
(Verlustleistung)??
Beim 1820 ist das nicht der Fall, der ist einer Edelstahlhülse. 
Vielleicht verhindert die Edelstahlhülle die eigene Erwärmung und leitet 
die Verlustleistung ab? , dann wäre die Differenz noch im Rahmen.


Ich habe den ganzen Aufwand nur wegen dem Abgleich für ein älteres 
Projekt mit analogen Sensoren gemacht und jetzt habe ich wieder keine 
exakte Referenztemperatur. Hat jemand einen Vorschlag ? Ein grad 
Abweichung ist mir zu hoch.

Gibt es bessere digitale Sensoren für 1-Wire?

Grüße Holger

von Murmelchen (Gast)


Lesenswert?

Holger W. schrieb:
> Die beiden 1820 sind gleich aber der 18B20 weicht um ein grad ab !!!

Vorab, die gelieferten Werte der Sensoren können durchaus um 1 Grad 
voneinander abweichen. Zudem sind die 1820 vermutlich auch schon älter 
und da gab es zumindest Gerüchte, dass dann auch noch größere 
Abweichungen möglich sein sollen, das heißt, dass die Sensoren dann auch 
möglicherweise durch Aufnahme von Feuchtigkeit verursacht auch Werte 
außerhalb ihrer Spezifikation liefern können.

Aber stimmen deine Berechnungen denn auch wirklich? Auch das ist ja 
aufgrund der vorherigen Beiträge auch nicht völlig auszuschließen. Und 
es geistern auch recht viele Quelltexte durchs Internet, bei denen die 
Berechnung zumindest unsauber oder sogar schlicht falsch programmiert 
wurde.

Mach Dir doch mal die Mühe, anhand der vom Sensor gelieferten Bits die 
jeweiligen Temperaturen von Hand zu berechnen und vergleiche die 
Ergebnisse dann mit denen durch die C-Routinen angezeigten Werte.


Murmelchen

von grundschüler (Gast)


Lesenswert?

Holger W. schrieb:
> Gibt es bessere digitale Sensoren für 1-Wire?

ich habe diverse ds1820 im Einsatz. Diese sind im Bereich 15-35° 
mindestens auf 0,2° genau, eher noch besser. Z.B. habe ich 
ds1820-Sensoren neben den PT1000-Sensoren eines geeichten 
Wärmemengenzählers sitzen. Ein signifikantes Abweichen der gemessenen 
Werte im Bereich 15-35° war nicht feststellbar.

von Klaus (Gast)


Lesenswert?

@ TO

Dieses abschliessende abschliessende Schieben um 4 Bit nach rechts (was 
als Division kommentiert ist) hat auch zur Folge, dass ein 
Rundungsfehler entsteht. Die Nachkommastellen der Festkommazahl werden 
nämlich damit einfach ignoriert. Das Wort Division suggeriert zumindest 
die Möglichkeit, dass damit auch eine Rundung geschehen könnte. Falls Du 
das gedacht hast: Das ist nicht so.

Es wären zwei interessante Übungen, Einmal zu prüfen ob eine Rundung 
nötig ist, und die zu berücksichtigen und zum Zweiten, die 
Nachkommastellen tatsächlich auszugeben.

Damit solltest Du auch höher aufgelöste Werte angezeigt bekommen. Ob der 
Sensor selbst so genau ist, wie gewünscht kann ich allerdings nicht 
sagen. Aber das siehst Du dann, weil Du die tatsächlichen Werte siehst 
und nichts falsch gerundetes oder mit weniger Stellen als möglich sind.

von Walter S. (avatar)


Lesenswert?

Cyblord -. schrieb:
> Man dividiert
> ganzzahlig (schieben) durch 16 und den Bruchteil erhält man durch eine
> Modulo 16 Operation.

dazu muss man wissen dass die "Modulo-Operation" in C keine 
Modulooperation im mathematischen Sinn ist, sondern eher eine 
"Divisionrest-Operation"

von Murmelchen (Gast)


Lesenswert?

Walter S. schrieb:
> dazu muss man wissen dass die "Modulo-Operation" in C keine
> Modulooperation im mathematischen Sinn ist, sondern eher eine
> "Divisionrest-Operation"

Dann zeigt doch mal eine (zumindest halbwegs elegante) Lösung, die für 
den hier behandelten Fall nur durch Integer-Arithmetik für alle Werte 
das vollständige oder das auf eine Stelle nach dem Komma korrekt 
gerundete Ergebnis liefert.


Murmelchen

von Bernd K. (prof7bit)


Lesenswert?

Cyblord -. schrieb:
> So macht man das aber nicht auf kleinen Controllern.

Doch, genauso macht man das.

von Cyblord -. (cyblord)


Lesenswert?

Walter S. schrieb:
> Cyblord -. schrieb:
>> Man dividiert
>> ganzzahlig (schieben) durch 16 und den Bruchteil erhält man durch eine
>> Modulo 16 Operation.
>
> dazu muss man wissen dass die "Modulo-Operation" in C keine
> Modulooperation im mathematischen Sinn ist, sondern eher eine
> "Divisionrest-Operation"

Wo ist denn nun, mathematisch, der Unterschied zwischen einer 
"Divisionrest-Operation" und einer Modulo-Operation? Ich hätte das jetzt 
Synonym gesehen.

von Cyblord -. (cyblord)


Lesenswert?

Bernd K. schrieb:
> Cyblord -. schrieb:
>> So macht man das aber nicht auf kleinen Controllern.
>
> Doch, genauso macht man das.

Da wolltest auch mal was sagen?

von H.Joachim S. (crazyhorse)


Lesenswert?

printf und float auf kleinem Controller - habe es gerade mal für einen 
AVR compiliert: +3200 byte. Also für kleine Controller ist das eher 
nichts.

von Cyblord -. (cyblord)


Lesenswert?

H.Joachim S. schrieb:
> printf und float auf kleinem Controller - habe es gerade mal für einen
> AVR compiliert: +3200 byte. Also für kleine Controller ist das eher
> nichts.

Ja aber Bernd K. sagt das macht man so ;-)

Du hast natürlich recht, darum Festkomma und für die Anzeige als Text 
entsprechend aufbereiten. Das ist bei den DS18B20 tausendfach bewährt. 
Man braucht kein float und auch kein sprintf.

von Bernd K. (prof7bit)


Lesenswert?

Cyblord -. schrieb:
> a aber Bernd K. sagt das macht man so ;-)

Das habe ich nicht gesagt.

von Walter S. (avatar)


Lesenswert?

Cyblord -. schrieb:
> Wo ist denn nun, mathematisch, der Unterschied zwischen einer
> "Divisionrest-Operation" und einer Modulo-Operation? Ich hätte das jetzt
> Synonym gesehen.

siehe Wikipedia:
Steht in einer Sprache wie C(++) oder Java nur die symmetrische Variante 
zur Verfügung, kann man Ergebnisse nach der mathematischen Variante 
erhalten mit:

    a mod b = ( a % b + b) % b

wobei % der symmetrischen Modulooperation entspricht und mod der 
mathematischen.

von H.Joachim S. (crazyhorse)


Lesenswert?

Und das ist tatsächlich was anderes?
Zum Rest addiert man nochmal den Teiler und teilt dann wieder durch den?
Auf den ersten Blick sieht das identisch aus - aber manchmal wundert man 
sich ja.

von Hans (Gast)


Lesenswert?

Es macht bei negativen Zahlen einen Unterschied.

von Walter S. (avatar)


Lesenswert?

Hans schrieb:
> Es macht bei negativen Zahlen einen Unterschied.

ganz genau,
und jetzt frag ich mich welche Null mir für meinen Post eine -1 gibt,
wäre interessant wenn die Bewertungen nicht anonym wären

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.