Wie verwende ich den GDB (Gnu Debugger) und OpenOCD zum Debuggen von Mikrocontrollern - vom Terminal aus?

Die Standardmethode (kostengünstig) zum Programmieren von ARM-Mikrocontrollern ist die Verwendung von Eclipse mit einer daran angeschlossenen komplexen Toolchain. Eclipse hat definitiv seine Vorzüge, aber ich möchte mich von dieser IDE unabhängig fühlen. Ich möchte herausfinden, was hinter den Kulissen passiert, wenn ich meine Software erstelle (kompiliere - verbinde - flashe) und wenn ich eine Debugsitzung ausführe. Um ein so tieferes Verständnis zu erlangen, wäre es wunderbar, die gesamte Prozedur von der Kommandozeile aus auszuführen.

Hinweis: Ich verwende 64-Bit-Windows 10. Die meisten hier erläuterten Informationen gelten jedoch auch für Linux-Systeme. Bitte öffnen Sie alle Befehlsterminals mit Administratorrechten. Dies kann Ihnen viele Probleme ersparen.

1. Erstellen der Software

Die erste Mission ist abgeschlossen. Ich bin jetzt in der Lage, meine Software zu kompilieren und in ein binäres @ zu verlinke.bin und ein.elf Bild über die Befehlszeile. Der Schlüssel zum Erfolg bestand darin, herauszufinden, wo Eclipse die Make-Files für ein bestimmtes Projekt ablegt. Sobald Sie wissen, wo sie sich befinden, müssen Sie lediglich ein Befehlsterminal öffnen und das @ eingebeGNU make Befehl

Dafür brauchst du Eclipse nicht mehr! Vor allem, wenn Sie das Makefile lesen (und verstehen) und an Ihre Bedürfnisse anpassen können, wenn Ihr Projekt voranschreitet.

Bitte beachten Sie, dass ich nach der Installation von SW4STM32 (System Workbench for STM32) die GNU-Tools (Compiler, Linker, make utility, GDB, ...) im folgenden Ordner gefunden habe:

C:\Ac6\SystemWorkbench\plugins\fr.ac6.mcu.externaltools.arm-none.win32_1.7.0.201602121829\tools\compiler\

Nächste Ich habe einen neuen Ordner auf meiner Festplatte erstellt und all diese GNU-Tools darin kopiert:

C:\Apps\AC6GCC
           |-> arm-none-eabi
           |-> bin
           '-> lib

Und ich füge diese Einträge der "Umgebungspfadvariablen" hinzu:

 - C:\Apps\AC6GCC\bin
 - C:\Apps\AC6GCC\lib\gcc\arm-none-eabi\5.2.1

Huray, jetzt habe ich alle GNU-Tools auf meinem System zum Laufen gebracht! Ich habe das folgendebuild.bat -Datei im selben Ordner wie dasmakefile:

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

make -j8 -f makefile all

echo.

Running diese Fledermaus-Datei sollte den Job machen! Wenn alles gut geht, bekommst du ein.bin und ein.elf Binärdatei als Ergebnis der Kompilierung.

2. Flashen und Debuggen der Firmware

Der folgende natürliche Schritt ist das Flashen der Firmware auf den Chip und das Starten einer Debugsitzung. In Eclipse genügt ein Klick - zumindest, wenn Eclipse für Ihren Mikrocontroller richtig konfiguriert ist. Aber was passiert hinter den Kulissen? Ich habe (einen Teil) der Masterarbeit von Dominic Rath gelesen - dem Entwickler von OpenOCD. Sie finden es hier:http: //openocd.net. Das habe ich gelernt:

Eclipse startet die OpenOCD-Software, wenn Sie auf das Debug-Symbol klicken. Eclipse stellt OpenOCD auch einige Konfigurationsdateien zur Verfügung - so dass OpenOCD weiß, wie eine Verbindung zu Ihrem Mikrocontroller hergestellt wird. "Wie man verbindet" ist keine triviale Sache. OpenOCD muss den richtigen USB-Treiber finden, um eine Verbindung zum JTAG-Adapter herzustellen (zum Beispiel STLink). Sowohl der JTAG-Adapter als auch der USB-Treiber werden normalerweise von Ihrem Chiphersteller geliefert (z. B. STMicroelectronics). Eclipse übergibt OpenOCD auch eine Konfigurationsdatei, die die Spezifikationen des Mikrocontrollers beschreibt. Sobald OpenOCD über all diese Dinge Bescheid weiß, kann es eine zuverlässige JTAG-Verbindung zum Zielgerät herstellen.

