jeudi 28 mars 2019

STM32-DUINO : déboguer




STM32-DUINO : déboguer


Dans cet article je vous présente plusieurs solutions pour déboguer un logiciel développé sur STM32, particulièrement si celui-ci est développé avec l'IDE ARDUINO et le package STM32DUINO, ou compilé en ligne de commande avec un Makefile.

Cet article fait suite à celui-ci : https://riton-duino.blogspot.com/2018/03/stm32f103c8t6-et-arduino.html

Nous allons commencer par expliquer le principe d'un débogueur (ou debugger en anglais).

1. Les bases

Le debugger est un logiciel permettant de chercher (et éventuellement de trouver et corriger) les bugs d'un logiciel en cours de développement ou d'un logiciel déjà opérationnel mais sur lequel a été détecté un dysfonctionnement.

Il permet différentes opérations comme :
  • lancer l'application
  • interrompre l'application
  • examiner les variables globales, statiques ou locales
  • examiner la pile (stack in english)
  • modifier une variable
  • mettre en place des points d'arrêt
  • faire un saut de ligne dans l'application
  • etc.
Un debugger peut être un logiciel en ligne de commande comme gdb par exemple, ou un logiciel avec une interface graphique.

2. La chaîne de développement

J'ai réuni les informations concernant les différents environnements de développement ici :

https://riton-duino.blogspot.com/2019/03/stm32-environnements-de-developpement.html

2.1. Le boîtier JTAG

2.1.1. Le boîtier ST-LINK

Vous utilisez peut-être une carte avec un connecteur SWD approprié, comme une BLUE PILL par exemple, auquel cas il vous faut aussi un boîtier ST-LINK pour charger et déboguer votre application.

Voir ici pour l'installation : https://riton-duino.blogspot.com/2019/03/stm32-boitier-st-link.html

2.1.2. Le boîtier J-LINK

Il existe bien d'autres boîtiers JTAG en particulier le J-LINK, supporté par beaucoup d'environnements de développement.

2.1.3. La carte d'évaluation

Vous travaillez peut-être sur une carte avec un connecteur USB, comme une ST NUCLEO ou DISCOVERY, auquel cas l'électronique du ST-LINK est déjà intégrée à la carte :

Vous trouverez des informations sur ces cartes ici : https://riton-duino.blogspot.com/2018/03/stm32f103c8t6-et-arduino.html

Avec tout ce beau matériel, quand vous vous retrouvez confronté à un bug difficile à trouver, il vous manque simplement une chose : le debugger.


3. Notions générales

Avec un STM32, pour faire fonctionner le debugger, il faut deux logiciels :
  • st-util
  • gdb 
Ces deux logiciels sont complémentaires et dialoguent ensemble.

Les explications qui suivent sont basées sur l'installation sur un système UBUNTU, mais normalement cela ne devrait pas poser de problème sous Windows.
Je ferai un essai quand j'aurai terminé. Promis.

3.1. Le logiciel à déboguer

Le logiciel sur lequel nous allons travailler doit être préparé spécialement pour être utilisé. En effet il doit contenir des informations spéciales qui vont permettre au debugger d'assurer son rôle :
  • des noms de fichier
  • des numéros de ligne
  • pas d'optimisation
Ces informations, dans un logiciel compilé classiquement ne sont pas incluses dans le binaire car elles occupent de la place.

Il faut donc utiliser une option -g pour la compilation :

gcc -Wall -g prog.c -o prog

Avec un STM32 nous utilisons arm-none-eabi-gcc :

arm-none-eabi-gcc -mcpu=cortex-m4 -c -g -Og source.c -o source.o

A l'édition de liens il faut aussi préciser que nous voulons une version debug :

arm-none-eabi-gcc -mcpu=cortex-m4 -g -Og -o prog.elf source.o main.o
arm-none-eabi-objcopy" -O binary  prog.elf prog.bin

Les lignes de commandes sont simplifiées bien sûr, car dans la réalité il y a beaucoup plus d'options.

On peut remarquer deux lignes de commande pour l'édition de liens : l'éditeur de lien produit un fichier .elf contenant d'une part le code binaire de l'application et d'autre part toutes les informations de debug qui nous intéressent.
Ce fichier est ensuite converti en fichier binaire pur afin d'être chargé dans la FLASH du STM32.

3.1.1. La compilation

