Forum: Mikrocontroller und Digitale Elektronik Double Variable liefert über Sprintf->Uart keine Dezimalzahl


von Heinz M. (subi)


Lesenswert?

Hallo,

ich versuche einen Temperaturwert über die UART ausgeben zu lassen. 
Dabei kommt aber nur Datenmüll raus.
1
    double Temperatur=24.764;
2
    char out[20];
3
    sprintf(out,"Temperatur: %4.2f",Temperatur);
4
    length=20;
5
6
    HAL_UART_Transmit(&huart1,(uint8_t*)out,length,1000);
7
    HAL_UART_Transmit(&huart1,(uint8_t*)"\n",2,1000);
8
9
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1);
10
    HAL_Delay(100);

liefert auf der Console:
00 54 65 6D 70 65 72 61 74 75 72 3A 20 00 00 00 00 B7 36 00 09 0A
   54 65 6D 70 65 72 61 74 75 72 3A 20 ist korrekt "Temperatur: " Aber 
der Rest sind Steuer und Sonderzeichen.

Also Uart und Text funktioniert. Aber die Zahl funktioniert nicht. Im 
Internet konnte ich keine Lösung oder Alternative für STM32 finden. Bei 
Atmel habe ich "dtostrf" benutzt, was hier aber nicht funktioniert.

Programmiert wird mit SW4STM32.

von Rolf M. (rmagnus)


Lesenswert?

Nutzt du denn eine Version von printf, die Gleitkommazahlen unterstützt?

von evtl (Gast)


Lesenswert?

evtl. liegt es daran
float --> %4.2f
double -> %4.2lf

von Rolf M. (rmagnus)


Lesenswert?

evtl schrieb:
> evtl. liegt es daran
> float --> %4.2f
> double -> %4.2lf

Nein. %f ist schon double. Wenn man einen float an printf übergibt, wird 
der immer erst automatisch nach double konvertiert. Deshalb passt %f 
sowohl für float, als auch für double.

von Jim M. (turboj)


Lesenswert?

evtl schrieb:
> evtl. liegt es daran
> float --> %4.2f
> double -> %4.2lf

Was für ein Schwachsinn. Lies Dir nochmal die Manpage für printf() 
durch.

Der OP verwendet vermutlich newlib und gcc, da geht %f in *printf nur 
mit reichlich heap für malloc(). Was auf µC in der Regel nicht 
ausreichend vorhanden ist.

Im obigen Falle könnte man das Ergebis aber klassisch mit Integern 
berechnen. Die funktionieren in *printf().

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Heinz M. schrieb:
> Aber der Rest sind Steuer und Sonderzeichen.

Du gibst ja auch mehr Zeichen aus, als der String enthält. Statt "len" 
auf den konstanten Wert 20 zu setzen, solltest Du vorher die Stringlänge 
bestimmen - das geht z.B. mit strlen. Dein String ist nämlich nach dem 
auf den Doppelpunkt folgenden Leerzeichen zuende - da steht in Deinem 
Hexdump 00.

Und spätestens dann kannst Du erkennen, daß Dein sprintf Dir keine 
float-/double-Zahl ausgibt.

Um den Code kleinzuhalten, wird auf Microcontrollern oft mit reduzierten 
printf-Varianten gearbeitet, die beispielsweise keine 
float-/double-Unterstützung haben. Das ist aber in der Dokumentation des 
verwendeten Compilers beschrieben, da wird auf die unterschiedlich 
umfangreichen printf-Varianten eingegangen.

von Heinz M. (subi)


Lesenswert?

Der Hinweis mit dem Compiler war gut, weil die Konvertierung 
standardmäßig abgeschalten ist:
http://www.openstm32.org/forumthread2108

"Check your linker flags (project properties > C/C++ Build > Setings > 
Tool Settings (TAB) > MCU GCC Linker > Miscellaneous > Linker flags),
if you are using nanolib "-specs=nano.specs" you need to add "-u 
_printf_float" to enable float printf." (TarekB)

Speicherverbrauch ist nicht ohne.
Sprintf ~10KB
Floatkonvertierung ~10KB
Zum Testen reicht es und später kann ich es als uint übertragen.


Die Konstante Länge war nur zum Testen. Optimiert sieht es jetzt so aus:
1
    char out[20];
2
    sprintf(out,"Temperatur: %3.2f\n",Temp1Berechnung);
3
    length=strlen(out);
4
    HAL_UART_Transmit(&huart1,(uint8_t*)out,length,1000);

von asdfasd (Gast)


Lesenswert?

1
-    sprintf(out,"Temperatur: %3.2f\n",Temp1Berechnung);
2
-    length=strlen(out);
3
+    length = sprintf(out,"Temperatur: %.2f\n",Temp1Berechnung);

