CMake进阶篇(一)——搭建stm32工程
前言:
本篇文章探讨通过CMake搭建一个stm32工程。
1 下载
本篇文章以STM32F10x芯片为例搭建CMake工程,现在芯片长官网下载标准库,以主流HAL库为例。登陆 STM32官网 ,在官网搜索框中输入 STM32CubeF1 来查找并下载完整的MCU固件包。这个压缩包内就包含了STM32F103ZE以及整个F1系列所有芯片的HAL库源码、示例项目和文档。

解压下载的 stm32cubef1.zip 包,目录结构如下。

搭建工程所需的文件就在 **stm32cubef1.zip里,或者也可以通过STM32Cube创建一个CMake工程的Demo,从Demo中获取搭建工程所需要的文件。
1.1 获取drv
HAL库和CMSIS在 Drivers/STM32F1xx_HAL_Driver 路径下,将其拷贝到我们搭建的工程中,这里我放到 drv 目录中使用。CMSIS(Cortex Microcontroller Software Interface Standard)是ARM Cortex-M处理器的硬件抽象层,对于工程构建是必须的。
1.2 获取启动文件
STM32F103ZE的启动文件为startup_stm32f103xe.s,在CMSIS中,路径为 CMSIS/Device/ST/STM32F1xx/Source/Templates/gcc ,由于我们使用ARM GCC编译,所以要使用gcc目录下这个,而arm路径下的用于keil工程,iar路径下的用于AIR工程。我们将文件拷贝到自己工程的startup目录中。
1.3 获取ld
stm32cubef1.zip包解压后,目录 Projects 内有官方例程,找到符合自己芯片型号的ld文件拷贝到工程中,这里我放到linker目录,如果没有找到,可以通过STM32Cube创建一个Demo,然后从Demo中获取。
1.4 获取core
在stm32cubef1包的 stm32cubef1/STM32Cube_FW_F1_V1.8.0/Projects/STM32F103RB-Nucleo/Templates 目录提供了模板源码,将其中 Src 和 Inc 拷贝自己工程中,这里我放到core目录中。
1.5 获取gcc-arm-none-eabi.cmake
gcc-arm-none-eabi.cmake文件的作用是配置CMake的交叉编译环境,让CMake知道如何为ARM Cortex-M系列微控制器编译代码。文件名的含义如下。
- gcc : 表示使用gcc编译,这里用arm gcc进行编译。
- arm : 表示编译ARM架构,比如这里是STM32F103ZE Cortext-M3。
- none : 目标操作系统,这里是裸机无操作系统,与其相对的有linux、rtos等。
- eabi : 即Embedded Application Binary Interface,作用是定义嵌入式系统的二进制接口标准。
该文件可以通过STM32Cube创建Demo获取。
2 编写CMake
工程根目录如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ╰─ tree -L 2 . ├── cmake │ └── gcc-arm-none-eabi.cmake ├── CMakeLists.txt ├── CMakeLists.txt.back ├── core │ ├── inc │ └── src ├── drv │ ├── CMSIS │ └── STM32F1xx_HAL_Driver ├── linker │ └── STM32F103XX_FLASH.ld └── startup └── startup_stm32f103xe.s
|
在工程根目录编写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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| cmake_minimum_required(VERSION 3.16)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS ON)
if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug") endif()
set(CMAKE_PROJECT_NAME ord32)
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/cmake/gcc-arm-none-eabi.cmake)
project(${CMAKE_PROJECT_NAME}) enable_language(C ASM)
add_executable(${CMAKE_PROJECT_NAME})
target_link_directories(${CMAKE_PROJECT_NAME} PRIVATE )
target_sources(${CMAKE_PROJECT_NAME} PRIVATE startup/startup_stm32f103xe.s
core/src/main.c core/src/stm32f1xx_it.c core/src/system_stm32f1xx.c core/src/stm32f1xx_hal_msp.c
drv/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c drv/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_cortex.c drv/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc.c drv/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio.c drv/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_dma.c drv/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_exti.c )
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE core/inc
drv/CMSIS/Include drv/CMSIS/Device/ST/STM32F1xx/Include
drv/STM32F1xx_HAL_Driver/Inc )
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE STM32F103xE USE_HAL_DRIVER HSE_VALUE=8000000 )
target_link_libraries(${CMAKE_PROJECT_NAME} m c nosys )
|
基于STM32Cube生成的gcc-arm-none-eabi.cmake修改,内容如下。
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
| set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER_ID GNU) set(CMAKE_CXX_COMPILER_ID GNU)
set(TOOLCHAIN_PREFIX arm-none-eabi-)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc) set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER}) set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++) set(CMAKE_LINKER ${TOOLCHAIN_PREFIX}g++) set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy) set(CMAKE_SIZE ${TOOLCHAIN_PREFIX}size)
set(CMAKE_EXECUTABLE_SUFFIX_ASM ".elf") set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") set(CMAKE_EXECUTABLE_SUFFIX_CXX ".elf")
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
set(TARGET_FLAGS "-mcpu=cortex-m3 ")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TARGET_FLAGS}") set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp -MMD -MP") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fdata-sections -ffunction-sections")
set(CMAKE_C_FLAGS_DEBUG "-O0 -g3") set(CMAKE_C_FLAGS_RELEASE "-Os -g0") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3") set(CMAKE_CXX_FLAGS_RELEASE "-Os -g0")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fno-exceptions -fno-threadsafe-statics")
set(CMAKE_EXE_LINKER_FLAGS "${TARGET_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -T \"${CMAKE_SOURCE_DIR}/linker/STM32F103XX_FLASH.ld\"") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --specs=nano.specs") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Map=${CMAKE_PROJECT_NAME}.map -Wl,--gc-sections") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--print-memory-usage") set(TOOLCHAIN_LINK_LIBRARIES "m")
|
3 编译
执行以下指令在linux下构建和编译stm32工程,会在build目录生成elf文件。
1 2 3 4
| mkdir build cd build cmake .. make -j20
|

GCC编译直接生成的编译产物为elf,而bin和hex文件是elf文件的衍生格式。一般调试时使用elf文件,其包含调试信息和符号表。
| 文件格式 |
生成方式 |
主要用途 |
包含内容 |
| ELF |
自动生成 |
调试、开发 |
代码+数据+调试信息+符号表 |
| HEX |
需要转换 |
烧录、生产 |
纯代码数据(Intel HEX格式) |
| BIN |
需要转换 |
烧录、OTA升级 |
纯二进制代码 |
要同时生成bin和hex文件,可以在根目录的cmake中添加如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13
|
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_OBJCOPY} -O ihex $<TARGET_FILE:${CMAKE_PROJECT_NAME}> ${CMAKE_PROJECT_NAME}.hex COMMAND ${CMAKE_OBJCOPY} -O binary $<TARGET_FILE:${CMAKE_PROJECT_NAME}> ${CMAKE_PROJECT_NAME}.bin COMMENT "Generating HEX and BIN files" )
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_SIZE} $<TARGET_FILE:${CMAKE_PROJECT_NAME}> COMMENT "Firmware size:" )
|