Ici nous allons parler uniquement de la génération de l'exécutable à partir de l'IDE ARDUINO et PlatformIO. Je pars du principe que celui qui utilise un Makefile maîtrise parfaitement la chose et n'a pas besoin d'explications.

3.1.1.1. IDE ARDUINO

Choisissez tout d'abord le type de carte avec le menu "Outils / Type de carte" : Nucleo-64 par exemple.
Choisissez le modèle de carte avec le menu "Outils / Board part Number" : Nucleo F401-RE par exemple.
Choisissez l'option "Outils / Optimize" : Debug (-g)
Choisissez l'option "Outils / Upload Method" : STLink

Ouvrez l'exemple BlinWithoutDelay à l'aide du menu "Fichier / Exemples / 02.Digital".
Modifiez-le pour ajouter quelques lignes pour afficher des messages sur la console :

const int ledPin =  LED_BUILTIN;// the number of the LED pin

// Variables will change:
int ledState = LOW;             // ledState used to set the LED

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated

// constants won't change:
const long interval = 1000;           // interval at which to blink (milliseconds)

void setup() {
  // set the digital pin as output:
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the difference
  // between the current time and last time you blinked the LED is bigger than
  // the interval at which you want to blink the LED.
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    Serial.print("currentMillis="); delay(100);
    Serial.println(currentMillis); delay(100);
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
  }
}


Branchez votre carte, 
Compilez et chargez l'application. Les messages suivants s'affichent pendant le chargement :

/home/riton/.arduino15/packages/STM32/tools/STM32Tools/1.2.1/tools/linux/stlink_upload ttyACM0 {upload.altID} {upload.usbID} /tmp/arduino_build_575652/BlinkWithoutDelay.ino.bin
 2019-03-28T18:26:48 INFO common.c: Loading device parameters....
2019-03-28T18:26:48 INFO common.c: Device connected is: F4 device (Dynamic Efficency), id 0x10006433
2019-03-28T18:26:48 INFO common.c: SRAM size: 0x18000 bytes (96 KiB), Flash: 0x80000 bytes (512 KiB) in pages of 16384 bytes
2019-03-28T18:26:48 INFO common.c: Attempting to write 35780 (0x8bc4) bytes to stm32 address: 134217728 (0x8000000)
EraseFlash - Sector:0x0 Size:0x4000 st-flash 1.5.1

Flash page at addr: 0x08000000 erasedEraseFlash - Sector:0x1 Size:0x4000
Flash page at addr: 0x08004000 erasedEraseFlash - Sector:0x2 Size:0x4000 2019-03-28T18:26:49 INFO common.c: Finished erasing 3 pages of 16384 (0x4000) bytes
2019-03-28T18:26:49 INFO common.c: Starting Flash write for F2/F4/L4
2019-03-28T18:26:49 INFO flash_loader.c: Successfully loaded flash loader in sram

Flash page at addr: 0x08008000 erased2019-03-28T18:26:49 INFO common.c: Starting verification of write complete
2019-03-28T18:26:50 INFO common.c: Flash written and verified! jolly good!

enabling 32-bit flash writes
size: 32768
size: 3012


Le nom de l'applicaion binaire générée est en gras. Sous LINUX le binaire est généré dans le répertoire /tmp, sous Windows c'est ici : C:\Users\Local Settings\Temp.

Après chargement dans la cible, tout se passe bien. La LED verte clignotte sur la carte.

3.1.1.2. PlatformIO
 
Sous PlatformIO c'est différent. Il faut créer un projet, choisir une carte parmi les nomreuses plateformes proposées.

Pour activer l'option de debug, il faut modifier le fichier platformio.ini :

[env:black_f407ve]
platform = ststm32
board = black_f407ve
framework = arduino
monitor_speed = 115200
build_flags = -g3 

upload_protocol = stlink
debug_tool = stlink


J'ai ajouté également les options indiquent comment le chargement devra s'opérer : stlink.

Je n'ai pas encore eu la possibilité de consacrer du temps à la mise oeuvre du débogeur sous PlatformIO. Mais cette possibilité est intégrée.

Lors du chargement l'IDE indique également l'emplacement du fichier exécutable :

Uploading .pioenvs/black_f407ve/firmware.elf

3.2. st-util

