使用VScode开发STM32:基于CMake 本教程使用VScode作为代码编辑工具、使用Cmake作为构建系统生成器、Make进行构建系统、使用arm-none-eabi-gcc进行交叉编译、使用OpenOCD作为代码下载与调试工具,最终搭建出适用于ARM架构系列芯片的开发环境。此教程以STM32F103ZET6芯片为例,演示LED灯闪烁的项目。
经验证,可满足基本基本项目需求。同时由于arm-none-eabi-gcc编译器相比于keil的AC5、AC6编译器,所编译的hex文件比较大,Flash占用较高,为了兼顾keil开发项目,也为了能够与其他人的项目兼容,这里的keil与VScode的项目文件互不干扰,满足兼容性需求。
本文涉及的软件安装包、工程模板已放在我的百度网盘中,需要自取。
1 2 3 链接:https://pan.baidu.com/s/1pO-5R3Li5NIfctx3UzaPig?pwd=ts91 提取码:ts91 --来自百度网盘超级会员V4的分享
一、软件安装 已默认电脑上存在VScode,这里不讲述Vscode的安装。
涉及软件的安装配置:
安装Cmake
安装arm-none-eabi-gcc
安装OpenOCD
MinGW
安装VScode插件C/C++、CMake、Cortex-Debug
1.1 安装CMake 1.1.1 安装 下载地址:
1 https://cmake.org/download/
选择适合自己电脑的最新版本进行下载并安装,我这里选择cmake-3.29.2-windows-x86_64.msi
,如下图:
1.1.2 添加环境变量 我们需要将cmake的可执行文件的文件夹路径添加到环境变量,方便使用命令调用cmake,我的路径为:
将以上目录添加到系统环境变量中去。
1.1.3 验证 在终端输入以下命令,验证是否安装成功。
成功则将显示以下内容:
1.2 安装arm-none-eabi-gcc 1.2.1 安装 下载地址:
1 https://developer.arm.com/downloads/-/gnu-rm
选择适合自己电脑的最新版本进行下载并安装,我这里选择gcc-arm-none-eabi-10.3-2021.10-win32.exe
,如下图:
1.2.2 添加环境变量 我们需要将arm-gcc的可执行文件的文件夹路径添加到环境变量,方便使用命令调用arm-gcc,我的路径为:
1 D:\RJ\ARM-GCC\10 2021.10\bin
将以上目录添加到系统环境变量中去。
1.2.3 验证 在终端输入以下命令,验证是否安装成功。
成功则将显示以下内容:
1.3 安装OpenOCD 1.3.1 安装 下载地址:
1 https://gnutoolchains.com/arm-eabi/openocd/
选择适合自己电脑的最新版本进行下载,直接下载的是压缩包文件,解压后可直接使用,我这里选择openocd-20231002.7z
,如下图:
1.3.2 添加环境变量 我们需要将OpenOCD的可执行文件的文件夹路径添加到环境变量,方便使用命令调用OpenOCD,我的路径为:
1 D:\RJ\OpenOCD-20231002-0.12.0\bin
将以上目录添加到系统环境变量中去。
1.3.3 验证 在终端输入以下命令,验证是否安装成功。
成功则将显示以下内容:
1.4 安装MinGW 1.4.1 安装 下载地址:
1 https://sourceforge.net/projects/mingw-w64/files/
选择适合自己电脑的最新版本进行下载,直接下载的是压缩包文件,解压后的mingw64可直接使用,我这里选择MinGW-W64GCC-8.1.0下的x86_64-posix-sjlj
,如下图:
1.4.2 添加环境变量 我们需要将make的可执行文件的文件夹路径添加到环境变量,方便使用命令调用make,我的路径为:
将以上目录添加到系统环境变量中去。
1.3.3 验证 在终端输入以下命令,验证是否安装成功(由于Window下make执行程序为mingw32-make.exe,我这里将其复制保存同目录下为副本,并改名为make.exe)。
成功则将显示以下内容:
1.5 在Vscode中安装插件 要安装的插件如下:
二、工程搭建 以下是我的工程框架
与ARM-MDK工程不同,我们配置工程还需要格外的文件,分别是CMakeLists.txt、startup_stm32f10x_hd.s、STM32F103ZETx_FLASH.ld。
2.1 配置CMakeLists.txt文件 CMake根据CMakeLists.txt进行构建,从而创建出Makefile,再由make根据 Makefile 定义的规则调用 GCC 执行编译工作,最终生成可执行的.elf或者.hex文件。以下是CMakeLists.txt的模板,需要更改的部分我已经标明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_VERSION 1) cmake_minimum_required(VERSION 3.20) 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_AR arm-none-eabi-ar) set(CMAKE_OBJCOPY arm-none-eabi-objcopy) set(CMAKE_OBJDUMP arm-none-eabi-objdump) set(SIZE arm-none-eabi-size) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) project(Project C CXX ASM) set(CMAKE_CXX_STANDARD 17) set(CMAKE_C_STANDARD 11) add_compile_options(-mcpu=cortex-m3 -mthumb -mthumb-interwork) add_compile_options(-ffunction-sections -fdata-sections -fno-common -fmessage-length=0) if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release" ) message(VERBOSE "Maximum optimization for speed" ) add_compile_options(-Ofast) elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo" ) message(VERBOSE "Maximum optimization for speed, debug info included" ) add_compile_options(-Ofast -g) elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel" ) message(VERBOSE "Maximum optimization for size" ) add_compile_options(-Os) else () message(VERBOSE "Minimal optimization, debug info included" ) add_compile_options(-Og -g) endif ()add_definitions(-DUSE_HAL_DRIVER -DSTM32F103xB -DUSE_STDPERIPH_DRIVER -DSTM32F10X_HD) include_directories(./STM32F10x_FWLib/inc ./User ./Project/Code-Cmake) file(GLOB_RECURSE SOURCES ./STM32F10x_FWLib/src/*.c ./User/*.c ./Project/Code-Cmake/*.*) set(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/Project/Code-Cmake/STM32F103ZETx_FLASH.ld) add_link_options(-Wl,-gc-sections,--print-memory-usage,-Map=${PROJECT_BINARY_DIR}/${PROJECT_NAME}.map) add_link_options(-mcpu=cortex-m3 -mthumb -mthumb-interwork) add_link_options(-T ${LINKER_SCRIPT}) add_link_options(-specs=nano.specs -specs=nosys.specs -u _printf_float) add_executable(${PROJECT_NAME}.elf ${SOURCES} ${LINKER_SCRIPT}) set(HEX_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.hex) set(BIN_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.bin) add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD COMMAND ${CMAKE_OBJCOPY} -Oihex $< TARGET_FILE:${PROJECT_NAME}.elf> ${HEX_FILE} COMMAND ${CMAKE_OBJCOPY} -Obinary $< TARGET_FILE:${PROJECT_NAME}.elf> ${BIN_FILE} COMMENT "Building ${HEX_FILE} Building ${BIN_FILE}" )
2.2 选择startup_stm32f10x_hd.s 在我们创建ARM-MDK工程时,我们从官方的固件包中选择的是arm版本的启动文件,在这里我们要选择gcc版本的启动文件,即下图中的gcc_ride7。同时为了与ARM-MDK有所区分,我将该文件放在了/Project/Code-Cmake文件夹下。
2.3 STM32F103ZETx_FLASH.ld STM32F103ZETx_FLASH.ld是一个链接脚本文件,它告诉编译器相关的编译后的可执行代码,内存变量,中断向量,链接在哪个存储区。获取方式主要有三种(请根据自己单片机型号选择):
使用CudeMax编译过程可以生成该链接脚本
搜索已有的工程,你可以直接在浏览器搜索STM32F103ZETx_FLASH.ld,一般都有。
如果你对该型号芯片足够了解,可以自行编写。
以下是我的STM32F103ZETx_FLASH.ld:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 /* ****************************************************************************** ** ** @file : LinkerScript.ld ** ** @author : Auto-generated by STM32CubeIDE ** ** @brief : Linker script for STM32F103ZETx Device from STM32F1 series ** 512Kbytes FLASH ** 64Kbytes RAM ** ** Set heap size, stack size and stack location according ** to application requirements. ** ** Set memory bank area and size if external memory is used ** ** Target : STMicroelectronics STM32 ** ** Distribution: The file is distributed as is, without any warranty ** of any kind. ** ****************************************************************************** ** @attention ** ** <h2><center>© Copyright (c) 2021 STMicroelectronics. ** All rights reserved.</center></h2> ** ** This software component is licensed by ST under BSD 3-Clause license, ** the "License"; You may not use this file except in compliance with the ** License. You may obtain a copy of the License at: ** opensource.org/licenses/BSD-3-Clause ** ****************************************************************************** */ /* Entry Point */ ENTRY(Reset_Handler) /* Highest address of the user mode stack */ _estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */ _Min_Heap_Size = 0x200 ; /* required amount of heap */ _Min_Stack_Size = 0x400 ; /* required amount of stack */ /* Memories definition */ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K } /* Sections */ SECTIONS { /* The startup code into "FLASH" Rom type memory */ .isr_vector : { . = ALIGN(4); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4); } >FLASH /* The program code and other data into "FLASH" Rom type memory */ .text : { . = ALIGN(4); *(.text) /* .text sections (code) */ *(.text*) /* .text* sections (code) */ *(.glue_7) /* glue arm to thumb code */ *(.glue_7t) /* glue thumb to arm code */ *(.eh_frame) KEEP (*(.init)) KEEP (*(.fini)) . = ALIGN(4); _etext = .; /* define a global symbols at end of code */ } >FLASH /* Constant data into "FLASH" Rom type memory */ .rodata : { . = ALIGN(4); *(.rodata) /* .rodata sections (constants, strings, etc.) */ *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ . = ALIGN(4); } >FLASH .ARM.extab : { . = ALIGN(4); *(.ARM.extab* .gnu.linkonce.armextab.*) . = ALIGN(4); } >FLASH .ARM : { . = ALIGN(4); __exidx_start = .; *(.ARM.exidx*) __exidx_end = .; . = ALIGN(4); } >FLASH .preinit_array : { . = ALIGN(4); PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(.preinit_array*)) PROVIDE_HIDDEN (__preinit_array_end = .); . = ALIGN(4); } >FLASH .init_array : { . = ALIGN(4); PROVIDE_HIDDEN (__init_array_start = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array*)) PROVIDE_HIDDEN (__init_array_end = .); . = ALIGN(4); } >FLASH .fini_array : { . = ALIGN(4); PROVIDE_HIDDEN (__fini_array_start = .); KEEP (*(SORT(.fini_array.*))) KEEP (*(.fini_array*)) PROVIDE_HIDDEN (__fini_array_end = .); . = ALIGN(4); } >FLASH /* Used by the startup to initialize data */ _sidata = LOADADDR(.data); /* Initialized data sections into "RAM" Ram type memory */ .data : { . = ALIGN(4); _sdata = .; /* create a global symbol at data start */ *(.data) /* .data sections */ *(.data*) /* .data* sections */ *(.RamFunc) /* .RamFunc sections */ *(.RamFunc*) /* .RamFunc* sections */ . = ALIGN(4); _edata = .; /* define a global symbol at data end */ } >RAM AT> FLASH /* Uninitialized data section into "RAM" Ram type memory */ . = ALIGN(4); .bss : { /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) *(.bss*) *(COMMON) . = ALIGN(4); _ebss = .; /* define a global symbol at bss end */ __bss_end__ = _ebss; } >RAM /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */ ._user_heap_stack : { . = ALIGN(8); PROVIDE ( end = . ); PROVIDE ( _end = . ); . = . + _Min_Heap_Size; . = . + _Min_Stack_Size; . = ALIGN(8); } >RAM /* Remove information from the compiler libraries */ /DISCARD/ : { libc.a ( * ) libm.a ( * ) libgcc.a ( * ) } .ARM.attributes 0 : { *(.ARM.attributes) } }
2.4 关于core_cm3.c文件 由于gcc编译的问题,如果不更改core_cm3.c,可能出现以下报错:
我对此做出以下两处更改,并放在了/Project/Code-Cmake文件夹下,与MDK-ARM分开:
2.5 配置.vscode文件夹 这是VScode配置文件的位置
2.4.1 添加并配置c_cpp_properties.json 将其C/C++模式更改为gcc-arm,注意将gcc路径替换为自己的路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 { "configurations" : [ { "name" : "Win32" , "includePath" : [ "${workspaceFolder}/**" ] , "defines" : [ "_DEBUG" , "UNICODE" , "_UNICODE" ] , "compilerPath" : "D:\\RJ\\mingw64\\bin\\gcc.exe" , "cStandard" : "gnu17" , "cppStandard" : "gnu++14" , "intelliSenseMode" : "gcc-arm" , "configurationProvider" : "ms-vscode.cmake-tools" } ] , "version" : 4 }
2.4.1 添加并配置launch.json
这个文件是关于烧录与调试相关的,在此目录下你可以选择你的下载器型号、芯片型号。其中的stm32f103.svd可以在调试时查看看寄存器的值,请将以下路径改为自己工程的路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 { "version" : "0.2.0" , "configurations" : [ { "cwd" : "${workspaceRoot}" , "executable" : "D:/GC/STM32F1/build/Project.elf" , "name" : "Debug with OpenOCD" , "request" : "launch" , "type" : "cortex-debug" , "servertype" : "openocd" , "configFiles" : [ "D:/RJ/OpenOCD-20231002-0.12.0/share/openocd/scripts/interface/stlink-v2.cfg" , "D:/RJ/OpenOCD-20231002-0.12.0/share/openocd/scripts/target/stm32f1x.cfg" ] , "svdFile" : "D:/GC/STM32F1/stm32f103.svd" , } ] }
三、编译、下载与调试 如果我们配置完成后,用VScode打开CMakeLists.txt所在文件夹工程过后,Cmake tool会自动提示配置Cmake,点击配置后,会生成build文件夹,产生的Makefile及其他中间文件会存放在该目录。
3.1 选择编译器 点击VScode下方的配置按钮,选择gcc-arm
3.2 编译 点击VScode下方的进行编译,生成目标文件
编译过程
在build文件夹下会生成目标文件
3.3 烧录 进入build文件夹下执行以下命令,其中将Project.hex替换为自己的目标文件,stlink-v2.cfg是选择下载器类型,stm32f1x.cfg是芯片型号
1 openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c "program Project.hex verify reset exit"
烧录成功
3.3 调试 打开左侧的运行和调试,选择Debug with OpenOCD
点击运行,可进行断点调试,变量监测,寄存器查看等操作。
3.4 关于变量定义 使用gcc编译时,我们一般需要告诉编译器这个变量是可变的,不然会造成内存覆盖,程序无法运行的情况,即voatile
关键词
四、参考链接
此文章参考以下文章,若描述不清,可查看下方文章
Vscode搭建开发调试STM32/RISC-V环境IDE(最全面)
VSCode 和 CMake 搭建嵌入式开发环境