Skip to content

Project Anatomy

Every firmware project has the same shape, whether it lives in src/, src/tests/ or starter-projects/. This page uses src/bmc as the example. For what each board actually does, see Boards.

Almost everything in a project directory is generated and will be overwritten. The files that are genuinely yours:

  • Src/*.cpp: your firmware. Picked up by a glob, so new files need no CMake change.
  • Inc/*.hpp: your headers, other than the generated <project>_config.hpp.
  • USER CODE BEGIN / USER CODE END blocks inside CubeMX’s main.c and main.h.
  • The .ioc file, edited through the CubeMX GUI.

Everything else regenerates.

src/bmc/
CMakeLists.txt generated by update_cmake_cfg.py
CMakePresets.json generated by update_cmake_cfg.py
bmc.ioc CubeMX, edited through the GUI
startup_stm32g431xx.s CubeMX
STM32G431XX_FLASH.ld CubeMX, referenced by the toolchain file
cmake/
gcc-arm-none-eabi.cmake CubeMX, used by every preset
starm-clang.cmake CubeMX, unused
Inc/
main.h CubeMX, USER CODE blocks are yours
stm32g4xx_hal_conf.h CubeMX
stm32g4xx_it.h CubeMX
bmc_config.hpp generated by config_gen.py
motor.hpp yours
err.hpp yours
type.hpp yours
Src/
main.c CubeMX, USER CODE blocks are yours
stm32g4xx_hal_msp.c CubeMX
stm32g4xx_it.c CubeMX
syscalls.c CubeMX
sysmem.c CubeMX
system_stm32g4xx.c CubeMX
controller.cpp yours

The five CubeMX .c files plus the startup assembly are compiled by lib/stm32g4, not by the project itself. The project’s own target_sources globs Src/*.cpp only.

Most projects keep sources at Src/ and Inc/. Some CubeMX versions emit Core/Src and Core/Inc instead, and src/tests/serial is the one project in this tree that does. The generator probes for both, so either works, but do not mix them within a project.

A project must set three variables before pulling in the library tree:

set(MX_SRC_DIR "${CMAKE_SOURCE_DIR}/Src")
set(MX_INC_DIR "${CMAKE_SOURCE_DIR}/Inc")
set(MX_STARTUP_S "${CMAKE_SOURCE_DIR}/startup_stm32g431xx.s")
add_subdirectory(../../lib fwlib)

lib/stm32g4 fails the configure step if any is missing, and lib/CMakeLists.txt uses their presence to decide whether this is an MCU build at all. lib/stm32g4 then reaches back up and adds sources to the project’s own executable target by name, which is why the ordering matters.

The bottom of a generated CMakeLists.txt looks like this:

# Add linked libraries
target_link_libraries(${CMAKE_PROJECT_NAME}
stm32cubemx
# Add user defined libraries
stm32
dbc
util
)

stm32cubemx is always present. The rest come from the --lib flags passed to scripts/new.sh:

Terminal window
./scripts/new.sh --board NUCLEO-G431RB --src src/example --lib stm32 --lib dbc --lib util

Library names are not validated. A typo becomes a missing-target error at build time.

There is no shell wrapper for this. Run the tool directly:

Terminal window
uv run --project tools python tools/scripts/update_cmake_cfg.py \
--src src/bmc --root . --ctx lib/stm32g4 \
--lib stm32 --lib dbc --lib util