使用VS Code开发STM32:基于CMake构建标准库和HAL库项目

使用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 tools
    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 settings
    project(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 $<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:

    /*
    ******************************************************************************
    **
    ** @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>&copy; 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 搭建嵌入式开发环境

    作者:二进制的向阳

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用VS Code开发STM32:基于CMake构建标准库和HAL库项目

    发表回复