使用VScode开发STM32:基于CMake(包含标准库和HAL库工程)_stm32 cmake
使用VScode开发STM32:基于CMake(包含标准库和HAL库工程)
本教程使用VScode作为代码编辑工具、Cmake作为构建系统生成器、Make进行构建系统、使用arm-none-eabi-gcc进行交叉编译、使用OpenOCD作为代码下载与调试工具,最终搭建出适用于ARM架构系列芯片的开发环境。此教程以STM32F103ZET6芯片为例。
经验证,可满足基本基本项目需求。但我发现arm-none-eabi-gcc编译器相比于keil的AC5、AC6编译器,所编译的hex文件比较大,Flash占用较高,为了兼顾keil开发项目,也为了能够与其他人的项目兼容,这里的keil与VScode的项目文件互不干扰,满足兼容性需求。
本文涉及的软件安装包、工程模板已放在我的百度网盘中,需要自取(本教程使用的是标准库,HAL库与其基本相同,具体差异请参考分享的文件自行对照)。
链接:https://pan.baidu.com/s/1N4DI9GpaRnCr-4J0uCTTHw?pwd=wqfz 提取码:wqfz --来自百度网盘超级会员V4的分享
一、软件安装
已默认电脑上存在VScode,这里不讲述Vscode的安装。
涉及软件的安装配置:
- 安装Cmake
- 安装arm-none-eabi-gcc
- 安装OpenOCD
- MinGW
- 安装VScode插件C/C++、CMake、Cortex-Debug
1.1 安装CMake
1.1.1 安装
下载地址:
https://cmake.org/download/
选择适合自己电脑的最新版本进行下载并安装,我这里选择cmake-3.29.2-windows-x86_64.msi
,如下图:
1.1.2 添加环境变量
我们需要将cmake的可执行文件的文件夹路径添加到环境变量,方便使用命令调用cmake,我的路径为:
D:\\RJ\\CMake\\bin
将以上目录添加到系统环境变量中去。
1.1.3 验证
在终端输入以下命令,验证是否安装成功。
cmake
成功则将显示以下内容:
1.2 安装arm-none-eabi-gcc
1.2.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,我的路径为:
D:\\RJ\\ARM-GCC\\10 2021.10\\bin
将以上目录添加到系统环境变量中去。
1.2.3 验证
在终端输入以下命令,验证是否安装成功。
arm-none-eabi-gcc
成功则将显示以下内容:
1.3 安装OpenOCD
1.3.1 安装
下载地址:
https://gnutoolchains.com/arm-eabi/openocd/
选择适合自己电脑的最新版本进行下载,直接下载的是压缩包文件,解压后可直接使用,我这里选择openocd-20231002.7z
,如下图:
1.3.2 添加环境变量
我们需要将OpenOCD的可执行文件的文件夹路径添加到环境变量,方便使用命令调用OpenOCD,我的路径为:
D:\\RJ\\OpenOCD-20231002-0.12.0\\bin
将以上目录添加到系统环境变量中去。
1.3.3 验证
在终端输入以下命令,验证是否安装成功。
openOCD
成功则将显示以下内容:
1.4 安装MinGW
1.4.1 安装
下载地址:
https://sourceforge.net/projects/mingw-w64/files/
选择适合自己电脑的最新版本进行下载,直接下载的是压缩包文件,解压后的mingw64可直接使用,我这里选择MinGW-W64GCC-8.1.0下的x86_64-posix-sjlj
,如下图:
1.4.2 添加环境变量
我们需要将make的可执行文件的文件夹路径添加到环境变量,方便使用命令调用make,我的路径为:
D:\\RJ\\mingw64\\bin
将以上目录添加到系统环境变量中去。
1.3.3 验证
在终端输入以下命令,验证是否安装成功(由于Window下make执行程序为mingw32-make.exe,我这里将其复制保存同目录下为副本,并改名为make.exe)。
make
成功则将显示以下内容:
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的模板,需要更改的部分我已经标明。
#THIS FILE IS AUTO GENERATED FROM THE TEMPLATE! DO NOT CHANGE!set(CMAKE_SYSTEM_NAME Generic)set(CMAKE_SYSTEM_VERSION 1)cmake_minimum_required(VERSION 3.20) # specify cross compilers and toolsset(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 settingsproject(Project C CXX ASM)set(CMAKE_CXX_STANDARD 17)set(CMAKE_C_STANDARD 11) #Uncomment for hardware floating point#add_compile_definitions(ARM_MATH_CM4;ARM_MATH_MATRIX_CHECK;ARM_MATH_ROUNDING)#add_compile_options(-mfloat-abi=hard -mfpu=fpv4-sp-d16)#add_link_options(-mfloat-abi=hard -mfpu=fpv4-sp-d16) #Uncomment for software floating point#add_compile_options(-mfloat-abi=soft) add_compile_options(-mcpu=cortex-m3 -mthumb -mthumb-interwork)add_compile_options(-ffunction-sections -fdata-sections -fno-common -fmessage-length=0) # uncomment to mitigate c++17 absolute addresses warnings#set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -Wno-register\")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)#添加头文件路径,即.h文件include_directories(./STM32F10x_FWLib/inc ./User ./Project/Code-Cmake)#添加源文件路径,即.c或者.s文件file(GLOB_RECURSE SOURCES ./STM32F10x_FWLib/src/*.c ./User/*.c ./Project/Code-Cmake/*.*)#添加你的STM32F103ZETx_FLASH.ld的连接脚本路径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)#选择cortex-m3内核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 $ ${HEX_FILE} COMMAND ${CMAKE_OBJCOPY} -Obinary $ ${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:
/*********************************************************************************** @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路径替换为自己的路径
{ \"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可以在调试时查看看寄存器的值,请将以下路径改为自己工程的路径。
{ // 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。 // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 \"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\", //在OpenOCD选择下载器 \"D:/RJ/OpenOCD-20231002-0.12.0/share/openocd/scripts/target/stm32f1x.cfg\" //在OpenOCD选择芯片 ], \"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是芯片型号
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 搭建嵌入式开发环境