von Heinz M. (subi)


Lesenswert?

Kann man dazu auch ein paar Sätze schreiben?

Mir sagt das jetzt ich soll das Array "Out" in die Variable "length" 
packen. Wozu auch immmer.

von Rolf M. (rmagnus)


Lesenswert?

Heinz M. schrieb:
> Kann man dazu auch ein paar Sätze schreiben?

Ist es zuviel verlangt, einmal einen Blick in die Doku von sprintf zu 
werfen?

> Mir sagt das jetzt ich soll das Array "Out" in die Variable "length"
> packen. Wozu auch immmer.

Dann hast du offenbar eine (falsche) Annahme darüber getroffen, was 
sprintf zurückliefert.

von Jacko (Gast)


Lesenswert?

Boah, mit einem Tiny und etwas Festkomma liefert man
Temperaturen von (je nach Sensor) -77,77 °C bis 555,55 °C
nach Berücksichtigung der Sensor-Parameter und Ausreizung
der vorgegebenen Genauigkeit mit < 0,5 KB Code an die
SW-UART.

Und hier bekommt man das nicht mit 5...50 KB sprintf-Lib
+ 50...500 KB Math-Lib hin...

Wie wäre es denn mit einem LESE-Lernkurs?

von m.n. (Gast)


Lesenswert?

Jacko schrieb:
> Und hier bekommt man das nicht mit 5...50 KB sprintf-Lib
> + 50...500 KB Math-Lib hin...

So entstehen Märchen, die wohl vor dem bösen "Float" warnen sollen! 
Früher war es der Wolf ;-)

Ich habe hier bei einem kleinen Programm für STM32Fxxx nachgesehen, 
wieviel Code für sprintf() benötigt wird: weniger als 4 KB. Der Rest des 
Programmes inkl. Verarbeitung von ein paar double-Werten ist 5 KB groß.
Auf einem STM32 mit Festkomma zu rechnen, insbesondere für langsame 
Temperaturwerte, ist Steinzeit Codierung.

Auf einem Tiny in C mit float programmiert paßt der Code locker in einen 
ATtiny45. Auch hier ist Festkomma fehl am Platz, es sei denn, die 
verwendeten Compiler taugen nicht die Bohne.

von W.S. (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Um den Code kleinzuhalten, wird auf Microcontrollern oft mit reduzierten
> printf-Varianten gearbeitet...

Aber sowas machen eben nur Leute, die zu doof sind, irgend eine simple 
Konvertierung sich selbst zu schreiben.

Herrje, ein einziger Blick in die mittlerweile steinalte Lernbetty 
hätte genügt, um eine formatierte Float-Ausgabe zu haben, die keinen 
Heap braucht, mit herzlich wenig Flash auskommt und auf diversen 
Architekturen läuft (ARM, Fujitsu, NEC, AVR).

Gerade Ein- und Ausgabekonvertierungen für Zahlen sind Basis-Themen, die 
eigentlich jeder anständige Programmierer aus dem Ärmel schütteln können 
sollte. Was sind das bloß für Leute, die ihr Handwerkszeug so schlecht 
beherrschen?

W.S.

von Heinz M. (subi)


Lesenswert?

@Rolf Magnus: Ich habe etliche Seiten durchstöbert, bevor ich den Thread 
eröffnet habe. Programmierung soll immer selbsterklärend und logisch 
sein wurde mir immer gesagt. Die Funktion sprintf dient zur Umwandlung 
von Werten in ein String/Char Format. Dass diese Funktion die Länge 
zurückgibt ist für mich unlogisch. Und genau da ist das Problem, wenn 
man sich neu einarbeitet. Überall wird es anders gemacht und jeder meint 
seine Lösung wäre die Lösung überhaupt. Im 6. Post wird empfohlen strlen 
zu benutzen und im 8. Post den Rückgabewert zu benutzen.

@Jacko: Dann besuche bitte diesen Lesekurs. In diesem Thread geht es 
weder um die Speicherplatzgröße, noch um die Genauigkeit.

@m.n.: Eben. Der µC hat 64KB Flash, 2 Temperatur-/Feuchtesensoren, I2C 
LED Anzeige, UART und bissl GPIOs.

Über STM32CubeMX und SW4STM32 sind das 27KB. Was bringt es mir wenn von 
den 64KB 63,5KB unbenutzt sind statt 37KB? Richtig, nichts.

@W.S.: Steht da auch drin, dass die Float Konvertierung in SW4STM32 
standardmäßig abgeschalten ist? (ironische Frage)
Ich bin kein Programmierer und daher kann ich es auch nicht so einfach 
aus dem Ärmel schütteln.

von Adib (Gast)


Lesenswert?

Wer bei C printf/scanf benutzt, sollte wissen was er tut.

Diese Funktionen sind prinzipiell anfällig für alle Arten von 
Programmier-Fehlern.

Und jetzt weißt du wieder etwas mehr;.)