st-util est le logiciel serveur qui va communiquer avec le boîtier ST-LINK ou votre carte NUCLEO ou DISCOVERY par le câble USB.

Si vous n'utilisez pas STM32DUINO il faut le télécharger :

https://www.st.com/en/development-tools/st-link-v2.html#tools-software

Sinon, le nécessaire se trouve ici dans le répertoire .arduino15 de votre répertoire personnel :
Il y a une librairie libstlink.so.1 à copier sous /usr/lib :

Si la machine est en 64bits :
sudo cp .arduino15/packages/STM32/tools/STM32Tools/1.2.1/tools/linux64/stlink/lib/libstlink.so.1 /usr/lib
Si la machine est en 32bits :
sudo cp .arduino15/packages/STM32/tools/STM32Tools/1.2.1/tools/linux/stlink/lib/libstlink.so.1
/usr/lib

Vous pouvez soit modifier votre variable d'environnement PATH pour y ajouter le répertoire où se trouve st-util, soit copier st-util dans /usr/local/bin :
Si la machine est en 64bits :
sudo cp .arduino15/packages/STM32/tools/STM32Tools/1.2.1/tools/linux/stlink/st-util /usr/local/bin
Si la machine est en 32bits :
sudo cp .arduino15/packages/STM32/tools/STM32Tools/1.2.1/tools/linux64/stlink/st-util
/usr/local/bin

Ensuite il suffit de brancher le boîtier ST-LINK ou la carte NUCLEO ou DISCOVERY et de lancer st-util dans un terminal :

st-util
 

st-util 1.5.1
2019-03-28T17:57:56 INFO common.c: Loading device parameters....
2019-03-28T17:57:56 INFO common.c: Device connected is: F4 device (Dynamic Efficency), id 0x10006433
2019-03-28T17:57:56 INFO common.c: SRAM size: 0x18000 bytes (96 KiB), Flash: 0x80000 bytes (512 KiB) in pages of 16384 bytes
2019-03-28T17:57:56 INFO gdb-server.c: Chip ID is 00000433, Core ID is  2ba01477.
2019-03-28T17:57:56 INFO gdb-server.c: Listening at *:4242...


Ici je viens de le lancer avec une carte NUCLEO F401RE branchée sur un port USB. Elle est parfaitement reconnue.

Cette fenêtre doit rester ouverte et st-util doit tourner pendant toute la session de debug.
A chaque fois que vous voulez recompiler et recharger l'application, il faut tuer st-util (CTRL_C), et le relancer après avoir compilé et rechargé le code dans la cible.

Il est très probable que sous LINUX vous ayez à créer un petit fichier pour autoriser l'accès au device ST-LINK.

Si vous lancez st-util et que vous voyez ceci :

2019-03-29T19:12:31 WARN usb.c: Couldn't find any ST-Link/V2 devices

Créez un fichier /etc/udev/rules.d/49-stlinkv2.1.rules :

# stm32 nucleo boards, with onboard st/linkv2-1
# ie, STM32F0, STM32F4.
# STM32VL has st/linkv1, which is quite different

SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", \
    MODE:="0666", \
    SYMLINK+="stlinkv2-1_%n"

SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", \
    MODE:="0666", \
    SYMLINK+="stlinkv2_%n"

# If you share your linux system with other users, or just don't like the
# idea of write permission for everybody, you can replace MODE:="0666" with
# OWNER:="yourusername" to create the device owned by you, or with
# GROUP:="somegroupname" and mange access using standard unix groups.


Ce fichier permet à l'utilisateur non root d'accéder au device, qu'il soit un ST-LINK ST MicroElectronics ou un ST-LINK chinois.

3.3. gdb

Le deuxième logiciel est le debugger lui-même. Il doit bien sûr comprendre le langage machine STM32, donc ce ne sera pas le gdb installé normalement sur un système LINUX, qui ne connait que le langage machine INTEL.

Que l'on utilise gdb en ligne de commande ou une interface graphique, gdb est nécessaire.

Ici encore, tout dépend si l'on utilise STM32DUINO ou pas.

Si vous n'utilisez pas STM32DUINO il faut télécharger la chaîne de compilation :

https://developer.arm.com/tools-and-software/open-source-software/gnu-toolchain/gnu-rm/downloads

