
Beim Debuggen von Mikrocontroller-Software ist es häufig erforderlich, Debugging-Meldungen, Protokolle, erfasste Daten und andere Dinge auf dem PC-Bildschirm auszugeben. Gleichzeitig möchte ich, dass die Ausgabe schneller ist und dass die Zeilen nirgendwo angezeigt werden, sondern direkt in der IDE - sozusagen ohne vom Code abzuweichen. Eigentlich geht es hier um den Artikel - wie ich versucht habe, printf () in meiner bevorzugten, aber nicht sehr mikrocontrollerischen Qt Creator-Umgebung auszugeben und anzuzeigen.
Im Allgemeinen können Sie eine Vielzahl von Möglichkeiten finden, um Textinformationen vom Mikrocontroller auszugeben. Die am häufigsten verwendeten Techniken sind jedoch nicht so viele:
Semihosting ist ziemlich langsam, RTT ist an Segger * Hardware- und Softwarelösungen gebunden, USB ist nicht in jedem Mikrocontroller enthalten. Daher bevorzuge ich normalerweise die letzten beiden - die Verwendung von UART und ITM. Über sie und wird unten diskutiert.
* Upd. - Wie in den Kommentaren vorgeschlagen, ist dies nicht der Fall. Es gibt Optionen sowohl auf der Software- als auch auf der Hardwareseite. Daher ist RTT von den oben genannten Methoden möglicherweise die universellste.
Und gleich eine Erklärung zu der Software, die als nächstes verwendet wird. Als Betriebssystem habe ich jetzt Fedora 28 und das aktuelle Softwarepaket für die Arbeit mit Mikrocontrollern lautet:
Leiten Sie printf () in GCC um
Um die Ausgabe von printf () in GCC umzuleiten, müssen Sie Linker-Schlüssel hinzufügen
-specs=nosys.specs -specs=nano.specs
Wenn Sie Gleitkommazahlen anzeigen müssen, müssen Sie sich den Schlüssel merken
-u_printf_float
Und implementieren Sie die Funktion _write (). Zum Beispiel so etwas
int _write(int fd, char* ptr, int len) { (void)fd; int i = 0; while (ptr[i] && (i < len)) { retarget_put_char((int)ptr[i]); if (ptr[i] == '\n') { retarget_put_char((int)'\r'); } i++; } return len; }
Dabei ist retarget_put_char () eine Funktion, die das Zeichen direkt in die gewünschte Schnittstelle lädt.
printf () -> ITM -> Qt Creator
Instrumentation Trace Macrocell (ITM) ist ein Block im Cortex-M3 / M4 / M7-Kern, der zur nicht-invasiven Ausgabe (Verfolgung) verschiedener Arten von Diagnoseinformationen verwendet wird. Um printf () über ITM zu implementieren, müssen Sie Folgendes wissen:
- Verwendet den TRACECLKIN-Takt, dessen Frequenz normalerweise der Kernfrequenz entspricht
- Verfügt über 32 sogenannte Stimulus-Ports für die Datenausgabe
- CMSIS enthält die Funktion ITM_SendChar (), die ein Symbol in den Stimulus-Port 0 lädt
- Die Daten werden entweder über einen synchronen Bus (TRACEDATA, TRACECLK) oder über eine asynchrone Single-Wire-SWO-Leitung (TRACESWO) ausgegeben.
- Die SWO-Leitung wird normalerweise mit JTDO gemultiplext, was bedeutet, dass sie nur im Debug-Modus von SWD funktioniert
- Die Auszahlung durch SWO erfolgt entweder mit dem Manchester-Code oder mit NRZ (UART 8N1).
- Daten werden in Frames eines bestimmten Formats übertragen - Sie benötigen einen Parser auf der Empfangsseite
- ITM wird normalerweise über die IDE oder das entsprechende Dienstprogramm konfiguriert (niemand verbietet jedoch das Einrichten im Programmcode - dann funktioniert die Ausgabe an SWO ohne eine ausgelöste Debugging-Sitzung).
Die bequemste Art, ITM zu verwenden, ist die Ausgabe über SWO mit NRZ-Codierung. Sie benötigen also nur eine Zeile, und es ist möglich, Daten nicht nur über einen Debugger mit einem speziellen Eingang, sondern auch über einen normalen USB-UART-Adapter zu empfangen, wenn auch mit einer geringeren Geschwindigkeit.
Ich folgte dem Pfad mit einem Debugger und musste meinen chinesischen STLink-V2 ändern , um SWO zu unterstützen. Dann ist alles einfach - wir verbinden den JTDO / TRACESWO-Mikrocontroller mit dem entsprechenden Debugger-Pin und konfigurieren die Software.
Openocd hat den Befehl "tpiu config" - damit können Sie die Methode zur Anzeige von Trace-Informationen konfigurieren (ausführlicher im OpenOCD-Benutzerhandbuch ). Also zum Beispiel mit Argumenten
tpiu config internal /home/esynr3z/itm.fifo uart off 168000000
Konfigurieren Sie die Ausgabe in die Datei /home/esynr3z/itm.fifo, verwenden Sie die NRZ-Codierung und berechnen Sie die maximale Übertragungsgeschwindigkeit basierend auf der TRACECLKIN-Frequenz von 168 MHz - für STLink sind es 2 MHz. Und noch ein Team
itm port 0 1
aktiviert den Null-Port für die Datenübertragung.
OpenOCD-Quellcode enthält das Dienstprogramm itmdump (contrib / itmdump.c) - mit ihm können Sie Zeichenfolgen aus den empfangenen Daten analysieren.
Zum Kompilieren geben wir ein
gcc itmdump.c -o itmdump
Geben Sie beim Start die erforderliche Datei / pipe / ttyUSB * und den Schalter -d1 an, um die empfangenen Datenbytes als Zeichenfolgen anzuzeigen
./itmdump -f /home/esynr3z/itm.fifo -d1
Und der letzte. Um ein Zeichen über SWO zu senden, ergänzen wir _write (), wie oben beschrieben, mit einer Funktion
int retarget_put_char(int ch) { ITM_SendChar((uint32_t)ch); return 0; }
Der allgemeine Plan lautet also: In Qt Creator konfigurieren wir openocd so, dass alle empfangenen Informationen zu SWO in einer zuvor erstellten Named Pipe gespeichert werden. Wir können Pipe lesen, Zeichenfolgen analysieren und mit itmdump anzeigen, das als externes Tool ausgeführt wird. Natürlich gibt es eine elegantere Möglichkeit, das Problem zu lösen - das entsprechende Plug-In für Qt Creator zu schreiben. Ich hoffe jedoch, dass sich der unten beschriebene Ansatz für jemanden als nützlich erweisen wird.
Gehen Sie zu den Einstellungen des Bare Metal Plugins (Extras-> Optionen-> Geräte-> Bare Metal).

