关于如何使用GDB(Gnu Debugger)和OpenOCD进行微控制器调试:如何从终端上使用GDB(Gnu Debugger)和OpenOCD进行微控制器调试?

How to use the GDB (Gnu Debugger) and OpenOCD for microcontroller debugging - from the terminal?

对ARM微控制器进行编程的标准(低成本)方法是使用带有插入的复杂工具链的Eclipse。 Eclipse绝对有其优点,但是我想脱离此IDE。我想发现在构建(编译-链接-刷新)软件以及运行调试会话时幕后发生的事情。为了获得更深入的了解,从命令行运行整个过程将是很棒的。

注意:我使用的是64位Windows10。但是,此处说明的大多数内容也适用于Linux系统。请打开具有管理员权限的所有命令终端。这样可以为您节省很多问题。

1.构建软件

第一个"任务"完成。现在,我可以通过命令行将软件编译并链接到二进制.bin.elf映像中。成功的关键是找出Eclipse将其make-files放置在特定项目中的位置。一旦知道它们在哪里,您所要做的就是打开命令终端,然后键入GNU make命令。

enter image description here

您不再需要Eclipse!特别是如果您可以阅读(并理解)makefile并在项目进行时根据需要调整它。

请注意,在安装SW4STM32(STM32的系统工作台)之后,在以下文件夹中找到了GNU工具(编译器,链接器,make实用程序,GDB,...):

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

接下来,我在硬盘上创建了一个新文件夹,并将所有这些GNU工具复制到其中:

1
2
3
4
C:\\Apps\\AC6GCC
           |-> arm-none-eabi
           |-> bin
           '-> lib

然后将这些条目添加到"环境路径变量"中:

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

Huray,现在我已经在系统上启动并运行了所有GNU工具!我将以下build.bat文件放在与makefile相同的文件夹中:

1
2
3
4
5
6
7
8
9
10
@echo off
echo.
echo."--------------------------------"
echo."-           BUILD              -"
echo."--------------------------------"
echo.

make -j8 -f makefile all

echo.

运行此bat文件应该可以完成任务!如果一切顺利,则编译后将得到一个.bin和一个.elf二进制文件。

2.刷新和调试固件

