Forum: Mikrocontroller und Digitale Elektronik Nachkommawert auf Display ausgeben (in C)


von Matze (Gast)


Lesenswert?

Guten Tag,

ich würde mir gerne einen Nachkommawert berechnen. Ich denke ich habe 
eine Lösung, diese ist aber ziemlich aufwendig. Vielleich kann mir 
jemand bei einer eleganteren Lösung behilflich sein.
Es geht um folgendes. Ich lese über den I2C Bus die Temperatur mittels 
ds1631 in eine 2 Byte int Variable aus. Eingestellt habe ich eine 12 Bit 
Auflösung, also eine 0.0625°C. Der Nachkommawert befindet sich in den 
vier höchstwertigen Bits des Lowbytes. Diese Maskiere ich und weise dann 
den entsprechenden Wert zu. Ich muss bzw. will ohne float Variable 
auskommen.
1
//*********************************************************************
2
// v_ausgabe_nachkommawert
3
//
4
//
5
// Übergabeparameter:
6
// char zeile:      Zeile auf LCD Display (0-3)
7
// char spalte:      Splate auf LCD Display (0-19)
8
// unsigned int wert:  Höchstwerigen 4 Bit von Lowbyte werden nacheinander maskiert. Nachkommawerte werden zugewiesen.
9
//            Bit 7  Maske: 0x0080 -> 0.5°C
10
//            Bit 6  Maske: 0x0080 -> 0.25°C
11
//            Bit 5  Maske: 0x0080 -> 0.125°C
12
//            Bit 4  Maske: 0x0080 -> 0.0625°C   
13
//
14
//
15
//*********************************************************************
16
17
void v_ausgabe_nachkommawert (char zeile, char spalte, unsigned int wert, char stellen)
18
{
19
unsigned char uc_zehntel, uc_hundertstel, uc_tausendstel, uc_zehntausendstel;
20
21
  
22
  if (li_Temperatur & 0x0080)
23
    lui_Nachkommawert += 5000;
24
  if (li_Temperatur & 0x0040)
25
    lui_Nachkommawert += 2500;
26
  if (li_Temperatur & 0x0020)
27
    lui_Nachkommawert += 1250;
28
  if (li_Temperatur & 0x0010)
29
    lui_Nachkommawert +=  625;
30
31
32
33
  uc_zehntel       = (wert/1000)%10;
34
  uc_hundertstel    = (wert/100)%10;
35
  uc_tausendstel    = (wert/10)%10;
36
  uc_zehntausendstel  = (wert/1)%10;
37
  
38
  moveto    (zeile, spalte);
39
  
40
  printchar (KOMMA);
41
  
42
  if (stellen == 4)
43
  {  
44
    printchar (uc_zehntel + ASCII);
45
    printchar (uc_hundertstel + ASCII);
46
    printchar (uc_tausendstel + ASCII);
47
    printchar (uc_zehntausendstel + ASCII);  
48
  }
49
  
50
  if (stellen == 3)
51
  {
52
    printchar (uc_zehntel + ASCII);
53
    printchar (uc_hundertstel + ASCII);
54
    printchar (uc_tausendstel + ASCII);
55
  }
56
  
57
  if (stellen == 2)
58
  {
59
    printchar (uc_zehntel + ASCII);
60
    printchar (uc_hundertstel + ASCII);
61
  }
62
  
63
  if (stellen == 1)
64
  {
65
    printchar (uc_zehntel + ASCII);
66
  }
67
}

Bin für jeden Tip dankbar.

Grüße
Matze

von Joachim B. (jar)


Lesenswert?

sprintf oder Ganzzahl rechnen und per Stringoperation ein Komma 
einsetzen!

von Rene S. (Firma: BfEHS) (rschube)


Lesenswert?

Mit ganzen Zahlen rechnen und einfach das Komma verschieben.
0.0625°C = 625

uint16_t => 2^16 = 65536
uint32_t => 2^32 = 4294967296

von Falk B. (falk)


Lesenswert?


von Joe F. (easylife)


Lesenswert?

"The DS1631 and DS1631A thermometer accuracy is ±0.5°C from 0°C to 
+70°C"

Da macht eine Darstellung mit 4 Nachkommastellen wohl kaum Sinn...
Es reicht wohl aus Bit 8 zu testen, wenn 1 -> ".5" anhängen, wenn 0 -> 
".0" anhängen.