OpenOCD startet zwei Server. Der erste ist ein Telnet-Server am TCP-Port 4444. Er ermöglicht den Zugriff auf die OpenOCD CLI (Command Line Interface). Ein Telnet-Client kann eine Verbindung herstellen und Befehle an OpenOCD senden. Diese Befehle können einfach "Stop", "Run", "Set Breakpoint", ... sein.

Solche Befehle könnten ausreichen, um Ihren Mikrocontroller zu debuggen, aber viele Leute waren bereits mit dem Gnu-Debugger (GDB) vertraut. Aus diesem Grund startet OpenOCD auch einen GDB-Server auf TCP-Port 3333. Ein GDB-Client kann eine Verbindung zu diesem Port herstellen und das Debuggen des Mikrocontrollers starten!

Der Gnu Debugger ist eine Kommandozeilen-Software. Viele Menschen bevorzugen eine visuelle Oberfläche. Genau das macht Eclipse. Eclipse startet einen GDB-Client, der eine Verbindung zu OpenOCD herstellt. Dies ist jedoch alles für den Benutzer verborgen. Eclipse bietet eine grafische Oberfläche, die hinter den Kulissen mit dem GDB-Client interagiert.

Ich habe eine Figur gemacht, um all diese Dinge zu erklären:

>> OpenOCD starten

Ich konnte OpenOCD über die Befehlszeile starten. Ich werde erklären, wie.

Stellen Sie zunächst sicher, dass Ihr STLink-V2 JTAG-Programmierer ordnungsgemäß installiert ist. Sie können die Installation mit dem "STLink Utility Tool" von STMicroelectronics testen. Es hat eine schöne GUI, und Sie klicken einfach auf die Schaltfläche "Verbinden".Nächster Download der ausführbaren OpenOCD-Software von dieser Website:http: //gnutoolchains.com/arm-eabi/openocd. Installieren Sie es und speichern Sie es in einem Ordner auf Ihrer Festplatte, z. B. "C: \ Apps \".

Öffnen Sie ein Befehlsterminal und starten Sie OpenOCD. Sie müssen OpenOCD einige Konfigurationsdateien geben, damit es weiß, wo es nach Ihrem Mikrocontroller suchen muss. In der Regel müssen Sie eine Konfigurationsdatei angeben, die den JTAG-Programmierer beschreibt, sowie eine Konfigurationsdatei, die Ihren Mikrocontroller definiert. Übergeben Sie diese Dateien mit dem @ an OpenOC-f Argument in der Befehlszeile. Sie müssen OpenOCD auch Zugriff auf das @ gebescripts Ordner, indem Sie es mit dem @ übergeb-s Streit. So starte ich OpenOCD auf meinem Computer mit der Kommandozeile:

> "C:\Apps\OpenOCD-0.9.0-Win32\bin\openocd" -f "C:\Apps\OpenOCD-0.9.0-Win32\share\openocd\scripts\interface\stlink-v2.cfg" -f "C:\Apps\OpenOCD-0.9.0-Win32\share\openocd\scripts\target\stm32f7x.cfg" -s "C:\Apps\OpenOCD-0.9.0-Win32\share\openocd\scripts"

Wenn Sie OpenOCD ordnungsgemäß gestartet haben (mit den richtigen Argumenten), wird es mit der folgenden Meldung gestartet:

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

Bitte beachten Sie, dass Ihr Terminalfenster jetzt blockiert ist. Sie können keine Befehle mehr eingeben. Aber das ist normal OpenOCD läuft im Hintergrund und blockiert das Terminal. Jetzt haben Sie zwei Möglichkeiten, mit OpenOCD zu interagieren: Sie starten eine Telnet-Sitzung in einem anderen Terminal und melden sich bei TCP-Port @ alocalhost:4444, damit Sie OpenOCD Befehle geben und Feedback erhalten können. Oder Sie starten eine GDB-Client-Sitzung und verbinden sie mit dem TCP-Portlocalhost:3333.

>> Starten einer Telnet-Sitzung zur Interaktion mit OpenOCD

