VSCode+arm-none-eabi-gcc交叉编译+CMake构建+OpenOCD(基于STM32的标准库/HAL库)

前言:什么是CMake?

简而言之,CMake是 makefile / build.ninja / rules.ninja 等构建文件的maker。

一、CMake的安装

有以下3种获取途径:

  • 直接访问GitHub的xPack项目xPack CMake v3.30.7-1,下载二进制发行版,将其放入指定目录即可,无需安装。
  • 或者进入CMake官网的下载地址Get the Software,根据操作系统下载并安装对应的Binary distributions版本。
    在这里插入图片描述
  • 或者在CMake国内镜像地址中获取二进制镜像安装包。 在这里插入图片描述

安装完后,记得添加用户/系统的环境变量,然后打开cmd窗口,输入cmake -version,查看CMake环境变量是否添加成功,设置成功后,如下图所示:
在这里插入图片描述

二、工程所需配置文件

本文以【STM32F103ZET6】单片机为示例来进行演示,以下配置文件对于标准库/HAL库工程是通用的。
更多细节请基于【VSCode+arm-none-eabi-gcc交叉编译+Makefile构建+OpenOCD(基于STM32标准库的保姆级教程)】该篇文章参考。

下面列出几个重要的配置文件:

①:

②:

  • CMakeLists.txt

  • 请参考本篇文章。

③:

三、VSCode脚本文件

3.1 settings.json

{
    // 字符集编码选择
    "files.encoding": "utf8",
    // 自动保存任意文件
    "files.autoSave": "afterDelay",
    // 文件图标主题:"material-icon-theme"
    "workbench.iconTheme": "material-icon-theme",
    // “现代深色”主题
    "workbench.colorTheme": "Default Dark Modern",
    //粘贴时格式化代码
    "editor.formatOnPaste": true,
    //保存时格式化代码
    "editor.formatOnSave": true,
    //设置字体的大小,最小值能设置为6
    "editor.fontSize": 15,
    //设置字体的粗细
    "editor.fontWeight": "500",
    //设置字体的样式
    // "terminal.integrated.fontFamily":"Courier New",
    //使用Ctrl+滚轮缩放编辑区的字体大小
    "editor.mouseWheelZoom": true,
    //使用Ctrl+滚轮缩放终端Terminal的字体大小
    "terminal.integrated.mouseWheelZoom": true,
    //设置为false,这样打开新的文件时,不会自动关闭旧的文件
    "workbench.editor.enablePreview": false,
    //据说可以减少VSCode的CPU和内存占用
    "search.followSymlinks": false,
    
    "security.workspace.trust.enabled": false,
    "VsCodeTaskButtons.showCounter": true,
    "VsCodeTaskButtons.tasks": [
        {
            "label": "$(tools) Build", // 显示标签
            "task": "CMake build", // 对应tasks.json里的任务label
            "tooltip": "🛠️ build" // 工具提示框
        },
        {
            "label": "$(notebook-delete-cell) Clean",
            "task": "CMake clean",
            "tooltip": "🧹 clean"
        },
        {
            "label": "$(notebook-delete-cell) $(tools) Re-bulid", //"$(notebook-delete-cell) & $(tools)",
            "task": "CMake cleanRebuild",
            "tooltip": "🛠️ rebuild" // "🧹 & 🛠️ rebuild"
        },
        {
            "label": "$(zap) Download", 
            // "task": "CMake download",
            "tasks": [
                {
                    "label": "⚓ CMSIS-DAP-Link", //icon copied from https://emojipedia.org/
                    "task": "flash with CMSIS-DAP-Link"
                },
                {
                    "label": "⤵️ ST-Link", //icon copied from https://emojipedia.org/
                    "task": "flash with ST-Link"
                },
                {
                    "label": "🚀 J-Link", //icon copied from https://emojipedia.org/
                    "task": "flash with J-Link"
                }
            ],
            "tooltip": "⚡ download"
        }
    ],
    //这2行配置很重要!!!在tasks.json中的任务类型如果是"shell",则这里的shell选择,关系到对应shell命令的使用适配与否。
    "terminal.explorerKind": "integrated",
    "terminal.integrated.defaultProfile.windows": "Command Prompt", //You can also choose from the following three shells:"Command Prompt" or "PowerShell" or "Windows PowerShell" or "Git Bash"
    //warning 1:若想使用makefile.makePath配置,则需安装Makefile Tools插件;
    //warning 2:若想使用cmake.generator生成器配置,则需安装CMake和CMake Tools这两个插件
    /*
                                    👇
                                    👇
                                    👇
    */
    // "makefile.makePath": "E:/embedded_dev_tools/xpack-ninja-build-1.12.1-1/bin/ninja.exe",
    // "cmake.generator": "Ninja", // VSCode的CMake插件默认配置生成器为Ninja
    //
    // "makefile.makePath": "E:/embedded_dev_tools/xpack-windows-build-tools-4.4.1-2/bin/make.exe",
    // "cmake.generator": "Unix Makefiles",
}

