Hallo, ich benutze in meiner Mikrocontroller Firmware für die Textausgabe auf der UART die Funktion sprintf(...). Gibt es dazu eine gute Alternative? Wenn ich meine Anwendung im Debug-Mode laufen lasse, dann verhält sich mein Mikrocontroller nach eine gewissen Zeit nicht so, wie es sein sollte. Ich muss dazu sagen dass ich für Testzwecke die sprintf Funktion benutze mit dieser werden zyklisch Textnachrichten über die UART versendet. Ich vermute das die sprintf zur Laufzeit Probleme bereitet.
:
Bearbeitet durch User
He S. schrieb: > Ich vermute das die sprintf zur Laufzeit Probleme bereitet. Es benötigt halt etwas Ram. Wenn es da schon eng ist, dann kann das noch enger werden. Es gibt sicherlich andere Möglichkeiten, aber ob die auf dem Modell "Mikrocontroller" mit der Software "Firmware" auch verfügbar sind, musst du da schon selber rausfinden. Oliver
He S. schrieb: > Gibt es dazu eine gute Alternative? He S. schrieb: > Ich vermute das die sprintf zur Laufzeit Probleme bereitet. Bitte Controller-Typ und weitere Details möglichst geheim halten. Salami-Schneidemaschine schon mal bereitstellen.
Wenn für sprintf kein Platz ist, benutze ich Funktionen aus der stdlib.h, um Zahlen in Text umzuwandeln.
> Ich vermute das die sprintf zur Laufzeit Probleme bereitet.
Warum sollte das Probleme bereiten? Das ist einfach ein printf
und damit genauso fett.
Auf kleinen Controllern schreibe dir die Funktion einfach
selber und beschraenke den Funktionsumfang auf das was du
brauchst. Also z.B als erstes mal die Unterstuetzung von
Fliesskomma weglassen. Und schon wird es klein....
Vanye
Vanye R. schrieb: > Warum sollte das Probleme bereiten? Das ist einfach ein printf > und damit genauso fett. Sprintf benötigt einen Ausgabepuffer im RAM. Printf kann die Ausgabe streamen.
He S. schrieb: > Ich vermute das die sprintf zur Laufzeit Probleme bereitet. Entweder die zu langsame UART oder der RAM, der mit konstanten Strings vollgestopft wird. Wenn AVR dann schaue mal unter sprintf_P und PSTR nach. https://cpp.hotexamples.com/examples/-/-/sprintf_P/cpp-sprintf_p-function-examples.html
Oliver S. schrieb: > He S. schrieb: >> Ich vermute das die sprintf zur Laufzeit Probleme bereitet. > > Es benötigt halt etwas Ram. Wenn es da schon eng ist, dann kann das noch > enger werden. Kaffeesatzleserei! Niemand weiß, was genau der TE mit sprintf() anstellt. Oder welchen Controller (wieviel RAM) er verwendet. Oder was er so schön nebulös mit "Probleme bereitet" umschreibt. Trivialerweise braucht sprintf() RAM in der Größe des Buffers. Wenn man das extra erwähnen muß, kann man sich auch gleich alle Hilfe sparen.
He S. schrieb: > für die Textausgabe auf der UART die Funktion sprintf Axel S. schrieb: > Niemand weiß, was genau der TE mit sprintf() anstellt? Sehr gute Frage, sprintf gibt von sich aus erstmal überhaupt nichts extern aus. Das erzeugt nur ein formatiertes Char-Array im RAM und sonst nichts. Da stellt sich also erstmal die Frage was wird da wie Formatiert. Eingabedaten auf Gültigkeit geprüft, Ausgabe-Puffer immer ausreichend usw. (deshalb nimmt man heutzutage auch sprintf_s) Und danach kommt dann erst wie wird des Array auf dem UART ausgegeben? - https://en.cppreference.com/w/c/io/fprintf
Bei relativ kleinen Controllern gibt es "eingeschränkte" (s)printfs, die z.B. Float-Behandlung weglassen. Man wählt dann irgendwie (Compiler/Linker-Flags) die passende. Eigentlich sollte (s)printf auf jeder Plattform so effizient sein (oder konfigurierbar sein), dass sich ein Eigenbau in den meisten fällen nicht lohnt. Trotzdem macht das meist jeder selber, sei es aus Stolz, zu früher Optimierung oder Größenwahn (bei mir alles 3 und es war immer unnötig!).
:
Bearbeitet durch User
Viele sprintf() Probleme können mit snprintf() garnicht erst entstehen. Trotzdem geht nichts über ein eigenes printf().
Ich benutzte aktuell die sprintf Funktion folgendermaßen:
1 | uint8_t TxData[64]; |
2 | st_TimeDate TimeDate; |
3 | |
4 | GetDateTime(&TimeDate); |
5 | |
6 | sprintf(TxData, "Time: %02d:%02d:%02d:%03d\n", TimeDate.hour, TimeDate.min, TimeDate.sec, TimeDate.ms, |
7 | |
8 | SendUartData(TxData, 19); |
Jede 5 Sekunden wird ein Zeitstempel ausgegeben.
Von wo aus wird diese Funktion aufgerufen? Wie bestimmst Du "alle fünf Sekunden"? Verwendest Du einen Timerinterrupt und rufst die Funktion /aus dem Interrupthandler/ auf?
He S. schrieb: > Ich benutzte aktuell die sprintf Funktion folgendermaßen: Zweite Salami-Scheibe. Wir warten ....
Zwar nicht schön wegen der fixen Länge in SendUart wo sprintf doch die Länge als return Wert hat, aber das Problem liegt sicher woanders. Es sollte natürlich genug Stack vorhanden sein.
J. S. schrieb: > Es sollte natürlich genug Stack vorhanden sein. Alternativ kann der TO das Wörtchen "static" vor die Buffer-Definition schreiben. Dann liegt der Buffer nicht mehr auf dem Stack. Wäre auf jeden Fall einen Versuch wert. Wir wissen leider nicht, ob es sich um einen klitzekleinen ATtiny mit 40 Bytes RAM oder um einen fetten STM32 mit einigen 100 Kilobytes handelt. @TO: Mehr Infos bitte, sonst wird das Kaffeesatzleserei.
Also der sprintf ist ja blockierend, schreibt aber normaler wiese in ein RAM Buffer. Daher das hat erstmal nix mit dem UART zu tun. Das muss man nachher noch aus dem Buffer Richtung UART kopieren. Also du kannst dein Problem aufteilen. Hast du eher Probleme mit dem sprintf? Sprich wenn du das mit dem UART weglässt, tut das Ding? Oder hast du schon auch mit dem sprintf Probleme? sprintf Probleme, können ja aus zu kleinen Buffer kommen. Wenn man char temp[32] mach und dann 32 bytes reinprintet dann wird noch am Ende eine 0 geprintet, und schon ist es passiert. Also Buffergrössen überprüfen. Bei UART ist es halt immer Problematisch einen String zu schicken. Denn meinst sind diese Funktionen Blockierend. Der UART läuft aber mit max 115kHz. Der CPU kann aber auch mit 200MHz laufen. Oder halt ältere nur mit 4MHz oder so. Das heißt aber, man wartet ewig dass ein Char oder der ganze String gesendet wird. Das frisst halt Laufzeit. Damit kommen die klassische Laufzeitproblemen. Lösung ist, wenn man den String asynchron verschickt. Also Buffer reinhauen, und interrupt arbeiten lassen. Hier kann man aber meist nie genug Buffer haben, denn Debug Prints kommen relativ oft, und der Buffer lauft voll. Dann wartet man halt (das ist aber genau so schlimm wie vorhin mit dem Syncronen senden), oder man wirft die Daten weg. Es werden teilweise halbe Strings gesendet. Und da man jetzt asynchron ist, ist der String was eben gesendet wird, schon älter. Also da ist kein Syncronität zu dem was der CPU wirklich macht.
Frank M. schrieb: > Alternativ kann der TO das Wörtchen "static" vor die Buffer-Definition > schreiben. Dann liegt der Buffer nicht mehr auf dem Stack. Die gleiche Menge RAM braucht er trotzdem. Und die steht dem Stack dann natürlich auch nicht mehr zur Verfügung. Wenn die Funktion nicht rekursiv aufgerufen wird, ist das Jacke wie Hose.
Eine vernuenftige Alternative, fuer Benutzer, welche etwas mitdenken ist Bin2Hex. Dessen Vorteil ist, dass die Zahl immer gleich lang ist. Was will man denn mit floating point ? Falls sich der Benutzer entgegen allen Vorgaben doch eine floating point Zahl reinziehen will, uebertrage ich trotzdem eine Hexzahl fuer den Wert, sowie eine oder zwei parametrierzahlen, womit der client dann den Floatwert berechnen kann.
> Sprintf benötigt einen Ausgabepuffer im RAM. Printf kann die Ausgabe > streamen. DAs hast du natuerlich recht. Aber man kann ja auch das normale printf nehmen und puts ueberlagern falls die Umgebung das unterstuetzt. Und wenn sie das nicht macht dann schreibt man sich doch sein eigenes printf und gibt sich dort alles mit einer eigenen Funktion in einer FIFO aus welche dann ihrerseits irgendwann von einem IRQ an die serielle geschoben wird. Das hat dann auch den charmanten Vorteil das man printf relativ hemmungslos in seinem Programm nutzen kann ohne das es gleich zu krassen Timingproblemen kommt. Vanye
MaWin O. schrieb: > Frank M. schrieb: >> Alternativ kann der TO das Wörtchen "static" vor die Buffer-Definition >> schreiben. Dann liegt der Buffer nicht mehr auf dem Stack. > > Die gleiche Menge RAM braucht er trotzdem. Und die steht dem Stack dann > natürlich auch nicht mehr zur Verfügung. Wenn es darum gehen würde, RAM einzusparen, dann würde man vor allem den Buffer nicht erst 64 Zeichen groß machen und dann nur 20 Zeichen davon verwenden (19 + die abschließende 0). Man würde wohl auch SendUartData() so implementieren, daß es mit C-Strings klarkommt und sich die explizite Längenangabe sparen. Und natürlich kann man in diesem speziellen Fall das sprintf() auch einfachst durch etwas handgeschriebenes ersetzen. Das wird zwar längerer Quellcode, aber sicher weniger Objectcode. Und schneller, wobei das nun wirklich kein Kriterium ist. Der UART ist sicher langsamer.
> Und schneller, wobei das nun > wirklich kein Kriterium ist. Der UART ist sicher langsamer. Nicht ganz. Wenn man sich noch eine eigene Fifo implementiert welche die Ausgabe spaeter im IRQ macht dann kann man sein eigenen printf in gewissen grenzen natuerlich, beliebig im einen Code verwenden ohne das man deutlich die Programmlaufzeit aendert. Das ist schon manchmal ganz nett. Die obercoolheit ist es im uebrigen wenn man in das eigene printf dann noch zwei Kommandos fuer x und y, Position einbaut und sich das dann als vt100 ausgibt. Dann kann man auf der PC-Seite ein Terminal verwenden und so seine Testdaten gleich etwas huebsch formatieren. Macht das Leben gleich viel einfacher. Vanye
Vanye R. schrieb: >> Und schneller, wobei das nun >> wirklich kein Kriterium ist. Der UART ist sicher langsamer. > > Nicht ganz. Wenn man sich noch eine eigene Fifo implementiert > welche die Ausgabe spaeter im IRQ macht Hahaha! Du bist ein Spaßvogel. Wie wahrscheinlich ist das nach der Fragestellung (wie formatiert man einen Zeitstempel ohne auf sprintf() zurückzugreifen) und der Art der Fragestellung (eine Scheibe Salami, bis jetzt) und dem gezeigten Codeschnipsel? Null? Oder sogar negativ?
He S. schrieb: > uint8_t TxData[64]; Das ist ein geradezu religiös anmutender Ausdruck der Hoffnung, das auf dem Stack auch immer und jedes mal 64 Bytes verfügbar sind. Wie wäre es mit:
1 | void debug_time() { |
2 | uint8_t slen = sizeof("Time: nn:nn:nn:nnn\n"); |
3 | char *TxData = alloca(slen); |
4 | if(TxData) { |
5 | snprintf(TxData, slen, "Time: %02d:%02d:%02d:%03d\n", 23, 59, 59, 999); |
6 | SendUartData(TxData, slen - 1); |
7 | }
|
8 | }
|
Das setzt ein funktionierendes alloca voraus; meinst Du, daß auf dem bislang komplett ungenannt bleibenden Microcontroller des Threadstarters eine saubere Stack-/Heap-Verwaltung aktiv ist?
Harald K. schrieb: > Das setzt ein funktionierendes alloca voraus; meinst Du, daß auf > dem > bislang komplett ungenannt bleibenden Microcontroller des Threadstarters > eine saubere Stack-/Heap-Verwaltung aktiv ist? Na wenn's schon für einen Winzling wie den tiny44 kompiliert… Aber die Frage ist natürlich legitim.
Norbert schrieb: > Na wenn's schon für einen Winzling wie den tiny44 kompiliert… Der gezeigte Schnipsel mag das tun, aber wer weiß, was da tatsächlich zum Einsatz kommt. Der Threadstarter jedenfalls schweigt hartnäckig und beantwortet keine Fragen.
Andras H. schrieb: > sprintf Probleme, können ja aus zu kleinen Buffer kommen. Wenn man char > temp[32] mach und dann 32 bytes reinprintet dann wird noch am Ende eine > 0 geprintet, und schon ist es passiert. Also Buffergrössen überprüfen. Sowohl die Größe des Buffers als auch der benötigte Speicherplatz sind bekannt, siehe Codeschnipsel aus dem Eröffnungsposting.
Nur sind händisch abgezählte Konstanten immer üble Fehlerquellen, da ist alloca schon besser. Oder die stdio Ausgabe auf den uart umbiegen und es dem printf überlassen. Wenn man sich den Luxus schon leistet, dann gleich richtig.
Harald K. schrieb: > Der Threadstarter jedenfalls schweigt hartnäckig und beantwortet keine > Fragen. Welche "kühnen" Vermutungen liesse uns das denken oder sagen? (Angemeldet seit 31.12.2005 14:14, 13 Beiträge verfasst)
Wastl schrieb: > Harald K. schrieb: >> Der Threadstarter jedenfalls schweigt hartnäckig und beantwortet keine >> Fragen. > > Welche "kühnen" Vermutungen liesse uns das denken oder sagen? > > (Angemeldet seit 31.12.2005 14:14, 13 Beiträge verfasst) Nun, er hat jetzt genau einen Post (neben dem Eröffnungspost) in diesen Thread abgesetzt. Und Fragen hat er genau 1 beantwortet. Wobei die gleich 3 (mindestens) neue aufwirft.
Norbert schrieb:
1 | void debug_time() { |
2 | uint8_t slen = sizeof("Time: nn:nn:nn:nnn\n"); |
3 | char *TxData = alloca(slen); |
4 | if(TxData) { |
5 | snprintf(TxData, slen, "Time: %02d:%02d:%02d:%03d\n", 23, 59, 59, 999); |
6 | SendUartData(TxData, slen - 1); |
7 | }
|
8 | }
|
FYI, alloca hat keinen Fehlerreturn - im Fehlerfall gibt's einfach einen Stackoverflow. Und selbst wenn, würde es nicht helfen: slen mag noch draufgepasst haben, die folgenden Funktionsaufrufe (snprintf/SendUartData) aber evtl nicht mehr.
Foobar schrieb:
1 | > uint8_t slen = sizeof("Time: nn:nn:nn:nnn\n"); |
2 | > char *TxData = alloca(slen); |
Für diesen Fall kann man übrigens auch schreiben:
1 | char TxDat[sizeof("Time: nn:nn:nn:nnn\n")]; |
alloca macht hier keinen Unterschied und ist an dieser Stelle komplett überflüssig. P.S. Man könnte nun auf die Idee kommen, dass VLAs (variable length arrays) alloca() komplett obsolet gemacht hätten. Es gibt jedoch einen klitzekleinen Unterschied zwischen VLA und alloca(), der hier erklärt wird: https://stackoverflow.com/questions/3488821/is-alloca-completely-replaceable In dem obigen Beispiel spielt dieser Unterschied jedoch keine Rolle.
:
Bearbeitet durch Moderator
Letzten Endes ist es immer eine Abwägung zwischen (Ausführungs)Geschwindigkeit und Sicherheit. Um Gewissheit zu erlangen könnte man jedoch zumindest einen Kanarienvogel ganz ans Stack-Ende schreiben und gelegentlich überprüfen. Vogel tot, LED an, zurück ans Zeichenbrett. ;-) Was aber trotzdem bleibt, ist der sparsamst mögliche Umgang mit den arg begrenzten Resourcen des Stacks. Wenn anstelle von 64Bytes nur 20Bytes alloziert werden, verbleibt eben eine riesige Menge an weiter nutzbaren Bytes. Diese könnte womöglich (sprich mit einiger Wahrscheinlichkeit) alle Folgeprobleme lösen.
> Um Gewissheit zu erlangen könnte man jedoch zumindest einen > Kanarienvogel ganz ans Stack-Ende schreiben und gelegentlich überprüfen. Ich fuelle in meinem StartUp Code den Stack komplett mit 0xdead. Dann habe ich in meinem Programm eine Funktion die von unten nach oben laeuft und prueft wann das erstemal was anderes im Stack steht. So kann ich die Stacknutzung ueberpruefen. Das ist in der Regel bei heutigen Microcontrollern mit 20-128k internem Ram aber kein Problem mehr. Braucht man nur noch in seltenen Ausnahmefaellen. Vanye
Vanye R. schrieb: > So kann ich die Stacknutzung ueberpruefen. > > Das ist in der Regel bei heutigen Microcontrollern mit 20-128k > internem Ram aber kein Problem mehr. Braucht man nur noch in seltenen > Ausnahmefaellen. Man ist ja neugierig ;) Aber uC mit 128k RAM sollten auch eine MMU/MPU o.ä. haben. Dann knallt es im Zweifelsfall gleich richtig, aber vor allem an exakt der richtigen Stelle. Ich möchte nicht mehr drauf verzichten. Mit -Wstack-usage=42 gibt der gcc eine Warnung aus, wenn eine Funktion mehr Stack braucht als geplant. Das funktioniert nicht bei jeder Art von Stack-Nutzung, aber meistens.
Bauform B. schrieb: > Ich möchte nicht mehr drauf verzichten. Wenn man zu doof ist mit seinen Resourcen bewusst umzugehen dann braucht man so etwas eben.
Wenn man sich die Maschinenbefehle nicht merken kann, braucht man eben einen Compiler...
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.