Nachtrag:
Negative Zahlen müssen dann eh nochmal gesondert behandelt werden.
Am besten erstmal Vorzeichen ausgeben (in Abhängigkeit von Bit 16), und 
negative Temperaturen für die weitere Ausgabe in positive Zahl 
umwandeln.
Dann stimmt auch die Nachkommastelle.

: Bearbeitet durch User
von posti (Gast)


Lesenswert?

Hi

Negative Zahlen werden glaub per 2er Komplement gebildet (also 
entweder $FF-x oder $00-x .. Eines davon passt).
(wenn's halbwegs zum DS18B20 kompatibel ist)

Dann hast Du den Zahlenwert wieder als positive Zahl, musst Dir halt 
merken, daß da nen Minus vor gehört (oder sogar bei der Umwandlung schon 
ausgeben).

Die Nachkommastellen nerechne ich mit den vollen 4 Bit (also 12bitig) 
und rechne die Komma-Werte zusammen.
0b0001 = 0625
0b0010 = 1250
0b0100 = 2500
0b1000 = 5000
=============
max      9375

Diese Zahl teile ich durch 100, ergibt maximal 93 (den Rest verwerfe 
ich)
Jetzt teile ich diese max 93 durch 10.
Wenn der Rest >=5 ist, wird das Ergebnis um 1 erhöht, sonst nicht.

Fertig ist die gerundete 1/10tel-Grad-Stelle

Ähnlich berechne ich auch die Ziffern der Grade, immer :10 und den Rest 
ausgeben.
Ist's eine Null, nur ausgeben, wenn's die Einer-Stelle ist.
Ziffer >0  und zuvor 0er ausgelassen? Dann jetzt die fehlenden Nuller 
ausgeben und die aktuelle Ziffer.
So unterdrückst Du gleich noch anführende Nullen.

Die Rumrechnerei mit nahezu beliebig vielen Bit ist, schriftlich, kein 
Hexenwerk und dürfte vom Speicherverbrauch einbindbare Bibliotheken um 
Längen schlagen. VOn der Ausführungsgeschwindigkeit müsste man schauen, 
was die Bibliothek braucht. Die Assembler-Routine ist da recht genügsam.
40bit : 8bit in 40Runden (bei der ersten Routine 26990 Takte, 1MHz ~27ms 
für die Umwandlung einer 40bit Zahl in Dezimal).

MfG

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Man kann natürlich auch das ganze Gefummel sein lassen, mit
Gleitkommazahlen rechnen, und sich anschließend wundern, dass der
Code 1) deutlich einfacher aussieht damit und 2) trotzdem noch in
den Flash passt. ;-)

Auch, wenn der Chip die Genauigkeit nicht bringt, könnte zumindest
eine komplette erste Nachkommastelle (also nicht nur ".0" oder ".5")
Sinn haben, falls man damit eine Temperaturtendenz erkennen möchte.

von Peter II (Gast)


Lesenswert?

Joe F. schrieb:
> Da macht eine Darstellung mit 4 Nachkommastellen wohl kaum Sinn...

doch macht es.

Damit kann man eine Änderung ablesen, ob der wert Absolut richtig ist, 
spielt oft keine rolle.

Es ist ja nicht so das der Wert um 0,5Grad schwankt, wenn eine konstante 
Temperatur vorhanden ist,

von Nico W. (nico_w)


Lesenswert?

Wenn es nicht so genau werden muss geht es recht einfach ohne division. 
Deine vier bits um zwei nach rechts schuppsen. Mit 25 multiplizieren und 
fertig.

Pseudocode:
print(8bit), print(','), print((4bit >> 2)*25),print('\n')

Edit: ggf. halt eine Variante davon die dein Bit7 auf null prüft. Dann 
gehen auch alle 4 bits rein mit dem Faktor 125.

: Bearbeitet durch User
von Ralph S. (jjflash)


Lesenswert?

0.0625°K = 62,5°mK !!!!

60 Millikelvin sind schon sehr sehr sportlich (auch wenn hier immer 
etwas anderes behauptet wird), 2°mK ... da sieht man keinen Trend, da 
sieht man nur noch Rauschen, garantiert.

Mein bester Referenzthermometer auf Arbeit löst 0,005 °K auf bei einer 
(vom Hersteller versprochenen) Genauigkeit von +- 1 Digit.

0,0625K Auflösung sind nicht wirklich sinnvoll und schon gar nicht zum 
Erkennen eines Trendes (von der absoluten Genauigkeit rede ich nicht).

von posti (Gast)


Lesenswert?

Hi