3.2 c_cpp_properties.json

{
    "configurations": [
        {
            "name": "STM32F103_ARM_GCC",
            "includePath": [    // 设置编辑器中的头文件查找目录
                "${workspaceFolder}/**"
            ],
            "defines": [
                "_DEBUG",
                "UNICODE",
                "_UNICODE",
                "__GNUC__"      // 解决编辑器中提示无法识别uint32_t, uint16_t, uint8_t的问题
            ],
            /*
            只要设置了系统环境变量,在此处填写arm-none-eabi-gcc即可。
            VSCode C/C++ IntelliSense插件会自动索引arm-none-eabi-gcc.exe所在路径,故无需在此填写完整路径。
            */
            "compilerPath": "arm-none-eabi-gcc",
            //这一配置在目前所搭建的环境下没用,但是如果你使用的是SDK,库和驱动文件与项目工程不在一个文件夹中,这一步就很有用了,它能让你在当前项目就能向库代码跳转。
            "compileCommands": "${workspaceFolder}/build/compile_commands.json",
            "cStandard": "gnu17",   // 设置使用的C标准
            "cppStandard": "gnu++17", // 设置使用的C++标准
            "intelliSenseMode": "gcc-arm", // 设置编译器类型,解决编辑器中提示无法识别例如__attribute__((weak))等gcc编译器功能选项
            "configurationProvider": "ms-vscode.cmake-tools"
        }
    ],
    "version": 4
}

3.3 tasks.json

