Die Printf() Funktion ist ja wohl sehr begehrt aber irgendwie finde ich keine brauchbare lib dazu. Weiß wer von euch ne gute Quelle?
Printf ist in der Standard C Library <stdio.h> enthalten. Damit sie Funktioniert, musst du die folgende C Funktion implementieren. Das folgende Beispiel leitet die Ausgaben auf den USB Port. Für serielle Ports musst du sie entsprechend umschreiben.
1 | // Redirect standard output to the USB port
|
2 | int _write(int file, char *ptr, int len) |
3 | {
|
4 | CDC_Transmit_FS( (uint8_t*) ptr, len); |
5 | return len; |
6 | }
|
Stefanus F. schrieb: > Damit sie Funktioniert, musst du die folgende C Funktion implementieren. Das ist aber jetzt schon ein sehr konkretes Beispiel. Der TO hat überhaupt nicht geschrieben, wo der Output (stdout) hingehen soll. Außerdem muss man dazusagen, dass nicht nur _write, sonder auch andere Syscalls benötigt werden. Zum Beispiel _sbrk für den Heap. Das braucht dann wiederum den notwendigen Speicherbereich, ggf. Im Linkerskript anlegen... Kenne mich mit CubeMX nicht aus. Bei den einzigen beiden Projekten, die ich damit gemacht, äh versucht, habe, musste ich im generierten Code von CubeMX Fehler beseitigen. Hätte jetzt gedacht, dass CubeMX eine Einstellung hat, wie es den Standardoutput legen soll und dir das dann alles richtig hinbastelt.
M. H. schrieb: > Das ist aber jetzt schon ein sehr konkretes Beispiel Darauf habe ich hingewiesen. Unkonkrete Beispiele sind hier unbeliebt. Entscheidend ist, dass der TO einen kleinen Schubser in die richtige Richtung bekommt. Wer AVR kennt, weiß man es dort ganz anders machen muss. > Außerdem muss man dazusagen, dass nicht nur _write, sonder auch andere > Syscalls benötigt werden. Zum Beispiel _sbrk für den Heap. Das braucht > dann wiederum den notwendigen Speicherbereich, ggf. Im Linkerskript > anlegen... Die werden üblicherweise vom Projektassistenten der IDE angelegt, bzw von Cube MX. > Kenne mich mit CubeMX nicht aus. Dann laber doch nicht dumm herum. Ich habe das vorher ausprobiert, es funktioniert genau so, wie ich es geschrieben habe. > Hätte jetzt gedacht, dass CubeMX eine Einstellung hat, > wie es den Standardoutput legen soll und dir das dann > alles richtig hinbastelt. Leider nicht.
Mein Liebling ist die sprintf Funktion.
1 | char Tx_Buffer[64]; |
2 | uint8_t Tx_len; |
3 | |
4 | uint8_t mytext[]="Bereit\r\n"; |
5 | |
6 | sprintf(Tx_Buffer,"%s",mytext); |
7 | Tx_len=strlen(Tx_Buffer); |
8 | |
9 | HAL_UART_Transmit_IT(&huart3,(uint8_t *)Tx_Buffer,Tx_len); |
Mit sprintf kann man alles schön formatieren und dann an eine beliebige Schnittstelle senden. z.B. HAL_I2C_Master_Transmit_IT(); oder an cdc, spi, can ....
pegel schrieb: > Mein Liebling ist die sprintf Funktion. Weil sie so schöne Buffer Overflows produziert? Wenn schon dann bitte snprintf ...
Niklas G. schrieb: > Weil sie so schöne Buffer Overflows produziert? Ist mir noch nicht passiert. Aber snprintf geht vermutlich auch.
pegel schrieb: > Ist mir noch nicht passiert. sprintf ist eine geradezu sprichwörtliche Sicherheitslücke :-) Es gibt bestimmt genug IoT-Geräte, welche sich darüber übernehmen lassen...
Wenn ich sprintf benutze, dass weiß ich meistens vorher, wie lang der String maximal werden kann. Entsprechend groß lege ich den Buffer aus. Wenn die Größe nicht vorher festlegen kann, benutze ich snprintf. Das Einzige Risiko besteht in schlampiger Programmierung.
Stefanus F. schrieb: > Das Einzige Risiko besteht in schlampiger Programmierung. Leider besteht hier ein großes Fehlerpotenzial. Man schätzt die maximale Größe schnell mal falsch ein. Da ist es doch viel einfacher, immer snprintf+sizeof zu nutzen. Das funktioniert dann auch nach Änderung der Puffer-Größe, Format-String oder Eingaben noch. Als schlampige Programmierung würde ich es sehen, wenn die Sicherheit der Anwendung davon abhängt, ob 3 Faktoren (Array-Größe, Format-String, Eingabe-Daten) genau richtig zueinander passen.
Niklas G. schrieb: > Man schätzt die maximale Größe schnell mal falsch ein. Du vielleicht. Ich schätze nicht, ich weiß genau, wie viel Puffer ich benötige. Wenn nicht, benutze ich snprintf und riskiere unvollständige Ergebnisse. Ist auch suboptimal. Niklas G. schrieb: > Als schlampige Programmierung würde ich es sehen, > wenn die Sicherheit der Anwendung > davon abhängt, ob ... Faktoren > genau richtig zueinander passen. Ich unterscheide hier zwischen Programmen, die ich voll im Griff habe, und andere. Für die anderen Programme ist C ohnehin die falsche Sprache.
Stefanus F. schrieb: > Ich schätze nicht, ich weiß genau, wie viel Puffer ich benötige. Ah, wie viel braucht man denn hierfür?
1 | sprintf (buffer, "Hallo %s, deine Note ist %f. Du hast %zu Bytes Speicher verbraucht.\n", userName, mark, memsize); |
Wenn das Ergebnis immer vollständig sein muss, nimmt man std::string o.ä. sprintf ist hier absolut keine Lösung. snprintf hat keinerlei Nachteil, ich verstehe nicht warum man auf sprintf bestehen würde, selbst wenn es in speziellen Sonderfällen korrekt wäre...
> Ah, wie viel braucht man denn hierfür? Dein Code ist unvollständig, daher kann ich Dir diese Frage nicht beantworten. Das ist so ein Fall von "wo ich den Code nicht voll im Griff habe". Da würde im Zweifelsfall snprintf verwenden. Strings in String einzufügen ist allerdings ohnehin fragwürdig. Wesentlich effizienter wäre die Ausgabe der Teilstrings, insbesondere wenn ich extrem lange userNamen erwarte. "Hallo " userName ", deine Note ist %f. Du hast %zu Bytes Speicher verbraucht.\n" > snprintf hat keinerlei Nachteil Doch, es ist langsamer. Müssen wir überhaupt über so eine Pillepalle diskutieren? Ich komme mir langsam blöd vor, ich habe wichtigere Sorgen.
Stefanus F. schrieb: > Dein Code ist unvollständig, daher kann ich Dir diese Frage nicht > beantworten. Okay, die maximale Länge von userName sei MaxUsernameLength. Mehr muss man nicht wissen. Stefanus F. schrieb: > Doch, es ist langsamer. Um wieviel, dass das Risiko akzeptabel ist? Welche printf-Ausgabe ist so zeitkritisch, dass es auf das Bisschen ankommt?
Niklas G. schrieb: > Um wieviel, dass das Risiko akzeptabel ist? Es gibt kein Risiko, wenn ich genau weiß, wie lang der String maximal werden kann. Ansonsten nutze ich wie gesagt snprintf oder gleich Java. Du verbeißt Dich da in ein "Problem" das gar nicht existiert, wenn man konzentriert arbeitet. Und wenn nicht, dann kann Dich die Programmiersprache und solche Konstrukte ohnehin nicht retten. Früher oder später baust du irgendwo einen Buffer, Stack oder Heap Überlauf - auch mit snprintf.
Ja, so stürzten früher ständig die Programme ab weil die Programmierer alles im Griff hatten und Schuld war Windows... MS hat extra eine Warnung in den Compiler eingebaut um darauf hinzuweisen das diese Funktionen unsicher sind. Und Fehler aus überschriebenen Stackvariablen sind die schönsten.
Stefanus F. schrieb: > Es gibt kein Risiko, wenn ich genau weiß, wie lang der String maximal > werden kann. Wie lang ist er denn jetzt? :-) Steht doch da, ist doch leicht erkennbar. Stefanus F. schrieb: > Früher > oder später baust du irgendwo einen Buffer, Stack oder Heap Überlauf - > auch mit snprintf. Mit der Einstellung braucht man sich dann auch nicht mehr über Sicherheitslücken und instabile Software beschweren.
Mit deinem snprintf tauschst du nur einen potentiellen Fehler gegen einen anderen aus. Jetzt bekommst du zwar garantiert an dieser Stelle keinen Pufferüberlauf, dafür bekommst du eine unvollständige Ausgabe. Auch das ist ein Fehler, der nicht vorkommen darf. Wenn schon, dann begrenzt du bei der Ausgabe die Länge des Namens, dann hast du wieder eine berechenbare Puffergröße. Oder du splittest es wie bereits empfohlen auf drei Teilstrings auf. Oder du machst den Puffer einfach groß genug: char buffer[n+MaxUsernameLength]; n darfst Du Dir selber ausrechnen.
Stefanus F. schrieb: > n darfst Du Dir selber ausrechnen. Du gibst also auf. War wohl doch nicht so einfach. Noch eine Einschränkung habe ich tatsächlich vergessen: mark liege zwischen 1.0 und 5.0. Stefanus F. schrieb: > der nicht vorkommen darf. Dieser Fehler kann nur verärgerte Nutzer, aber kein übernommenes und für illegale Aktivitäten genutztes System zur Folge haben.
> Du gibst also auf.
Ich habe anderes zu tun als diese bescheute Diskussion. Jetzt mach ich
wirklich Schluss.
Stefanus F. schrieb: > Jetzt mach ich > wirklich Schluss. Ok. Dann gebe ich für die Zuschauer zuhause selbst die Lösung: Die Länge des Puffers muss man korrekterweise so berechnen:
1 | char buffer [62 + MaxUsernameLength + 8 + (CHAR_BIT * sizeof (size_t) * 10 + 32) / 33]]; |
2 | sprintf (buffer, "Hallo %s, deine Note ist %f. Du hast %zu Bytes Speicher verbraucht.\n", userName, mark, memsize); |
Das ist allerdings aufgerundet, in Randfällen ist es ggf. ein Byte zu viel. Exakt muss man es mit dem Taschenrechner machen. Wem das zu umständlich ist, nimmt snprintf:
1 | char buffer [100]; |
2 | snprintf (buffer, sizeof(buffer), "Hallo %s, deine Note ist %f. Du hast %zu Bytes Speicher verbraucht.\n", userName, mark, memsize); |
Hier kann zwar Text abgeschnitten werden, das ist aber keine Sicherheitslücke und daher deutlich weniger schlimm. Falsch hingegen ist:
1 | char buffer [80 + MaxUsernameLength]; |
2 | sprintf (buffer, "Hallo %s, deine Note ist %f. Du hast %zu Bytes Speicher verbraucht.\n", userName, mark, memsize); |
Dies wird spätestens bei der Portierung auf eine 64bit-Plattform zu Problemen führen.
:
Bearbeitet durch User
M. H. schrieb: > Kenne mich mit CubeMX nicht aus. Bei den einzigen beiden Projekten, die > ich damit gemacht, äh versucht, habe, musste ich im generierten Code von > CubeMX Fehler beseitigen. War das eine Aspach-Uralt Version? Ich bekam von CubeMX bis jetzt immer lauffähigen Code, sogar mit umfangreicheren Sachen wie FreeRTOS und USB. Man muss darauf achten, dass man eine möglichst aktuelle Version verwendet und wenn möglich eine direkt unterstützte Entwicklungsumgebung wie z.B. TrueSTUDIO, SW4STM32, IAR, ... > Hätte jetzt gedacht, dass CubeMX eine > Einstellung hat, wie es den Standardoutput legen soll und dir das dann > alles richtig hinbastelt. Bei der riesigen Anzahl an Kommunikationsmöglichkeiten, die ein STM32 hat, wäre das dann vielleicht doch ein wenig zuviel verlangt.
Johnny B. schrieb: > War das eine Aspach-Uralt Version? > Ich bekam von CubeMX bis jetzt immer lauffähigen Code, sogar mit > umfangreicheren Sachen wie FreeRTOS und USB. War vor ca. einem Jahr. Habe versucht etwas mit I2S und USB zu basteln. CubeMX hat in der Clock Initialisierung ein ODER (|) in der Zuweisung vergessen. Also statt clock->register |= mask nur ein = gehabt. Habe ca. ne Stunde den Code gedebuggt, um das zu finden. Habe den Fehler gemeldet, genauso wie die fehlerhaften Linkerskripte. Kann sein, dass sie es repariert haben. Habe seitdem nichts mehr versucht. Die Linkerskripte sind glaube ich immernoch fehlerhaft und funktionieren nur, weil der STM auch Speicher hat, der im Datenblatt als "reserved area" gelistet ist. Das ist der Thread: Beitrag "SRAM Lücke STM32Fxxx, Linkerskripte falsch" Vorallem schön, wenn man das produktiv für sichere Sachen nutzt :D Johnny B. schrieb: > Bei der riesigen Anzahl an Kommunikationsmöglichkeiten, die ein STM32 > hat, wäre das dann vielleicht doch ein wenig zuviel verlangt. Keineswegs. Das Tool kann mir nen Heap automatisch basteln und tut auch sonst so als wäre es die ultimative Lösung, dann wird es mir auch generieren können, dass ich stdout auf eine aktivierte streamingfähige Schnittstelle lege. Stefanus F. schrieb: > Dann laber doch nicht dumm herum. Ich habe das vorher ausprobiert, es > funktioniert genau so, wie ich es geschrieben habe. Ich wollte nur darauf hinweisen, dass das einfügen der von dir geposteten Funktion nicht magisch dazu führt, dass es geht.
Hatte die printf funktion erfolgreich zum laufen gebracht, jedoch bei einem neuen Projekt werden Bytes Verschluckt. Uart1 auf printf: int _write(int file, char *ptr, int len) { HAL_UART_Transmit_IT(&huart1, (uint8_t*) ptr, len); return len; } Hauptprogramm: while (1) { printf("STM32 Glump Test mit UART \r\n"); printf("ABCDEF1234567890 \r\n"); HAL_Delay(50); } Kommt nur noch das raus am Terminal: SBCDEF1234567890 t UART SBCDEF1234567890 t UART STCDEF1234567890 t UART STCDEF1234567890 t UART Beim vorherrigen Projekt war es kein Problem zweimal direckt hintereinander Print zu machen. Was kann das sein?
Schorsch schrieb: > Was kann das sein? Ein Hardware Abstraction Layer, programmiert von der Marketing Abteilung von ST? HAL_UART_Transmit_IT() funktioniert offenbar nicht (na gut: nicht so, wie man es erwartet). Das erste printf() kopiert den Text in einen Puffer und ruft HAL... auf. Das kehrt zurück während das UART das erste 'S' ausgibt. Das erste printf() hat nichts weiter zu tun und kehrt auch zurück. Jetzt kopiert das zweite printf() seinen Text in den gleichen Puffer und überschreibt damit den ersten Text von 'S' bis zum 'i' vom "mit". Irgendwann wird das UART mit dem 'S' fertig und gibt das nächste Zeichen aus. Das ist inzwischen nicht mehr das 'T' vom ersten Text sondern das 'B' vom zweiten. Normal wäre jetzt "man HAL_UART_Transmit_IT" angesagt...
Beitrag #5692423 wurde von einem Moderator gelöscht.
Vor dem Senden eines neuen Strings musst du daher warten, bis der vorherige Sendevorgang abgeschlossen ist. Ausserdem kannst du keine Strings senden, die länger sind, als der Puffer. Manchmal ist es doch besser, solche Dinge selbst zu programmieren. In diesem Fall mit einem Ringpuffer und ggf. einer Warteschleife, falls der Puffer nicht ausreicht (so wie Arduino es macht).
So in der art hab ich mir das Problem auch vorgestellt. Allerdings hätte ich gedacht/erwartet dass erst nach dem fertig Raussenden wieder ins Programm zurückgekert wird. Zum Lösungsansatz: Wie mache ich dass am einfachsten mit einem Ringpuffer?
Schorsch schrieb: > Allerdings hätte ich gedacht/erwartet dass erst nach dem fertig > Raussenden wieder ins Programm zurückgekert wird. Du hast aber die Interrupt-gesteuerte Variante (mit "IT") verwendet. Wenn diese Funktion den Programmablauf blockieren würde, wäre sie im Verglich zu Version ohne "IT" sinnlos.
> Wie mache ich dass am einfachsten mit einem Ringpuffer?
Schau erst einmal in der HAL Doku ob die etwas dazu hergibt.
Der Punkt ist, dass der Ringpuffer in der ISR entsprechend programmiert
werden muss. Wenn die HAL das nicht drin hat, musst du zumindest an
dieser Stelle auf die HAL verzichten. Willst du das? Wohl kaum.
Wenn du warten willst, solltest du am besten einfach die andere Variante
ohne "IT" verwenden.
Wenn du nicht warten willst, kopiere den String in einen Puffer und
beauftrage die HAL, diesen zu senden. So ungefähr stelle ich mir das
vor:
1 | volatile int txComplete=1; |
2 | volatile uint8_t txBuffer[200]; |
3 | |
4 | void HAL_UART_TxCpltCallback (UART_HandleTypeDef *huart) |
5 | {
|
6 | txComplete=1; |
7 | }
|
8 | |
9 | int _write(int file, char *ptr, int len) |
10 | {
|
11 | // wait until the buffer is free
|
12 | while (!txComplete); |
13 | |
14 | // mark the buffer as "in use"
|
15 | txComplete=0; |
16 | |
17 | // copy the string into the buffer (with size limit)
|
18 | if (len>sizeof(buffer)) { |
19 | len=sizeof(buffer) |
20 | }
|
21 | memcpy(buffer,ptr,len); |
22 | |
23 | // send the buffer
|
24 | HAL_UART_Transmit_IT(&huart1, buffer, len); |
25 | |
26 | return len; |
27 | }
|
Bei diesem Code dürfen die Strings nicht grösser sein, als der Puffer. Du könntest zu grosse Strings in mehrere Teilstücke zerlegen. Mit ein bisschen Nachdenken kannst du diesen Ansatz auf zwei Puffer ausbauen, die wechselweise verwendet werden. Dadurch reduzieren sich die Wartezeiten weiter so dass es einem Ringpuffer kaum noch nach steht.
Schorsch schrieb: > Hatte die printf funktion erfolgreich zum laufen gebracht, jedoch bei > einem neuen Projekt werden Bytes Verschluckt. Jaja. Auf dem PC kann man sich dank riesigen RAM's so ziemlich alles erlauben, auch printf und Konsorten. Aber auf einem µC, wo man nur einen recht begrenzten RAM hat, ebenso einen im Vergleich zum PC recht kleinen Stack und Heap, da sind eigentlich andere Methoden als printf gefragt. Der ewige Denkfehler beim Benutzen von printf ist, daß man eben immer zu allererst RAM für das Aufbauen eines Ausgabe-Blockes benötigt und dann einen fetten und oftmals in seiner Größe schwer abschätzbaren Block hat, der dann ausgegeben werden soll. Entweder man macht das dann blockierend per Polling, oder man braucht nochmals eine noch größere Portion an RAM als Zwischenpuffer für den Lowlevel-Treiber. Das Ganze ist eben sehr RAM-hungrig.. und den RAM hat man hier nicht im Überfluß. Die Lösung wäre, einfach auf printf und all die daran hängenden Dinge zu verzichten und die Ausgaben ohne Aufbauen eines Zwischen-Strings zu erledigen. Aber das wäre natürlich eine Umgewöhnung beim Programmierer und das scheint hier immerzu nur schlecht anzukommen. Naja - und was man von ST's HAL zu halten hat, da ist meine Ansicht: garnix. W.S.
Stefanus F. schrieb: > Mit ein bisschen Nachdenken kannst du diesen Ansatz auf zwei Puffer > ausbauen Hmm.. noch mehr RAM reservieren? Wo du ja in jedem Falle ohnehin warten mußt? W.S.
W.S. schrieb: > Hmm.. noch mehr RAM reservieren? Wo du ja in jedem Falle ohnehin warten > mußt? Der erste String wird gesendet. Den zweiten String kann man in den zweiten Puffer legen, ohne zu warten. Bis der dritte String gesendet werden muss, ist der erste Puffer hoffentlich wieder frei. Das war zumindest die Idee dahinter. Kann man beliebig weiter spinnen - bis zum Out of Memory. Ganz mutige legen unendlich viele Puffer on-demand mit malloc an - ich rate davon ab. Oder man programmiert es mit Ringpuffer, so sparsam wie üblich. Aber die HAL hat ihre Prioritäten woanders, wie wir beide wissen.
W.S. schrieb: > Der ewige Denkfehler beim Benutzen von printf ist, daß man eben immer zu > allererst RAM für das Aufbauen eines Ausgabe-Blockes benötigt und dann > einen fetten und oftmals in seiner Größe schwer abschätzbaren Block hat, > der dann ausgegeben werden soll. Entweder man macht das dann blockierend > per Polling, oder man braucht nochmals eine noch größere Portion an RAM > als Zwischenpuffer für den Lowlevel-Treiber. Ob per Interrupt oder per Polling ausgegeben wird, hat doch nichts mit printf zu tun. Für den Interrupt-Luxus muss ich halt RAM spendieren, aber printf selbst braucht wenig. Mein printf (von ChibiOS geklaut) verwendet einen 12 Byte Puffer auf dem Stack und wahrscheinlich könnte man den auch noch sparen. > Die Lösung wäre, einfach auf printf und all die daran hängenden Dinge zu > verzichten und die Ausgaben ohne Aufbauen eines Zwischen-Strings zu > erledigen. Ich hab es lange Zeit so gemacht. Jetzt mit printf finde ich es wesentlich angenehmer. Deine Lösung würde aber auf jeden Fall blockieren? Wobei mich das nicht stört, mein printf blockiert auch. > Aber das wäre natürlich eine Umgewöhnung beim Programmierer > und das scheint hier immerzu nur schlecht anzukommen. Natürlich, weil es nur bei extremem Speichermangel nötig ist. Außerdem brauchst du wahrscheinlich mehr Flash für viele kleine Funktionsaufrufe.
Bauform B. schrieb: > aber printf selbst braucht wenig Die printf() Funktion aus der newlib-nano Library belegt 1468 Bytes RAM.
Stefanus F. schrieb: > Bauform B. schrieb: >> aber printf selbst braucht wenig > > Die printf() Funktion aus der newlib-nano Library belegt 1468 Bytes RAM. Nichts ist so unnütz, dass es nicht als schlechtes Beispiel dienen könnte ;)
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.