Forum: Mikrocontroller und Digitale Elektronik double Ausgabe


von neuer_benutzer55 (Gast)


Lesenswert?

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?

von Dirk B. (dirkb2)


Lesenswert?

Die -l Flags gehören zum Linkern

von Jim M. (turboj)


Lesenswert?

Linker Flags sind normalerweise LFLAGS und nicht CFLAGS, aber ich sehe 
ja das Makefile nicht.

von M. K. (sylaina)


Lesenswert?

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
von Wolfgang (Gast)


Lesenswert?

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?

von neuer_benutzer55 (Gast)


Lesenswert?

@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.

von Dirk B. (dirkb2)


Lesenswert?

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?

von Stefan F. (Gast)


Lesenswert?

Der avr-gcc kennt kein double. Wenn du double hin schreibst, nutzt er in 
Wirklichkeit float.

von neuer_benutzer55 (Gast)


Lesenswert?

@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.

von Stefan F. (Gast)


Lesenswert?

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

von Dirk B. (dirkb2)


Lesenswert?

Dirk B. schrieb:
> Gefällt dir der Zahlenwert von sprintf nicht, oder kommt nur wirres Zeug
> raus?

ich meinte wirklich sprintf und nicht dtostrf

von Wolfgang (Gast)


Lesenswert?

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?

von Dirk B. (dirkb2)


Lesenswert?

Wolfgang schrieb:
> Double und Float haben völlig verschiedene Auflösung.

nicht immer

von Dirk B. (dirkb2)


Lesenswert?

Wolfgang schrieb:
> Nochmal: Warum Double und nicht Single/Float?

da dbl nur ein Zwischenergebnis aufnimmt und printf kein float kann, ist 
double ok.

von Stefan F. (Gast)


Lesenswert?

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

von Wolfgang (Gast)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

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.

von Wolfgang (Gast)


Lesenswert?

Dirk B. schrieb:
> Also ist es Standardkonform, wenn float und double denselben
> Wertebereich haben.

Trotzdem ist es Käse, wenn doubleIEEE double precision ist, weil 
man dann keine portierbare Möglichkeit hat, gesichert mit /IEEE double 
precision/ zu rechnen.

von Stefan F. (Gast)


Lesenswert?

Portierbaren Code bekommst du z.B. mit Java. Bei C liegt der Fokus eher 
in der Hardware-Nähe.

von Dirk B. (dirkb2)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

Stefanus F. schrieb:
> Portierbaren Code bekommst du z.B. mit Java.

Java auf einem 8-Bit AVR ?

(da galt die Einschränkung float == double)

von Bauform B. (bauformb)


Lesenswert?

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
}

von neuer_benutzer55 (Gast)


Lesenswert?

@Stefanus F.

/16.0 funktioniert :)


Mit der Prezision, double, float, single werde ich mich Morgen 
auseinander setzen.

von foobar (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.