my solution for building project for STM32F7 evaluation board with ** CMAKE **
Its an age old battle. Manufactuer eclipse-based IDE vs CMake. Chip manufactuers make an IDE available to simplify the software development process, and get users started quickly. Expericed SW engineers appreciate an IDE, particularly for debugging, but prefer to have more control over the build process with CMake. Usually CMake based builds are command-line driven, and lend themselves well to multiple build targets, tool integration, static analysis, unit test, CI, DevOps, Jenkins/buildbot, etc.
In order to setup my own CMake build that would run on the STM32F7 eval board, I took these steps.
- Used IDE to create empty cmake project (New --> STM32 CMake Project )
- Used IDE to create empty IDE project (New --> STM32 Project)
- Pulled together the available cmake files and source code from each project
When creating the IDE project, I chose my eval board from the wizard menu. When the project was created, it populated the source folder with a number of generated source files (see image below). I copied these files into my own project folder.

In my top level folder, i added x-cube-azrtos-f7 as a git submodule. This allowed access to the HAL source code and Azure RTOS (ThreadX, etc.).
This is the top-level cmake project file, please note building Azure RTOS is out of scope for this post. More on that later! The examples subdirectory has the application level code, 'hello_world' in this case. The hal subdirectory only has a single CMakeLists.txt file that references the HAL source code in the x-cube-azrtos-f7 folder.
cmake_minimum_required(VERSION 3.20)
project(my_project)
# Example projects for eval boards
add_subdirectory(examples)
# STM32F7 HAL (Hardware Abstraction Layer) library
add_subdirectory(hal)
# ThreadX library
set(THREADX_ARCH cortex_m7)
set(THREADX_TOOLCHAIN gnu)
# FIXME doesn't compile yet
# add_subdirectory(x-cube-azrtos-f7/Middlewares/ST/threadx)The STM32 Cmake project created in the IDE generated a toolchain cmake file. I then renamed that as arm-gcc.cmake and put in my own project.
# CMake toolchain definition for STM32CubeIDE
set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "")
set (CMAKE_SYSTEM_NAME "Generic" CACHE STRING "")
###################### CONSTANTS ######################################
set (PROJECT_TYPE_EXECUTABLE "exe")
set (PROJECT_TYPE_STATIC_LIBRARY "static-lib")
set (MCPU_CORTEX_M0 "-mcpu=cortex-m0")
set (MCPU_CORTEX_M0PLUS "-mcpu=cortex-m0plus")
set (MCPU_CORTEX_M3 "-mcpu=cortex-m3")
set (MCPU_CORTEX_M4 "-mcpu=cortex-m4")
set (MCPU_CORTEX_M7 "-mcpu=cortex-m7")
set (MCPU_CORTEX_M33 "-mcpu=cortex-m33")
set (MFPU_FPV4_SP_D16 "-mfpu=fpv4-sp-d16")
set (MFPU_FPV5_D16 "-mfpu=fpv5-d16")
set (RUNTIME_LIBRARY_REDUCED_C "--specs=nano.specs")
set (RUNTIME_LIBRARY_STD_C "")
set (RUNTIME_LIBRARY_SYSCALLS_MINIMAL "--specs=nosys.specs")
set (RUNTIME_LIBRARY_SYSCALLS_NONE "")
set (MFLOAT_ABI_SOFTWARE "-mfloat-abi=soft")
set (MFLOAT_ABI_HARDWARE "-mfloat-abi=hard")
set (MFLOAT_ABI_MIX "-mfloat-abi=softfp")
#######################################################################
# Skip link step during toolchain validation.
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
# Specify toolchain.
# NOTE: When building from inside STM32CubeIDE the location of the toolchain
# is resolved by the "MCU Toolchain" project setting (via PATH).
set(TOOLCHAIN_PREFIX "arm-none-eabi-")
set(CMAKE_C_COMPILER "${TOOLCHAIN_PREFIX}gcc")
set(CMAKE_ASM_COMPILER "${TOOLCHAIN_PREFIX}gcc")
set(CMAKE_CXX_COMPILER "${TOOLCHAIN_PREFIX}g++")
set(CMAKE_AR "${TOOLCHAIN_PREFIX}ar")
set(CMAKE_LINKER "{TOOLCHAIN_PREFIX}ld")
set(CMAKE_OBJCOPY "${TOOLCHAIN_PREFIX}objcopy")
set(CMAKE_RANLIB "${TOOLCHAIN_PREFIX}ranlib")
set(CMAKE_SIZE "${TOOLCHAIN_PREFIX}size")
set(CMAKE_STRIP "${TOOLCHAIN_PREFIX}ld")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)Creating cmake configuration for the HAL library was probably the most interesting part of this. When the IDE creates a project, it simply copies the hal source files it needs to the project folder. I'm not fond of having copies in two places, why not build directly from the source location (e.g. x-cube-azrtos-f7 ) ? Since we didn't want to put a CMakeLists.txt file in the x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver where the HAL source is, we created a separate folder with just a CMakeLists.txt that references the source code folder. <top level>/hal/CMakeLists.txt
The list of source files was chosen based on the list of files found in the original IDE project. The library is defined as a INTERFACE type (rather than STATIC or SHARED) because the HAL source includes the application specific generated configuration file stm32f7xx_hal_conf.h. It ensures the library source code it built along with the application, not as completely separate library.
project(stm32f7_hal C ASM)
# Hardware Abstration Layer for STM32F7 is part of ST x-cube-azrtos-f7 distribution
set(STM32F7_HAL_SOURCE_DIR ${CMAKE_SOURCE_DIR}/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src)
set(STM32F7_HAL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Inc)
set(STM32F7_CMSIS_INCLUDE_DIR
${CMAKE_SOURCE_DIR}/x-cube-azrtos-f7/Drivers/CMSIS/Include
${CMAKE_SOURCE_DIR}/x-cube-azrtos-f7/Drivers/CMSIS/Device/ST/STM32F7xx/Include )
message(STATUS "STM32F7 HAL source file path: ${STM32F7_HAL_SOURCE_DIR}")
message(STATUS "STM32F7 HAL include file path: ${STM32F7_HAL_INCLUDE_DIR}")
set (SOURCE_FILES
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_adc_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_adc.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_cortex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_dma_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_dma.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_dsi.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_exti.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_flash_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_flash.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_gpio.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_i2c_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_i2c.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_mmc.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_nor.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_pcd_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_pcd.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_pwr_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_pwr.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_qspi.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_rcc_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_rcc.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_sai_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_sai.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_sdram.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_spdifrx.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_sram.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_tim_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_tim.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_uart_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_uart.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_ll_fmc.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_ll_sdmmc.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_ll_usb.c
)
# debug only
# message(STATUS "STM32F7 HAL source files: ${SOURCE_FILES}")
# The HAL source code includes a configuration header file from the application, so the library can not be built
# in isolation.
# Defining the library as type INTERFACE allows the source to be built with the application that uses the library.
add_library(${PROJECT_NAME} INTERFACE )
target_sources(${PROJECT_NAME} INTERFACE ${SOURCE_FILES})
target_include_directories (${PROJECT_NAME} INTERFACE ${STM32F7_HAL_INCLUDE_DIR} ${STM32F7_CMSIS_INCLUDE_DIR} )
The hello world application is in the <proj dir>/examples/hello_world folder. It includes its own CMakeLists.txt with the source code copied from the generated IDE project.
¤ CMakeLists.txt
¤ README.md
¤
+---Inc
¤ main.h
¤ stm32f7xx_hal_conf.h
¤ stm32f7xx_it.h
¤
+---Src
¤ main.c
¤ stm32f7xx_hal_msp.c
¤ stm32f7xx_it.c
¤ syscalls.c
¤ sysmem.c
¤ system_stm32f7xx.c
¤
+---Startup
startup_stm32f769nihx.sThe hello world CMakeLists.txt has the much of the same cmake code copied from the STM32 CMake Project top level. Notice the linker (.ld) script file is included from the top level/link folder. It was moved there so it can be used by other cmake subprojects with in the same folder structure. The hal library is included using target_link_libraries identifying stm327_hal, the name of the hal project.
###################### VARIABLES ######################################
set (HELLO_WORLD_PROJECT_NAME "stm32f769i-eval_hello-world")
set (PROJECT_TYPE "exe")
set (LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/link/STM32F769NIHX_FLASH.ld")
set (MCPU "-mcpu=Cortex-M7")
set (MFPU "-mfpu=fpv5-d16")
set (MFLOAT_ABI "")
set (RUNTIME_LIBRARY "--specs=nano.specs")
set (RUNTIME_LIBRARY_SYSCALLS "--specs=nosys.specs")
set (PROJECT_SOURCES
# LIST SOURCE FILES HERE
Src/main.c
Src/stm32f7xx_hal_msp.c
Src/stm32f7xx_it.c
Src/syscalls.c
Src/sysmem.c
Src/system_stm32f7xx.c
Startup/startup_stm32f769nihx.s
)
set (PROJECT_DEFINES
# LIST COMPILER DEFINITIONS HERE
USE_HAL_DRIVER
STM32F769xx
)
set (PROJECT_INCLUDES
# LIST INCLUDE DIRECTORIES HERE
Inc/
)
################## PROJECT SETUP ######################################
project(stm32f769i-eval_hello-world C ASM)
add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})
target_link_libraries (${PROJECT_NAME} stm32f7_hal )
#add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_SIZE} $<TARGET_FILE:${CMAKE_PROJECT_NAME}>)
#if (${PROJECT_TYPE} MATCHES ${PROJECT_TYPE_EXECUTABLE})
#lseif (${PROJECT_TYPE} MATCHES ${PROJECT_TYPE_STATIC_LIBRARY})
# add_library(${PROJECT_NAME} ${PROJECT_SOURCES})
#endif()
add_compile_definitions (${PROJECT_DEFINES})
include_directories (${PROJECT_INCLUDES})
set (CMAKE_EXECUTABLE_SUFFIX ".elf")
set (CMAKE_STATIC_LIBRARY_SUFFIX ".a")
set (CMAKE_C_FLAGS "${MCPU} -std=gnu11 ${MFPU} ${MFLOAT_ABI} ${RUNTIME_LIBRARY} -mthumb -Wall -Werror -Wfatal-errors")
set (CMAKE_EXE_LINKER_FLAGS "-T${LINKER_SCRIPT} ${RUNTIME_LIBRARY_SYSCALLS} -Wl,-Map=test.map -Wl,--gc-sections -static -Wl,--start-group -lc -lm -Wl,--end-group")
set (CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp")
Finally when running the cmake configure and build commands, here's the command and output.
$ cmake -DCMAKE_TOOLCHAIN_FILE=arm-gcc.cmake -S ./ -B Debug -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug
-- The C compiler identification is GNU 11.3.1
-- The CXX compiler identification is GNU 11.3.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/ST/STM32CubeIDE_1.13.1/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.11.3.rel1.win32_1.1.0.202305231506/tools/bin/arm-none-eabi-gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/ST/STM32CubeIDE_1.13.1/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.11.3.rel1.win32_1.1.0.202305231506/tools/bin/arm-none-eabi-g++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- The ASM compiler identification is GNU
-- Found assembler: C:/ST/STM32CubeIDE_1.13.1/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.11.3.rel1.win32_1.1.0.202305231506/tools/bin/arm-none-eabi-gcc.exe
-- STM32F7 HAL source file path: C:/Users/jerry.morrow/git/<proj>/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src
-- STM32F7 HAL include file path: C:/Users/jerry.morrow/git/<proj>/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Inc
-- Configuring done (8.1s)
-- Generating done (0.0s)
-- Build files have been written to: C:/Users/jerry.morrow/git/<proj>/DebugThis is the build/compile output.
$ cmake --build Debug/
[ 2%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/Src/main.c.obj
[ 4%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/Src/stm32f7xx_hal_msp.c.obj
[ 7%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/Src/stm32f7xx_it.c.obj
[ 9%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/Src/syscalls.c.obj
[ 11%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/Src/sysmem.c.obj
[ 14%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/Src/system_stm32f7xx.c.obj
[ 16%] Building ASM object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/Startup/startup_stm32f769nihx.s.obj
[ 19%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/__/__/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_adc_ex.c.obj
[ 21%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/__/__/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_adc.c.obj
....
....
[ 90%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/__/__/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal.c.obj
[ 92%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/__/__/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_fmc.c.obj
[ 95%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/__/__/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_sdmmc.c.obj
[ 97%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/__/__/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_usb.c.obj
[100%] Linking C executable stm32f769i-eval_hello-world.elf
[100%] Built target stm32f769i-eval_hello-worldOnce you have the .elf file, you can open in IDE and it will automagically load the board and allow you step through code from the top of main function!
ENJOY CMAKE LOVERS!!