So starten Sie eine Telnet-Sitzung, um mit dem laufenden OpenOCD-Programm zu interagieren:

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

> telnet 127.0.0.1 4444

Wenn es gut funktioniert, wird auf Ihrem Terminal die folgende Meldung angezeigt:

Open On-Chip Debugger
> ..

Und Sie können Befehle an OpenOCD senden! Aber ich werde jetzt zur GDB-Sitzung wechseln, da dies der bequemste Weg ist, mit OpenOCD zu interagieren.

>> Starten einer GDB-Client-Sitzung zur Interaktion mit OpenOCD

Öffnen Sie ein weiteres Terminalfenster und geben Sie den folgenden Befehl ein:

> "C:\Apps\AC6GCC\bin\arm-none-eabi-gdb.exe"

Dieser Befehl startet einfach dasarm-none-eabi-gdb.exe GDB-Client. Wenn alles gut geht, startet GDB mit der folgenden Meldung:

    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)..

Jetzt verbinden Sie diesen GDB-Client mit dem GDB-Server in OpenOCD:

    (gdb) target remote localhost:3333

Jetzt bist du mit OpenOCD verbunden! Gut zu wissen: Wenn Sie einen nativen OpenOCD-Befehl verwenden möchten (genau wie in einer Telnet-Sitzung), stellen Sie dem Befehl einfach das Schlüsselwort @ voramonitor. Auf diese Weise verarbeitet der GDB-Server in OpenOCD den Befehl nicht selbst, sondern leitet ihn an den nativen OpenOCD-Deamon weiter.

Nun ist es an der Zeit, den Chip zurückzusetzen, zu löschen und anzuhalten:

    (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

Der Chip ist jetzt bereit, Anweisungen von uns zu erhalten. Zuerst werden wir dem Chip mitteilen, dass seine Flash-Abschnitte 0 bis 7 (das sind alle Flash-Abschnitte in meinem 1-MB-Chip) nicht geschützt werden sollten:

    (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

Next Ich halte den Chip nochmal an. Nur um sicher zu gehen.

    (gdb) monitor halt

Schließlich übergebe ich die binäre.elf file to 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.

etzt ist der Moment der Wahrheit. Ich bitte GDB, diese Binärdatei in den Chip zu laden. Daumen drücken

    (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

Leider war es nicht erfolgreich. In OpenOCD wird folgende Meldung angezeigt:

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

EDIT: Hardwareproblem behoben.

Anscheinend war es ein Hardwareproblem. Ich hätte nie gedacht, dass mein Chip defekt sein würde, da das Laden der Binärdatei auf den Chip mit dem STLink Utility-Tool problemlos funktionierte. Nur OpenOCD hat sich beschwert und Fehler gemeldet. Also habe ich natürlich OpenOCD beschuldigt - und nicht den Chip selbst. Siehe meine Antwort unten für weitere Details.

EDIT: Alternative elegante Art, den Chip zu flashen - mit makefile!

Da das Problem behoben wurde, werde ich mich jetzt auf eine alternative Möglichkeit konzentrieren, den Flash und das Debuggen des Chips auszuführen. Ich glaube das ist wirklich interessant für die Community!

Möglicherweise haben Sie bemerkt, dass ich mit Windows-cmd-Befehlen alle erforderlichen Schritte ausgeführt habe. Dies kann in einer Batch-Datei automatisiert werden. Aber es gibt eine elegantere Möglichkeit: Alles in einem Makefile zu automatisieren! Mr./Mss. Othane hat das folgende Makefile für seinen / ihren Cortex-M vorgeschlagen? Chip. Ich nehme an, dass die Vorgehensweise für einen Cortex-M7-Chip sehr ähnlich ist:

            #################################################
            #        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) 

Sehr geehrter Herr / Frau. Othane, könnten Sie erklären, wie Sie dieses Makefile für die folgenden Schritte verwenden:

Bilden Sie die Binärdatei aus dem QuellcodeFlash den Chip

Ich kenne einige Grundlagen zu Makefiles, aber dein Makefile geht wirklich ziemlich tief. Sie scheinen einige Funktionen des GNU-Dienstprogramms make zu nutzen. Bitte geben Sie uns eine Erklärung, und ich werde Ihnen den Bonus gewähren; -)

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

Antworten auf die Frage(10)

Ihre Antwort auf die Frage