使用STM32标准库构建VSCode+gcc+openOCD开发STM32

 

    目前为止,使用STM32的平台比较流行的是keil for Arm和IAR for ARM,这两个平台都比较类似,集成编辑、编译和调试环境,俗称IDE。用户只需简单的操作就能编译和调试STM32,非常方便。不过,比较遗憾的是keil和IAR虽然方便使用,但却不是免费的。正版的keil,不管是专业版还是非专业板,每套费用都是上万元。如果是大企业员工用使用盗版,可能keil或IAR的律师函已经在路上了。你可能想到,虽然我使用的keil或者IAR是破解的,但我开发的电脑不联网,不就行了吗?只要企业的产品有使用到单片机,并且是自主研发类型,收到律师函的可能性也不低。或者某天有某个间谍在公司研发部转一圈,也可以大概知道用到哪些收费软件。对于芯片方案商而言,肯定是希望自己的芯片被更多用户放心使用。ST官方近两年推出了STM32CubeIDE,STM32CubeIDE不仅对用户免费,而且支持window、Linux和macOS。如果用户使用STM32CubeIDE开发STM32,那就可以省去购买软件的成本。另外,不仅ST推出免费的STM32CubeIDE,TI也很早就推出了免费的CCS。我认为,跨平台、免费和开源是未来趋势。

    STM32CubeIDE和CCS都是基于eclipse,再加上编译器和调试组件,就可以编译和调试单片机了。eclipse调试单片机,速度会稍微慢点,而且编辑功能不够强大。这里推荐一款软件VSCode(堪称宇宙最强编辑器),VSCode是微软推出的免费开源软件,不仅可以安装各种功能扩展组件,而且支持Windows、Linux和macOS,绝对是无敌的存在。如果能用VSCode开发STM32,绝对爽爆了。国内有不少用户使用VSCode开发单片机程序,仅仅用作文本编辑功能,程序编译和调试还是在keil或者IAR上进行。每次修改都要在两个软件来回切换,甚是繁琐,本质上还是依靠Keil或者IAR做开发。实际上,VSCode+开源gcc+开源openOCD,开发STM32是完全可行的。然后再VSCode添加task来实现程序的编译和下载重启。调试基于开源openOCD,支持多种仿真工具,比如STLINK和CMSIS_DAP。开源VSCode+开源gcc+开源openOCD的组合没有用到任何收费软件,可以放心开发。实际体验丝毫不逊于keil和IAR,该有的功能都具备。接下来我分享一下在windows 7利用VSCode编译和在线调试STM32F103RC单片机的详细过程。Linux和MAC平台也是类似做法。我使用的库是STM32F1x标准库,可能对习惯用标准库的用户有所帮助。

  注意!本帖介绍从零开始搭建gcc开发环境,有一点的难度系数,需要懂一点单片机的开发环境基础。如果你是单片机小白,很可能搞不定,还是推荐用keil或者IAR。

 一、相关准备工作
    1、安装VSCode扩展组件
    自己去VSCode官网下载VSCode。为了方便开发STM32,我们需要下载VSCode的扩展组件,我自己使用的扩展组件如下图所示:

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第1张图片

其中:ARM组件是提供ARM汇编环境的支持;
   C/C++、C/C++ Snipprts和C++Intellisense组件提供C/C++语言的支持、跟踪定义、语法判断等功能;
   Cortex-Debug组件是调试Cortex内核的工具,配合开源openOCD和开源pyOCD等调试软件,就可以实现调试,比如单步,全速运行、进入函数等功能。还可以查看单片机各种寄存器状态,变量的数值。有了Cortex-Debug组件,我们连接调试软件就很方便,无需使用端口号连接调试软件。
  Cortex-Debug:device Support Pack-STM32F1组件是STM32F1个设备支持包,需要配合Cortex-Debug组件使用。

2、安装gcc-arm-none-eabi
    这个工具是gcc对应的arm编译器,下载地址:https://developer.arm.com/tools- ... in/gnu-rm/downloads,我下载的文件是:gcc-arm-none-eabi-9-2019-q4-major-win32-sha2.exe,点击安装到默认地址就行,完成前,记得勾选add path to environment variable。

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第2张图片

