VSCode+arm-none-eabi-gcc交叉编译+CMake构建+OpenOCD(基于STM32的标准库/HAL库)
前言:什么是CMake?
简而言之,CMake是Make的maker。
一、CMake的安装
- 进入CMake官网的下载地址Get the Software,根据系统安装对应的Binary distributions。
- 或者在CMake——国内镜像获取二进制镜像安装包。
- 或者访问GitHub的xPack项目xPack CMake v3.28.6-1,下载即可。
记得添加用户/系统的环境变量,然后打开cmd窗口,输入
cmake -version
,查看CMake环境变量是否添加成功,如下图所示。
二、工程所需配置文件
本文以【STM32F103ZET6
】单片机为示例来进行演示,以下配置文件对于标准库/HAL库工程是通用的。
更多细节请基于【VSCode+arm-none-eabi-gcc交叉编译+Makefile构建+OpenOCD(基于STM32标准库的保姆级教程)】该篇文章参考。
下面列出几个重要的配置文件:
①:
settings.json
c_cpp_properties.json
tasks.json
launch.json
⭐ settings.json、c_cpp_properties.json、tasks.json、launch.json的修改要点请参考【.vscode文件夹中各个JSON脚本需要修改的地方】。
②:
CMakeLists.txt
⭐ 请参考本篇文章。
③:
链接脚本
汇编启动文件
寄存器文件
⭐ MCU链接脚本.ld、MCU汇编启动文件.s、MCU寄存器文件.svd的获取方式请参考【VSCode+arm-none-eabi-gcc交叉编译+Makefile构建+OpenOCD(基于STM32标准库的保姆级教程)】。
三、VSCode脚本文件
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\" } ], //该行配置很重要!!!在tasks.json中的任务类型如果是\"shell\",则这里的shell选择,关系到对应shell命令的使用适配与否。 \"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\" \"terminal.explorerKind\": \"integrated\" /****************************VSCode的CMake默认构建生成器为Ninja******************************/ /******************************************************************************************/ /******************************************************************************************/ /******************************************************************************************/ //\"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\"}
c_cpp_properties.json:
{ \"configurations\": [ { \"name\": \"STM32F103_ARM_GCC\", \"includePath\": [ // 设置编辑器中的头文件查找目录 \"${workspaceFolder}/**\" ], \"defines\": [ \"_DEBUG\", \"UNICODE\", \"_UNICODE\", \"__GNUC__\" // 解决编辑器中提示无法识别uint32_t, uint16_t, uint8_t的问题 ], \"compilerPath\": \"E:/embedded_dev_tools/xpack-arm-none-eabi-gcc-13.3.1-1.1/bin/arm-none-eabi-gcc.exe\", \"compileCommands\": \"${workspaceFolder}/build/compile_commands.json\", //这一配置在目前所搭建的环境下没用,但是如果你使用的是SDK,库和驱动文件与项目工程不在一个文件夹中,这一步就很有用了,它能让你在当前项目就能向库代码跳转。 \"cStandard\": \"gnu17\", // 设置使用的C标准 \"cppStandard\": \"gnu++17\", // 设置使用的C++标准 \"intelliSenseMode\": \"gcc-arm\", // 设置编译器类型,解决编辑器中提示无法识别例如__attribute__((weak))等gcc编译器功能选项 \"configurationProvider\": \"ms-vscode.cmake-tools\" } ], \"version\": 4}
tasks.json:
{ //快捷键ctrl+shift+B调出各个task命令,等效于在终端输入命令行 \"tasks\": [ { \"label\": \"CMake configure\", \"type\": \"shell\", \"command\": \"cmake\", //要执行的命令,前提是已经在系统环境变量PATH中安装了CMake \"args\": [ \"-D\", \"CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE\", //生成compile_commands.json的文件,该文件是为了让vscode有代码导航、自动补全和语法高亮功能。 \"-G\", //配置生成器类型 \"Ninja\", //\"Unix Makefiles\", \"-B\", //配置生成Makefile or Ninja脚本及其相关文件的路径, 使得源码和构建过程文件分开,以便更好地管理项目 \"./build\" //这条命令会告诉 CMake 创建一个名为 build 的目录(如果它不存在的话),并在该目录下生成构建文件 //\"-DCMAKE_BUILD_TYPE=Debug\", //设置构建类型:Debug,Release,RelWithDebInfo,MinSizeRel //\"--debug-output\", //显示详细的CMake配置过程信息 ], \"group\": { \"kind\": \"build\", \"isDefault\": true }, \"problemMatcher\": \"$gcc\" }, { \"label\": \"CMake build\", \"type\": \"shell\", //直接使用shell的手动输入命令行“cmake --build {构建目录的绝对路径} 其他可选的配置参数” \"command\": \"cmake\", \"args\": [ \"--build\", //调用CMake的统一命令在某个文件夹下构建,具有跨平台特性。不论后端构建工具是 Make or Ninja \"${workspaceFolder}/build\", //生成可执行文件.elf和进制文件.hex/.bin的路径 \"--config Debug\", \"--target ${workspaceRootFolderName}.elf\", //\"all\", //允许指定单一或多个目标来构建,而不是构建整个项目。这可以显著减少构建时间 \"--parallel 5\" //使用-j 5是等效的 //\"--verbose\" //让底层的构建工具(如 Make 或 Ninja)输出详细的编译命令,为CMake通用命令,可简写为 -v ], \"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.integrated.defaultProfile.windows\": \"PowerShell\" // \"command\": \"rmdir /q /s build\", //cmd(windows操作系统))可调用:这会直接将build目录本身删除。 //调用该命令的前提是在settings.json中设置了:\"terminal.integrated.defaultProfile.windows\": \"Command Prompt\" \"command\": \"rm -rf ./build/*\", //rm -rf为Linux用法,只删除build目录下的所有文件。 //如果用户安装了像Git Bash的类Unix环境,则选择cmd终端同样可间接调用rm -rf命令。 //调用该命令的前提是在settings.json中设置了:\"terminal.integrated.defaultProfile.windows\": \"Git Bash\" \"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\"}
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 | _BINARY_DIR |# +------------------+--------------------+--------------------------+# 这三个变量指代的内容是一致的,指的是工程顶级目录# +------------------+--------------------+--------------------------+# | CMAKE_SOURCE_DIR | PROJECT_SOURCE_DIR | _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})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 sizeadd_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 fileadd_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开发效率:完整配置与构建流程(一)
五、tips和遗留问题
📚① 默认情况下,CMake使用本地编译器,如gcc,而嵌入式开发需使用交叉编译器,如arm-none-eabi-gcc,因此需要明确地告知CMake使用交叉编译器。在VSCode中,CMake切换本地的arm-none-eabi-gcc或gcc工具链的方法步骤如下图所示:
同时要修改c_cpp_properties.json
的\"compilerPath\"
处为arm-none-eabi-gcc.exe
的绝对路径,如下图所示,
📚② 如果读者将上述的CMakeLists.txt
文件移植到自己的标准库 / HAL库工程后或者更改了CMake
的Makefile
构建生成器后,需要重新生成CMake和当前项目相关的配置,否则会报错。
参考方法的截图如下,
📚③ 设置Windows/VSCode中CMake的构建生成器(已解决)
-
查看CMake在Windows操作系统中默认的构建生成器:打开cmd→输入
cmake -G
→下拉观察到有*
符号在前的选项,是默认选择的CMake构建生成器,如下图所示:笔者的Windows操作系统默认选择NMake Makefiles
构建生成器。
-
修改Windows中CMake的构建生成器:
-
在Windows操作系统中:按下键盘的开始菜单键→直接打出
“高级”
→查看高级系统设置→设置环境变量
→在用户变量区新建环境变量,设置变量名为CMAKE_GENERATOR
和变量值为Unix Makefiles
,之后一直点击确定即可。
NOTE:Windows操作系统的CMake构建生成器可选择不设置环境变量,只在VSCode中设置即可,此处只是作拓展说明。
再次打开cmd,输入cmake -G
,可以看到CMake在操作系统中默认的构建生成器已被修改为Unix Makefiles
,如下图所示。
-
在VSCode中CMake的构建生成器:依次打开VSCode左侧的CMake Tools→项目大纲右侧的【更多操作】→编辑CMake缓存(UI)→进入到CMake缓存编辑器→找到
CMAKE_GENERATOR
,可以看到VSCode中的CMake默认使用Ninja
来构建项目。一般地,VSCode中CMake默认使用Ninja
来构建项目文件。
那么问题来了:我们如何修改CMake默认的构建生成器呢?例如将 Ninja
→Unix Makefiles
。
答案是:在工程目录的.vscode中settings.json文件中加入\"cmake.generator\": \"Unix Makefiles\"
即可。
修改方法和步骤如下述json段和下图所示:
\"cmake.generator\": \"Unix Makefiles\",\"makefile.makePath\": \"E:/embedded_dev_tools/xpack-windows-build-tools-4.4.1-2/bin/make.exe\"
更换了VSCode中CMake默认的构建生成器后,如果此时点击构建工程会发生如下错误:
则我们可以按如下指示,令CMake清理所有项目并重新配置即可消除该错误。
可以看到,CMake缓存编辑器的CMAKE_GENERATOR
构建生成器已经被切换成Unix Makefiles
。
📚④ /build构建目录
上面我们讲到了,在VSCode中,CMake默认使用Ninja来构建项目:
- 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。
因此在CMakeLists.txt
中,笔者删除了-u后面的空格,将显式启用修改为-u_printf_float -u_scanf_float
,即可消除报错并能正常打印浮点数,如下图所示。