Hallo, bei der Ausgabe der Temperatur eines DS18b20 stehe ich vor der Problematik die Nachkommastellen auszugeben, laut diesem Beitrag: https://stackoverflow.com/questions/37815771/snprintf-not-working-as-expected-with-avr-gcc Fehlten mir: -Wl,-u,vfprintf -lprintf_flt -lm diese habe ich hier hinzugefügt: CFLAGS += -Wl,-u,vfprintf -lprintf_flt -lm (Die Makefile kommt hier aus dem Forum, minimal verändert) Hier der Code zur Ausgabe, es wird ein "?" gesendet. USART_end() sendet '\r\n'. char zeichen[15]; double myFloat2 = 2.2345; sprintf(zeichen, "%.4f", myFloat2); for (char *p = zeichen; *p; p++) { USART_transmit(*p); } USART_end(); Der rest vom Code funktioniert, ein USART_transmit('a'); sendet dieses ohne Probleme. Sind die "Linker" Optionen richtig in den CFLAGS?
Linker Flags sind normalerweise LFLAGS und nicht CFLAGS, aber ich sehe ja das Makefile nicht.
Dieses Gehampel mit der Float/Double-Ausgabe hab ich umgangen indem ich die Funktion:
1 | ...
|
2 | #include <stdlib.h> |
3 | ...
|
4 | dtostrf(float_variable, |
5 | zahl_aller_stellen_incl_komma, |
6 | zahl_der_nachkommastellen, |
7 | stringvariable); |
8 | ...
|
benutze. Vielleicht hilft das dir auch.
:
Bearbeitet durch User
neuer_benutzer55 schrieb: > bei der Ausgabe der Temperatur eines DS18b20 stehe ich vor der > Problematik die Nachkommastellen auszugeben Mit Float (single Precision) Datenformat hast du 24 Bit für den Zahlenwert. Ein DS18B20 liefert dir maximal 12 Bit Auflösung bei einer Genauigkeit von 0.5K, i.e. 8 1/2 Bit. Was versprichst du dir dabei von der Verwendung von Double?
@Dirkb CFLAGS += -Wl,-u,vfprintf und: LDFLAGS += -lprintf_flt -lm so funktioniert es leider auch nicht :( makefile: https://bpaste.net/show/WQn0 @Wolfgang Bisher habe ich diesen Code für die Ausgabe verwendet:
1 | int temp; |
2 | int temp2; |
3 | temp2 = temp / 16; |
4 | char buf[10]; |
5 | itoa(temp2, buf, 10); |
6 | |
7 | for (char *p = buf; *p; p++) { |
8 | USART_transmit(*p); |
9 | } |
10 | USART_end(); |
Warum durch 16 teilen? Weil dieses Library: https://github.com/Jacajack/avr-ds18b20/wiki den Wert mit 16 multipliziert, für eine einfachere Ausgabe weil die Werte im Register des Sensors anders liegen sonst. Durch die Integer Divsion kann natürlich auch nur ein Integer Wert angezeigt werden. Darum bin ich auf double / float gekommen. Zweiter Versuch mit double:
1 | volatile double db1; |
2 | db1 = temp / 16; |
3 | char buf[20]; |
4 | dtostrf(db1, 4, 3, buf); |
5 | for (char *p = buf; *p; p++) { |
6 | USART_transmit(*p); |
7 | } |
8 | USART_end(); |
Das liefert jetzt 20.000, 21.000 aber nie Werte dazwischen. Wenn ihr da ein besserers Library habt immer her damit.
neuer_benutzer55 schrieb: > CFLAGS += -Wl,-u,vfprintf > und: > LDFLAGS += -lprintf_flt -lm neuer_benutzer55 schrieb: > CFLAGS += -Wl,-u,vfprintf > und: > LDFLAGS += -lprintf_flt -lm > > so funktioniert es leider auch nicht :( > makefile: https://bpaste.net/show/WQn0 Zeile 121 auskommentieren hätte gereicht. Gefällt dir der Zahlenwert von sprintf nicht, oder kommt nur wirres Zeug raus?
Der avr-gcc kennt kein double. Wenn du double hin schreibst, nutzt er in Wirklichkeit float.
@Dirkb: Ahja, stimmt. 121 ist der passende Ort. Die Zahlen können von meinem Bluetooth Terminal interpretiert werden und passen auch, aber es wundert mich das ich bisher nur 20.000 oder 21.000 gesehen habe, in den Beispielen im Datenblatt gibt es auch; +25.0625. Also 4 Nachkommastellen Präzision, da müsste ja irgendwann mal ein anderer Wert gekommen sein.
neuer_benutzer55 schrieb: > db1 = temp / 16; Wenn temp ein Integer ist, dann ist das Ergebnis der Division auch ein Integer. Simple Lösung: db1 = temp / 16.0
Dirk B. schrieb: > Gefällt dir der Zahlenwert von sprintf nicht, oder kommt nur wirres Zeug > raus? ich meinte wirklich sprintf und nicht dtostrf
neuer_benutzer55 schrieb: > Darum bin ich auf double / float gekommen. Double und Float haben völlig verschiedene Auflösung. Nochmal: Warum Double und nicht Single/Float?
Wolfgang schrieb: > Nochmal: Warum Double und nicht Single/Float? da dbl nur ein Zwischenergebnis aufnimmt und printf kein float kann, ist double ok.
Wolfgang schrieb: > Double und Float haben völlig verschiedene Auflösung. Nicht bei AVR. https://gcc.gnu.org/wiki/avr-gcc#Deviations_from_the_Standard
Stefanus F. schrieb: > Wolfgang schrieb: >> Double und Float haben völlig verschiedene Auflösung. > > Nicht bei AVR. > https://gcc.gnu.org/wiki/avr-gcc#Deviations_from_the_Standard Soviel zum Thema Portierbarkeit von C ;-( Nichtmal auf die Auflösung der Zahlenformate für rationale Zahlen kann man sich verlassen.
Wolfgang schrieb: > Soviel zum Thema Portierbarkeit von C ;-( > > Nichtmal auf die Auflösung der Zahlenformate für rationale Zahlen kann > man sich verlassen. Der C-Standard gibt nur einen Mindeswertebereich für float und double vor. Und der ist identisch. Also ist es Standardkonform, wenn float und double denselben Wertebereich haben.
Dirk B. schrieb: > Also ist es Standardkonform, wenn float und double denselben > Wertebereich haben. Trotzdem ist es Käse, wenn double ≠ IEEE double precision ist, weil man dann keine portierbare Möglichkeit hat, gesichert mit /IEEE double precision/ zu rechnen.
Portierbaren Code bekommst du z.B. mit Java. Bei C liegt der Fokus eher in der Hardware-Nähe.
Wolfgang schrieb: > Trotzdem ist es Käse, wenn double ≠ IEEE double precision ist, weil > man dann keine portierbare Möglichkeit hat, gesichert mit /IEEE double > precision/ zu rechnen. IEEE Unterstützung wird auch erst seit C11 gefordert. Viele Systeme unterstützten das auch vorher. Nur sollte man bei C nicht von dem Verhalten des eigenen System auf die Allgemeinheit schließen.
Stefanus F. schrieb: > Portierbaren Code bekommst du z.B. mit Java. Java auf einem 8-Bit AVR ? (da galt die Einschränkung float == double)
Auf einem 8-Bit uC hatte ich es mal sinngemäß so gemacht (hier als PC-Version zum ausprobieren)
1 | #include <stdint.h> |
2 | #include <stdio.h> |
3 | #include <stdlib.h> |
4 | |
5 | int
|
6 | main (int argc, char *argv[]) |
7 | {
|
8 | char *bp, buf[42]; |
9 | int bc, len; |
10 | int32_t temp; |
11 | |
12 | if (argc != 2) { |
13 | printf ("usage: %s DS18B20-Datenwort\n", argv[0]); |
14 | exit (EXIT_FAILURE); |
15 | }
|
16 | temp = strtol (argv[1], NULL, 0); |
17 | |
18 | |
19 | if (temp & 2048) { |
20 | temp |= 0xfffff000UL; |
21 | }
|
22 | len = snprintf (buf, sizeof buf, "%+06d", temp * 625); |
23 | bp = buf; |
24 | for (bc = 0; bc < 9 - len; bc++) { |
25 | putchar (' '); |
26 | }
|
27 | for (bc = 0; bc < len - 4; bc++) { |
28 | putchar (*bp++); |
29 | }
|
30 | putchar ('.'); |
31 | for (bc = 0; bc < 4; bc++) { |
32 | putchar (*bp++); |
33 | }
|
34 | putchar ('\r'); |
35 | putchar ('\n'); |
36 | return 0; |
37 | }
|
@Stefanus F. /16.0 funktioniert :) Mit der Prezision, double, float, single werde ich mich Morgen auseinander setzen.
Wenn es dir nur um die Nachkommastellen, die durch die Division mit 16 entstehen, geht, kannst du das auch ohne float/double erreichen:
1 | char buf[12]; |
2 | unsigned t = abs(temp); |
3 | sprintf(buf, "%u.%03u", t/16, t%16 * 1000 / 16); |
4 | if (temp < 0) |
5 | USART_transmit('-'); |
6 | USART_transmit_string(buf); |
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.