编程ARM微控制器的标准(低成本)方法是使用Eclipse,并在其中插入复杂的工具链。 Eclipse肯定有它的优点,但我想要独立于这个IDE。我想发现当我构建(编译 - 链接 - 闪存)我的软件以及运行调试会话时幕后发生的事情。为了更深入地理解,从命令行运行整个过程会很棒。
注意:我使用的是64位Windows 10.但这里解释的大部分内容也适用于Linux系统。请使用管理员权限打开所有命令终端。这可以为您节省很多问题。
1。构建软件
第一个任务'完成了。我现在可以通过命令行编译并将我的软件链接到二进制.bin
和.elf
图像。成功的关键是找出Eclipse为特定项目提供make文件的位置。一旦你知道它们在哪里,你所要做的就是打开一个命令终端,然后输入GNU make
命令。
你不再需要Eclipse了!特别是如果您可以阅读(并理解)makefile并在项目进展时根据您的需要进行调整。
请注意,在安装SW4STM32(System Workbench for STM32)之后,我在以下文件夹中找到了GNU工具(编译器,链接器,make实用程序,GDB,...):
C:\Ac6\SystemWorkbench\plugins\fr.ac6.mcu.externaltools.arm-none.win32_1.7.0.201602121829\tools\compiler\
接下来,我在我的硬盘上创建了一个新文件夹,并将所有这些GNU工具复制到其中:
C:\Apps\AC6GCC
|-> arm-none-eabi
|-> bin
'-> lib
我将这些条目添加到"环境路径变量":
- C:\Apps\AC6GCC\bin
- C:\Apps\AC6GCC\lib\gcc\arm-none-eabi\5.2.1
Huray,现在我已经在我的系统上启动并运行了所有GNU工具!我将以下build.bat
文件放在与makefile
:
@echo off
echo.
echo."--------------------------------"
echo."- BUILD -"
echo."--------------------------------"
echo.
make -j8 -f makefile all
echo.
运行这个bat文件应该可以胜任!如果一切顺利,您将获得一个.bin
和一个.elf
二进制文件作为编译结果。
2。闪存和调试固件
自然的后续步骤是将固件刷新到芯片并启动调试会话。在Eclipse中,只需点击一下按钮即可。 - 至少是否为您的微控制器正确配置了Eclipse。但是幕后会发生什么? 我从Dominic Rath(OpenOCD的开发者)那里阅读(部分)硕士论文。你可以在这里找到它: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调试器(GDB)。这就是为什么OpenOCD还在TCP端口3333上启动GDB服务器.GDB客户端可以连接到该端口,并开始调试微控制器!
Gnu Debugger是一个命令行软件。很多人更喜欢可视化界面。这正是Eclipse所做的。 Eclipse启动了一个连接到OpenOCD的GDB客户端 - 但这对用户来说都是隐藏的。 Eclipse提供了一个图形界面,可以在幕后与GDB客户端进行交互。
我做了一个数字来解释所有这些事情:
>>启动OpenOCD
我设法从命令行启动OpenOCD。我将解释如何。
打开命令终端,然后启动OpenOCD。您需要为OpenOCD提供一些配置文件,以便它知道在哪里寻找您的微控制器。通常,您需要提供描述JTAG编程器的配置文件,以及定义微控制器的配置文件。使用命令行中的-f
参数将这些文件传递给OpenOCD。您还需要通过使用scripts
参数传递给-s
文件夹的OpenOCD访问权限。这是我使用命令行在计算机上启动OpenOCD的方法:
> "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(使用正确的参数),它将使用以下消息启动:
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
。
&GT;&GT;启动Telnet会话以与OpenOCD进行交互
这是启动Telnet会话以与正在运行的OpenOCD程序进行交互的方法:
> dism /online /Enable-Feature /FeatureName:TelnetClient
> telnet 127.0.0.1 4444
如果效果很好,您将在终端上收到以下消息:
Open On-Chip Debugger
> ..
您已准备好向OpenOCD发送命令!但我现在转而使用GDB会话,因为这是与OpenOCD交互的最便捷方式。
&GT;&GT;启动GDB客户端会话以与OpenOCD进行交互
打开另一个终端窗口,然后键入以下命令:
> "C:\Apps\AC6GCC\bin\arm-none-eabi-gdb.exe"
此命令只是启动arm-none-eabi-gdb.exe
GDB客户端。如果一切顺利,GDB会启动以下消息:
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服务器:
(gdb) target remote localhost:3333
现在您已连接到OpenOCD了!很高兴知道:如果您想使用本机OpenOCD命令(就像在Telnet会话中那样),只需在命令前加上关键字monitor
即可。这样,OpenOCD中的GDB服务器本身不会处理命令,而是将其传递给本机OpenOCD守护程序。
所以,现在是时候重置芯片,擦除它并停止它:
(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芯片中的所有闪存部分)不应受到保护:
(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
接下来我再次停止芯片。只是为了确定..
(gdb) monitor halt
最后,我将二进制.elf
文件移交给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.
现在是关键时刻。我请GDB将这个二进制文件加载到芯片中。手指交叉:
(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中收到以下消息:
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中的所有内容! Mr./Mss。 Othane为他/她的Cortex-M建议了以下makefile?芯片。我想Cortex-M7芯片的程序非常相似:
#################################################
# 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)
亲爱的Mr.Mss。 Othane,你能解释如何使用这个makefile进行以下步骤:
我知道一些关于makefile的基础知识,但你的makefile确实非常深入。您似乎使用了GNU make实用程序的一些功能。请给我们一些解释,我会给你奖金; - )
答案 0 :(得分:7)
我记得它也有直接加载命令的问题,所以我切换到&#34; flash write_image erase my_project.hex 0 ihex&#34; ..显然我使用的是十六进制文件,但它看起来像elf文件应该工作,请参阅http://openocd.org/doc/html/Flash-Commands.html ...关于这个命令的好处是它也只擦除了写入的flash部分非常方便而且你不需要擦除
在运行上述命令之前,您需要运行&#34; stm32f1x unlock 0&#34;确保芯片解锁并允许连接到闪存......请参阅有关此内容的数据表
也是为了开始命令&#34; stm32f1x mass_erase 0&#34;将完全和快速地擦除芯片,以确保您以已知状态开始
我知道其中一些命令说它们是针对f1的,但相信我他们为f4系列工作
顺便说一句,这个文件包含我用来闪存f4的大部分命令,所以它可能是一个很好的参考https://github.com/othane/mos/blob/master/hal/stm32f373/stm32f373.mk
我希望能让你解开
答案 1 :(得分:2)
这是一个简短而不是很好的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&amp;&amp; ;在一个终端中制作.flash&amp;&amp;和.debug“,并在另一个终端中调用你的交叉编译器gdb,如”arm-none-eabi-gdb -x ./debug_gpio_utest.gdbinit“......这将启动gdb之后刷新代码,你可以使用gdb等正常的break和list命令与代码交互(注意我在.gdbinit文件中如何定义一个reset命令,检查mon命令的帮助...基本上它会让你通过gdb将命令直接发送到openocd并且非常有用)
很抱歉,答案非常简短且有很多链接,但我希望它能帮到你。
答案 2 :(得分:2)
关于OpenOCD,我建议在与GDB实例相同的目录中启动它,这样如果你从GDB内部调用它,二进制下载似乎是透明的(最简单的方法)。您还可以选择创建一个启动OpenOCD并加载代码的脚本,但之后您必须在每次编译后重新启动它。
答案 3 :(得分:1)
您现在只需调用&#34; gdb&#34;并将其连接到&#34;远程服务器&#34; (如果服务器和gdb在同一台机器上运行,则为localhost)。配置GDB,使其知道源代码的位置和ELF文件的位置。有大量网站经历了GDB的基本用法..
似乎有一个适用于Windows的GDB(http://www.equation.com/servlet/equation.cmd?fa=gdb)
GDB中的以下命令可以帮助您入门:
目标远程localhost:3333
目录/路径/到/项目
symbol-file /path/to/project.elf
答案 4 :(得分:0)
显然这是一个硬件问题。我从未想过我的芯片会出现缺陷,因为使用STLink Utility工具将二进制文件加载到芯片上工作没有问题。只有OpenOCD抱怨并给出错误。所以我自然而然地指责OpenOCD - 而不是芯片本身。
今天我在电路板上使用新芯片尝试了相同的程序,现在它可以工作了!
发出load
命令时,我在GDB中获得以下输出:
(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)
感谢所有尽力帮助我的人: - )