目前为止,使用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的扩展组件,我自己使用的扩展组件如下图所示:
其中: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。
然后在桌面敲击“窗口图标+R",进入运行,输入”CMD",打开命令窗口,输入arm-none-eabi-gcc -v,可以查看到版本,说明安装成功。
由于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,可以查看到版本,说明安装成功。
我们也可以用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的指示灯会闪烁。
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结尾的文件,它们的区别如下图所示:
我使用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。
为了方便编译工程文件,还需要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 ***
其中有几个地方需要注意,路径需要根据自己的工程填写,如下图所示。
二、调试工程
1、编译工程
完成上面的步骤,就可以编译工程了。用VSCode打开工程文件夹。下图是我的工程文件夹里面各个文件的存放位置。
打开终端,输入make,然后回车结束。这时可以看到编译有如下错误:
可以发现,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的四个文件复制到工程中。
我们需要在工程中新建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
}
]
}
]
}
需要注意几个地方,如图所示:
添加完毕后,保存一下。然后按快捷键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可以准确跳转到函数定义处,对代码也有完美的补全功能。
帖子到这来结束,感兴趣的,可以下载工程附件。如果有任何问题,欢迎在评论区留言!