Sinon, le nécessaire se trouve dans le répertoire .arduino15 de votre répertoire personnel. Vous pouvez soit modifier votre variable d'environnement PATH pour y ajouter le répertoire où se trouve arm-none-eabi-gdb, soit copier arm-none-eabi-gdb dans /usr/local/bin :

sudo cp .arduino15/packages/STM32/tools/arm-none-eabi-gcc/6-2017-q2-update/bin/arm-none-eabi-gdb /usr/local/bin

4. gdb

gdb est un debugger en mode ligne de commande. Je sens que certains vont passer directement au chapitre suivant.

Le lancement :

Il suffit de lancer arm-none-eabi-gdb suivi du nom de l'appplication.

Nous avons vu plus haut que lors de la compilation (voir   3.1.1. La compilation) les messages de chargement de l'IDE ARDUINO indiquent le nom de l'application :

/tmp/arduino_build_575652/BlinkWithoutDelay.ino.bin

Copiez ce nom complet et remplacez .bin par .elf

Idem pour PlatformIO, fauf que le nom de fichier est déjà prêt :

.pioenvs/black_f407ve/firmware.elf

gdb se lance aussi dans un terminal depuis le répertoire où se situe vos fichiers source :
arm-none-eabi-gdb /tmp/arduino_build_575652/BlinkWithoutDelay.ino.elf

Ou :

arm-none-eabi-gdb .pioenvs/black_f407ve/firmware.elf

gdb se lance :

GNU gdb (GNU Tools for ARM Embedded Processors 6-2017-q2-update) 7.12.1.20170417-git
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-linux-gnu --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /tmp/arduino_build_575652/BlinkWithoutDelay.ino.elf...done.
(gdb)


Remarque :

Reading symbols from /tmp/arduino_build_575652/BlinkWithoutDelay.ino.elf...done.

Ce message indique que gdb a bien trouvé l'application et qu'il a chargé la table des symboles.
Si vous avez oublié de choisir l'option "Debug (-g)" dans "Outils / Optimize", vous aurez un message différent :

Reading symbols from /tmp/arduino_build_575652/BlinkWithoutDelay.ino.elf...(no debugging symbols found)...done.

L'invite de commande (gdb) apparaît. 
Si vous n'avez pas pas lancé st-util, faites-le (voir 3.1. st-util).
Connectons-nous au serveur st-util.

(gdb) target extended-remote :4242
Remote debugging using :4242
0x080010ec in Reset_Handler ()
(gdb) 


Dans ce qui suit l'invite de commande et la commande à taper est en gras. J'indique les commandes à taper et leur abbréviation.

Voyons où nous en sommes (commande "list" ou "l") :

(gdb) list 
41    #endif
42    #ifndef D_CACHE_DISABLED
43      SCB_EnableDCache();
44    #endif
45    #endif
46   
47      init();
48    }
49   
50    /*
(gdb) list 

51     * \brief Main entry point of Arduino application
52     */
53    int main( void )
54    {
55      initVariant();
56   
57      setup();
58   
59      for (;;)
60      {
(gdb) list 

61    #if defined(CORE_CALLBACK)
62        CoreCallback();
63    #endif
64        loop();
65        if (serialEventRun) serialEventRun();
66      }
67   
68      return 0;
69    }
(gdb)


Il s'agit du code d'initialization et plus bas on voit la fonction main().

Posons un point d'arrêt (commande "break" ou "b") :

(gdb) break loop
Breakpoint 1 at 0x8000fdc: file /home/riton/Arduino/BlinkWithoutDelay/BlinkWithoutDelay.ino, line 50.
(gdb)


Lançons l'application (commande "continue" ou "c") :

