Comment utiliser gdb (Gnu Debugger) et OpenOCD pour le débogage des microcontrôleurs - depuis le terminal?

la méthode standard (à faible coût) pour programmer les microcontrôleurs ARM utilise Eclipse avec une chaîne d'outils complexe branchée dessus. Eclipse a certainement ses mérites, mais j'aimerais me sentir indépendant de cette IDE. J'aimerais découvrir ce qui se passe dans les coulisses quand je construis (compiler - link - flash) mon logiciel, et quand je lance une session de débogage. Pour obtenir une telle compréhension, il serait merveilleux d'exécuter toute la procédure à partir de la ligne de commande.

Note: j'utilise 64-bit Windows 10. Mais la plupart des choses expliquées ici s'appliquent aussi aux systèmes Linux. Veuillez ouvrir TOUS les terminaux de commande avec les droits d'administration. Cela peut vous épargner beaucoup de problèmes.

1. Construire le logiciel

La première mission est accomplie. Je suis maintenant capable de compiler et de lier mon logiciel dans un binaire .bin et .elf image via la ligne de commande. La clé du succès a été de trouver où Eclipse met ses make-files pour un projet spécifique. Une fois que vous savez où ils sont, tout ce que vous avez à faire est d'ouvrir un terminal de commande, et taper le GNU make la commande.

enter image description here

vous n'avez plus besoin D'Eclipse pour cela! Surtout si vous pouvez lire (et comprendre) le makefile et l'adapter à vos besoins lorsque votre projet avance.

notez que j'ai trouvé les outils GNU (compilateur, linker, make utility, GDB, ...) dans le dossier suivant, après installation de SW4STM32 (système Workbench pour les microcontrôleurs STM32):

C:Ac6SystemWorkbenchpluginsfr.ac6.mcu.externaltools.arm-none.win32_1.7.0.201602121829toolscompiler

Ensuite, j'ai fait un nouveau dossier sur mon disque dur et copié tous ces outils GNU:

C:AppsAC6GCC
           |-> arm-none-eabi
           |-> bin
           '-> lib

Et j'ai ajouter ces entrées à la "variable d'Environnement Path":

 - C:AppsAC6GCCbin
 - C:AppsAC6GCClibgccarm-none-eabi.2.1

Huray, maintenant j'ai tous les outils GNU en marche sur mon système! J'ai mis la suivante build.bat le fichier dans le même dossier que le makefile:

@echo off
echo.
echo."--------------------------------"
echo."-           BUILD              -"
echo."--------------------------------"
echo.

make -j8 -f makefile all

echo.

lancer ce fichier bat devrait faire l'affaire! Si tout se passe bien, vous obtenez l'un des .bin et un .elf fichier binaire à la suite de la compilation.

2. Clignotement et débogage du firmware

l'étape suivante consiste à lancer le micrologiciel sur la puce et à lancer une session de débogage. Dans Eclipse, il ne s'agit que d'un "clic sur un bouton" - au moins si Eclipse est configuré correctement pour votre microcontrôleur. Mais ce qui se passe derrière les coulisses? J'ai lu (en partie) la thèse de maîtrise de Dominic Rath - le développeur D'OpenOCD. Vous pouvez le trouver ici: http://openocd.net/ . C'est ce que j'ai appris:

  • Eclipse démarre le logiciel OpenOCD en cliquant sur l'icône' debug'. Eclipse fournit également quelques fichiers de configuration pour OpenOCD-de sorte Qu'OpenOCD sait comment se connecter à votre microcontrôleur. "Comment se connecter" n'est pas une chose banale. OpenOCD doit trouver le pilote USB approprié pour se connecter à l'adaptateur JTAG (par exemple STLink). L'adaptateur JTAG et son pilote USB sont livré par votre fabricant de puces (par exemple STMicroelectronics). Eclipse remet également un fichier de configuration à OpenOCD qui décrit les spécifications du microcontrôleur. Une fois Qu'OpenOCD est au courant de toutes ces choses, il peut établir une connexion JTAG fiable avec le périphérique cible.

  • OpenOCD démarre deux serveurs. Le premier est un serveur Telnet sur le port TCP 4444. Il donne accès au CLI D'OpenOCD (interface en ligne de commande). Un client Telnet peut se connecter et envoyer des commandes à OpenOCD. Ces commandes peuvent être un simple "stop", "exécuter", de "définir un point d'arrêt', ...

  • de telles commandes pourraient suffire pour déboguer votre microcontrôleur, mais beaucoup de gens connaissaient déjà le débogueur Gnu (gdb). C'est pourquoi OpenOCD démarre également un serveur GDB sur le port TCP 3333. Un client GDB peut se connecter à ce port, et commencer à déboguer le microcontrôleur!

  • le débogueur Gnu est un logiciel de ligne de commande. Beaucoup les gens préfèrent une interface visuelle. C'est exactement ce que Eclipse n'. Eclipse lance un client GDB qui se connecte à OpenOCD - mais tout est caché pour l'utilisateur. Eclipse fournit une interface graphique qui interagit avec le client gdb dans les coulisses.