Wählen Sie den verwendeten GDB-Server aus und fügen Sie die Zeileninitialisierungsbefehle am Ende der Liste hinzu
monitor tpiu config internal /home/esynr3z/itm.fifo uart off 168000000 monitor itm port 0 1
Kurz bevor der Debugger den Cursor ganz am Anfang von main () platziert, wird ITM konfiguriert.
Fügen Sie itmdump als externes Tool hinzu (Extras-> Extern-> Konfigurieren ...).

Vergessen Sie nicht, die Variable einzustellen
QT_LOGGING_TO_CONSOLE=1
um die Dienstprogrammausgabe auf der Qt Creator-Konsole anzuzeigen (Panel 7 Allgemeine Meldungen).
Schalten Sie nun itmdump ein, aktivieren Sie den Debug-Modus, starten Sie die Codeausführung und ... nichts passiert. Wenn Sie jedoch das Debuggen unterbrechen, wird die Ausführung von itmdump beendet und alle über printf () gedruckten Zeilen werden auf der Registerkarte Allgemeine Nachrichten angezeigt.
Nach einer kurzen Recherche wurde festgestellt, dass die Zeilen von itmdump gepuffert und in stderr angezeigt werden sollten - dann werden sie interaktiv in der Konsole angezeigt, während das Programm debuggt wird. Ich habe eine modifizierte Version von itmdump auf GitHub hochgeladen.
Es gibt noch eine Einschränkung. Das Debuggen beim Start hängt von der Ausführung des Befehls "monitor tpiu config ..." ab, wenn itmdump zuvor nicht ausgeführt wurde. Dies geschieht aufgrund der Tatsache, dass das Öffnen der Pipe (/home/esynr3z/itm.fifo) in openocd zum Schreiben blockiert und der Debugger hängt, bis sich die Pipe zum Lesen am anderen Ende öffnet.
Dies ist etwas unangenehm, insbesondere wenn irgendwann ITM nicht benötigt wird, Sie es jedoch im Leerlauf ausführen müssen, entweder ständig den GDB-Server wechseln oder Zeilen in seinen Einstellungen löschen / hinzufügen müssen. Deshalb musste ich ein wenig Openocd-Quellen graben und den Ort finden, an dem Sie eine kleine Krücke ersetzen müssen.
In der Datei src / target / armv7m_trace.c befindet sich eine Zeile mit dem gewünschten Öffnungsvorgang
armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab");
es muss ersetzt werden durch
int fd = open(CMD_ARGV[cmd_idx], O_CREAT | O_RDWR, 0664); armv7m->trace_config.trace_file = fdopen(fd, "ab");
Jetzt öffnet sich unsere Pfeife sofort und leuchtet nicht mehr. Sie können also die Bare-Metal-Einstellungen in Ruhe lassen und itmdump nur bei Bedarf ausführen.
Infolgedessen sieht die Ausgabe von Nachrichten während des Debuggens so aus

printf () -> UART -> Qt Creator
In diesem Fall ist alles ungefähr gleich:
- Fügen Sie dem Code eine Funktion mit UART-Initialisierung hinzu
- Wir implementieren retarget_put_char (), wobei das Zeichen an den Transceiver-Puffer gesendet wird
- Wir schließen den USB-UART-Adapter an
- Fügen Sie den externen Tools ein Dienstprogramm hinzu, das Zeilen vom virtuellen COM-Anschluss liest und auf dem Bildschirm anzeigt.
Ich habe ein solches Dienstprogramm in C - uartdump entworfen . Die Verwendung ist recht einfach - Sie müssen nur den Portnamen und die Baudrate angeben.

Es lohnt sich jedoch, eine Funktion zu erwähnen. Dieses Dienstprogramm hängt nicht vom Debuggen ab, und Qt Creator bietet keine Optionen zum Schließen laufender externer Tools. Um das Lesen des COM-Anschlusses zu beenden, habe ich ein weiteres externes Tool hinzugefügt.

Für alle Fälle werde ich einen Link zur CMake-Vorlage für das Projekt anhängen, das auf den Screenshots angezeigt wurde - GitHub .