tutorial - gdb windows
¿Cómo usar el GDB(Gnu Debugger) y OpenOCD para la depuración del microcontrolador-desde el terminal? (5)
A primera vista, la distribución en gnutoolchains.com debería ser lo suficientemente buena. Hay una serie de scripts de compilación para elaborar tu propia versión. Tengo el mío para incluir el ARM7TDMI. Funciona bien en Linux y FreeBSD pero MinGW falló la última vez que lo probé :-(
Con respecto a OpenOCD, recomendaría iniciarlo en el mismo directorio que su instancia de GDB, para que la descarga binaria parezca transparente si la invoca desde GDB (la forma más fácil). También tiene la opción de crear un script que inicie OpenOCD y cargar el código, pero luego tendrá que reiniciarlo después de cada compilación.
La manera estándar (de bajo costo) de programar los microcontroladores ARM es usar Eclipse con una compleja cadena de herramientas conectada a él. Eclipse tiene definitivamente sus méritos, pero me gustaría sentirme independiente de este IDE. Me gustaría descubrir qué sucede tras bambalinas cuando compilo (compile - link - flash) mi software, y cuando ejecuto una sesión de depuración. Para obtener una comprensión más profunda, sería maravilloso ejecutar todo el procedimiento desde la línea de comandos.
Nota: estoy usando Windows 10 de 64 bits. Pero la mayoría de las cosas que se explican aquí también se aplican a los sistemas Linux. Por favor, abra todos los terminales de comando con derechos de administrador. Esto puede ahorrar muchos problemas.
1. Construyendo el software
La primera ''misión'' se cumple. Ahora puedo compilar y vincular mi software en un .bin
binario y una imagen .elf
través de la línea de comandos. La clave del éxito fue descubrir dónde Eclipse coloca sus make-files para un proyecto específico. Una vez que sepa dónde están, todo lo que tiene que hacer es abrir un terminal de comando y escribir el comando GNU make
.
¡Ya no necesitas Eclipse para eso! Especialmente si puede leer (y entender) el makefile y adaptarlo a sus necesidades cuando avance su proyecto.
Tenga en cuenta que encontré las herramientas GNU (compilador, vinculador, make utility, GDB, ...) en la siguiente carpeta, después de instalar SW4STM32 (System Workbench para STM32):
C:/Ac6/SystemWorkbench/plugins/fr.ac6.mcu.externaltools.arm-none.win32_1.7.0.201602121829/tools/compiler/
Luego hice una nueva carpeta en mi disco duro y copié todas estas herramientas GNU en ella:
C:/Apps/AC6GCC
|-> arm-none-eabi
|-> bin
''-> lib
Y agrego estas entradas a la "variable de ruta de entorno":
- C:/Apps/AC6GCC/bin
- C:/Apps/AC6GCC/lib/gcc/arm-none-eabi/5.2.1
¡Huray, ahora tengo todas las herramientas GNU en funcionamiento en mi sistema! Puse el siguiente archivo build.bat
en la misma carpeta que el makefile
:
@echo off
echo.
echo."--------------------------------"
echo."- BUILD -"
echo."--------------------------------"
echo.
make -j8 -f makefile all
echo.
Ejecutar este archivo de bat debería hacer el trabajo! Si todo va bien, obtienes un archivo binario .bin
y uno .elf
como resultado de la compilación.
2. Flasheo y depuración del firmware.
El siguiente paso natural es actualizar el firmware del chip e iniciar una sesión de depuración. En Eclipse es solo un ''clic en un botón'', al menos si Eclipse está configurado correctamente para su microcontrolador. Pero, ¿qué pasa detrás de las escenas? He leído (parte de) la tesis de maestría de Dominic Rath, el desarrollador de OpenOCD. Puedes encontrarlo aquí: http://openocd.net/ . Esto es lo que aprendí:
Eclipse inicia el software OpenOCD cuando hace clic en el ícono ''depurar''. Eclipse también proporciona algunos archivos de configuración para OpenOCD, de modo que OpenOCD sabe cómo conectarse a su microcontrolador. ''Cómo conectarse'' no es una cosa trivial. OpenOCD necesita encontrar el controlador USB adecuado para conectarse al adaptador JTAG (por ejemplo, STLink). Tanto el adaptador JTAG como su controlador USB suelen ser suministrados por el fabricante de su chip (por ejemplo, STMicroelectronics). Eclipse también entrega un archivo de configuración a OpenOCD que describe las especificaciones del microcontrolador. Una vez que OpenOCD conoce todas estas cosas, puede establecer una conexión JTAG confiable con el dispositivo de destino.
OpenOCD inicia dos servidores. El primero es un servidor Telnet en el puerto TCP 4444. Da acceso a la CLI (interfaz de línea de comandos) de OpenOCD. Un cliente Telnet puede conectarse y enviar comandos a OpenOCD. Esos comandos pueden ser un simple ''detener'', ''ejecutar'', ''establecer punto de interrupción'', ...
Dichos comandos podrían ser suficientes para depurar su microcontrolador, pero muchas personas ya estaban familiarizadas con el Gnu Debugger (GDB). Esta es la razón por la que OpenOCD también inicia un servidor GDB en el puerto TCP 3333. ¡Un cliente GDB puede conectarse a ese puerto y comenzar a depurar el microcontrolador!
El Gnu Debugger es un software de línea de comandos. Muchas personas prefieren una interfaz visual. Eso es exactamente lo que hace Eclipse. Eclipse inicia un cliente GDB que se conecta a OpenOCD, pero eso está oculto para el usuario. Eclipse proporciona una interfaz gráfica que interactúa con el cliente GDB detrás de escena.
He hecho una figura para explicar todas estas cosas:
>> Puesta en marcha de OpenOCD
Logré iniciar OpenOCD desde la línea de comandos. Te explico como
- Primero asegúrese de que su programador STLink-V2 JTAG esté correctamente instalado. Puede probar la instalación con la "herramienta de utilidad STLink" de STMicroelectronics. Tiene una interfaz gráfica de usuario agradable y simplemente hace clic en el botón Conectar.
- A continuación, descargue el ejecutable del software OpenOCD desde este sitio web: http://gnutoolchains.com/arm-eabi/openocd/ . Instálelo y colóquelo en una carpeta de su disco duro, como "C: / Apps /".
Abra un terminal de comando e inicie OpenOCD. Deberá darle a OpenOCD algunos archivos de configuración, de manera que sepa dónde buscar su microcontrolador. Por lo general, debe proporcionar un archivo de configuración que describa al programador JTAG y un archivo de configuración que defina su microcontrolador. Pase esos archivos a OpenOCD con el argumento
-f
en la línea de comando. También deberá dar acceso a OpenOCD a la carpeta descripts
, pasándolo con el argumento-s
. Así es como inicio OpenOCD en mi computadora con la línea de comando:> "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"
Si inició OpenOCD correctamente (con los argumentos correctos), se iniciará con el siguiente mensaje:
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
Observe que su ventana de terminal ahora está bloqueada. Ya no puede escribir comandos. Pero eso es normal. OpenOCD se ejecuta en segundo plano y bloquea el terminal. Ahora tiene dos opciones para interactuar con OpenOCD: inicia una sesión de Telnet en otro terminal e inicia sesión en el puerto TCP
localhost:4444
, por lo que puede dar comandos a OpenOCD y recibir comentarios. O inicia una sesión de cliente GDB y conéctela al puerto TCPlocalhost:3333
.
>> Iniciar una sesión de Telnet para interactuar con OpenOCD
Así es como inicia una sesión de Telnet para interactuar con el programa OpenOCD en ejecución:
> dism /online /Enable-Feature /FeatureName:TelnetClient
> telnet 127.0.0.1 4444
Si funciona bien, recibirá el siguiente mensaje en su terminal:
Open On-Chip Debugger
> ..
¡Y ya está listo para enviar comandos a OpenOCD! Pero ahora voy a cambiar a la sesión de GDB, ya que es la forma más conveniente de interactuar con OpenOCD.
>> Iniciar una sesión de cliente GDB para interactuar con OpenOCD
Abre otra ventana de terminal y escribe el siguiente comando:
> "C:/Apps/AC6GCC/bin/arm-none-eabi-gdb.exe"
Este comando simplemente inicia el cliente GDB de arm-none-eabi-gdb.exe
. Si todo va bien, GDB comienza con el siguiente mensaje:
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)..
Ahora conecte este cliente GDB al servidor GDB dentro de OpenOCD:
(gdb) target remote localhost:3333
Ahora estás conectado a OpenOCD! Es bueno saberlo: si desea usar un comando OpenOCD nativo (como lo haría en una sesión de Telnet), preceda el comando con el monitor
palabras clave. De esta manera, el servidor GDB dentro de OpenOCD no manejará el comando por sí mismo, sino que lo pasará al deamon nativo de OpenOCD.
Entonces, ahora es el momento de restablecer el chip, borrarlo y detenerlo:
(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
El chip ya está listo para recibir algunas instrucciones de nosotros. Primero le diremos al chip que sus secciones de flash 0 a 7 (que son todas las secciones de flash en mi chip de 1Mb) no deben estar protegidas:
(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
A continuación paro el chip de nuevo. Sólo para estar seguro..
(gdb) monitor halt
Finalmente le .elf
archivo binario .elf
a 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.
Ahora es el momento de la verdad. Le pido a GDB que cargue este binario en el chip. Dedos cruzados:
(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
Lamentablemente no tuvo éxito. Recibo el siguiente mensaje en OpenOCD:
Error: error waiting for target flash write algorithm
Error: error writing to flash at address 0x08000000 at offset 0x00000000
EDIT: problema de hardware solucionado.
Aparentemente era un problema de hardware. Nunca pensé que mi chip estaría defectuoso, ya que cargar el binario en el chip con la herramienta STLink Utility funcionó sin problemas. Solo OpenOCD se quejaba y daba errores. Así que naturalmente culpé a OpenOCD, y no al chip en sí. Ver mi respuesta a continuación para más detalles.
EDITAR: ¡Una forma elegante y alternativa de flashear el chip - usando makefile!
A medida que se solucionó el problema, ahora me centraré en una forma alternativa de ejecutar el flash y la depuración del chip. ¡Creo que esto es realmente interesante para la comunidad!
Es posible que haya notado que usé los comandos de Windows cmd para ejecutar todos los pasos necesarios. Esto puede ser automatizado en un archivo por lotes. Pero hay una forma más elegante: automatizar todo en un archivo make! Sr./Mss. ¿Othane ha sugerido el siguiente makefile para su Cortex-M? chip. Supongo que el procedimiento para un chip Cortex-M7 es muy similar:
#################################################
# 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)
Estimado Sr./Mss. Othane, ¿podría explicar cómo usar este makefile para los siguientes pasos?
- Construye el binario desde el código fuente
- Flashear el chip
Sé algunos conceptos básicos sobre los makefiles, pero tu makefile es realmente muy profundo. Parece que utilizas bastantes características de la utilidad make de GNU. Por favor, danos una explicación más y te concederé el bono ;-)
------------------------------
Ahora simplemente invoca "gdb" y conéctalo al "servidor remoto" (localhost si el servidor y gdb se ejecutan en la misma máquina). Configure GDB de modo que sepa la ubicación del código fuente y la ubicación del archivo ELF. Hay un montón de sitios web que pasan por el uso básico de GDB ...
Parece que hay un GDB para Windows ( http://www.equation.com/servlet/equation.cmd?fa=gdb )
Los siguientes comandos en GDB deberían ayudarte a comenzar:
destino remoto localhost: 3333
directorio / ruta / a / proyecto
archivo de símbolos /path/to/project.elf
Aparentemente era un problema de hardware. Nunca pensé que mi chip estaría defectuoso, ya que cargar el binario en el chip con la herramienta STLink Utility funcionó sin problemas. Solo OpenOCD se quejaba y daba errores. Así que naturalmente culpé a OpenOCD, y no al chip en sí.
Hoy intenté el mismo procedimiento con un nuevo chip en el tablero, ¡y ahora funciona!
Obtengo la siguiente salida en GDB al emitir el comando de load
:
(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)
Gracias a todos los que hicieron todo lo posible por ayudarme :-)
Como lo recuerdo, también tuve algunos problemas con el comando de carga directa, así que cambié a "flash write_image borre my_project.hex 0 ihex" ... obviamente estaba usando archivos hexadecimales, pero parece que los archivos elf deberían funcionar, vea http://openocd.org/doc/html/Flash-Commands.html ... lo bueno de este comando es que también borra solo las secciones de flash que se están escribiendo, lo que es realmente útil y no necesita borrado
Antes de ejecutar el comando anterior, deberá ejecutar "stm32f1x unlock 0" para asegurarse de que el chip esté desbloqueado y que pueda conectarse al flash ... Consulte la hoja de datos sobre esto
También para comenzar, el comando "stm32f1x mass_erase 0" borrará el chip por completo y rápidamente, por lo que es bueno asegurarse de que comiences en un estado conocido
Sé que algunos de estos comandos dicen que son para los f1, pero confía en mí, trabajan para la serie f4
Por cierto, este archivo contiene la mayoría de los comandos que utilizo para actualizar mi f4, por lo que podría ser una buena referencia https://github.com/othane/mos/blob/master/hal/stm32f373/stm32f373.mk
Espero que eso te despegue
Este es un poco breve y no es un gran estilo de , pero le señalaría mi código donde lo he configurado para mi biblioteca "mos" para STM32F4 y STM32F1 ( https://github.com/othane/mos ). Este es un gran tema para responder, así que creo que un ejemplo podría ser mejor.
En resumen, mi proyecto es un árbol de Makefiles, ya que tiene su código para compilar el principal de su interés aquí se encuentra aquí https://github.com/othane/mos/blob/master/hal/stm32f373/stm32f373.mk ... básicamente necesitas openocd y luego tengo una serie de comandos para permitir borrar el chip, o destellar y depurar el nuevo código, etc. simplemente escribiendo make .erase o make .flash o make .debug
Finalmente, si observa mis pruebas de unidad (estos son básicamente programas de ejemplo) encontrará el Makefile para compilarlo + un archivo gdbinit como este https://github.com/othane/mos/blob/master/utest/gpio/debug_gpio_utest.gdbinit ... entonces simplemente haces "make && make .flash && make .debug" en un terminal, y llamas a tus compiladores cruzados gdb así "arm-none-eabi-gdb -x ./debug_gpio_utest.gdbinit" en otro ... esto iniciará gdb después de actualizar el código y puede usar los comandos normales de ruptura y lista de gdb, etc. para interactuar con el código (observe cómo definí un comando de reinicio en el archivo .gdbinit, consulte la ayuda para el mon comando ... básicamente, le permitirá enviar comandos a través de gdb directamente a openocd y es realmente útil)
Lo siento, la respuesta es bastante breve y con muchos enlaces, pero espero que se ponga en marcha.