VSCode编译调试STM32的环境搭建
VSCode编译调试STM32的环境搭建
深夜了,我很饿,我只是想起床吃个泡面。
@firestaradmin 2021年5月13日00:49:02
一、软件环境安装
-
主角不用多说。
-
用于生成带Makefile的HAL库工程。(本文章后面会介绍如何替换为STD标准库)
-
arm用的GNU工具链,记得把bin文件夹添加到系统Path环境变量。
OpenOCD :
开源的片上调试器(Open On-Chip Debugger),记得把bin文件夹添加到系统Path环境变量。
linux可以直接
apt install openocd
。Git:
git版本管理,我们这里要使用的是下载git附带的git bash。这个bash是基于mingw的,非常轻量了,甚至于make都没有。但是问题不大,相信你们电脑里肯定都装有MinGW,或者QT,进入他们的目录,搜索
mingw32-make.exe
,复制一份重命名为make.exe
,然后将存放的目录添加到系统环境变量即可。(如果是Linux则无视这条)
我的环境变量如下:

环境变量修改完毕后,可能需要重启电脑才能生效。然后打开cmd,输入:
arm-none-eabi-gcc --version
and
openocd --version
能看到版本号,说明ok。
二、工程创建
1. 用STM32CubeMX创建工程
新建工程,如下:
选择工程后配置,这里就不详细介绍配置了。主要配置一下时钟和调试接口,SWD就选Serial Wire调试模式。
之后到Project Manager页面 ,选择Toolchain 为 MakeFile,如图:
紧接着,选择拷贝所有的库到工程目录,这样HAL库就全部在工程目录了。当然到时候你可以自己替换为标准库。
最后点击 GENERATE CODE 按钮,生成工程:
于是乎,生成了如下的文件:
2. 配置VSCode
可以直接用VSCode 打开当前目录,或者重新复制一份:

这里有些文件,是后面生成的,亲们目录里没有不要着急哦。
这里 .ioc 文件和 .mxproject 文件是STM32Cube的工程文件,Drivers文件夹里是STM32和ARM CMSIS的库。Core文件夹里的Inc和Src是用户源码和头文件。
接下来装一些插件啦
- **C/C++**:提供代码补全、智能感知和debug功能;
- C/C++ Snippets:C/C++ 代码模板片段,自动生成代码段;
- ARM:提供ARM汇编语言的代码高亮;
- Cortex-Debug:结合ARM工具链和OpenOCD等工具可以进行图形化调试操作,这样就不用了敲命令来GDB调试了。
以下为可选插件:
**Chinese (Simplified)**:中文支持;
file-icons:文件图标;
LinkerScript:ld链接脚本语法高亮。
其他的插件,你们自己想装就装咯。
下一步(Linux 无视之),Ctrl + Shift + P 快捷键,搜索 settings json
,会出现下图,选中打开设置(JSON)
之后在文件末尾添加如下两行设置,用来设置VSCode默认终端为我们上面下载的Git自带的Bash。
"terminal.integrated.shell.windows": "D:\\Program Files\\Git\\bin\\bash.exe",
Ctrl + S 保存,Ctrl + ~(Tab按键上面那个键) 打开终端。
或者如果你只想在当前工程目录下使用bash开发,可以只在当前目录下新建一个.vscode文件夹,然后新建一个单独的settings.json,在里面写上:
{
"terminal.integrated.shell.windows": "C:\\Program Files\\Git\\bin\\bash.exe"
}
这里如果你是另存为工作区了,可能新建settings.json 文件设置不了终端,需要下图的设置打上勾
然后点击你的工作区文件,进行设置,如下图:
现在其实已经可以打开终端,make 了。但是VSCode 会提示很多波浪线,让人头大,而且会发现也没有语法提示,这怎么能受得了呢。
以前用Keil 开发的时候,我们会在Project Options里设置全局宏定义,如:

