Project Setup (STM32)
Configure an arm-none-eabi CMake project for the STM32 (HAL) platform of kbox-stack — toolchain, the compiler flags that must match the prebuilt library, includes, linking, and the linker script.
This page describes how a consumer project targets the STM32 (HAL) platform: the
toolchain, the compiler flags, and how to include the headers and link the
prebuilt library. The snippets are taken from the led-indicator example's
CMakeLists.txt.
Toolchain
The platform builds with the arm-none-eabi bare-metal GCC toolchain for
Cortex-M3. CMake is put into cross-compile mode and pointed at the toolchain
binaries:
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(TOOLCHAIN_PREFIX ${TOOLCHAIN_BIN}/arm-none-eabi-)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++)
set(CMAKE_ASM_COMPILER ${TOOLCHAIN_PREFIX}gcc)
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy)
set(CMAKE_SIZE ${TOOLCHAIN_PREFIX}size)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY is required because a bare-metal
toolchain cannot link a normal test executable during CMake's compiler check.
The example defaults TOOLCHAIN_BIN to the GCC bundled with STM32CubeIDE; pass
-DTOOLCHAIN_BIN=/path/to/arm-none-eabi/bin to override it.
Your compiler flags must be ABI-compatible with the prebuilt .a. The
library is compiled for Cortex-M3, Thumb, no exceptions, no RTTI. If your
CPU/float/ABI flags differ, the archive will not link correctly (or will fail at
runtime). Do not change the CPU flags below.
Compiler flags
set(COMMON_FLAGS
-mcpu=cortex-m3
-mthumb
-fdata-sections
-ffunction-sections
-Wall
-fno-exceptions
)
set(C_FLAGS ${COMMON_FLAGS})
set(CXX_FLAGS ${COMMON_FLAGS} -fno-rtti)
set(ASM_FLAGS -mcpu=cortex-m3 -mthumb -x assembler-with-cpp)-mcpu=cortex-m3 -mthumb— the CPU/instruction set the.awas built for.-fno-exceptions(C and C++) and-fno-rtti(C++) — match the library and keep code size down; the stack does not use exceptions or RTTI.-ffunction-sections -fdata-sections— put each function/object in its own section so the linker can garbage-collect unused ones (paired with--gc-sectionsbelow).
Optimization is per-config — Release is size-optimized, Debug is debug-friendly:
set(CMAKE_C_FLAGS_RELEASE "-Os -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-Os -DNDEBUG")
set(CMAKE_C_FLAGS_DEBUG "-Og -g")
set(CMAKE_CXX_FLAGS_DEBUG "-Og -g")The platform defines identify the MCU and the HAL. WATCHDOG_ENABLED is turned
on only in Release so the debugger is not reset while halted:
add_compile_definitions(
STM32F103xB
USE_HAL_DRIVER
$<$<CONFIG:Debug>:DEBUG>
$<$<CONFIG:Release>:WATCHDOG_ENABLED>
)Include & link
The public headers live under dist/include and are reached as <Stack/...>.
From the example root, dist/ is three directories up:
include_directories(
${CMAKE_SOURCE_DIR}/Core/Inc
${CMAKE_SOURCE_DIR}/Drivers/STM32F1xx_HAL_Driver/Inc/Legacy
${CMAKE_SOURCE_DIR}/Drivers/STM32F1xx_HAL_Driver/Inc
${CMAKE_SOURCE_DIR}/Drivers/CMSIS/Device/ST/STM32F1xx/Include
${CMAKE_SOURCE_DIR}/Drivers/CMSIS/Include
${CMAKE_SOURCE_DIR}/../../../dist/include
)Link the prebuilt archive by relative path:
target_link_libraries(${CMAKE_PROJECT_NAME}.elf PRIVATE
${CMAKE_SOURCE_DIR}/../../../dist/lib/libkbox-stack---cortexm3---STM32F103xB.a
)The link step uses the linker script and enables dead-code stripping:
set(LINK_FLAGS
-mcpu=cortex-m3
-mthumb
-T${LINKER_SCRIPT}
-Wl,--gc-sections
-Wl,--no-warn-rwx-segments
-Wl,-Map=${CMAKE_PROJECT_NAME}.map,--cref
--specs=nano.specs
--specs=nosys.specs
-lc
-lm
)-Wl,--gc-sections removes unreferenced sections (works together with
-ffunction-sections -fdata-sections). --specs=nano.specs --specs=nosys.specs
select newlib-nano and bare-metal syscall stubs.
The concept of the peripheral interfaces the .a implements lives in the
portable core: Peripheral Interfaces. The
concrete STM32 drivers you construct are on the
Peripherals page.
Linker script & flash reservation
The single-image example uses STM32F103TBUX_FLASH.ld and boots directly at
0x08000000. Its memory map reserves the top of flash for the stack's
persistent data:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 125K
FLASH_USER (r) : ORIGIN = 0x0801F400, LENGTH = 0xC00 /* pages 125-127 */
}The reserved region must not be used by application code. FLASH is only
125 KB (not the full 128 KB) precisely so that FLASH_USER
(0x0801F400–0x0801FFFF, 3 pages) stays free for the stack's persistent
configuration and state. The linker script even asserts this window is exactly 3
pages. The address you pass to HalFlash (0x0801, 0xF400 → 0x0801F400)
must sit at the start of this reserved window; MakeFlashLayout() partitions
the rest.
IDE options
Open led-indicator.code-workspace. The .vscode/ folder ships build tasks and
a Cortex-Debug launch configuration (needs the Cortex-Debug extension plus
OpenOCD).
The project ships a CubeMX .ioc (led-indicator.ioc) that matches the
peripheral configuration in Core/Src/main.cpp (pins, clock tree, USART2/DMA,
TIM2, IWDG, EXTI). Open it in CubeMX to inspect or change the hardware setup
and regenerate the init code — the application logic lives in USER CODE
blocks, which CubeMX preserves across regeneration. It is also a plain CMake
project: import it with File → Import → C/C++ → Existing Code as CMake
Project (or Import CMake project), then build and debug from the IDE.
Configure and build directly with CMake:
cmake -S . -B build_dbg -DCMAKE_BUILD_TYPE=Debug
cmake --build build_dbgAdd -DTOOLCHAIN_BIN=/path/to/arm-none-eabi/bin if the toolchain is not on the
default path.