然后在桌面敲击“窗口图标+R",进入运行,输入”CMD",打开命令窗口,输入arm-none-eabi-gcc -v,可以查看到版本,说明安装成功。

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第3张图片

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第4张图片

由于gcc-arm-none-eabi软件的目录的bin文件夹不带make.exe,不能直接使用make命令编译程序,所以需要安装make.exe。详细安装过程可以查看我的其他帖子介绍,链接地址:http://www.51hei.com/bbs/dpj-185236-1.html。最后,将make.exe复制到C:\Program Files (x86)\GNU Tools Arm Embedded\9 2019-q4-major\bin。

4、下载开源openOCD
   本次使用的调试软件是openOCD,下载链接地址:https://gnutoolchains.com/arm-eabi/openocd/。然后将目录openocd-20200408\OpenOCD-20200408-0.10.0\bin的三个文件复制到目录:C:\Program Files (x86)\GNU Tools Arm Embedded\9 2019-q4-major\bin。再在桌面敲击“窗口图标+R",进入运行,输入”CMD",打开命令窗口,输入openocd -v,可以查看到版本,说明安装成功。

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第5张图片

 我们也可以用openOCD测试STLINK,建立一个文件夹openocd test,复制目录openocd-20200408\OpenOCD-20200408-0.10.0\share\openocd\scripts\target的swj-dp.tcl和stm32f1x.cfg,复制目录openocd-20200408\OpenOCD-20200408-0.10.0\share\openocd\scripts\interface的stlink.cfg,复制目录:openocd-20200408\OpenOCD-20200408-0.10.0\share\openocd\scripts的mem_helper.tcl。现在文件夹openocd test里面就有四个文件了,打开stm3f1x.cfg,修改source [find target/swj-dp.tcl]为source [swj-dp.tcl],保存。在文件夹openocd test里面,键盘长按shift按键,然后鼠标右击,选择“在此处打开命令串口”,然后在命令窗口输入:openocd -f stlink.cfg -f stm32f1x.cfg,可以看到如下信息,STLINK的指示灯会闪烁。

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第6张图片

5、下载安装git for windows
    git for windows提供git的支持和MINGW64指令终端,下载链接:https://gitforwindows.org/。下载安装完毕后,打开VSCode,在文件-首选项-设置,搜索terminal,设置内置终端的shell为Bash。打开终端后,可以看到终端被设置成bash了。

  6、下载安装LLVM
    LLVM能够对代码进行有效补全,可以理解为Clang,下载链接:https://releases.llvm.org/download.html。安装完毕后,需要手动添加环境变量。方法如下:右击电脑属性-高级系统设置-高级-环境变量-系统变量,打开变量path,添加C:\Program Files\LLVM\bin。

7、整理STM32工程文件(最重要)
    我们用到的编译器是gcc-arm-none-eabi,需要用到STM32的汇编启动文件、设备支持文件、Cortex-M3文件和链接配置文件。我们要去ST官网下载标准库文件,STM32F103的标准库为STM32F10x_StdPeriph_Lib_V3.5.0。下载解压后,打开Libraries文件夹,里面有两个文件夹:STM32F10x_StdPeriph_Driver和CMSIS。STM32F10x_StdPeriph_Driver存放STM32F10x系列单片机的标准库文件,需要把整个文件复制到我们的工程里面。CMSIS有两个文件夹:CoreSupport和DeviceSupport,将CoreSupport复制到我们的工程里面。打开目录\DeviceSupport\ST\STM32F10x,将stm32f10x.h、system_stm32f10x.c和system_stm32f10x.h复制到我们的工程里面。startup文件夹存放汇编启动文件,打开startup文件夹,可以看到文件夹:arm、gcc_ride7、iar和TrueSTUDIO。其中,arm是MDK-kei使用的、gcc_ride7是RIDE7 toolchain使用的,iar是IAR使用的,TrueSTUDIO是Atollic toolchain使用的。我们使用TrueSTUDIO文件夹里面的汇编启动文件,进入此文件夹,可以看到有8个.s结尾的文件,它们的区别如下图所示:

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第7张图片

