Rediriger printf () de STM32 vers Qt Creator Console

kdpv.svg


Souvent, lors du débogage d'un logiciel de microcontrôleur, il devient nécessaire d'afficher des messages de débogage, des journaux, des données capturées et d'autres choses sur l'écran du PC. En même temps, je veux que la sortie soit plus rapide et que les lignes ne soient affichées nulle part, mais directement dans l'IDE - sans s'écarter du code, pour ainsi dire. En fait, il s'agit de l'article - comment j'ai essayé printf () pour sortir et afficher dans mon environnement Qt Creator préféré, mais pas très microcontrôleur.


En général, vous pouvez trouver un grand nombre de façons de sortir des informations textuelles à partir du microcontrôleur. Cependant, les techniques les plus utilisées ne sont pas si nombreuses:



Le semi-hébergement est plutôt lent, RTT est lié aux solutions matérielles et logicielles Segger *, l'USB n'est pas dans tous les microcontrôleurs. Par conséquent, généralement, je préfère les deux derniers - l'utilisation de l'UART et de l'ITM. À leur sujet et seront discutés ci-dessous.


* Upd. - en fait, comme suggéré dans les commentaires, il n'en est pas ainsi. Il existe des options tant sur le plan logiciel que matériel. Par conséquent, parmi les méthodes ci-dessus, RTT sera peut-être la plus universelle.


Et tout de suite quelques explications sur le logiciel qui sera utilisé ensuite. J'ai maintenant Fedora 28 comme système d'exploitation, et l'ensemble de logiciels actuel pour travailler avec des microcontrôleurs est le suivant:



Rediriger printf () dans GCC


Donc, pour rediriger la sortie de printf () dans GCC, vous devez ajouter des clés de l'éditeur de liens


-specs=nosys.specs -specs=nano.specs 

Si vous devez afficher des nombres à virgule flottante, vous devez vous rappeler la clé


 -u_printf_float 

Et implémentez la fonction _write (). Par exemple, quelque chose comme ça


 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; } 

où retarget_put_char () est une fonction qui chargera le caractère directement dans l'interface souhaitée.


printf () -> ITM -> Qt Creator