{
    //快捷键ctrl+shift+B调出各个task命令,等效于在终端输入命令行
    "tasks": [
        {
            "label": "CMake configure",
            "type": "shell",
            "command": "cmake", //要执行的命令,前提是已经在系统环境变量PATH中安装了CMake
            "args": [
                //严格顺序要求:必须按 -S <source> -> -B <build> -> -G <generator> -> [其他选项] 的顺序排列,否则可能导致配置失败或逻辑错误
                "-S", //可选,-S <sourceDir>。若未显式指定,CMake 默认从当前工程目录开始查找 CMakeLists.txt。
                ".", // "."表示指定源代码目录为包含 CMakeLists.txt 的根目录。
                "-B", //必选,-B <buildDir>。配置生成Makefile or Ninja脚本及其相关文件的路径, 使得源码和构建过程文件分开,以便更好地管理项目
                "./build", //这条命令会告诉 CMake 在工程根目录下创建一个名为 build 的子目录(如果它不存在的话),并在该子目录下生成makefile/build.ninja/rules.ninja等构建文件
                "-G", //配置生成器类型
                "Ninja", //"Unix Makefiles",
                //"-D", //设置构建类型,可选择如下:Debug/Release/RelWithDebInfo/MinSizeRel
                //"CMAKE_BUILD_TYPE=Debug",
                "-D",
                "CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE", //生成compile_commands.json的文件,该文件是为了让vscode有代码导航、自动补全和语法高亮功能。
                // "--debug-output" //显示详细的CMake配置过程信息,可用于调试 CMakeLists.txt 脚本的逻辑问题。
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "problemMatcher": "$gcc"
        },
        {
            "label": "CMake build",
            "type": "shell", //直接使用shell的手动输入命令行“cmake --build {构建目录的绝对路径} 其他可选的配置参数”
            "command": "cmake",
            "args": [
                "--build", //不论后端构建工具是Make or Ninja, 只需调用CMake的统一命令"--build", 便能实现在某个文件夹下构建(生成可执行文件),具有跨平台特性。
                "${workspaceFolder}/build", //生成可执行文件.elf和进制文件.hex/.bin的路径
                "--config Debug",//选项优先级大小:(构建阶段)--config > (配置阶段)-DCMAKE_BUILD_TYPE
                "--target ${workspaceRootFolderName}.elf", //"all",构建整个项目。允许指定单一或多个目标来构建,而不是构建整个项目,这可以显著减少构建时间。
                "--parallel 5" //CMake的现代化语法,其功能是并行编译,区别于 CMakeLists.txt 中的多线程功能开关:add_compile_options(-pthread)
                //"--verbose" //可简写为 -v,让底层的构建工具(如 Make 或 Ninja)输出详细的构建(编译和链接)过程信息,为CMake通用命令。
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "dependsOn": [
                "CMake configure"
            ],
            "problemMatcher": [
                "$gcc"
            ]
        },
        {
            "type": "shell",
            "label": "CMake cleanRebuild",
            "dependsOrder": "sequence",
            "dependsOn": [
                "CMake clean",
                "CMake build"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "problemMatcher": [
                "$gcc"
            ]
        },
        {
            "label": "CMake clean",
            "type": "shell",
            // "command": "Remove-Item -Path ./build/* -Recurse", 
            ////powershell(windows10及以上的操作系统)可调用,只删除build目录下的所有文件。
            ////使用该命令的前提是在settings.json中设置了:
            //// "terminal.explorerKind": "integrated",
            //// "terminal.integrated.defaultProfile.windows": "PowerShell",
            /*
            ||此为分隔标识,无需反注释
            ||此为分隔标识,无需反注释
            ||此为分隔标识,无需反注释
            */
            // "command": "rmdir /q /s build", 
            ////cmd(windows操作系统任意版本)可调用,这会直接将build目录本身一起删除。
            ////使用该命令的前提是在settings.json中设置了:
            //// "terminal.explorerKind": "integrated",
            //// "terminal.integrated.defaultProfile.windows": "Command Prompt",
            /*
            ||此为分隔标识,无需反注释
            ||此为分隔标识,无需反注释
            ||此为分隔标识,无需反注释
            */
            "command": "rm -rf ./build/*", 
            ////rm -rf为Linux用法,只删除build目录下的所有文件。
            ////使用该命令的前提是在settings.json中设置了:
            //// "terminal.explorerKind": "integrated",
            //// "terminal.integrated.defaultProfile.windows": "Git Bash",
            //// 只要安装了像Git Bash的类Unix环境,Windows的cmd终端可以间接调用rm -rf命令,因此也可在settings.json中设置:
            //// "terminal.explorerKind": "integrated",
            //// "terminal.integrated.defaultProfile.windows": "Command Prompt",
            "args": [],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "problemMatcher": [
                "$gcc"
            ]
        },
        {
            "type": "shell",
            "label": "flash with CMSIS-DAP-Link",
            "command": "openocd",
            "args": [
                "-f",
                "interface/cmsis-dap.cfg",
                "-f",
                "target/stm32f1x.cfg",
                "-c",
                "program build/${workspaceRootFolderName}.elf verify reset exit"
            ],
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        {
            "type": "shell",
            "label": "flash with ST-Link",
            "command": "openocd",
            "args": [
                "-f",
                "interface/stlink.cfg",
                "-f",
                "target/stm32f1x.cfg",
                "-c",
                "program build/${workspaceRootFolderName}.elf verify reset exit"
            ],
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        {
            "type": "shell",
            "label": "flash with J-Link",
            "command": "openocd",
            "args": [
                "-f",
                "interface/jlink-swd.cfg",
                "-f",
                "target/stm32f1x.cfg",
                "-c",
                "program build/${workspaceRootFolderName}.elf verify reset exit"
            ],
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        }
    ],
    "version": "2.0.0"
}

3.4 launch.json

{
    // 使用 IntelliSense 了解相关属性。
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug with CMSIS-DAP-Link",
            "cwd": "${workspaceRoot}",
            "executable": "./build/${workspaceRootFolderName}.elf",
            "request": "launch",
            "type": "cortex-debug",
            "servertype": "openocd",
            "device": "STM32F103VET6",
            // "openOCDLaunchCommands": [
            //     "adapter speed 10000"
            // ],
            "configFiles": [
                "interface/cmsis-dap.cfg", // D:/Software/arm_riscv_develop_tools/openocd/openocd/scripts/interface
                "target/stm32f1x.cfg" // D:/Software/arm_riscv_develop_tools/openocd/openocd/scripts/target
            ],
            "svdFile": "./STM32F103.svd", //选择寄存器文件
            "liveWatch": {
                "enabled": true,
                "samplesPerSecond": 4
            },
            "searchDir": [],
            "runToEntryPoint": "main",
            "showDevDebugOutput": "none",
            "preLaunchTask": "flash with CMSIS-DAP-Link"
        },
        {
            "name": "Debug with ST-Link",
            "cwd": "${workspaceRoot}",
            "executable": "./build/${workspaceRootFolderName}.elf",
            "request": "launch",
            "type": "cortex-debug",
            "servertype": "openocd",
            "device": "STM32F103VET6",
            // "openOCDLaunchCommands": [
            //     "adapter speed 10000"
            // ],
            "configFiles": [
                "interface/stlink.cfg", // D:/Software/arm_riscv_develop_tools/openocd/openocd/scripts/interface
                "target/stm32f1x.cfg" // D:/Software/arm_riscv_develop_tools/openocd/openocd/scripts/target
            ],
            "svdFile": "./STM32F103.svd", //选择寄存器文件
            "liveWatch": {
                "enabled": true,
                "samplesPerSecond": 4
            },
            "searchDir": [],
            "runToEntryPoint": "main",
            "showDevDebugOutput": "none",
            "preLaunchTask": "flash with ST-Link"
        },
        {
            "name": "Debug with J-Link",
            "cwd": "${workspaceRoot}",
            "executable": "./build/${workspaceRootFolderName}.elf",
            "request": "launch",
            "type": "cortex-debug",
            "servertype": "openocd",
            "device": "STM32F103VET6",
            // "openOCDLaunchCommands": [
            //     "adapter speed 10000"
            // ],
            "configFiles": [
                "interface/jlink-swd.cfg", // D:/Software/arm_riscv_develop_tools/openocd/openocd/scripts/interface
                "target/stm32f1x.cfg" // D:/Software/arm_riscv_develop_tools/openocd/openocd/scripts/target
            ],
            "svdFile": "./STM32F103.svd", //选择寄存器文件
            "liveWatch": {
                "enabled": true,
                "samplesPerSecond": 4
            },
            "searchDir": [],
            "runToEntryPoint": "main",
            "showDevDebugOutput": "none",
            "preLaunchTask": "flash with J-Link"
        }
    ]
}

四、CMakeLists.txt构建文件

CMakeLists.txt:

#THIS FILE IS AUTO GENERATED FROM THE TEMPLATE! DO NOT CHANGE!
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_VERSION 1)
# cmake最低版本需求
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(CMAKE_SIZE arm-none-eabi-size)
set(CMAKE_READELF arm-none-eabi-readelf)
set(CMAKE_NM arm-none-eabi-nm)
# 链接的类型设置为STATIC, 以便嵌入式ARM-GNU通过CMake的"编译器检查"
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)

# project : 定义工程名称,并可以指定工程可支持的语言,语法格式为 project(项目域名 语言)
# .HEX  .bin  .elf  .map的文件名设置
project(Led_Toggle  C CXX ASM)
# C/C++语言标准版本配置
set(CMAKE_C_STANDARD 17)
set(CMAKE_CXX_STANDARD 17)

#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)

#编译选项定义
#   对于 cortex-m0 / cortex-m0+ / cortex-m1
#   +---------------------------------------------------+
#   | -mthumb -mcpu=cortex-m0/cortex-m0+/cortex-m1 或 -mthumb -march=armv6-m |
#   +---------------------------------------------------+

#   对于 cortex-m3
#   +---------------------------------------------------+
#   | -mthumb -mcpu=cortex-m3 或 -mthumb -march=armv7-m |
#   +---------------------------------------------------+

#   对于 cortex-m4
#   +---------------------------------------------------+
#   | -mthumb -mcpu=cortex-m4 或 -mthumb -march=armv7-m|
#   +---------------------------------------------------+

#   对于 cortex-m7
#   +---------------------------------------------------+
#   | -mthumb -mcpu=cortex-m7 或 -mthumb -march=armv7-m |
#   +---------------------------------------------------+

# -mcpu=cortex-m3 告诉编译器为 ARM Cortex-M3 处理器生成代码
# -mthumb 启用 Thumb 指令集,Thumb 指令集是一种 16 位指令集
# -march=armv7-m 指定目标架构,ARMv7-M 是 Cortex-M3 处理器使用的架构
set(MCU_FLAGS -mcpu=cortex-m3 -mthumb -mthumb-interwork)    #   编译选项定义修改处

#-fdata-sections用于将每个符号创建为一个sections,其中每个sections名与data名保持一致。
#-ffunction-sections用于将每个函数创建为一个sections,其中每个sections名与function名保持一致。
#用于代码的分割和裁剪,会将每一个函数都拆分成.text(Code+RO-data)段、.data(RW-data)段、.bss(ZI-data)段,这部分和对象文件的链接有关。如果没有这两个参数,编译器就会按文件分段而不是按照函数分段。
#加上这两个参数,配合链接器可以#去除代码中无用的部分,减少最终可执行文件的大小。
#-fno-common用于未初始化的全局变量当成强符号,重复定义就会报错。
#开启-fmessage-length=0会让编译器展示所有的消息而不会限制错误和警告输出的长度
set(OPTIMIZE_COMPILE_FLAGS -ffunction-sections -fdata-sections -fno-common -fmessage-length=0)
# 针对所有编译器,开启编译警告 (包括C、C++编译器),-Wall可开启所有警告;-Werror将所有警告视为error
# add_compile_options("-Wall -Werror")
add_compile_options(${MCU_FLAGS})
add_compile_options(${OPTIMIZE_COMPILE_FLAGS})

# /*
# 编译等级选项:优化等级
# -O0:无任何优化,关闭所有优化选项
# -O、-O1:1级优化,
# -O2: 2级优化,
# -Os: 2.5级优化,-Os启用所有通常不会增加代码大小的-O2优化。 它还执行旨在减少代码大小的进一步优化。
# -O3: 最高级优化。
# -Og:优化调试体验。 -Og启用不会干扰调试的优化。 它是标准编辑 - 编译 - 调试周期可以选择的优化级别,提供合理的优化级别,同时保持快速编译和良好的调试体验。
# -Ofast:无视严格的标准合规性。 -Ofast启用所有-O3优化。 它还打开并非对所有符合标准的程序有效的优化。
# */

#设置代码调试等级
set(CMAKE_BUILD_TYPE "Debug")
#   +---------------+---------------+--------------+--------------+----------+
#   |               |               | optimization | assert works | stripped |
#   +---------------+---------------+--------------+--------------+----------|
#   |               |     None      |              |              |          |
#   |    -g         |     Debug     |     no       |     yes      |    no    |
#   |-O3 -DNDEBUG   |    Release    |    full      |      no      |   yes    |
#   |-O2 -g -DNDEBUG| RelWithDebInfo|    good      |      no      |    no    |
#   |-Os -DNDEBUG   |   MinSizeRel  |    size      |      no      |   yes    |
#   +---------------+---------------+--------------+--------------+----------+
# Release 进行优化,提高速度 -排除调试信息
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
    message(VERBOSE "Maximum optimization for speed")
    add_compile_options(-Ofast)
# RelWithDebInfo 进行优化,提高速度 -包含调试信息
elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
    message(VERBOSE "Maximum optimization for speed, debug info included")
    add_compile_options(-Ofast -g)
# MinSizeRel 优化二进制大小 -排除调试信息
elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel")
    message(VERBOSE "Maximum optimization for size")
    add_compile_options(-Os)
# Debug 禁用优化 -包含调试信息
else ()
    message(VERBOSE "Minimal optimization, debug info included")
    add_compile_options(-Og -g)
endif ()

#添加C文件编译宏定义
add_definitions(
				-DUSE_HAL_DRIVER
                -DSTM32F103xE
                -DUSE_STDPERIPH_DRIVER
                -DSTM32F10X_HD
                )
# add_definitions(-DUSE_STDPERIPH_DRIVER -DSTM32F10X_HD)

#添加头文件路径,即.h文件
include_directories(
					./STM32F10x_FWLib/inc
                    ./SYSTEM/delay
                    ./SYSTEM/sys
                    ./SYSTEM/usart
                    ./HARDWARE/inc
                    ./USER/inc
                    ./CORE/inc
                    ./CMSIS/inc
                    )

#   这三个变量指代的内容是一致的,指的是工程编译产生的目录
#   +------------------+--------------------+--------------------------+
#   | CMAKE_BINARY_DIR | PROJECT_BINARY_DIR | <projectname>_BINARY_DIR |
#   +------------------+--------------------+--------------------------+

#   这三个变量指代的内容是一致的,指的是工程顶级目录
#   +------------------+--------------------+--------------------------+
#   | CMAKE_SOURCE_DIR | PROJECT_SOURCE_DIR | <projectname>_SOURCE_DIR |
#   +------------------+--------------------+--------------------------+

#添加汇编启动文件路径,startup文件是STM32CubeMX生成的(需要gcc版本,不要使用MDK版本)
ENABLE_LANGUAGE(ASM) #为了让cmake识别启动文件
set(SRC_STARTUP "${CMAKE_SOURCE_DIR}/startup_s/startup_stm32f103xe.s")

#添加源文件路径,即.c文件
#file语法,前一个参数是固定的GLOB_RECURSE, 后面一个参数自行定义
file(GLOB_RECURSE SOURCES
     ./STM32F10x_FWLib/src/*.c
     ./USER/src/*.c
     ./CMSIS/src/*.c
     ./CORE/src/*.c
     ./SYSTEM/delay/*.c
     ./SYSTEM/sys/*.c
     ./SYSTEM/usart/*.c
     ./HARDWARE/src/*.c
    )

# -Wl,--gc-sections 链接使用的分段方式,需要配合C文件/汇编生成obj的时候同样选型分段方式,
# 好处是链接的时候源文件中的未使用变量和未调用函数将不会被链接到elf文件中,最终会使得可执行文件elf非常精简。
# -no-warn-rwx-segments:消除 LOAD segment with RWX permissions 警告
# -flto, 链接时优化,会减少程序体积,同时也会略微降低性能(当测试coremark时)
# -specs=nano.specs选择链接精简C库newlib-nano,而非标准C库glibc或newlib。
# -specs=nosys.specs禁止链接任何系统库(如glibc或newlib)。
# -u_printf_float显式启用浮点数打印 -u_scanf_float显式启用浮点数输入
set(OPTIMIZE_LD_FLAGS -Wl,--gc-sections,--no-warn-rwx-segments -flto -specs=nano.specs -specs=nosys.specs -u_printf_float -u_scanf_float)

#添加.ld链接脚本路径
# -T$(LDSCRIPT)依赖的可执行文件链接脚本
set(LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/link_script/STM32F103VETx_FLASH.ld")

#-cref则生成交叉引用表方便查找未定义的符号引用(比如编译时出现的undefined reference)
#--print-memory-usage选项提供链接器文件中定义的每个内存区域使用的内存的详细信息
# -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref 生成map文件
set(MAP_FLAGS -Wl,--print-memory-usage,-Map=${PROJECT_BINARY_DIR}/${PROJECT_NAME}.map,--cref)

#链接选项配置
add_link_options(${MCU_FLAGS})
add_link_options(${OPTIMIZE_LD_FLAGS})
add_link_options(-T ${LINKER_SCRIPT})
add_link_options(${MAP_FLAGS})

#根据源文件、汇编启动文件、链接脚本 生成 .elf可执行文件
add_executable(${PROJECT_NAME}.elf ${SRC_STARTUP} ${SOURCES} ${LINKER_SCRIPT})

#启用多线程支持
#⚠️仅适用于 GCC/Clang 等遵循 POSIX 标准的编译器,对 MSVC 无效(MSVC 通过 <thread> 头文件原生支持)
add_compile_options(-pthread)

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)

# Print executable size
add_custom_command(TARGET "${PROJECT_NAME}.elf" POST_BUILD
        COMMENT "Invoking: Cross ARM GNU Print Size"
        COMMAND ${CMAKE_SIZE} --format=berkeley ${PROJECT_NAME}.elf
        )
        
# Create hex & bin file
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}")

CMakeLists.txt文件及注释的参考链接如下:
1)使用VScode开发STM32:基于CMake(包含标准库和HAL库工程)
2)VSCode 和 CMake 搭建嵌入式开发环境
3) 在Windows上使用VS Code搭建开发环境-基于STM32F103C8T6
4)cmake:设置编译选项
5)CMake进阶(一)设置编译选项
6)跨平台编译工具-CMake的语法特性与常用变量
7)【gcc】gcc优化等级 -O1 -O2 -O3 -Os -Ofast -Og|gcc关闭优化
8)arm-none-eabi-gcc编译、链接选项详解
9)gcc for arm 工具链使用(一)
10)arm gcc编译选项
11)【ARMv8M Cortex-M33 系列 3.1 – RT-Thread renesas/ra4m2-eco 移植编译篇 nosys.specs 介绍】
12)GCC编译器的常用参数及用法,你都清楚吗?
13)用CMake提升STM32开发效率:完整配置与构建流程(一)

五、问题注意

📚① /build构建目录
CMake的构建生成器选择配置,在tasks.json文件中的CMake configure-G中可以修改:

  • CMakeFiles:包含临时文件的目录,CMake用于检测操作系统、编译器等。此外,根据所选的生成器 make / ninja,它还包含特定的文件。
  • cmake_install.cmake:处理安装规则的CMake脚本,在项目安装时使用。
  • CMakeCache.txt:如文件名所示,CMake缓存。CMake在重新运行配置时使用这个文件
  • Makefile/Ninja: make将运行指令来构建项目。(如果选择ninja构建生成器,则该文件为build.ninja和rules.ninja:包含了Ninja的所有的构建语句和构建规则

📚② 显式启用 “浮点数输入” 的问题(已解决)

arm-none-eabi-gcc工具链发布时自带有2个基于newlib的预构建C库:一个是标准的newlib,另一个是newlib-nano(优化了代码大小)。
要使用newlib-nano,用户应该提供额外的gcc编译和链接时选项:-specs=nano.specs。
在编译时,如果-specs=nano.specs被传递给编译器,那么一个专门为newlib-nano配置的“newlib.h”头文件会被使用。
nano.specs还能处理另外两个gcc库:libstdc++_nano.a和libsupc++_nano.a,它们同样针对代码大小进行了优化。
newlib-nano相比newlib,不仅仅是库的名字上的区别。浮点数的格式化input/output被实现为弱符号(隐式)。
如果要使用%f,则必须通过显式指定"-u"命令选项来引入该符号。

#显式指定"-u"命令选项
  -u _scanf_float
  -u _printf_float

参考链接:
1)Newlib 与 Newlib-Nano区别
2) [开发工具]为什么gcc编译出来的程序大小和Keil差别这么大
3)Shrink Your MCU code size with GCC ARM Embedded 4.7
4)嵌入式GCC库: newlib与nanolib区别
5)CoIDE 1.6.2下使用ARM GCC 4.7 Newlib-nano printf 重定向到UART
6)Stm32串口搭配DMA实现自定义printf、scanf

CMakeLists.txt的可选链接选项配置中,如果仿照Makefile中一样的-u _printf_float -u _scanf_float显式启用,则会发生报错:无法找到_scanf_float,如下图所示。
在这里插入图片描述
那么原因是什么呢?CMake的现代化语法对 空格 极为敏感,相对makefile来说没这么宽松。
其解决方法有以下2种:
方法①—删除空格
CMakeLists.txt中,笔者删除了-u后面的空格,将显式启用修改为-u_printf_float -u_scanf_float,即可消除报错并能正常打印浮点数,如下图所示。
在这里插入图片描述
方法②——对于有空格的选项参数,在其两边加上英文双引号" "
如下图所示,亦可消除报错并能正常打印浮点数。
在这里插入图片描述

在这里插入图片描述

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值