我使用STM32F103RC,所以选择startup_stm32f10x_hd.s,复制到我们的工程文件中。打开Project\STM32F10x_StdPeriph_Examples\GPIO\IOToggle,复制stm32f10x_conf.h、stm32f10x_it.c和stm32f10x_it.h到我们的工程里面。现在还需要链接配置文件,打开STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template\TrueSTUDIO\STM3210C-EVAL,复制stm32_flash.ld到我们的工程里面。然后打开stm32_flash.ld,修改如下参数,以便适配STM32F103RC。

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第8张图片

为了方便编译工程文件,还需要makefile文件,由于官方库文件的示例没有带makefile文件,所以需要自己实现,我参考也其他工程资料,整理出一份makefile,代码如下:



# 工程文件夹
TARGET = build_pro

# č编译生成文件夹
BUILD_DIR = build

# C源文件
C_SOURCES =  \
USER/main.c \
USER/stm32f10x_it.c \
CMSIS/CM3/CoreSupport/core_cm3.c \
CMSIS/DeviceSupport/ST/STM32F10x/system_stm32f10x.c \
FWLIB/src/misc.c \
FWLIB/src/stm32f10x_gpio.c \
FWLIB/src/stm32f10x_rcc.c \
FWLIB/src/stm32f10x_usart.c \
DRIVER/src/led.c \
DRIVER/src/systick.c \
DRIVER/src/uart.c
 
######################################



# ASM sources
ASM_SOURCES =  \
CMSIS/Startup/startup_stm32f10x_hd.s

######################################
# building variables
######################################
# debug build?
DEBUG = 1
# optimization
OPT = -Og


#######################################
# binaries
#######################################
PREFIX = arm-none-eabi-
ifdef GCC_PATH
CC = $(GCC_PATH)/$(PREFIX)gcc
AS = $(GCC_PATH)/$(PREFIX)gcc -x assembler-with-cpp
CP = $(GCC_PATH)/$(PREFIX)objcopy
SZ = $(GCC_PATH)/$(PREFIX)size
else
CC = $(PREFIX)gcc
AS = $(PREFIX)gcc -x assembler-with-cpp
CP = $(PREFIX)objcopy
SZ = $(PREFIX)size
endif
HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S
 
#######################################
# CFLAGS
#######################################
# cpu
CPU = -mcpu=cortex-m3

# fpu
# NONE for Cortex-M0/M0+/M3

# float-abi


# mcu
MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI)

# macros for gcc
# AS defines
AS_DEFS = 

# C defines   宏定义标志
C_DEFS =  \
-DUSE_STDPERIPH_DRIVER \
-DSTM32F10X_HD

# AS includes
AS_INCLUDES = 

# C includes  C头文件路径
C_INCLUDES =  \
-ICMSIS/CM3/CoreSupport \
-ICMSIS/DeviceSupport/ST/STM32F10x \
-IFWLIB/inc \
-IDRIVER/inc \
-IUSER

# compile gcc flags
ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections

CFLAGS = $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections

ifeq ($(DEBUG), 1)
CFLAGS += -g -gdwarf-2
endif


# Generate dependency information
CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"


#######################################
# LDFLAGS
#######################################
# link script  链接配置文件
LDSCRIPT = CMSIS/STARTUP/linker/stm32_flash.ld

# libraries
#LIBS = -lc -lm -lnosys 
LIBS = -lc
LIBDIR = 
#LDFLAGS = $(MCU) -specs=nano.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections
LDFLAGS = $(MCU) -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections

# default action: build all
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin


#######################################
# build the application
#######################################
# list of objects
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))
# list of ASM program objects
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))

$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) 
	$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@

$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)
	$(AS) -c $(CFLAGS) $< -o $@

$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile
	$(CC) $(OBJECTS) $(LDFLAGS) -o $@
	$(SZ) $@

$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
	$(HEX) $< $@
	
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
	$(BIN) $< $@	
	
$(BUILD_DIR):
	mkdir $@		

