printf()Pendant le développement d'un programme embarqué, il est souvent utile d'afficher des messages pour suivre son exécution ou vérifier la valeur de certaines variables.
Sur un STM32F722, la fonction printf() peut envoyer des messages
vers l'environnement de débogage grâce au mécanisme ITM
(Instrumentation Trace Macrocell) et à la sortie SWO
(Serial Wire Output).
Cette méthode nécessite une sonde de débogage compatible, comme la J-Link, ainsi qu'une configuration adaptée du microcontrôleur et de VS Code. Voici les principales étapes pour mettre en place cette solution.
Relier les signaux suivants entre le microcontrôleur et la sonde de débogage :
| STM32F722 | J-Link | Fonction |
|---|---|---|
PA13 / SWDIO |
SWDIO |
Données de débogage |
PA14 / SWCLK |
SWCLK |
Horloge de débogage |
GND |
GND |
Masse commune |
NRST |
RESET |
Réinitialisation, conseillée |
PB3 / SWO |
SWO |
Sortie des messages ITM |
Important : la liaison SWD permet de programmer et de déboguer le microcontrôleur, mais la broche SWO est nécessaire pour transmettre les messages ITM vers la sonde.
Dans STM32CubeMX, ouvrir System Core → SYS, puis sélectionner
Trace Asynchronous SW dans le paramètre Debug.
Cette configuration active le mode de trace asynchrone. Vérifier également que
la broche PB3 est bien configurée pour la fonction SWO.
Régénérer ensuite le code et compiler le projet.
launch.jsonLa configuration dépend de l'extension de débogage installée dans VS Code. Avec Cortex-Debug, il faut notamment configurer la fréquence du processeur et activer la prise en charge de SWO.
Attention : cpuFrequency doit correspondre à la
fréquence réelle du cœur du processeur. Ne pas recopier automatiquement
la fréquence du PLL : la fréquence HCLK dépend également des diviseurs d'horloge.
Exemple de configuration à adapter à votre projet :
{
"name": "STM32F722 - SWO",
"type": "cortex-debug",
"request": "launch",
"servertype": "jlink",
"interface": "swd",
"runToEntryPoint": "main",
"swoConfig": {
"enabled": true,
"cpuFrequency": 216000000,
"swoFrequency": 2000000,
"source": "probe"
}
}
Les fréquences indiquées sont des exemples et doivent être adaptées à votre
configuration. Vérifier aussi la syntaxe attendue par la version de Cortex-Debug
utilisée : ce bloc ne constitue pas nécessairement un fichier
launch.json complet.
Avant d'envoyer des caractères, il faut activer le bloc de trace et le port ITM utilisé pour la sortie. Dans un projet STM32F7 utilisant les en-têtes CMSIS, on peut commencer par cette fonction :
#include "stm32f7xx.h"
static void ITM_Init(void)
{
/* Autoriser l'accès au bloc de trace */
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
/* Activer le port ITM numéro 0 */
ITM->TCR = ITM_TCR_ITMENA_Msk
| (1UL << ITM_TCR_TraceBusID_Pos);
ITM->TER = 1UL;
}
Appeler ITM_Init() une fois au démarrage, après la configuration
système et les horloges.
Cette initialisation active le port ITM numéro 0. Elle ne configure pas à elle seule toute la chaîne SWO : la sonde et l'outil de débogage doivent également être configurés pour recevoir les données.
printf() vers l'ITM
Pour que les caractères envoyés par printf() arrivent sur le port
ITM, il faut rediriger la sortie standard. Avec de nombreuses configurations
utilisant newlib, cela peut être réalisé en définissant la fonction
_write().
#include <stdio.h>
#include "stm32f7xx.h"
int _write(int file, char *ptr, int len)
{
(void)file;
for (int i = 0; i < len; i++)
{
/* Ne pas attendre si la trace n'est pas activée */
if ((CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk) == 0U
|| (ITM->TCR & ITM_TCR_ITMENA_Msk) == 0U
|| (ITM->TER & 1UL) == 0U)
{
return len;
}
ITM_SendChar((uint32_t)ptr[i]);
}
return len;
}
Cette fonction transmet les caractères au port ITM numéro 0. Elle convient
aux projets dont la bibliothèque C utilise _write() pour la sortie
standard. Selon la bibliothèque et les options de liaison, une autre fonction
de redirection peut être nécessaire.
Les contrôles évitent de rester bloqué lorsque l'ITM n'est pas activé. Ils ne garantissent toutefois pas que les messages seront reçus si la sonde ou la configuration SWO est incorrecte.
stdio.h
Dans chaque fichier C qui utilise printf(), ajouter :
#include <stdio.h>
Cette en-tête déclare la fonction printf(). Sans lui, le compilateur
peut signaler une déclaration manquante.
printf()Une fois la redirection en place, les messages peuvent être écrits comme dans un programme C classique :
printf("Demarrage du programme\n");
printf("Valeur du compteur : %lu\n", (unsigned long)compteur);
printf("Temperature : %.1f C\n", temperature);
Le caractère \n termine la ligne, mais ne garantit pas à lui seul
l'envoi immédiat du message. Le comportement dépend notamment de la bibliothèque C
et de la mise en tampon de la sortie standard.
Pour les messages de diagnostic, privilégier des textes courts et éviter d'envoyer trop de données dans une boucle très rapide.
Démarrer une session de débogage dans VS Code avec la configuration adaptée à la J-Link et à SWO.
Selon l'extension et sa version, les messages apparaissent dans une vue dédiée à la trace ou à l'ITM, et pas nécessairement dans le terminal intégré classique.
Exemple de résultat attendu :
Demarrage du programme
Valeur du compteur : 1234
Temperature : 24.5 CSi aucun message n'apparaît, vérifier les points suivants :
PB3 / SWO est correctement reliée à la J-Link.cpuFrequency correspond à la fréquence réelle du cœur._write() est compilée et liée au projet.ITM->TCR et ITM->TER sont correctement configurés.
Pour afficher quelques messages pendant les premiers développements, SWO avec
printf() est une solution simple et pratique.
Pour des projets plus importants, SEGGER RTT constitue souvent une alternative intéressante : il offre généralement un débit supérieur, plusieurs canaux de communication et ne dépend pas d'une liaison de trace SWO.
Pour nos premiers développements autour du STUSB4500, la solution SWO reste toutefois adaptée pour afficher des messages de diagnostic sans ajouter de liaison série dédiée.