Kelvin doch ohne Grad, oder??
Da das 12.te (letzte) Bit 0,0625K entspricht, ist eh bestenfalls das 
11.te Bit aussagekräftig, wobei eine reine Versteifung auf das dann 
'letzte Bit' auch wieder keine Ruhe in die Anzeige bringt.

Für Ruhe sorgt man, wenn man der Änderung um das letzte Bit 'hinterher 
hinkt' - wenn es weiter in die gleiche Richtung geht, kommen wir schon 
hinterher, wenn das letzte Bit aber wackelt (und ggf. dadurch Das davor 
auch), wird die Anzeige erst angepasst, wenn der Messwert 'mehr als 
einen Schritt' entfernt ist.

Gerade erst aus den Fingern gesaugt, klingt aber ganz brauchbar :)
(und ganz ohne Fließkommazahlen und 'noch nen Pfund in den Flash ... 
kommen se ruhig rein, wir verkaufen auch größere Steinchen)

MfG

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

posti schrieb:
> (und ganz ohne Fließkommazahlen und 'noch nen Pfund in den Flash ...
> kommen se ruhig rein, wir verkaufen auch größere Steinchen)

Ausprobiert oder daher gelabert?

von posti (Gast)


Lesenswert?

Hi

Nur gelabert :)
In meinem Flash sind nur handverlesene Bits und Bytes - ganz ohne 
fließende Kommas und so'n Kram.

von W.S. (Gast)


Lesenswert?

Matze schrieb:
> Ich lese über den I2C Bus die Temperatur mittels
> ds1631 in eine 2 Byte int Variable aus. Eingestellt habe ich eine 12 Bit
> Auflösung, also eine 0.0625°C. Der Nachkommawert befindet sich in den
> vier höchstwertigen Bits des Lowbytes.

Watt is ne Dampfmaschin?..

Versuche doch mal, nachzudenken. Also du hast da ein 2 Byte-Ergebnis, 
dessen LSB für dein gewünschtes Ergebnis eine Wertigkeit von 0.0625 hat.

Also ist irgendwo weiter links dein gedachter Dezimalpunkt. Klaro?

Zunächst wandelst du die Zahl ins Positive um und gibst ggf. ein 
Minuszeichen aus.

Jetzt kannst du per Schiebeoperation erstmal deinen ganzen Teil 
separieren und ausgeben. Ebenfalls klaro? (Wie man sowas macht, solltest 
du beherrschen. Nein, nicht mit printf!)

Nun wendest du dich dem Rest zu, also deinem echt gebrochenen Teil. Den 
wandelt man so um, daß man ihn zuerst mit 10 multipliziert und dann den 
dabei entstehenden ganzen Teil abtrennt (eben so, wie du das zuvor mit 
dem ganzen Teil gemacht hast). Der kann jetzt logischermaßen nur 0..9 
sein, die Umwandlung in eine Ziffer sollte also auch dir gelingen.

Diese letzte Operation, also Multiplizieren mit 10, ganzen Teil 
abtrennen und als Ziffer ausgeben, kannst du so oft machen wie du 
willst. In jeder Runde gibt's ne Ziffer. Ab wann die eine Hausnummer 
ist, entscheidest du. Bei 4 Nachkommabits würde ich die Runde nur ein 
einziges Mal drehen, alle weiteren Stellen sind Mumpitz.

Hast du das jetzt begriffen?

Mir ist das zum Kopfschütteln, mit ansehen zu müssen, mit was für 
hochtrabenden Projekten die Leute sich befassen, ohne auch nur 
ansatzweise die grundlegenden Fertigkeiten zu beherrschen.

W.S.

von Wilhelm M. (wimalopaan)


Lesenswert?

Man könnte auch einfach die fixed-point extension verwenden in C:

https://gcc.gnu.org/onlinedocs/gcc/Fixed-Point.html

Oder in C++ sich einen DT für die Festkomma-Arithmetik im Q-Format

https://en.wikipedia.org/wiki/Q_(number_format)

definieren, etwa ein Q12.4 Format im 2er-Komplement (das liefert glaube 
ich der ds1631).

von eProfi (Gast)


Lesenswert?

Beitrag "DS18S20 - extended resolution bei Temperaturen um 0°C"
Siehe ganz unten.
Code ist zwar ursprünglich für DS18S20, es ist jedoch auch eine Routine 
drin, die den 12-bit-Temperaturwert direkt ausgibt.
Wahlweise mit oder ohne Vor-Null-Unterdrückung.

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.