(gdb) continue
 Continuing.
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 2, loop () at /home/riton/Arduino/BlinkWithoutDelay/BlinkWithoutDelay.ino:50
50    void loop() {
(gdb)


L'application s'est arrêtée sur la fonction loop().
Visualisons quelques variables :

(gdb) print interval
$1 = 1000
(gdb) 

(gdb) print previousMillis
$2 = 0
(gdb)
(gdb) print currentMillis
$3 = <optimized out>

(gdb)

La variable interval vaut 1000, previousMillis vaut 0, ce qui semble assez normal.
La variable currentMillis n'existe pas encore.

Voyons un peu plus loin :

(gdb) list
45      // set the digital pin as output:
46      Serial.begin(115200);
47      pinMode(ledPin, OUTPUT);
48    }
49   
50    void loop() {
51      // here is where you'd put code that needs to be running all the time.
52   
53      // check to see if it's time to blink the LED; that is, if the difference
54      // between the current time and last time you blinked the LED is bigger than
(gdb) list

55      // the interval at which you want to blink the LED.
56      unsigned long currentMillis = millis();
57   
58      if (currentMillis - previousMillis >= interval) {
59        // save the last time you blinked the LED
60        Serial.print("currentMillis="); delay(100);
61        Serial.println(currentMillis); delay(100);
62        previousMillis = currentMillis;
63   
64        // if the LED is off turn it on and vice-versa:
(gdb)


Visualisons les points d'arret :

(gdb) info breakpoints
 1       breakpoint     keep y   0x08000fdc in loop() at /home/riton/Arduino/BlinkWithoutDelay/BlinkWithoutDelay.ino:50
(gdb)


Supprimons le premier car l'application va sans arrêt s'arrêter sur celui-ci si nous continuons :

(gdb) del 1
(gdb)

Posons un breakpoint en ligne 60 :

(gdb) break 60
Breakpoint 2 at 0x8000ff2: file /home/riton/Arduino/BlinkWithoutDelay/BlinkWithoutDelay.ino, line 60.
(gdb)


Continuons :

(gdb) c 
Continuing.

Breakpoint 3, loop () at /home/riton/Arduino/BlinkWithoutDelay/BlinkWithoutDelay.ino:60
60        Serial.print("currentMillis="); delay(100);
(gdb)


L'application s'est arrêtée en ligne 60.
Avançons de quelques pas :

(gdb) next
61        Serial.println(currentMillis); delay(100);
(gdb) next
62        previousMillis = currentMillis;
(gdb) next
65        if (ledState == LOW) {
(gdb)


Visualisons quelques variables :

(gdb) p previousMillis
$4 = 1000
(gdb) p currentMillis

$5 = 1000 
(gdb)

Cette fois-ci previousMillis vaut 1000, ce qui semble assez normal vu ce qui s'est passé en ligne 62.


Continuons :

(gdb) c 
Continuing.

Breakpoint 3, loop () at /home/riton/Arduino/BlinkWithoutDelay/BlinkWithoutDelay.ino:60
60        Serial.print("currentMillis="); delay(100);
(gdb)


Entrons dans la méthode print :

(gdb) step 
Print::print (this=this@entry=0x200003a4 <Serial2>, str=str@entry=0x80082a0 "currentMillis=") at /home/riton/.arduino15/packages/STM32/hardware/stm32/1.5.0/cores/arduino/Print.cpp:55
55    {
(gdb)


Nous sommes à l'entrée de la méthode print de l'objet Serial.
Visualisons le paramètre str :

(gdb) print str
$6 = 0x80082a0 "currentMillis="
(gdb)


Nous pourrions bien entendu afficher les paramètres de nos propres fonctions.

Visualisons la pile :

(gdb) backtrace
#0  Print::print (this=this@entry=0x200003a4 <Serial2>, str=str@entry=0x80082a0 "currentMillis=") at /home/riton/.arduino15/packages/STM32/hardware/stm32/1.5.0/cores/arduino/Print.cpp:55
#1  0x08000ffc in loop () at /home/riton/Arduino/BlinkWithoutDelay/BlinkWithoutDelay.ino:60
#2  0x080042b2 in main () at /home/riton/.arduino15/packages/STM32/hardware/stm32/1.5.0/cores/arduino/main.cpp:64
(gdb)


Nous sommes dans la méthode print, appelée depuis la fonction loop() située dans le fichier BlinkWithoutDelay.ino et l'appel se situe en ligne 60. Bien entendu, on retrouve en fin de pile d'appel la fonction principale : main(), dont la plupart des bidouilleurs sur ARDUINO ignorent l'existence.

Sortons de print :

(gdb) ret 
Make Print::print(char const*) return now? (y or n) y
#0  0x08000ffc in loop () at /home/riton/Arduino/BlinkWithoutDelay/BlinkWithoutDelay.ino:60
60        Serial.print("currentMillis="); delay(100);
(gdb)

Nous sommes de retour dans loop().

Voici une petite présentation sommaire qui se déroule plutôt pas mal.

J'ajoute quelques petits détails supplémentaires sur l'auto-complétion :

Quand on tape le début du nom d'une commande ou le début du nom d'une variable ou d'une fonction, une tabulation permet de compléter ou d'obtenir plusieurs propositions :

(gdb) c <tab>
call              cd                clone-inferior    commands          compile           condition         core-file        
catch             clear             collect           compare-sections  complete          continue         
(gdb) con <tab>

condition  continue  
(gdb) p prev <tab>

(gdb) p previousMillis
$7 = 7000
(gdb)


Sortons (CTRL-D)

(gdb) quit 
A debugging session is active.

    Inferior 1 [Remote target] will be killed.

Quit anyway? (y or n) y


De nombreux tutos sur gdb et des manuels vous atendent sur le WEB. C'est un très vieux logiciel très bien documenté et très puissant.

Remarque : à chaque fois que vous voulez recompiler et recharger l'application, il faut tuer st-util (CTRL_C).
Ensuite il faut compiler, recharger, et relancer  st-util.

5. gdbgui

Ici nous abordons le debugger graphique gdbgui. Je sens que ceux qui n'ont pas lu le chapitre précédent respirent.

Pour l'installer : https://gdbgui.com/downloads.html

C'est un logiciel écrit en PYTHON. Bien entendu les intimes de ce langage peuvent l'intaller autrement :

sudo pip install gdbgui

Nous laissons tourner st-util bien entendu. Il est indispensable.

Lançons-le. Bien sûr il faut lui dire que nous utilisons un gdb ARM.

gdbgui se lance aussi dans un terminal, mais vous pouvez aussi vous écrire un petit lanceur ou un raccourci :

gdbgui -g arm-none-eabi-gdb


Surprise !

Il s'affiche dans votre navigateur préféré. Une belle performance.

Dans le champ (gdb) tout en bas, entrons la commande permettant de se connecter à st-util :

(gdb) target extended-remote :4242

Rassurez-vous, tout le reste se passe avec la souris.
D'autre part avec les touches de rappel du clavier 🠅 et 🠇 il est possible de rappeler les commandes précédentes même après un redémarrage de la machine.

Il est toujours possible d'entrer dans cette zone les commandes gdb comme au chapitre précédent.

Dans la zone de texte en haut entrons le nom de notre application :

/tmp/arduino_build_575652/BlinkWithoutDelay.ino.elf

Appuyer sur <Entrée>.

Ici aussi, entre deus sessions, gdbgui conserve le nom de l'application précédemment entré.

Il affiche notre point d'entrée : main()

Supprimons le point d'arrêt sur la ligne 55 (en bleu). Un clic suffit.
Plaçons-en un autre sur la ligne 64 : l'appel à loop(). Un clic suffit aussi.

Lançon l'exécution par un clic sur le bouton 🡂 en haut à droite.

La ligne d'appel à loop() change. Elle devient gris clair. L'application s'est arrêtée sur le point d'arrêt.

Supprimons le point d'arrêt sur la ligne 64 : un clic suffit aussi.

Entrons dans la fonction loop d'un clic sur le bouton 🠇.


Nous voici dans le vif du sujet, la fonction loop(). Un point d'arrêt sur la ligne 60 nous permettra de nous arrêter après la condition :

if (currentMillis - previousMillis >= interval) {

Encore un clic sur le bouton 🡂.

L'application s'arrête bien sur la ligne voulue :

Serial.print("currentMillis="); delay(100);

Dans la fenêtre de droite il est possible de visualiser en direct les variables locales. Mais il y a mieux : il suffit de placer la souris sur le nom d'une variable pour que sa valeur s'affiche.

Lorsque l'on place la souris sur un bouton une bulle explique son utilisation.

Voilà, c'est un premier jet.

Bien évidemment les accros à la souris préfèreront la version graphique, mais je reste quand même fidèle à mon bon vieux gdb en ligne de commande. L'habitude sans doute. Cela ne m'empêche pas de comprendre le besoin de confort.

La suite au prochain numéro ...

Peut-être le débogage avec PlatformIO ?


6. Liens utiles

https://riton-duino.blogspot.com/2018/03/stm32f103c8t6-et-arduino.html
https://riton-duino.blogspot.com/2019/03/stm32-environnements-de-developpement.html
https://riton-duino.blogspot.com/2019/03/stm32-boitier-st-link.html



Cordialement

Henri

Aucun commentaire:

Enregistrer un commentaire