In diesem Sinne; schönen Sonntag.

Adib.
--

von W.S. (Gast)


Lesenswert?

Heinz M. schrieb:
> Steht da auch drin, dass die Float Konvertierung in SW4STM32
> standardmäßig abgeschalten ist? (ironische Frage)

Ähemm.. Es ist DEIN Problem, nicht meines. Und ob da irgendwo 
irgendeine Konvertierung ab- zu- um- oder sonstwie geschaltet ist, geht 
mich und meine Firmware nix an.

Also nochmal im Klartext: Wenn du denn unbedingt printf und Konsorten 
benutzen willst, dann solltest du auch die dafür benötigten 
Grundkenntnisse mitbringen. So wie Hans GuckindieLuft drauflos zu 
programmieren, ohne sein System hinreichend zu beherrschen, führt genau 
zu solchen Bauchlandungen, wie du sie im Eröffnungspost beschrieben 
hast.

Nochwas:
Gegen mangelhafte Grundkenntnisse hilft übrigens auch kein (uint8_t*)out 
- gelle? Wozu überhaupt bei einem Character-Stream aus einem char 
out[20] ein Typecast? Es sind ja doch schiere char's, die da über die 
Schnittstelle gehen sollen.

W.S.

von Yalu X. (yalu) (Moderator)


Lesenswert?

W.S. schrieb:
> Wozu überhaupt bei einem Character-Stream aus einem char
> out[20] ein Typecast? Es sind ja doch schiere char's, die da über die
> Schnittstelle gehen sollen.

Im konkreten Fall, ja. Im allgemeinen Fall übeträgt ein UART aber
einfach 8-Bit-Bytes. Ob darin Text oder ein Audio-Strem kodiert ist,
ist dem UART egal. Der korrekte Datentyp für ein 8-Bit-Byte ist aber
nicht char, sondern uint8_t. Deswegen erwartet die Funktion
HAL_UART_Transmit als zweites Argument keinen char- sondern einen
uint8_t-Pointer, und der Cast an dieser Stelle ist völlig richtig.

https://developer.mbed.org/users/EricLew/code/STM32L4xx_HAL_Driver/docs/tip/group__UART__Exported__Functions__Group2.html

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

W.S. schrieb:
> Aber sowas machen eben nur Leute, die zu doof sind, irgend eine simple
> Konvertierung sich selbst zu schreiben.

Das machen die Compilerhersteller, die unterschiedlich umfangreiche 
printf-Varianten ausliefern, wie z.B. IAR. Die sind natürlich alle zu 
doof, statt den Benutzer dazu zu erziehen, bloß kein printf zu 
verwenden.

von Christopher J. (christopher_j23)


Lesenswert?

Heinz M. schrieb:
> @Rolf Magnus: Ich habe etliche Seiten durchstöbert, bevor ich den Thread
> eröffnet habe. Programmierung soll immer selbsterklärend und logisch
> sein wurde mir immer gesagt. Die Funktion sprintf dient zur Umwandlung
> von Werten in ein String/Char Format. Dass diese Funktion die Länge
> zurückgibt ist für mich unlogisch.

Naja, die Logik ergibt sich doch aus deiner Benutzung und eine Funktion 
wie
1
size_t uebertrage_mittels_xy(uint8_t *von, size_t mit_laenge, uint8_t *nach);
ist eben in C absoluter Standard. Da ist es egal ob du jetzt in eine 
Datei, auf einen Socket oder eben deinen UART schreibst. Die Länge musst 
du immer mit angeben. Was willst du denn sonst mit deinem String machen 
außer ihn irgendwohin zu schreiben? Selbst wenn du den einfach nur mit 
memcpy in einen anderen Buffer kopieren willst musst du die Länge 
angeben:
1
void *memcpy(void *dest, const void *src, size_t n);


Jim M. schrieb:
> Was für ein Schwachsinn. Lies Dir nochmal die Manpage für printf()
> durch.

Auch wenn das nicht an dich (Heinz) adressiert war kann ich Man-Pages 
für C-Standard-Funktionen absolut empfehlen. Auch wenn die 
Implementierung zwischen glibc, newlib, musl und wie sie alle heißen 
unterschiedlich sein mag, so ist die Signatur und das Verhalten - wie 
der Name schon sagt - standardisiert. Dank Internet gibt es manpages 
heute auch für Windows-Nutzer. Eine einfache Google-Suche nach "man 
sprintf" liefert bei mir als erstes Ergebnis 
https://linux.die.net/man/3/sprintf und dort unter "Return value" steht 
dann, was sprintf und Konsorten zurückgeben.