j'ai fait un chiffre d'expliquer toutes ces choses:

enter image description here

>> démarrage OpenOCD

j'ai réussi à démarrer OpenOCD depuis la ligne de commande. Je vais vous expliquer comment.

  1. assurez-vous D'abord que votre programmeur JTAG STLink-V2 est correctement installé. Vous pouvez tester l'installation avec "L'outil utilitaire STLink" de STMicroelectronics. Il a une belle interface graphique, et vous cliquez simplement sur le bouton Connecter. enter image description here
  2. suivant téléchargez L'exécutable du logiciel OpenOCD sur ce site web:http://gnutoolchains.com/arm-eabi/openocd/ . L'installer, et le mettre dans un dossier sur votre disque dur, comme "C:Apps".
  3. ouvrir un terminal de commande, et lancer OpenOCD. Vous aurez besoin de donner à OpenOCD quelques fichiers de configuration, afin qu'il sache où chercher votre microcontrôleur. Typiquement, vous devez fournir un fichier de configuration qui décrit le programmeur JTAG, et un fichier de configuration qui définit votre microcontrôleur. Passez ces fichiers à OpenOCD avec le -f argument en ligne de commande. Vous aurez également besoin de donner Accès OpenOCD à la scripts en le passant avec le -s argument. C'est ainsi que je démarre OpenOCD sur mon ordinateur avec la ligne de commande:

    > "C:AppsOpenOCD-0.9.0-Win32binopenocd" -f "C:AppsOpenOCD-0.9.0-Win32shareopenocdscriptsinterfacestlink-v2.cfg" -f "C:AppsOpenOCD-0.9.0-Win32shareopenocdscriptstargetstm32f7x.cfg" -s "C:AppsOpenOCD-0.9.0-Win32shareopenocdscripts"
    
  4. si vous avez démarré OpenOCD correctement (avec les bons arguments), il démarre avec le message suivant:

    Open On-Chip Debugger 0.9.0 (2015-08-15-12:41)
    Licensed under GNU GPL v2
    For bug reports, read
            http://openocd.org/doc/doxygen/bugs.html
    Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
    Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
    adapter speed: 2000 kHz
    adapter_nsrst_delay: 100
    srst_only separate srst_nogate srst_open_drain connect_deassert_srst
    Info : Unable to match requested speed 2000 kHz, using 1800 kHz
    Info : Unable to match requested speed 2000 kHz, using 1800 kHz
    Info : clock speed 1800 kHz
    Info : STLINK v2 JTAG v24 API v2 SWIM v4 VID 0x0483 PID 0x3748
    Info : using stlink api v2
    Info : Target voltage: 3.231496
    Info : stm32f7x.cpu: hardware has 8 breakpoints, 4 watchpoints
    Info : accepting 'gdb' connection on tcp/3333
    Info : flash size probed value 1024
    
  5. notez que la fenêtre de votre terminal est maintenant bloquée. Vous ne pouvez plus taper des commandes. Mais c'est normal. OpenOCD est en cours d'exécution dans l'arrière-plan, et il les blocs de la borne. Vous avez maintenant deux options pour interagir avec OpenOCD: vous commencez une session Telnet dans un autre terminal, et vous vous connectez au port TCP localhost:4444, de sorte que vous pouvez donner des commandes à OpenOCD et recevoir des commentaires. Ou vous démarrez une session client GDB, et vous la connectez au port TCP localhost:3333.

