本文以启明欣欣的工控板举例,讲述如何使用VSCode来开发STM32。MCU是STM32F407ZGT6
一 准备
1. 交叉编译器
打开https://gitlab.arm.com/tooling/gnu-toolchains-for-arm,找到编译器列表,

选择一个合适的,这里选最新的15.3.rel1,点击进入后下拉,找到如下Windows hosted交叉编译器,

选择红框中的任意一个,第一个是安装版,第二个是免安装版,本人下载了第二个,下载后找个目录解压,然后把目录下的bin目录添加到系统环境变量PATH里,例如本人的解压路径是D:\SW\arm-gnu-toolchain-15.3.rel1,

然后把D:\SW\arm-gnu-toolchain-15.3.rel1\bin添加到系统环境变量Path里,
- 右键 "此电脑" → "属性" → "高级系统设置" → "环境变量"
- 在 "系统变量" 中找到 Path,点击 "编辑",添加:D:\SW\arm-gnu-toolchain-15.3.rel1\bin
2. ninja
ninja用来代替make做实际的编译工作。
打开https://github.com/ninja-build/ninja/releases/,在Assets里找到Windows版本,

下载后解压到一个目录,然后把这个目录地址添加到系统环境变量Path了
3. openocd
打开https://github.com/xpack-dev-tools/openocd-xpack/releases,在Assets里找到Windows版本,

解压后把bin目录添加到系统环境变量Path里
4. CMake
下载Windows版本并安装,也要添加系统环境变量Path里,不再赘述
5. VSCode插件
安装CMake Tools和Cortex-Debug这2个插件即可

6. CubeMX工程
可以参考https://blog.csdn.net/whahu1989/article/details/144226202来创建CubeMX工程,同时把PE3,PE4和PG9配置成输出的GPIO,可以操作板子上的灯
在生成项目时注意以下几点,

Toolchain / IDE选择Makefile,这样生成的启动汇编文件和链接器脚本可以直接复用,另外勾选如下,

二 移植到VSCode
创建个目录然后使用VSCode打开,然后创建如下目录和文件,

然后按照如下步骤进行拷贝,
- CubeMX工程的Core目录里的东西拷贝到VSCode工程的Core目录里
- CubeMX工程的Drivers目录里的东西拷贝到VSCode工程的Drivers目录里
- CubeMX工程的STM32F407xx_FLASH.ld拷贝到VSCode工程的ld目录
- CubeMX工程的startup_stm32f407xx.s拷贝到VSCode工程的startup目录
CMakeLists.txt的内容如下,
bash
cmake_minimum_required(VERSION 3.20)
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
# 交叉编译器
set(TOOLCHAIN_PREFIX arm-none-eabi-)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++)
set(CMAKE_ASM_COMPILER ${TOOLCHAIN_PREFIX}gcc)
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy)
set(CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}objdump)
project(STM32F407_APP LANGUAGES C CXX ASM)
# ================= STM32F407 M4F 配置 =================
set(MCU_CORE cortex-m4)
# 编译标志:每个参数单独一项,禁止空格拼接长字符串
add_compile_options(
-mcpu=${MCU_CORE}
-mthumb
-mfpu=fpv4-sp-d16
-mfloat-abi=hard
-Og
-g3
-Wall
-ffunction-sections
-fdata-sections
-std=c99
)
# 汇编文件启用cpp预处理
add_compile_options($<$<COMPILE_LANGUAGE:ASM>:-xassembler-with-cpp>)
# 链接参数同样!拆成独立元素,不要长字符串
add_link_options(
-mcpu=${MCU_CORE}
-mthumb
-mfpu=fpv4-sp-d16
-mfloat-abi=hard
-T${CMAKE_SOURCE_DIR}/ld/STM32F407xx_FLASH.ld
-Wl,--gc-sections
-Wl,-Map=${PROJECT_NAME}.map
--specs=nano.specs
--specs=nosys.specs
)
# ================= 文件列表 =================
file(GLOB_RECURSE USER_SOURCES
Core/Src/*.c
Drivers/STM32F4xx_HAL_Driver/Src/*.c
Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c
)
# GCC启动汇编
set(ASM_SOURCES startup/startup_stm32f407xx.s)
# 头文件路径
include_directories(
Core/Inc
Drivers/STM32F4xx_HAL_Driver/Inc
Drivers/CMSIS/Device/ST/STM32F4xx/Include
Drivers/CMSIS/Include
)
# 预定义宏(和CubeMX保持一致)
add_definitions(
-DSTM32F407xx
-DUSE_HAL_DRIVER
)
add_executable(${PROJECT_NAME}.elf ${USER_SOURCES} ${ASM_SOURCES})
# 生成hex、bin、反汇编
add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -O ihex ${PROJECT_NAME}.elf ${PROJECT_NAME}.hex
COMMAND ${CMAKE_OBJCOPY} -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin
COMMAND ${CMAKE_OBJDUMP} -d ${PROJECT_NAME}.elf > ${PROJECT_NAME}.lst
)
.vscode目录下创建settings.json和launch.json,
settings.json内容如下,
bash
{
"cmake.generator": "Ninja",
"cmake.buildDirectory": "${workspaceFolder}/build",
"C_Cpp.default.compilerPath": "arm-none-eabi-gcc",
"C_Cpp.default.includePath": [
"${workspaceFolder}/**"
],
"C_Cpp.default.defines": [
"STM32F407xx",
"USE_HAL_DRIVER"
]
}
launch.json内容如下,
bash
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug STM32F407 STLink",
"type": "cortex-debug",
"request": "launch",
"servertype": "openocd",
"executable": "${workspaceFolder}/build/STM32F407_APP.elf",
"configFiles": [
"interface/stlink.cfg",
"target/stm32f4x.cfg"
],
"runToEntryPoint": "main",
"cwd": "${workspaceFolder}",
"svdFile": "${workspaceFolder}/STM32F407.svd"
}
]
}
注意:CMakeLists.txt里设定的输出目标名字是STM32F407_APP.elf,所以在launch.json里配置"executable"时也要使用相同的文件
三 编译
在工程目录下打开终端,然后执行下面的命令进行编译,
bash
cmake -GNinja -B build -S .
cmake --build build
编译完成后可以在build目录下看到生成的elf,hex和bin文件,

四 下载和调试
launch.json里设定了下载和调试,此时打开左侧的"Run and Debug",可以看到在已经有一个调试配置,这个名字和launch.json里设定的是一样的

点击这个名字左侧的绿色三角调试按钮,就可以自动下载并开启调试,直接走到main函数里并停下。用户可以在main.c里加点操作GPIO的代码来验证效果。
五 小结
本文讲述了如何使用VSCode开发STM32,可能会问为什么不使用ST提供的VSCode插件?或者为什么不使用PlatformIO?本人是为了简化和通用性,ST的插件只能用在ST的芯片,如果有其它厂的芯片就用不了,PlatformIO感觉有点重而且它不是使用CMake的。
最重要的一点是,如果项目已经很成熟,MCU及其外设什么的都是固定的,那么这样做就会简单很多,降低维护成本。