Avec une sonde de débogage compatible, comme la J-Link, SEGGER RTT Viewer permet d'afficher directement sur l'ordinateur les informations transmises par un microcontrôleur. Il devient ainsi plus facile de suivre l'exécution d'un programme, d'afficher des messages de diagnostic et de repérer d'éventuelles erreurs.
Son principal avantage est de pouvoir surveiller une variable en temps réel, sans devoir arrêter le programme à chaque vérification. On peut ainsi observer l'évolution de ses valeurs pendant le fonctionnement du système et mieux comprendre le comportement de son application embarquée.
Nous allons découvrir comment configurer et utiliser RTT Viewer pour suivre le fonctionnement d'un programme et observer les variables qui nous intéressent.
SEGGER RTT (Real Time Transfer) est un mécanisme de communication qui permet d'échanger des informations entre un microcontrôleur et un ordinateur, par l'intermédiaire d'un débogueur compatible, comme la sonde J-Link.
Contrairement à une communication série classique utilisant l'UART, SEGGER RTT présente plusieurs avantages :
Le logiciel SEGGER RTT Viewer permet notamment d'afficher en temps réel les messages envoyés par l'application embarquée. C'est un moyen simple de suivre son fonctionnement, de contrôler certaines valeurs et de faciliter la recherche d'erreurs pendant le développement.
Le fonctionnement de SEGGER RTT repose sur une zone mémoire réservée aux échanges de données, appelée RTT Buffer. L'application exécutée sur le microcontrôleur STM32 y écrit les informations qu'elle souhaite transmettre.
Le débogueur J-Link accède ensuite à cette mémoire par l'interface de débogage et récupère les données disponibles. Celles-ci sont transmises au logiciel RTT Viewer, qui les affiche sur l'ordinateur.
Le cheminement des données peut être résumé de la manière suivante :
STM32 → RTT Buffer → J-Link → RTT Viewer
Ce mécanisme permet de consulter les informations transmises par le programme sans utiliser de liaison série UART dédiée. Il est ainsi possible de suivre son exécution et d'analyser les valeurs affichées pendant le fonctionnement du système.
Pour commencer, il faut télécharger et installer le package SEGGER J-Link, disponible sur le site du fabricant. Il regroupe les outils nécessaires au débogage et à la programmation des microcontrôleurs compatibles.
Après l'installation, plusieurs utilitaires sont disponibles, notamment :
Pour utiliser RTT dans un projet STM32, il faut ajouter les fichiers
sources et les fichiers d'en-tête correspondants. Ils se trouvent
dans le sous-répertoire SEGGER/RTT/ du package SEGGER.
Les fichiers à intégrer au projet sont les suivants :
SEGGER_RTT.cSEGGER_RTT.hSEGGER_RTT_printf.cSEGGER_RTT_printf.h
Les fichiers .c doivent être ajoutés à la compilation
du projet. Les fichiers .h doivent être accessibles
au compilateur grâce aux chemins d'inclusion configurés dans
l'environnement de développement.
Dans une configuration classique, SEGGER RTT ne nécessite pas d'initialisation explicite. Le mécanisme est initialisé lors de son premier appel, à condition que les fichiers nécessaires soient correctement intégrés au projet.
Pour commencer à utiliser RTT dans le fichier main.c,
il suffit d'inclure le fichier d'en-tête :
#include "SEGGER_RTT.h"
Le programme pourra ensuite utiliser les fonctions RTT, par exemple pour afficher un message ou transmettre la valeur d'une variable vers l'ordinateur.
Pour vérifier que RTT fonctionne correctement, il suffit d'envoyer
un premier message à l'aide de la fonction
SEGGER_RTT_WriteString().
SEGGER_RTT_WriteString(0, "Application started\r\n");
Résultat dans RTT Viewer :
Application started
Le paramètre 0 désigne le canal RTT utilisé.
Le canal 0 correspond au canal de sortie par défaut.
RTT permet également d'afficher la valeur d'une variable.
Dans l'exemple suivant, la fonction
SEGGER_RTT_printf() affiche la valeur du compteur
counter à l'aide du format %lu,
adapté à un entier non signé de type unsigned long.
uint32_t counter = 123;
SEGGER_RTT_printf(0,
"Counter = %lu\r\n",
(unsigned long)counter);
Affichage dans RTT Viewer :
Counter = 123
Il est possible d'afficher plusieurs variables dans un même message.
Chaque spécificateur de format, comme %lu ou
%.1f, indique comment la valeur correspondante doit
être présentée.
uint32_t adc_value = 2048;
float temperature = 25.7f;
SEGGER_RTT_printf(0,
"ADC=%lu Temp=%.1f C\r\n",
(unsigned long)adc_value,
(double)temperature);
Affichage dans RTT Viewer :
ADC=2048 Temp=25.7 C
Pour suivre l'évolution d'une valeur au cours du temps, on peut
afficher régulièrement le compteur de temps du STM32, fourni
par la fonction HAL_GetTick().
Dans cet exemple, un message est envoyé chaque seconde.
while (1)
{
SEGGER_RTT_printf(0,
"Tick=%lu\r\n",
(unsigned long)HAL_GetTick());
HAL_Delay(1000);
}
Exemple d'affichage dans RTT Viewer :
Tick=1000
Tick=2000
Tick=3000
...
Remarque : ces valeurs sont données à titre
d'exemple. HAL_GetTick() renvoie le nombre de
millisecondes écoulées depuis le démarrage du compteur système.
La première valeur affichée dépend donc du moment où le programme
commence à envoyer les messages.
RTT peut aussi faciliter le débogage d'une structure contenant plusieurs variables. Il suffit de transmettre ses différents champs à la fonction d'affichage.
typedef struct
{
uint32_t speed;
uint32_t rpm;
float current;
} Motor_t;
Motor_t motor =
{
.speed = 1500,
.rpm = 3200,
.current = 2.35f
};
SEGGER_RTT_printf(0,
"Speed=%lu RPM=%lu Current=%.2f\r\n",
(unsigned long)motor.speed,
(unsigned long)motor.rpm,
(double)motor.current);
Affichage dans RTT Viewer :
Speed=1500 RPM=3200 Current=2.35
Cette méthode est particulièrement pratique pour observer simultanément plusieurs paramètres d'une application embarquée. Elle permet de suivre leur évolution pendant l'exécution du programme et de repérer plus facilement une valeur inattendue, sans devoir arrêter systématiquement le microcontrôleur.
Pour simplifier l'affichage des messages de débogage, il est
possible de définir une macro appelée DEBUG_PRINT.
Elle permet d'utiliser une syntaxe plus courte tout en conservant
les possibilités de la fonction SEGGER_RTT_printf().
#define DEBUG_PRINT(...) \
SEGGER_RTT_printf(0, __VA_ARGS__)
Grâce à cette macro, il suffit ensuite d'appeler
DEBUG_PRINT() pour afficher un message ou la valeur
d'une variable.
Exemple d'utilisation :
DEBUG_PRINT("Value=%lu\r\n", (unsigned long)value);
L'intérêt est de rendre le code plus lisible et de faciliter l'ajout de messages de diagnostic tout au long du programme.
Voici un exemple qui illustre l'utilisation de SEGGER RTT dans une application STM32. Après l'initialisation du microcontrôleur, un premier message confirme le démarrage. Le programme affiche ensuite la valeur d'un compteur toutes les secondes.
#include "SEGGER_RTT.h"
int main(void)
{
HAL_Init();
SystemClock_Config();
SEGGER_RTT_WriteString(0,
"STM32 Started\r\n");
uint32_t counter = 0;
while (1)
{
SEGGER_RTT_printf(0,
"Counter=%lu\r\n",
(unsigned long)counter++);
HAL_Delay(1000);
}
}
Sortie dans RTT Viewer :
STM32 Started
Counter=0
Counter=1
Counter=2
Counter=3
...
Cet exemple montre comment suivre simplement l'exécution d'un programme sans utiliser de liaison UART dédiée. En remplaçant le compteur par une variable issue d'un capteur, d'un convertisseur analogique-numérique (ADC) ou d'un calcul, il devient possible d'observer son évolution en temps réel et de faciliter le débogage.
SEGGER RTT permet d'adapter la taille des buffers aux besoins
de l'application. Cette configuration s'effectue généralement
dans le fichier SEGGER_RTT_Conf.h, lorsqu'il est
fourni avec la version utilisée. Selon le package, il peut se
trouver dans le dossier RTT/ ou dans un répertoire
de configuration spécifique.
Deux paramètres importants permettent de définir la taille des buffers utilisés pour les échanges entre le microcontrôleur et l'ordinateur :
BUFFER_SIZE_UP : taille du buffer utilisé
pour transmettre les données du STM32 vers le PC.
BUFFER_SIZE_DOWN : taille du buffer utilisé
pour transmettre les données du PC vers le STM32.
Dans une configuration courante, le buffer montant possède une taille par défaut de 1024 octets, tandis que le buffer descendant est souvent limité à 16 octets. Ces valeurs peuvent varier selon la version et la configuration du package SEGGER.
Lorsque l'application génère beaucoup de messages de débogage, il peut être utile d'augmenter la taille du buffer montant. Cela permet de stocker davantage de données en attente de lecture par le débogueur.
Par exemple, dans le fichier de configuration :
#define BUFFER_SIZE_UP (4096)
#define BUFFER_SIZE_DOWN (16)
Une taille de 4096 octets peut convenir à certaines applications. Si la mémoire RAM disponible le permet, une taille supérieure peut également être envisagée.
Attention : augmenter la taille du buffer ne garantit pas à elle seule l'absence de perte de données. Si l'application produit des informations plus rapidement que RTT Viewer ne peut les récupérer, le buffer finira par se remplir. Le mode de fonctionnement choisi détermine alors le comportement du programme.
SEGGER RTT propose trois modes d'écriture. Ils définissent le comportement du programme lorsque le buffer ne dispose plus d'assez de place pour recevoir les données.
En mode bloquant, l'application attend que suffisamment de place soit disponible pour écrire les données dans le buffer.
Avantage :
Inconvénient :
Ce mode est à réserver aux situations où la conservation des données est prioritaire sur le respect des contraintes temporelles.
Dans ce mode, appelé SEGGER_RTT_MODE_NO_BLOCK_SKIP,
l'application n'attend pas que le buffer se libère. Si l'espace
disponible est insuffisant pour accueillir l'ensemble du message,
celui-ci est ignoré.
Avantage :
Inconvénient :
Le mode SEGGER_RTT_MODE_NO_BLOCK_TRIM permet
d'écrire autant de données que la place disponible le permet.
Si le buffer est presque plein, seule une partie du message
est enregistrée ; le reste est abandonné.
Avantage :
Inconvénient :
Ce mode peut être utile pour des traces de débogage lorsque la continuité de l'exécution est importante.
Pour le canal de sortie par défaut, le mode peut généralement
être sélectionné dans le fichier SEGGER_RTT_Conf.h
à l'aide du paramètre SEGGER_RTT_MODE_DEFAULT.
Par exemple, pour utiliser le mode non bloquant avec suppression :
#define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_NO_BLOCK_SKIP
Pour privilégier la conservation des données, on peut choisir le mode bloquant :
#define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL
Le mode par défaut est généralement non bloquant avec suppression. Il convient de vérifier la configuration de la version utilisée. Le mode choisi doit aussi être adapté à l'application : le mode bloquant peut perturber les tâches qui doivent respecter des délais stricts.
Imaginons une application qui doit transmettre 1000 échantillons acquis par un convertisseur analogique-numérique (ADC). Si les données sont envoyées en continu, plusieurs facteurs peuvent entraîner une perte d'informations :
SEGGER_RTT_MODE_NO_BLOCK_SKIP et ne dispose plus d'assez de place pour un message complet.Pour limiter ces risques, plusieurs solutions sont possibles :
BUFFER_SIZE_UP, en tenant compte de la RAM disponible.SEGGER_RTT_printf().Pour des acquisitions rapides, il est également préférable de séparer la collecte des échantillons de leur transmission vers le PC. L'application peut ainsi enregistrer les mesures dans une mémoire tampon dédiée, puis les envoyer par blocs lorsque cela est possible.
Pour transférer de grandes quantités de données, il est souvent
plus efficace d'utiliser SEGGER_RTT_Write() ou
SEGGER_RTT_WriteNoLock(), plutôt que de multiplier
les appels à SEGGER_RTT_printf().
En effet, la fonction SEGGER_RTT_printf() doit
convertir les valeurs en texte avant de les transmettre.
Pour des données déjà organisées dans un buffer, l'écriture
directe permet de limiter ce travail supplémentaire.
Par exemple, pour transmettre un bloc de données :
SEGGER_RTT_Write(0,
(const char *)buffer,
buffer_size);
Le canal 0 correspond au canal de sortie par défaut.
Le paramètre buffer désigne la zone mémoire contenant
les données, tandis que buffer_size indique leur
longueur en octets.
La fonction SEGGER_RTT_WriteNoLock() permet également
d'écrire des données sans utiliser le mécanisme de verrouillage
interne. Elle doit toutefois être réservée aux situations où
l'application garantit elle-même que les accès concurrents
ne provoqueront pas de conflit.
RTT Viewer permet de suivre les échanges de données avec le microcontrôleur. Selon la version du logiciel, les informations affichées peuvent notamment comprendre :
Le débit réellement obtenu dépend de plusieurs facteurs : le modèle de la sonde J-Link, la fréquence de l'interface SWD, le microcontrôleur utilisé, la taille des buffers et la quantité de données produites par l'application.
Avec une sonde adaptée et une liaison SWD suffisamment rapide, des débits de plusieurs centaines de kilo-octets par seconde peuvent être atteints dans certaines configurations. Il est toutefois préférable de mesurer le débit sur le matériel utilisé plutôt que de considérer cette valeur comme garantie.
Pour exporter de grandes quantités de données, par exemple des acquisitions ADC, des valeurs issues d'un régulateur PID, des mesures provenant d'un capteur IMU ou des données destinées à être enregistrées au format CSV, quelques précautions sont utiles.
Si la mémoire RAM disponible le permet, il est conseillé d'augmenter la taille du buffer RTT. Par exemple :
#define BUFFER_SIZE_UP (16384)
Une taille de 16 ko constitue un point de départ possible. Elle peut être augmentée à 32 ko ou davantage si l'application produit beaucoup de données et si la mémoire disponible le permet. Il faut également tenir compte de la configuration effective du buffer dans la version de SEGGER RTT utilisée.
Quelques bonnes pratiques permettent d'améliorer les transferts :
SEGGER_RTT_printf()
dans les interruptions rapides, afin de limiter leur durée
d'exécution.
SEGGER_RTT_Write() pour transmettre
de gros blocs de données déjà préparés en mémoire.
Sur un STM32F4 disposant de plusieurs centaines de kilo-octets de RAM, un buffer RTT de 16 à 32 ko peut représenter un bon compromis pour certaines applications. Il convient cependant de vérifier que cette réservation mémoire ne pénalise pas les autres fonctions du programme.
À retenir : le principal intérêt de SEGGER RTT est de pouvoir observer les messages et les valeurs transmises par le microcontrôleur pendant l'exécution du programme. En choisissant correctement la taille du buffer et le mode d'écriture, cet outil peut également servir à récupérer efficacement des données de mesure pour les analyser sur PC.
Voici quelques définitions des termes et abréviations utilisés dans cette présentation de SEGGER RTT Viewer.