>> démarrage D'une session Telnet pour interagir avec OpenOCD

c'est comme ça que vous démarrez une session Telnet pour interagir avec la course OpenOCD programme:

> dism /online /Enable-Feature /FeatureName:TelnetClient

> telnet 127.0.0.1 4444

Si elle fonctionne bien, vous obtiendrez le message suivant sur votre terminal:

Open On-Chip Debugger
> ..

et vous êtes prêt à envoyer des commmands à OpenOCD! Mais je vais maintenant passer à la session GDB, puisque c'est la façon la plus commode d'interagir avec OpenOCD.

>> démarrage d'une session client GDB pour interagir avec OpenOCD

ouvrez encore une autre fenêtre de terminal, et tapez ce qui suit: commande:

> "C:AppsAC6GCCbinarm-none-eabi-gdb.exe"

Cette commande démarre simplement le arm-none-eabi-gdb.exe GDB client. Si tout se passe bien, GDB démarre avec le message suivant:

    GNU gdb (GNU Tools for ARM Embedded Processors) 7.10.1.20151217-cvs
    Copyright (C) 2015 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=i686-w64-mingw32 --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".
    (gdb)..

maintenant connectez ce client GDB au serveur GDB à L'intérieur D'OpenOCD:

    (gdb) target remote localhost:3333

Maintenant vous êtes connectés à OpenOCD! Bon à savoir: si vous voulez utiliser une commande native D'OpenOCD (comme vous le feriez dans une session Telnet), précédez simplement la commande avec le mot-clé monitor. De cette façon, le serveur GDB à l'intérieur OpenOCD ne se chargera pas de la commande lui-même, mais la transmettra au Deamon OpenOCD natif.

alors, maintenant il est temps de réinitialiser la puce, de l'effacer et de l'arrêter:

    (gdb) monitor reset halt
       target state: halted
       target halted due to debug-request, current mode: Thread
       xPSR: 0x01000000 pc: 0xfffffffe msp: 0xfffffffc
    (gdb) monitor halt

    (gdb) monitor flash erase_address 0x08000000 0x00100000
       erased address 0x08000000 (length 1048576) in 8.899024s (115.069 KiB/s)
    (gdb) monitor reset halt
       target state: halted
       target halted due to debug-request, current mode: Thread
       xPSR: 0x01000000 pc: 0xfffffffe msp: 0xfffffffc
    (gdb) monitor halt

la puce est maintenant prête à recevoir des instructions de notre part. Tout d'abord, nous dirons à la puce que ses sections 0 à 7 (c'est-à-dire toutes les sections flash de ma puce 1Mb) ne devraient pas être protégées:

    (gdb) monitor flash protect 0 0 7 off

    (gdb) monitor flash info 0
       #0 : stm32f7x at 0x08000000, size 0x00100000, buswidth 0, chipwidth 0
            #  0: 0x00000000 (0x8000 32kB) not protected
            #  1: 0x00008000 (0x8000 32kB) not protected
            #  2: 0x00010000 (0x8000 32kB) not protected
            #  3: 0x00018000 (0x8000 32kB) not protected
            #  4: 0x00020000 (0x20000 128kB) not protected
            #  5: 0x00040000 (0x40000 256kB) not protected
            #  6: 0x00080000 (0x40000 256kB) not protected
            #  7: 0x000c0000 (0x40000 256kB) not protected

après j'arrête la puce à nouveau. Juste pour être assurer..

    (gdb) monitor halt

Enfin, j'ai la main sur le binaire .elf fichier GDB:

    (gdb) file C:..myProgram.elf
       A program is being debugged already.
       Are you sure you want to change the file? (y or n) y
       Reading symbols from C:..myProgram.elf ...done.

