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.
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:
>> démarrage OpenOCD
j'ai réussi à démarrer OpenOCD depuis la ligne de commande. Je vais vous expliquer comment.
- 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.
- 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".
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 à lascripts
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"
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
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 TCPlocalhost: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 ;-)
------------------------------
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