#######################################
# clean up
#######################################
clean:
	-rm -fR $(BUILD_DIR)
  
#######################################
# dependencies
#######################################
-include $(wildcard $(BUILD_DIR)/*.d)

# *** EOF ***

其中有几个地方需要注意,路径需要根据自己的工程填写,如下图所示。

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第9张图片

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第10张图片

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第11张图片

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第12张图片

​​

 二、调试工程

1、编译工程

完成上面的步骤,就可以编译工程了。用VSCode打开工程文件夹。下图是我的工程文件夹里面各个文件的存放位置。

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第13张图片

打开终端,输入make,然后回车结束。这时可以看到编译有如下错误:

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第14张图片

可以发现,core_cm3.c这个文件有点小问题,将出错的地方修改如下:

uint32_t __STREXB(uint8_t value, uint8_t *addr)
{
   uint32_t result=0;
  
   //__ASM volatile ("strexb %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );
   __ASM volatile ("strexb %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r" (value) );
   return(result);
}

uint32_t __STREXH(uint16_t value, uint16_t *addr)
{
   uint32_t result=0;
  
   //__ASM volatile ("strexh %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );
   __ASM volatile ("strexh %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r" (value) );
   return(result);
}

然后,通过终端,再次make一下,这次就编译通过了。

2、构建openOCD仿真环境

openOCD需要配合cortex-debug扩展组件使用。将前面测试STLINK的四个文件复制到工程中。

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第15张图片

我们需要在工程中新建launch.json文件,里面代码如下:

{
    // 使用 IntelliSense 了解相关属性。 
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        
        
        {
            "cwd": "${workspaceRoot}",
            "executable": "./build/build_pro.elf",
            "name": "Cortex Debug",
            "request": "launch",
            "type": "cortex-debug",
            "svdFile": "C:/Users/wyt/.vscode/extensions/marus25.cortex-debug-dp-stm32f1-1.0.0/data/STM32F103xx.svd",
            "servertype": "openocd",
            "configFiles": [  
                "./Debugger/stlink.cfg",
                "./Debugger/stm32f1x.cfg"
            ]
        },
        {
            "name": "(gdb) 启动",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/Scr/a.exe",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "miDebuggerPath": "/path/to/gdb",
            "setupCommands": [
                {
                    "description": "为 gdb 启用整齐打印",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ]
        }
    ]
}

需要注意几个地方,如图所示:

使用STM32标准库构建VSCode+gcc+openOCD开发STM32_第16张图片

添加完毕后,保存一下。然后按快捷键F5,就会成功进入仿真界面

3、创建task实现Build、Rebuild和Build and Download

每次编译文件都要在终端输入make指令,可能有点不方便。为了能像keil或者IAR那样,可以方便快捷编译和下载代码,我们可以新建task.json,将其打包成任务。代码如下:

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            //编译
            "label": "Build Target",
            "type": "shell",
            "command": "make",
            "problemMatcher": [
                "$gcc"
            ]
        },
        {
            //先删除后编译
            "label": "Rebuild Target",
            "type": "shell",
            "command": "rm -rf build && make",
            "problemMatcher": [
                "$gcc"
            ]
        },
        {
            //编译并且下载
            "label": "Build and Download",
            "type": "shell",
            "command": "make && cp build/build_pro.hex Debugger/AutoDownload/bin/target.hex && cd Debugger/AutoDownload/bin/ && ./Stlink_write_image.bat",
            "problemMatcher": [
                "$gcc"
            ]
        }
    ]
}

可以看到,Build Target就是执行make指令,Rebuild Target就是先删除build文件夹,然后再执行make指令,Build and Download则是先执行make指令。然后复制hex文件,再利用脚本实现下载和重启。有对脚本感兴趣的,可以自行查看脚本代码,这里就不做说明。

4、为VSCode填写头文件路径和宏定义标志

    makefile里面已经有对头文件和宏定义标志,gcc编译器可以读懂makefile的内容,但是VSCode编辑器没法读懂。我们用keil或者IAR,也要填写头文件路径和宏定义,而且只需要填写一次即可。实际上,keil或者IAR内部已经做了处理了,它让编辑器和编译器能够共享头文件路径和宏定义。VSCode有自己的实现方式,我们需要新建c_cpp_properties.json,里面填写头文件和宏定义标志,可以参考makefile的部分内容。里面填写如下代码:

{
    "configurations": [
        {
            "name": "Win32",
            "browse": {
                "path": [
                    "${workspaceFolder}/CMSIS/CM3/CoreSupport/*",
                    "${workspaceFolder}/CMSIS/DeviceSupport/ST/STM32F10x/*",
                    "${workspaceFolder}/FWLIB/inc/*",
                    "${workspaceFolder}/DRIVER/inc/*",
                    "${workspaceFolder}/USER/*",
                    "${workspaceFolder}",
                    "C:/Program Files (x86)/GNU Tools Arm Embedded/9 2019-q4-major/arm-none-eabi/include",
                    "C:/Program Files (x86)/GNU Tools Arm Embedded/9 2019-q4-major/arm-none-eabi/include/c++/9.2.1",
                    "C:/Program Files (x86)/GNU Tools Arm Embedded/9 2019-q4-major/arm-none-eabi/include/c++/9.2.1/arm-none-eabi/thumb/v7-m",
                    "C:/Program Files (x86)/GNU Tools Arm Embedded/9 2019-q4-major/arm-none-eabi/include/c++/9.2.1/backward",
                    "C:/Program Files (x86)/GNU Tools Arm Embedded/9 2019-q4-major/arm-none-eabi/include/sys",
                    "C:/Program Files (x86)/GNU Tools Arm Embedded/9 2019-q4-major/lib/gcc/arm-none-eabi/9.2.1/include",
                    "C:/Program Files (x86)/GNU Tools Arm Embedded/9 2019-q4-major/lib/gcc/arm-none-eabi/9.2.1/include-fixed"
                ],
                "limitSymbolsToIncludedHeaders": true
            },
            "includePath": [
                "${workspaceFolder}/CMSIS/CM3/CoreSupport/*",
                "${workspaceFolder}/CMSIS/DeviceSupport/ST/STM32F10x/*",
                "${workspaceFolder}/FWLIB/inc/*",
                "${workspaceFolder}/DRIVER/inc/*",
                "${workspaceFolder}/USER/*",
                "${workspaceFolder}",
                "C:/Program Files (x86)/GNU Tools Arm Embedded/9 2019-q4-major/arm-none-eabi/include",
                "C:/Program Files (x86)/GNU Tools Arm Embedded/9 2019-q4-major/arm-none-eabi/include/c++/9.2.1",
                "C:/Program Files (x86)/GNU Tools Arm Embedded/9 2019-q4-major/arm-none-eabi/include/c++/9.2.1/arm-none-eabi/thumb/v7-m",
                "C:/Program Files (x86)/GNU Tools Arm Embedded/9 2019-q4-major/arm-none-eabi/include/c++/9.2.1/backward",
                "C:/Program Files (x86)/GNU Tools Arm Embedded/9 2019-q4-major/arm-none-eabi/include/sys",
                "C:/Program Files (x86)/GNU Tools Arm Embedded/9 2019-q4-major/lib/gcc/arm-none-eabi/9.2.1/include",
                "C:/Program Files (x86)/GNU Tools Arm Embedded/9 2019-q4-major/lib/gcc/arm-none-eabi/9.2.1/include-fixed"
            ],
            "defines": [
                "_DEBUG",
                "UNICODE",
                "_UNICODE",
                "USE_STDPERIPH_DRIVER",
                "STM32F10X_HD"
            ],
            "compilerPath": "C:\\Program Files\\LLVM\\bin\\clang-format.exe",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "clang-x64"
        }
    ],
    "version": 4
}

有这个文件,VSCode就能够知道哪些文件是工程当前使用的,查找函数定义,VSCode可以准确跳转到函数定义处,对代码也有完美的补全功能。

    帖子到这来结束,感兴趣的,可以下载工程附件。如果有任何问题,欢迎在评论区留言!

你可能感兴趣的:(使用STM32标准库构建VSCode+gcc+openOCD开发STM32)