Hallo zusammen, ich sitze an einem BareMetall-Programm für den (CortexM4 des i.MX8MM) und möchte der PRINTF-Funktion von NXP einen Loglevel hinzufügen. Die PRINTF-Funktion unterstützt eine variable Anzahl an Argumenten. Dazu möchte ich ebenfalls eine Log-Funktion mit Variabler Anzahl von Argumenten implementieren, die nach dem Loglevel schaut und - sofern er paßt - einfach die Argumente 1:1 an die PRINTF-Funktion von NXP weitergibt. Weiß jemand wie das geht? Eine Anzahl variabler Argumente einfach an einen Call weitergeben? Vielen Dank für Eure Hilfe, Christoph
Christoph F. schrieb: > Weiß jemand wie das geht? Eine Anzahl variabler Argumente einfach an > einen Call weitergeben? Jörg W. schrieb: > vprintf bzw. vfprintf is your friend Hier noch etwas zum Nachlesen https://en.cppreference.com/w/c/io/vfprintf (mit Beispiel).
Jörg W. schrieb: > vprintf bzw. vfprintf is your friend Danke Dir. Freunde schon, die Nützlichkeit sehe ich gerade nicht so. vprintf will auch printen, das muss jedoch die PRINTF-Funktion von nxp, welche es auf dem UART ausgibt und vfprintf möchte einen FILE-Pointer als Ziel beglücken. Ich bewege mich bare-metall auf dem CortexM4. Programmiersprache ist C.
Christoph F. schrieb: > Ich bewege mich bare-metall auf dem CortexM4. Programmiersprache ist C. Ja, und? Habe ich auch oft genug gemacht. Die Standardbibliothek bei ARM-GCC ist typischerweise ja eine newlib, und die hat die komplette printf-Familie an Bord. Man muss dann halt nur das Backend implementieren (_write in diesem Falle). Heißt die Funktion wirklich PRINTF (in Großbuchstaben)? Dann hast du so keine Chance. Wenn sie kein "v"-Pendant dazu liefern, dem man eine va_list als Argument überreichen kann, ist deine Aufgabe so schlicht nicht lösbar.
:
Bearbeitet durch Moderator
Jörg W. schrieb ...: > vprintf bzw. vfprintf is your friend So grundsätzlich war das mit dem v und printf schon gut. Mit vsnprintf() aus dem Hause "stdio.h" bin ich nun glücklich geworden. > Heißt die Funktion wirklich PRINTF (in Großbuchstaben)? Ja. Ist leider so. Herzlichen Dank für die schnellen und guten Tipps. -Christoph
Christoph F. schrieb: > So grundsätzlich war das mit dem v und printf schon gut. Mit vsnprintf() > aus dem Hause "stdio.h" bin ich nun glücklich geworden. Naja gut, musst du halt erst einen Puffer bereitstellen und dann hoffen, dass er immer groß genug ist. >> Heißt die Funktion wirklich PRINTF (in Großbuchstaben)? > Ja. Ist leider so. Würde ich wegwerfen und durch Standard-printf ersetzen bzw. habe dies auch so getan.
Das PRINTF von NXP ist ein Macro, das wahlweise in DbgConsole_Printf oder das gewöhnliche printf expandiert wird. Natürlich kann man das printf auch direkt aufrufen. Man könnte auch das Macro printf (also gleich wie die zugrundeliegende Bibliotheksfunktion) nennen, nur ist dann nicht mehr direkt ersichtlich, dass es sich um ein Macro handelt.
Man könnte aber auch das Backend so umbauen, dass es die Funktion dieses DbgConsole_Printf abstrahiert. Dann braucht man den Makro-Kram nicht, und kann sogar zur Laufzeit umschalten, wenn man will. Bei uns sieht das dann ungefähr so aus:
1 | freopen(":uart0", "r", stdin); |
2 | freopen(":uart0", "w", stdout); |
3 | #ifdef DEBUG
|
4 | freopen(":uart1", "w", stderr); |
5 | #else
|
6 | freopen(":null", "w", stderr); |
7 | #endif
|
_open durchläuft dann für all Gerätedateinamen (mit Doppelpunkt beginnend) eine Liste von vorher registrierten Treibern. Alle anderen Namen gehen auf die SD-Karte, sofern eine solche in der Konfiguration vorhanden ist.
Jetzt könnte man da statt des : ein ∕dev∕ davor stellen und davon auch ein Directory-Listing anbieten, dann ist es schon fast wie auf einem Unix-System.
:
Bearbeitet durch User
Jörg W. schrieb: > Naja gut, musst du halt erst einen Puffer bereitstellen und dann hoffen, > dass er immer groß genug ist. So ein Tipp in dieser Zeit... hoffentlich kein IoT-Device. ;-))
Rolf M. schrieb: > Jetzt könnte man da statt des : ein ∕dev∕ davor stellen und davon auch > ein Directory-Listing anbieten, dann ist es schon fast wie auf einem > Unix-System. Ja, fand ich nur nicht weiter nützlich. Der einzelne Doppelpunkt tut's auch (und ist insbesondere viel einfacher zu analysieren als ein abschließender Doppelpunkt wie in "LPT1:" oder "NUL:"). Klar ist die newlib Posix-zentrisch aufgebaut, das sieht man an den Backend-Funktionen, auf denen sie aufsetzen möchte (die letztlich Posix-Syscalls sind), aber für eine simple MCU muss man das auch nicht übertreiben. (prx) A. K. schrieb: >> Naja gut, musst du halt erst einen Puffer bereitstellen und dann hoffen, >> dass er immer groß genug ist. > > So ein Tipp in dieser Zeit... hoffentlich kein IoT-Device. ;-)) Er schrieb ja was von vsnprintf(), also wird er wohl hoffentlich die passende Länge übergeben. Ist dann trotzdem doof, wenn von "Diese Meldung ist superla" die letzten Buchstaben abgeschnitten werden …
Jörg W. schrieb: > Christoph F. schrieb: > >> Mit vsnprintf() aus dem Hause "stdio.h" bin ich nun glücklich geworden. > > Naja gut, musst du halt erst einen Puffer bereitstellen und dann hoffen, > dass er immer groß genug ist. Das tut die Funktion von NXP auch. Die vsnprintf() hat die Pufferlänge als Argument, sollte also - oberflächlich betrachtet - sicher sein und die Ausgaben kürzen. Das SDK von NXP soll in dem Projekt unangetastet bleiben, damit es einfacher getauscht werden kann. Es geht auch nur um ein paar eigene Hilfs-Ausgaben (debug/info/error) in der Geräteinternen i.MX8MM/CM4-Console, wo nur Entwickler dran kommen. Für eine saubere Schnittstelle an die der Endkunde ebenfalls ran kommt, ist so etwas indiskutabel. Da würde ich direkt auf dem UART-Modul aufsetzen, sofern eine UART-Schnittstelle dafür wiederum überhaupt in Frage käme. Der i.MX8 hat da geschickteres im Bauch. Danke euch, für mich passt es erst mal.
Christoph F. schrieb: > Das tut die Funktion von NXP auch. Die vsnprintf() hat die Pufferlänge > als Argument, sollte also - oberflächlich betrachtet - sicher sein und > die Ausgaben kürzen. Ich wollte auch keine Pufferüberläufe unterstellen sondern eben eher den oben beschriebenen Fall ("640 Kilobyte genügen für jeden" :), bei dem dann vielleicht der interessante Teil der Ausgabe wegfällt, weil er nicht mehr gepasst hat. Die normale printf-Familie hat das Problem nicht, weil sie nicht zwingend alles puffern muss, sondern dann ans Gerät rausschreibt.
Christoph F. schrieb: > Dazu möchte ich ebenfalls eine Log-Funktion mit Variabler Anzahl von > Argumenten implementieren Es ist einfacher, wenn du ein log-Makro implementierst. Dann brauchst du auch keine "v" Variante der NXP Funktion.
Christoph F. schrieb: > Hallo zusammen, > > ich sitze an einem BareMetall-Programm für den (CortexM4 des i.MX8MM) > und möchte der PRINTF-Funktion von NXP einen Loglevel hinzufügen. Die > PRINTF-Funktion unterstützt eine variable Anzahl an Argumenten. > > Dazu möchte ich ebenfalls eine Log-Funktion mit Variabler Anzahl von > Argumenten implementieren, die nach dem Loglevel schaut und - sofern er > paßt - einfach die Argumente 1:1 an die PRINTF-Funktion von NXP > weitergibt. > > Weiß jemand wie das geht? Eine Anzahl variabler Argumente einfach an > einen Call weitergeben? > > Vielen Dank für Eure Hilfe, > Christoph
1 | extern int log_level |
2 | |
3 | const char *level_strings[] { |
4 | "[DEBUG]: ", |
5 | "[INFO ]: ", |
6 | "[WARN ]: ", |
7 | "[ERROR]: "
|
8 | };
|
9 | |
10 | // kleiner Trick, damit die Funktion auch ohne Formatier-Parameter funktioniert.
|
11 | #define LOG(level, __VA_ARGS__) LOG_(lovel, __VA_ARGS)
|
12 | |
13 | // PRINTF ist hier das nxp-Makro, kann aber auch printf() sein.
|
14 | #define LOG_(level, fmt, ...) \
|
15 | if(level >= log_level) { \
|
16 | PRINTF("%s" fmt, log_level, __VA_ARGS__); \
|
17 | }
|
Sowas vielleicht (ungetestet)?
1 | extern int log_level; |
2 | |
3 | const char *level_strings[] { |
4 | "[DEBUG]: ", |
5 | "[INFO ]: ", |
6 | "[WARN ]: ", |
7 | "[ERROR]: "
|
8 | };
|
9 | |
10 | // kleiner Trick, damit die Funktion auch ohne Formatier-Parameter funktioniert.
|
11 | |
12 | #define LOG(level, __VA_ARGS__) LOG_(lovel, __VA_ARGS)
|
13 | |
14 | // PRINTF ist hier das nxp-Makro, kann aber auch printf() sein.
|
15 | |
16 | #define LOG_(level, fmt, ...) \
|
17 | if(level >= log_level) { \
|
18 | PRINTF("%s" fmt, level_strings[log_level], __VA_ARGS__); \
|
19 | }
|
Ups, kann leider nicht editieren ;)
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.