自然的后续步骤是将固件刷新到芯片上并开始调试会话。在Eclipse中,这只是一个"单击按钮"-至少在为您的微控制器正确配置了Eclipse的情况下。但是幕后发生了什么?
我已经阅读过OpenOCD开发人员Dominic Rath的硕士论文(部分)。您可以在这里找到它:http://openocd.net/。这是我学到的:

  • 当您单击"调试"图标时,Eclipse将启动OpenOCD软件。 Eclipse还为OpenOCD提供了一些配置文件-这样OpenOCD知道如何连接到您的微控制器。"如何连接"不是一件小事。 OpenOCD需要找到正确的USB驱动程序以连接到JTAG适配器(例如STLink)。 JTAG适配器及其USB驱动程序通常由芯片制造商(例如STMicroelectronics)提供。 Eclipse还将配置文件移交给OpenOCD,该文件描述了微控制器的规格。一旦OpenOCD了解了所有这些信息,它就可以与目标设备建立可靠的JTAG连接。

  • OpenOCD启动两个服务器。第一个是TCP端口4444上的Telnet服务器。它可以访问OpenOCD CLI(命令行界面)。 Telnet客户端可以连接并向OpenOCD发送命令。这些命令可以是简单的"停止","运行","设置断点",...

  • 这样的命令足以调试您的微控制器,但是许多人已经熟悉Gnu Debugger(GDB)。这就是OpenOCD还在TCP端口3333上启动GDB服务器的原因。GDB客户端可以连接到该端口,并开始调试微控制器!

  • Gnu调试器是命令行软件。许多人喜欢可视界面。这正是Eclipse所做的。 Eclipse启动了一个GDB客户端,该客户端连接到OpenOCD-但对用户而言,所有这些都隐藏了。 Eclipse提供了一个图形界面,可以在后台与GDB客户端进行交互。

  • 我做了一个图来解释所有这些事情:

    enter image description here

    >>启动OpenOCD

    我设法从命令行启动OpenOCD。我会解释如何。

  • 首先,请确保正确安装了STLink-V2 JTAG编程器。您可以使用STMicroelectronics的" STLink Utility工具"测试安装。它具有漂亮的GUI,您只需单击连接按钮。
    enter image description here
  • 接下来从该网站下载可执行的OpenOCD软件:http://gnutoolchains.com/arm-eabi/openocd/。安装它,并将其放在硬盘驱动器上的文件夹中,例如" C:\ Apps"。
  • 打开命令终端,然后启动OpenOCD。您将需要给OpenOCD一些配置文件,以便它知道在哪里寻找您的微控制器。通常,您需要提供一个描述JTAG编程器的配置文件和一个定义您的微控制器的配置文件。在命令行中使用-f参数将这些文件传递给OpenOCD。您还需要通过使用-s参数传递给OpenOCD访问scripts文件夹的权限。这是通过命令行在计算机上启动OpenOCD的方法:

    1
    >"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"
  • 如果正确启动了OpenOCD(使用正确的参数),它将以以下消息启动:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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
  • 请注意,您的终端窗口现已被阻止。您不能再键入命令。但这很正常。 OpenOCD在后台运行,它阻止了终端。现在,您有两个与OpenOCD交互的选项:在另一个终端上启动Telnet会话,然后登录到TCP端口localhost:4444,因此可以向OpenOCD提供命令并接收反馈。或者,您启动GDB客户端会话,并将其连接到TCP端口localhost:3333

  • >>启动Telnet会话以与OpenOCD进行交互

    这是启动Telnet会话以与正在运行的OpenOCD程序进行交互的方式:

    1
    2
    3
    > dism /online /Enable-Feature /FeatureName:TelnetClient

    > telnet 127.0.0.1 4444

    如果运行良好,您将在终端上收到以下消息:

    1
    2
    Open On-Chip Debugger
    > ..

    您已经准备好向OpenOCD发送命令!但是,现在我将切换到GDB会话,因为这是与OpenOCD进行交互的最方便的方法。

    >>启动GDB客户端会话以与OpenOCD进行交互

    打开另一个终端窗口,然后键入以下命令:

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

    此命令仅启动arm-none-eabi-gdb.exe GDB客户端。如果一切顺利,GDB将启动,并显示以下消息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
        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)..

    现在,将此GDB客户端连接到OpenOCD内的GDB服务器:

    1
        (gdb) target remote localhost:3333

    现在您已连接到OpenOCD!提示:如果您想使用本机OpenOCD命令(就像在Telnet会话中一样),只需在命令前加上关键字monitor。这样,OpenOCD中的GDB服务器将不会自己处理命令,而是将其传递给本机OpenOCD守护进程。

    因此,现在是时候重置芯片,擦除并暂停它了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
        (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

    现在,该芯片已准备就绪,可以向我们获取一些说明。首先,我们将告诉芯片其闪存部分0至7(即我的1Mb芯片中的所有闪存部分)不应受到保护:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
        (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

    接下来,我再次暂停芯片。只是要确定..

    1
        (gdb) monitor halt

    最后,我将二进制.elf文件移交给GDB:

    1
    2
    3
    4
        (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.

    现在是关键时刻。我要求GDB将二进制文件加载到芯片中。手指交叉:

    1
    2
    3
    4
    5
    6
    7
    8
        (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

    不幸的是,它没有成功。我在OpenOCD中收到以下消息:

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

    编辑:修复了硬件问题。

    显然这是硬件问题。我从未想过我的芯片会出现故障,因为使用STLink Utility工具将二进制文件加载到芯片上可以正常工作。只有OpenOCD在抱怨并给出错误。因此,我自然地将责任归咎于OpenOCD-而不是芯片本身。有关更多详细信息,请参见下面的答案。

    编辑:另一种优雅的方式来刷新芯片-使用makefile!

    由于问题已解决,我现在将重点介绍执行闪存和芯片调试的另一种方法。我相信这对于社区真的很有趣!

    您可能已经注意到,我使用Windows cmd命令执行所有必需的步骤。 这可以在批处理文件中自动执行。 但是,还有一种更优雅的方法:自动化makefile中的所有内容!
    先生/女士 Othane为他/她的Cortex-M建议了以下makefile? 芯片。 我认为Cortex-M7芯片的过程非常相似:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
                #################################################
                #        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)

    尊敬的先生/女士 Othane,您能否解释一下如何将此Makefile用于以下步骤:

  • 从源代码构建二进制文件
  • 闪存芯片
  • 我知道一些关于makefile的基础知识,但是您的makefile确实非常深入。 您似乎使用了GNU make实用程序的许多功能。 请给我们更多的解释,我将给您奖金;-)

    ------------------------------
    好。


    我记得它在直接加载命令上也遇到了一些麻烦,所以我切换到" flash write_image擦除my_project.hex 0 ihex" ..显然我正在使用十六进制文件,但看起来elf文件应该可以使用,请参见http: //openocd.org/doc/html/Flash-Commands.html ...关于此命令的好处是它还只擦除了确实方便的闪存部分,因此您不需要擦除

    在运行上述命令之前,您将需要运行" stm32f1x unlock 0",以确保芯片已解锁并允许您连接至闪存...请参阅有关此数据表

    另外,要开始使用该命令," stm32f1x mass_erase 0"将完全快速擦除芯片,因此最好确保以已知状态启动

    我知道其中一些命令说它们适用于f1,但请相信我它们适用于f4系列

    顺便说一句,这个文件包含了我用来刷新f4的大多数命令,因此它可能是一个很好的参考https://github.com/othane/mos/blob/master/hal/stm32f373/stm32f373.mk

    我希望你能解开


    乍一看,在gnutoolchains.com上的分发应该足够好。有许多构建脚本可以酿造您自己的版本。我确实包括ARM7TDMI。在Linux和FreeBSD上它可以正常工作,但是MinGW上次尝试它失败了:-(

    关于OpenOCD,我建议在与GDB实例相同的目录中启动它,这样,如果您从GDB中调用它,二进制下载看起来是透明的(最简单的方法)。您还可以选择创建一个脚本来启动OpenOCD并加载代码,但是每次编译后都必须重新启动它。


    这是一个简短但不是很好的stackoverflow样式,但是我会向您指出我的代码,在该代码中我为STM32F4和STM32F1(https://github.com/othane/mos)的" mos"库进行了设置。这是一个很重要的话题,所以我通过一个例子可能会更好

    简而言之,我的项目是一棵Makefiles树,因为您可以在这里编译自己感兴趣的主要代码,请参见https://github.com/othane/mos/blob/master/hal/stm32f373/stm32f373.mk ...基本上,您需要openocd,然后我有一系列命令来擦除芯片,或者通过简单地键入make .erase或make .flash或make .debug来擦除和调试新代码等。

    最后,如果您查看我的单元测试(这些基本上是示例程序),您会找到用于构建它的Makefile +像这样的gdbinit文件https://github.com/othane/mos/blob/master/utest/gpio/ debug_gpio_utest.gdbinit ...然后您只需在一个终端中执行" make && make .flash && make .debug",然后在其中调用交叉编译器gdb,例如" arm-none-eabi-gdb -x ./debug_gpio_utest.gdbinit"。另一个...这将在刷新代码后启动gdb,您可以使用gdb等中的常规break和list命令与代码进行交互(请注意,我如何在.gdbinit文件中定义了reset命令,请查看mon的帮助命令...基本上,它将使您可以通过gdb将命令直接发送到openocd,这确实很有用)

    抱歉,答案很简短,有很多链接,但我希望它能帮助您前进。


    现在,您只需调用" gdb"并将其连接到"远程服务器"(如果服务器和gdb在同一台计算机上运行,??则为localhost)。配置GDB,使其知道源代码的位置和ELF文件的位置。有大量的网站会介绍GDB的基本用法。

    Windows似乎有一个GDB(http://www.equation.com/servlet/equation.cmd?fa=gdb)

    GDB中的以下命令可以帮助您入门:

    目标远程本地主机:3333

    目录/ path / to / project

    符号文件/path/to/project.elf


    显然这是硬件问题。 我从未想过我的芯片会出现故障,因为使用STLink Utility工具将二进制文件加载到芯片上可以正常工作。 只有OpenOCD在抱怨并给出错误。 因此,我自然地将责任归咎于OpenOCD-而不是芯片本身。

    今天,我在板上使用了新芯片,尝试了相同的步骤,现在可以正常工作!

    发出load命令时,我在GDB中得到以下输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        (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
           Start address 0x8003450, load size 15388
           Transfer rate: 21 KB/sec, 2564 bytes/write.
        (gdb)

    感谢所有竭尽所能帮助我的人:-)