maintenant est le moment de vérité. Je demande à GDB de charger ce binaire dans la puce. Je croise les doigts:

    (gdb) load
       Loading section .isr_vector, size 0x1c8 lma 0x8000000
       Loading section .text, size 0x39e0 lma 0x80001c8
       Loading section .rodata, size 0x34 lma 0x8003ba8
       Loading section .init_array, size 0x4 lma 0x8003bdc
       Loading section .fini_array, size 0x4 lma 0x8003be0
       Loading section .data, size 0x38 lma 0x8003be4
       Error finishing flash operation

Malheureusement, il n'a pas réussi. Je reçois le message suivant dans OpenOCD:

    Error: error waiting for target flash write algorithm
    Error: error writing to flash at address 0x08000000 at offset 0x00000000

modifier: problème de matériel corrigé.

Apparemment c'était un problème matériel. Je n'avais jamais pensé que ma puce serait défaut, puisque le chargement du binaire sur la puce avec L'outil utilitaire STLink a fonctionné sans problème. Seul OpenOCD se plaignait et donnait des erreurs. Donc naturellement J'ai blâmé L'OpenOCD - et pas la puce elle-même. Voir ma réponse ci-dessous pour plus de détails.


EDIT: Alternative élégante façon de flash la puce-en utilisant makefile!

comme le problème a été corrigé, je vais maintenant me concentrer sur une façon alternative d'exécuter le flash et le puce. Je crois que c'est vraiment intéressant pour la communauté!