Instrumentation Trace Macrocell (ITM) est un bloc à l'intérieur du noyau Cortex-M3 / M4 / M7 utilisé pour la sortie (traçage) non invasive de divers types d'informations de diagnostic. Pour implémenter printf () sur ITM, vous devez connaître les éléments suivants:


  • Utilise l'horloge TRACECLKIN, dont la fréquence est généralement égale à la fréquence centrale
  • Dispose de 32 ports dits de stimulus pour la sortie de données
  • CMSIS incorpore la fonction ITM_SendChar (), qui charge un symbole dans le port de stimulation 0
  • Les données sont sorties soit via un bus synchrone (TRACEDATA, TRACECLK), soit via une ligne SWO monofilaire asynchrone (TRACESWO)
  • La ligne SWO est généralement multiplexée avec JTDO, ce qui signifie qu'elle ne fonctionne qu'en mode débogage par SWD
  • Le retrait par SWO s'effectue soit en utilisant le code Manchester, soit en NRZ (UART 8N1)
  • Les données sont transmises dans des trames d'un certain format - vous avez besoin d'un analyseur côté réception
  • ITM est généralement configuré à partir de l'EDI ou de l'utilitaire correspondant (cependant, personne n'interdit de le configurer dans le code du programme - alors la sortie vers SWO fonctionnera sans une session de débogage élevée)

La façon la plus pratique d'utiliser ITM est de sortir via SWO en utilisant l'encodage NRZ - ainsi, vous n'avez besoin que d'une seule ligne, et il sera possible de recevoir des données non seulement en utilisant un débogueur avec une entrée spéciale, mais aussi un adaptateur USB-UART ordinaire, bien qu'à une vitesse inférieure.


J'ai suivi le chemin à l'aide d'un débogueur et j'ai été forcé de modifier mon STLink-V2 chinois pour prendre en charge SWO. Ensuite, tout est simple - nous connectons le microcontrôleur JTDO / TRACESWO à la broche de débogage correspondante, et allons configurer le logiciel.


Openocd a la commande "tpiu config" - avec elle, vous pouvez configurer la méthode d'affichage des informations de trace (plus en détail dans le Guide d'utilisation d'OpenOCD ). Ainsi, par exemple, en utilisant des arguments


 tpiu config internal /home/esynr3z/itm.fifo uart off 168000000 

configurez la sortie dans le fichier /home/esynr3z/itm.fifo, utilisez l'encodage NRZ et calculez la vitesse de transfert maximale en fonction de la fréquence TRACECLKIN de 168 MHz - pour STLink, elle est de 2 MHz. Et une autre équipe


 itm port 0 1 

activera le port zéro pour le transfert de données.


Le code source d'OpenOCD inclut l'utilitaire itmdump (contrib / itmdump.c) - avec lui, vous pouvez analyser des chaînes à partir des données reçues.


Pour compiler on entre


 gcc itmdump.c -o itmdump 

Au démarrage, spécifiez le fichier / pipe / ttyUSB * et le commutateur -d1 nécessaires pour afficher les octets de données reçus sous forme de chaînes


 ./itmdump -f /home/esynr3z/itm.fifo -d1 

Et le dernier. Pour envoyer un caractère via SWO, nous complétons _write (), décrit ci-dessus, avec une fonction


 int retarget_put_char(int ch) { ITM_SendChar((uint32_t)ch); return 0; } 

Donc, le plan général est le suivant: dans Qt Creator, nous configurons openocd pour enregistrer toutes les informations reçues sur SWO dans un canal nommé précédemment créé, et nous pouvons lire le tube, analyser les chaînes et l'afficher à l'aide d'itmdump, qui s'exécute comme un outil externe. Bien sûr, il existe un moyen plus élégant de résoudre le problème: écrire le plug-in approprié pour Qt Creator. Cependant, j'espère que l'approche décrite ci-dessous s'avérera utile à quelqu'un.


Accédez aux paramètres du plugin Bare Metal (Outils-> Options-> Périphériques-> Bare Metal).


config_baremetal.png


Sélectionnez le serveur GDB utilisé et ajoutez les commandes d'initialisation de ligne à la fin de la liste


 monitor tpiu config internal /home/esynr3z/itm.fifo uart off 168000000 monitor itm port 0 1 

Maintenant, juste avant que le débogueur place le curseur au tout début de main (), ITM sera configuré.


Ajoutez itmdump en tant qu'outil externe (Outils-> Externe-> Configurer ...).


external_itmdump.png


N'oubliez pas de régler la variable


 QT_LOGGING_TO_CONSOLE=1 

pour afficher la sortie de l'utilitaire sur la console Qt Creator (panneau 7 Messages généraux).


Maintenant, activez itmdump, activez le mode débogage, lancez l'exécution du code et ... rien ne se passe. Cependant, si vous interrompez le débogage, l'exécution d'itmdump se terminera et toutes les lignes imprimées via printf () apparaîtront dans l'onglet Messages généraux.


Après une courte recherche, il a été constaté que les lignes de itmdump devraient être mises en mémoire tampon et affichées dans stderr - puis elles apparaissent de manière interactive dans la console, lors du débogage du programme. J'ai téléchargé une version modifiée d'itmdump sur GitHub .


Il y a une autre mise en garde. Le débogage au démarrage suspendra l'exécution de la commande "monitor tpiu config ..." si itmdump n'est pas exécuté auparavant. Cela se produit en raison du fait que l'ouverture du canal (/home/esynr3z/itm.fifo) dans openocd pour l'écriture est bloquante, et le débogueur se bloque jusqu'à ce que le canal s'ouvre pour la lecture de l'autre extrémité.


C'est quelque peu désagréable, surtout si à un moment donné, ITM n'est pas nécessaire, mais vous devez exécuter itmdump de façon inactive, soit constamment changer de serveur GDB, soit supprimer / ajouter des lignes dans ses paramètres. Par conséquent, j'ai dû creuser un peu de sources openocd et trouver l'endroit où vous devez remplacer une petite béquille.


Dans le fichier src / target / armv7m_trace.c, il y a une ligne avec la procédure d'ouverture souhaitée


 armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab"); 

il doit être remplacé par


 int fd = open(CMD_ARGV[cmd_idx], O_CREAT | O_RDWR, 0664); armv7m->trace_config.trace_file = fdopen(fd, "ab"); 

Maintenant, notre pipe s'ouvrira immédiatement et ne brillera pas. Vous pouvez donc laisser les paramètres Bare Metal seuls, et itmdump ne s'exécute que lorsque cela est nécessaire.


Par conséquent, la sortie des messages pendant le débogage ressemble à ceci


debug.png


printf () -> UART -> Qt Creator


Dans ce cas, tout est à peu près le même:


  • Ajouter une fonction avec initialisation UART au code
  • Nous implémentons retarget_put_char (), où le caractère sera envoyé au tampon de l'émetteur-récepteur
  • Nous connectons l'adaptateur USB-UART
  • Ajoutez un utilitaire aux outils externes qui lira les lignes du port COM virtuel et les affichera à l'écran.

J'ai esquissé un tel utilitaire dans C - uartdump . L'utilisation est assez simple - il vous suffit de spécifier le nom du port et la vitesse de transmission.


external_uartdump.png


Cependant, il convient de noter une caractéristique. Cet utilitaire ne dépend pas du débogage et Qt Creator ne propose aucune option pour fermer les outils externes en cours d'exécution. Par conséquent, pour arrêter la lecture du port COM, j'ai ajouté un autre outil externe.


external_uartdump_close.png


Eh bien, juste au cas où, je vais attacher un lien vers le modèle CMake pour le projet qui est apparu sur les captures d'écran - GitHub .

Source: https://habr.com/ru/post/fr440024/


All Articles