: Bearbeitet durch User
von Heinz M. (subi)


Lesenswert?

@W.S.: Wenn dir das Problem eines Threads egal ist, dann bist du in 
einem Forum fehl am Platz.

Typecast, weil es sonst nicht geht.

@Christopher: Ja solche Man Pages suche ich mir auch gerne raus.
Wenn man damit anfängt kommt halt jede Menge auf einmal. Da kommt man 
vom 100sten ins 1000ste. Nur um dann von Leuten wie W.S. an den Kopf 
geworfen zu bekommen, dass man die Grundlagen nicht beherrscht. Ja wie 
denn auch, wenn man sie gerade lernt.

Die Kellerlüftungssteuerung funktioniert erst mal soweit. Testaufbau 
liegt auf dem Fensterbrett zum Fehler suchen.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Schau dir doch mal tinystdio an. Das finde ich im Embedded Bereich supi.

von Rolf M. (rmagnus)


Lesenswert?

Heinz M. schrieb:
> @Rolf Magnus: Ich habe etliche Seiten durchstöbert, bevor ich den Thread
> eröffnet habe. Programmierung soll immer selbsterklärend und logisch
> sein wurde mir immer gesagt.

Eine weitere wichtige Grundregel des Programmierens ist, keine Annahmen 
zu treffen. Und zumindest sollte mal gelten: "If everything else fails, 
read the manual". ;-)

> Die Funktion sprintf dient zur Umwandlung von Werten in ein String/Char
> Format. Dass diese Funktion die Länge zurückgibt ist für mich unlogisch.

Für mich nicht. Die Länge wird nachher oft gebraucht, wie in deinem 
Fall, und fällt praktisch eh an. Da finde ich es logisch und sinnvoll, 
sie auch zurückzuliefern. Den Zeiger, den man selbst reingegeben hat, 
wieder rauszugeben, hat dagegen wenig Mehrwert.
Davon abgesehen ist sprintf() ja auch nur eine Abwandlung von printf(). 
Was sollte man dort zurückgeben? Da gibt es diesen Zeiger ja gar nicht. 
Oder soll bei jeder *printf()-Funktion was anderes zurückgegeben werden? 
Das wäre dann ziemlich inkonsistent.

> Und genau da ist das Problem, wenn man sich neu einarbeitet. Überall wird
> es anders gemacht und jeder meint seine Lösung wäre die Lösung überhaupt.
> Im 6. Post wird empfohlen strlen zu benutzen und im 8. Post den
> Rückgabewert zu benutzen.

strlen() muss eben nochmal den ganzen String durchlaufen, was man sich 
komplett sparen kann, wenn man den Rückgabewert von sprintf() nutzt. 
Viele wissen nur nicht, dass die *printf-Funktionen überhaupt einen 
Returnwert haben - eben weil sie das Handbuch nicht gelesen haben.

Heinz M. schrieb:
> Typecast, weil es sonst nicht geht.

Das ist ehrlich gesagt der falsche Grund. Hier ist der Cast völlig 
richtig, aber man sollte, wenn man einen Cast benutzt, auch immer genau 
wissen, warum er nötig ist. Mit einem Cast kriegt man den Compiler fast 
immer irgendwie ruhig gestellt, aber das heißt nicht, dass dabei auch 
das richtige rauskommt.

von BummsfalleraTschingBumm (Gast)


Lesenswert?

Jacko schrieb:
> Boah, mit einem Tiny und etwas Festkomma liefert man
> Temperaturen von (je nach Sensor) -77,77 °C bis 555,55 °C
> nach Berücksichtigung der Sensor-Parameter und Ausreizung
> der vorgegebenen Genauigkeit mit < 0,5 KB Code an die
> SW-UART.
>
> Und hier bekommt man das nicht mit 5...50 KB sprintf-Lib
> + 50...500 KB Math-Lib hin...

Manche Leute implementieren halt lieber ihr eigentliches Projekt, statt 
zu versuchen, optimierte Versionen von Standard-Library-Funktionen zu 
implementieren, weil sie ein überteuertes Steinzeitrelikt mit 8Bit 
verwenden.

Außerdem ist das lang nicht so schlimm wie du behauptest. Ich habe 
mehrere Projekt auf PIC24FV32KA304 mit float und snprintf - und bin 
immer wunderbar mit den 32k Flash ausgekommen.

Noch eine Anmerkung zum Thema:
Ich persönlich würde noch etwas mehr Flash investieren, und snprintf() 
verwenden. Zumindest, wenn dein Compiler das anbietet.
Man kann sprintf schon verwenden, so ist das nicht, aber snprintf() ist 
um einiges sicherer. Insbesondere wenn die Arraylängen wegen des knappen 
RAMs im Controller kürzer sind.

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.