而我们用CubeMX生成的Makefile项目中,这两个宏是通过gcc的-D参数在编译时添加的:
但是,VS Code只是一个编辑器,它检查代码的时候并不会去读Makefile,而是只看.h和.c文件,于是在STM32的头文件(如stm32f1xx.h)中就检测不到那个宏,从而认为这个宏没有被定义。
因此我们需要在当前目录的.vscode文件夹下创建c_cpp_properties.json
配置文件,用来告诉VS Code我们定义了这些宏。
Ctrl + Shift + P 打开搜索 c/c++
,选择编辑配置,如图:
这里是设置好的(我会解释每个设置的作用):
{
"configurations": [
{
"name": "Win32",
"includePath": [
"D:/Program Files/gcc-arm-none-eabi-10-2020-q4-major/**",
"${workspaceFolder}/**"
// "${workspaceFolder}/Drivers/CMSIS",
// "${workspaceFolder}/Core/Inc",
// "${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc",
// "${workspaceFolder}/Drivers/CMSIS/Device/ST/STM32F1xx/Include"
],
"defines": [
"USE_HAL_DRIVER",
"STM32F103xE"
],
"compilerPath": "D:\\Program Files\\gcc-arm-none-eabi-10-2020-q4-major\\bin\\arm-none-eabi-gcc.exe",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "gcc-x64"
}
],
"version": 4
}
name:用于标记当前平台,如win32、Linux或Mac。也就是说,这个json里“configuration“下可以写三组配置,只要每组配置前面写上不同的平台,即可在不同的操作系统上使用就会自动适配不同的配置。
includePath:VSCode会从这些目录寻找头文件。
第一个目录是ARM交叉编译工具链的目录,/** 代表递归寻找目下的文件,可能会降低识别的速度,当文件夹下文件夹很多的时候,尽量用详细点的路径比较好。 目录可以参考Makefile, “${workspaceFolder}”`表示项目文件夹。
defines:全局宏定义,告诉VSCode这些宏都被定义了,只是没写在源码中而已。上述多加的两个宏是Makefile里的。
compilerPath:指定编译器的路径。因为有些宏是编译器自带的,连Makefile里也没有,例如
__GNUC__
。有些教程里会让你在defines里面加上__GNUC__
,但是这是没必要的。只要你指定了编译器路径,所有的编译器自带的宏都会导入VS Code。intelliSenseMode:gcc选gcc-x64。
cStandard: 使用的c标准(一般不影响)
cppStandard: 使用的c++标准(一般不影响)
配置好后,Ctrl + S 保存, 神情气爽,没有红色波浪线了,如果有,请慢慢找问题,一般是头文件路径的问题。
这个时候make编译,提示没有make的,去MinGW,或者QT,进入他们的目录,搜索mingw32-make.exe
,复制一份重命名为make.exe
,然后将存放的目录添加到系统环境变量即可。
(可以无视这里,命令不多的情况下,我喜欢用手敲指令,这里只是介绍一下)为了方便操作,我们在.vscode目录下创建 tasks.json 文件,内容如下:
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "make",
"args": [
"-j"
]
},
{
"label": "clean",
"type": "shell",
"command": "make",
"args": [
"clean"
]
}
]
}
这个文件创建了两个任务,分别叫做build和clean,build任务就是在bash里执行了make -j,clean任务就是在bash里执行了make clean。
VS Code是可以给任务绑定快捷键的,有兴趣可以自己搜索。
不使用快捷键而执行task的方法:按Ctrl + P,然后输入”task[空格]“,就会出现可用的任务列表。
三、编译调试
1、Openocd 设置
直接在项目文件夹下新建一个openocd.cfg文件,内容如下
# 选择调试器
#source [find interface/jlink.cfg]
source [find interface/cmsis-dap.cfg]
# 选择接口为SWD
transport select swd
# 选择目标芯片
source [find target/stm32f1x.cfg]
#source [find target/stm32f4x.cfg]
openocd启动时,会自动在当前目录下寻找名为openocd.cfg的文件作为配置文件。
本配置文件中引用到的配置文件,都在openocd安装目录下的share/openocd/scripts目录下。其中interface目录下都是接口相关配置文件、target目录下都是芯片相关的配置文件。
2、下载svd文件(可选)
在<我是链接>寻找对应STM32的svd文件。
CMSIS-SVD是CMSIS的一个组件,它包含完整微控制器系统(包括外设)的程序员视图的系统视图描述 XML 文件。VS Code可以通过它来知道外设寄存器的地址分布,从而把寄存器内容展示到窗口中。将下载好的STM32FXXX.svd文件放在项目文件夹根目录即可。
3. 配置VS Code的调试功能
在.vscode文件夹中新建一个launch.json,内容如下:
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Cortex Debug",
"cwd": "${workspaceRoot}",
"executable": "${workspaceRoot}/build/${workspaceFolderBasename}.elf",
"request": "launch",
"type": "cortex-debug",
"device":"STM32F103ZE", //使用J-link GDB Server时必须;其他GBD Server时可选(有可能帮助自动选择SVD文件)。支持的设备见 https://www.segger.com/downloads/supported-devices.php
//"svdFile": "./STM32F407.svd", //svd文件,有这个文件才能查看寄存器的值,每个单片机都不同。可以在以下地址找到 https://github.com/posborne/cmsis-svd
"servertype": "openocd", //使用的GDB Server
"configFiles": [
"${workspaceRoot}/openocd.cfg"
],
//"preLaunchTask": "build",
//"armToolchainPath": "C:/Program Files (x86)/GNU Arm Embedded Toolchain/9 2020-q2-update/bin/"
}
]
}
解释几个重要选项:
- executable:编译出的文件,也就是最终烧录到单片机中的,这里是elf文件。根据芯片的不同,可能产生不同的后缀。
- request:可以选launch或attach。launch是指启动调试时同时开始执行程序;attcah是指程序已经在运行了,然后开始调试。
- type:调试的类型,选cortex-debug,这是我们装的插件。默认是cppdbg之类的,但是那样我们得自己配置gdb,配置起来听说很非常麻烦。
- device:使用J-link GDB Server时必须;其他GBD Server时可选(有可能帮助自动选择SVD文件)。支持的设备见 https://www.segger.com/downloads/supported-devices.php
- svdFile:svd文件的路径。
- servertype:要选择的gdb server。我们用openocd。
- configFiles:gdb server的配置文件路径。其实openocd会自动读当前目录下的openocd.cfg文件,这个选项不填也行。但是如果你想把openocd.cfg放在别处,就可以用这个选项指定配置文件的路径。
- preLaunchTask:在启动调试前,预先执行的任务。在这里如果配置成前面可选的build任务。这样每次调试前都会先自动编译好。
- armToolchainPath:工具链的路径。配置了全局环境变量的情况下好像不设置也行
(PS:我注释掉某些的意思就是,大家可以多变通,不必跟着教程一步一步来,按照自己的想法来(当然我自己就是上面那么配置的)。)
4、编译调试
配置完后,如下图:
接下来,按F5 或者点击 左侧:
就开始调试、设置断点、单步执行和暂停了。
左边可以看到变量窗口、调用堆栈、断点、外设寄存器、CPU寄存器。
配置已经全部完成,你的show time 到了。
四、替换为标准库
0、删掉之前的HAL库文件,如图红框中的文件:
1、下载对应芯片的标准库,解压后:
2、进入Library 文件夹,复制目录下的两个文件夹,到我们工程目录下的Drivers。
3、进入解压标准库下的STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\USART\Printf文件夹,也就是随便找个例程
然后全部复制到我们工程目录下的Core文件夹(readme.txt 可以不复制),文件夹的命名当然随意了,如图:
4、打开VSCode,目录结构如下:

5、修改之前的c_cpp_properties.json
文件,包含头文件,修改后如下:
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"D:/Program Files/gcc-arm-none-eabi-10-2020-q4-major/**"
],
"defines": [
"USE_STDPERIPH_DRIVER",
"STM32F10X_HD"
],
"compilerPath": "D:\\Program Files\\gcc-arm-none-eabi-10-2020-q4-major\\bin\\arm-none-eabi-gcc.exe",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "gcc-x64"
}
],
"version": 4
}
这里主要修改了define 里的定义,因为之前是HAL库,现在是STD库。
6、打开根目录下的MakeFile文件,修改
首先是 C_SOURCES 变量:
C_SOURCES = \
Core/main.c \
Core/stm32f10x_it.c \
Drivers/CMSIS/CM3/CoreSupport/core_cm3.c \
Drivers/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c \
$(wildcard Drivers/STM32F10x_StdPeriph_Driver/src/*.c)
也就是将用到的.c 添加进 C_SOURCES 变量。$(wildcard Drivers/STM32F10x_StdPeriph_Driver/src/*.c) ,会读取目录下所有.c文件,你也可以不全部添加,一个一个输入。下图中有两个system_stm32f10x.c
文件,随便包含一个即可,这是因为刚刚复制的printf demo里有,重复了,可以删掉一个。

下一步,修改ASM_SOURCES 变量:
这里为什么要选择TrueSTUDIO 下的启动文件呢,因为在其中,arm是MDK-kei使用的、gcc_ride7是RIDE7 toolchain使用的,iar是IAR使用的,TrueSTUDIO是Atollic toolchain使用的。我们使用TrueSTUDIO文件夹里面的汇编启动文件即可。
修改C_DEFS 变量:
C_DEFS = \
-DUSE_STDPERIPH_DRIVER \
-DSTM32F10X_HD
因为替换为了STD标准库,定义当然不一样了。
修改 C_INCLUDES 变量:
C_INCLUDES = \
-ICore \
-IDrivers/CMSIS/CM3/CoreSupport/ \
-IDrivers/CMSIS/CM3/DeviceSupport/ST/STM32F10x \
-IDrivers/STM32F10x_StdPeriph_Driver/inc
也就是包含用到的头文件。
接下来make 编译试试,发现报错:
说main.c 里找不到 stm32_eval.h 因为我们没有复制该文件,我们简单的修改main.c 文件如下:
#include "stm32f10x.h"
#include <stdio.h>
int main(){
while(1){}
}
然后再次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) );
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) );
return(result);
}
函数分别位于core_cm3.c 中的732、749行:
替换后如下:
这个时候我们再次make编译:
成功,神清气爽!
你的show time 又到了。
五、使用CMAKE生成
重点就是在CMakeLists.txt 文件首行添加 set(CMAKE_SYSTEM_NAME Generic)
。
这里有一份我写好的F103 的CMAKE文件,STD库的。修改头文件路径和源文件就好了。如果是F4的或者其他的,照着修改就好了,主要就是编译参数什么的,可以去默认的Makefile文件里找,慢慢测试,不要心急。
set(CMAKE_SYSTEM_NAME Generic)
cmake_minimum_required(VERSION 3.0.0)
project(mydemo C CXX ASM)
# specify the cross compiler
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
set(CMAKE_ASM_COMPILER arm-none-eabi-gcc)
set(CMAKE_OBJCOPY arm-none-eabi-objcopy)
set(CMAKE_OBJDUMP arm-none-eabi-objdump)
set(CMAKE_SIZE arm-none-eabi-size)
## add project components
set(ELF_TARGET ${PROJECT_NAME}.elf)
# set the build type
set(CMAKE_BUILD_TYPE Debug)
# generate flags from user variables
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(DBG_FLAGS "-O0 -g2 -ggdb")
elseif(CMAKE_BUILD_TYPE MATCHES Release)
set(DBG_FLAGS "-O3")
endif()
# print build type.
if(CMAKE_BUILD_TYPE MATCHES Debug)
message(STATUS "Build type: Debug")
elseif(CMAKE_BUILD_TYPE MATCHES Release)
message(STATUS "Build type: Release")
endif()
#######################################
# MCU_FLAGS
#######################################
# cpu
set(CPU "-mcpu=cortex-m3")
# fpu
# NONE for Cortex-M0/M0+/M3
set(FPU "")
# float-abi
set(FLOAT_ABI "")
## auto-set variables from user input
set(MCU_FLAGS "${CPU} -mthumb ${FPU} ${FLOAT_ABI}")
#######################################
# LDFLAGS
#######################################
# link script
set(LDSCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/STM32F103ZETx_FLASH.ld)
# libraries
set(LIBS "-lc -lm -lnosys")
set(LIBDIR "")
set(LDFLAGS "${MCU} -specs=nano.specs -T${LDSCRIPT} ${LIBDIR} ${LIBS} -Wl,-Map=${PROJECT_BINARY_DIR}/${PROJECT_NAME}.map,--cref -Wl,--gc-sections")
set(CMAKE_EXE_LINKER_FLAGS "${LDFLAGS}")
# compiler: language specific flags
set(CMAKE_C_FLAGS "${MCU_FLAGS} -Wall -fdata-sections -ffunction-sections ${DBG_FLAGS} ")
set(CMAKE_CXX_FLAGS "${MCU_FLAGS} -fno-rtti -fno-exceptions -fno-builtin -Wall -fdata-sections -ffunction-sections ${DBG_FLAGS} ")
set(CMAKE_ASM_FLAGS "${MCU_FLAGS} -x assembler-with-cpp ${DBG_FLAGS} ")
# global define.
add_definitions(
-DUSE_STDPERIPH_DRIVER
-DSTM32F10X_HD
)
# include path.
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/Core
${CMAKE_CURRENT_SOURCE_DIR}/Drivers/CMSIS/CM3/CoreSupport
${CMAKE_CURRENT_SOURCE_DIR}/Drivers/CMSIS/CM3/DeviceSupport/ST/STM32F10x
${CMAKE_CURRENT_SOURCE_DIR}/Drivers/STM32F10x_StdPeriph_Driver/inc
)
# C source files
set(C_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/Core/main.c
${CMAKE_CURRENT_SOURCE_DIR}/Core/stm32f10x_it.c
${CMAKE_CURRENT_SOURCE_DIR}/Drivers/CMSIS/CM3/CoreSupport/core_cm3.c
${CMAKE_CURRENT_SOURCE_DIR}/Drivers/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c
)
# 添加文件夹里所有的源文件到 C_SOURCES_2
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/Drivers/STM32F10x_StdPeriph_Driver/src/ C_SOURCES_2)
# ASM sources
set(ASM_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/Drivers/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/TrueSTUDIO/startup_stm32f10x_hd.s
)
add_executable(${ELF_TARGET} ${C_SOURCES} ${C_SOURCES_2} ${ASM_SOURCES})
# # name of targets
set(ELF_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.elf)
set(HEX_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.hex)
set(BIN_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.bin)
# # create binary & hex files and show size of resulting firmware image
add_custom_command(TARGET "${PROJECT_NAME}.elf" POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -Obinary ${ELF_FILE} ${BIN_FILE}
COMMAND ${CMAKE_OBJCOPY} -Oihex ${ELF_FILE} ${HEX_FILE}
COMMENT "Building ${PROJECT_NAME}.bin and ${PROJECT_NAME}.hex"
COMMAND ${CMAKE_COMMAND} -E copy ${HEX_FILE} "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.hex"
COMMAND ${CMAKE_COMMAND} -E copy ${BIN_FILE} "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.bin"
COMMAND ${CMAKE_SIZE} --format=berkeley ${PROJECT_NAME}.elf ${PROJECT_NAME}.hex
COMMENT "Invoking: Cross ARM GNU Print Size"
)
1、编译生成
CTRL+` 键打开终端, 输入
mkdir build & cd build
新建文件夹并进入输入
cmake .. -G "MinGW Makefiles"
生成makefile 文件。make -j
编译
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!