vous avez peut-être remarqué que J'ai utilisé les commandes cmd de Windows pour exécuter toutes les étapes nécessaires. Cela peut être automatisé dans un fichier de commandes. Mais il y a une façon plus élégante: automatiser tout dans un makefile! Mr. / Mss. Othane a suggéré le makefile suivant pour son Cortex-M? puce. Je suppose que la procédure pour une puce Cortex-M7 est très similaire:

            #################################################
            #        MAKEFILE FOR BUILDING THE BINARY       #
            #        AND EVEN FLASHING THE CHIP!            #
            # Author: Othane                                #
            #################################################

    # setup compiler and flags for stm32f373 build 
    SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST))) 


    CROSS_COMPILE ?= arm-none-eabi- 
    export CC = $(CROSS_COMPILE)gcc 
    export AS = $(CROSS_COMPILE)gcc -x assembler-with-cpp 
    export AR = $(CROSS_COMPILE)ar 
    export LD = $(CROSS_COMPILE)ld 
    export OD   = $(CROSS_COMPILE)objdump 
    export BIN  = $(CROSS_COMPILE)objcopy -O ihex 
    export SIZE = $(CROSS_COMPILE)size 
    export GDB = $(CROSS_COMPILE)gdb 


    MCU = cortex-m4 
    FPU = -mfloat-abi=hard -mfpu=fpv4-sp-d16 -D__FPU_USED=1 -D__FPU_PRESENT=1 -DARM_MATH_CM4 
    DEFS = -DUSE_STDPERIPH_DRIVER -DSTM32F37X -DRUN_FROM_FLASH=1 -DHSE_VALUE=8000000 
    OPT ?= -O0  
    MCFLAGS = -mthumb -mcpu=$(MCU) $(FPU) 


    export ASFLAGS  = $(MCFLAGS) $(OPT) -g -gdwarf-2 $(ADEFS) 
    CPFLAGS += $(MCFLAGS) $(OPT) -gdwarf-2 -Wall -Wno-attributes -fverbose-asm  
    CPFLAGS += -ffunction-sections -fdata-sections $(DEFS) 
    export CPFLAGS 
    export CFLAGS += $(CPFLAGS) 


    export LDFLAGS  = $(MCFLAGS) -nostartfiles -Wl,--cref,--gc-sections,--no-warn-mismatch $(LIBDIR) 


    HINCDIR += ./STM32F37x_DSP_StdPeriph_Lib_V1.0.0/Libraries/CMSIS/Include/  
        ./STM32F37x_DSP_StdPeriph_Lib_V1.0.0/Libraries/CMSIS/Device/ST/STM32F37x/Include/  
        ./STM32F37x_DSP_StdPeriph_Lib_V1.0.0/Libraries/STM32F37x_StdPeriph_Driver/inc/  
        ./ 
    export INCDIR = $(patsubst %,$(SELF_DIR)%,$(HINCDIR)) 




    # openocd variables and targets 
    OPENOCD_PATH ?= /usr/local/share/openocd/ 
    export OPENOCD_BIN = openocd 
    export OPENOCD_INTERFACE = $(OPENOCD_PATH)/scripts/interface/stlink-v2.cfg 
    export OPENOCD_TARGET = $(OPENOCD_PATH)/scripts/target/stm32f3x_stlink.cfg 


    OPENOCD_FLASH_CMDS = '' 
    OPENOCD_FLASH_CMDS += -c 'reset halt' 
    OPENOCD_FLASH_CMDS += -c 'sleep 10'  
    OPENOCD_FLASH_CMDS += -c 'stm32f1x unlock 0' 
    OPENOCD_FLASH_CMDS += -c 'flash write_image erase $(PRJ_FULL) 0 ihex' 
    OPENOCD_FLASH_CMDS += -c shutdown 
    export OPENOCD_FLASH_CMDS 


    OPENOCD_ERASE_CMDS = '' 
    OPENOCD_ERASE_CMDS += -c 'reset halt' 
    OPENOCD_ERASE_CMDS += -c 'sleep 10'  
    OPENOCD_ERASE_CMDS += -c 'sleep 10'  
    OPENOCD_ERASE_CMDS += -c 'stm32f1x mass_erase 0' 
    OPENOCD_ERASE_CMDS += -c shutdown 
    export OPENOCD_ERASE_CMDS 


    OPENOCD_RUN_CMDS = '' 
    OPENOCD_RUN_CMDS += -c 'reset halt' 
    OPENOCD_RUN_CMDS += -c 'sleep 10' 
    OPENOCD_RUN_CMDS += -c 'reset run' 
    OPENOCD_RUN_CMDS += -c 'sleep 10'  
    OPENOCD_RUN_CMDS += -c shutdown 
    export OPENOCD_RUN_CMDS 


    OPENOCD_DEBUG_CMDS = '' 
    OPENOCD_DEBUG_CMDS += -c 'halt' 
    OPENOCD_DEBUG_CMDS += -c 'sleep 10' 


    .flash: 
        $(OPENOCD_BIN) -f $(OPENOCD_INTERFACE) -f $(OPENOCD_TARGET) -c init $(OPENOCD_FLASH_CMDS) 


    .erase: 
        $(OPENOCD_BIN) -f $(OPENOCD_INTERFACE) -f $(OPENOCD_TARGET) -c init $(OPENOCD_ERASE_CMDS) 


    .run: 
        $(OPENOCD_BIN) -f $(OPENOCD_INTERFACE) -f $(OPENOCD_TARGET) -c init $(OPENOCD_RUN_CMDS) 


    .debug: 
        $(OPENOCD_BIN) -f $(OPENOCD_INTERFACE) -f $(OPENOCD_TARGET) -c init $(OPENOCD_DEBUG_CMDS) 

Cher Monsieur/Mss. Othane, pourrais - tu expliquer comment utiliser ce makefile pour les étapes suivantes:

  • construisez le binaire à partir du code source
  • Flash de la puce

je connais quelques notions de base sur les makefiles, mais votre makefile est vraiment très profond. Vous semblez utiliser plusieurs fonctionnalités de L'utilitaire GNU make. Veuillez nous donner plus d'explication, et je vais vous accorder le bonus ;-)

------------------------------

27
demandé sur K.Mulier 2016-06-26 00:29:05

5 réponses

comme je m'en souviens, j'ai eu quelques problèmes avec la commande straight load aussi, donc je suis passé à "Flash write_image erase my_project.hex 0 ihex".. évidemment j'utilisais des fichiers hex mais il semble que les fichiers elf devraient fonctionner pour, voir http://openocd.org/doc/html/Flash-Commands.html