This guide walks through setting up an STM32 microcontroller project using CMake as the build system. The approach leverages the stm32-cmake framework to simplify project configuration and automatically fetch dependencies like CMSIS and HAL libraries.
- CMake (v3.14 or newer)
- ARM GCC Toolchain installed (arm-none-eabi-gcc)
- VS Code with the following extensions (recommended):
- J-Link or ST-Link debugger for hardware debugging
- Git (for fetching dependencies)
-
Create a new project directory
mkdir stm32-project cd stm32-project -
Create the basic directory structure
mkdir -p src include .vscode
-
Create the main project files:
CMakeLists.txt(build configuration)src/main.c(your application entry point).vscode/settings.json(VS Code configuration).vscode/launch.json(debugging configuration)
# Minimum CMake version
cmake_minimum_required(VERSION 3.14)
# Import the stm32-cmake framework
include(FetchContent)
FetchContent_Declare(
stm32_cmake
GIT_REPOSITORY https://github.com/ObKo/stm32-cmake.git
GIT_TAG v2.1.0 # Update to latest version as needed
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/board_sdk/stm32-cmake
)
FetchContent_MakeAvailable(stm32_cmake)
# Set toolchain file (must come before project() declaration)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/board_sdk/stm32-cmake/cmake/stm32_gcc.cmake)
# Define your project
project(YourProjectName C CXX ASM)
# Target STM32 family (choose appropriate one)
stm32_fetch_cmsis(F4) # Change to your target family (F1, F4, L4, etc.)
stm32_fetch_hal(F4) # Fetches HAL libraries automatically
# Specify the target chip
set(STM32_CHIP STM32F411xE) # Change to your specific chip
# Add executable
add_executable(${PROJECT_NAME}
src/main.c
src/system_stm32f4xx.c
# Add other source files here
)
# Link with HAL and CMSIS
target_link_libraries(${PROJECT_NAME}
STM32::F4::CMSIS
STM32::F4::HAL
)
# Set compiler options
target_compile_definitions(${PROJECT_NAME} PRIVATE
-DUSE_HAL_DRIVER
-D${STM32_CHIP}
)
# Compile options to optimize code size and debugging experience
# -g3: Include all debug information
# -Og: Decrease code size, but keep the debugability
# -fno-exceptions: Disables C++ and C exceptions (enabling them requires malloc and heap functionality!)
# $<..>: Enable only when compiling C++ sources
# -fno-rtti: Disable generation of information about every class with virtual functions (saves RAM and flash space)
# -fno-use-cxa-atexit: Disables registration of destructors for static objects (saves RAM)
add_compile_options(-g3
-Og
-fno-exceptions
$<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>
$<$<COMPILE_LANGUAGE:CXX>:-fno-use-cxa-atexit>
-fmacro-prefix-map=${CMAKE_CURRENT_LIST_DIR}/=/)
# The linker script is handled automatically by stm32-cmake
# Post-build command to create .bin file
stm32_print_size_of_target(${PROJECT_NAME})
stm32_generate_binary_file(${PROJECT_NAME})- Create a
builddirectory:mkdir build && cd build - Configure CMake:
cmake .. - Build the project:
cmake --build .
Create the following configuration files in your .vscode directory:
To create or edit your VS Code settings:
- Open the Command Palette with
Cmd+Shift+P(macOS) orCtrl+Shift+P(Windows/Linux) - Type "Preferences: Open Workspace Settings (JSON)"
- Select this option to open/create the
.vscode/settings.jsonfile
{
"cmake.configureOnOpen": true,
"cmake.generator": "Ninja",
"cmake.buildDirectory": "${workspaceFolder}/build",
"cmake.parallelJobs": 4,
"cortex-debug.variableUseNaturalFormat": true,
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"
}- Create this file in the
.vscodedirectory - Adjust the "device" field to match your specific STM32 chip
{
"version": "0.2.0",
"configurations": [
{
"name": "Cortex Debug",
"cwd": "${workspaceFolder}",
"executable": "${command:cmake.launchTargetPath}",
"request": "launch",
"type": "cortex-debug",
"runToEntryPoint": "main",
"servertype": "jlink", // Change to "stlink" if using ST-Link
"device": "STM32F411xE", // Change to your specific chip
"interface": "swd",
"svdFile": "${workspaceFolder}/STM32F4xx.svd", // Path to SVD file for peripheral visualization
"armToolchainPath": "/usr/local/bin", // Adjust based on your ARM GCC installation path
"preLaunchTask": "Build", // Optional: automatically build before debugging
"showDevDebugOutput": "parsed"
}
]
}{
"version": "2.0.0",
"tasks": [
{
"label": "Build",
"type": "shell",
"command": "cmake --build ${workspaceFolder}/build",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": ["$gcc"]
},
{
"label": "Clean",
"type": "shell",
"command": "cmake --build ${workspaceFolder}/build --target clean",
"problemMatcher": []
}
]
}Here's a minimal example to get started with:
#include "stm32f4xx_hal.h"
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
int main(void)
{
/* Reset of all peripherals, initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* Infinite loop */
while (1)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // Toggle LED on many STM32 boards
HAL_Delay(500); // 500ms delay
}
}
/* System Clock Configuration */
void SystemClock_Config(void)
{
// Your clock configuration code here
// This will vary depending on your specific STM32 model
}
/* GPIO Initialization Function */
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
/* Configure GPIO pin : PA5 */
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}-
First-time setup:
- Open your project folder in VS Code
- Let VS Code detect and install recommended extensions
- Wait for CMake to configure (this may take a while the first time)
-
Selecting a Kit:
- Use
Cmd+Shift+P(macOS) orCtrl+Shift+P(Windows/Linux) - Search for "CMake: Select a Kit"
- Choose your ARM GCC installation (e.g., "arm-none-eabi-gcc")
- Use
-
Build your project:
- Click the "Build" button in the CMake Tools status bar
- Or use
Cmd+Shift+P→ "CMake: Build" - Or use keyboard shortcut
F7
-
Start debugging:
- Connect your debugger (J-Link or ST-Link)
- Press
F5to start debugging - Or use
Cmd+Shift+P→ "Debug: Start Debugging"
-
CMake commands (via
Cmd+Shift+P):- "CMake: Configure" - Regenerate build files
- "CMake: Clean" - Clean build directory
- "CMake: Build Target" - Build specific target
- "CMake: Select Variant" - Choose Debug/Release
-
Compiler not found:
- Ensure ARM GCC is in your PATH
- For macOS: Install via Homebrew with
brew install arm-none-eabi-gcc - Verify installation with
arm-none-eabi-gcc --version
-
Missing libraries:
- The FetchContent commands should automatically download dependencies
- Check your internet connection and firewall settings
- Try manually cloning the repository if FetchContent fails
-
Build errors:
- Check that you've selected the correct STM32 family and chip
- Verify your CMake version (
cmake --version) meets requirements - Look for missing include files or undefined references
-
Debug connection failed:
- Verify hardware connections to J-Link/ST-Link
- Check device settings in launch.json match your actual chip
- Make sure debugger drivers are installed properly
- Try restarting VS Code or reconnecting the debugger
-
SVD file not found:
- Download the appropriate SVD file for your chip from ST's website
- Place it in your project directory
- Update the svdFile path in launch.json
The stm32-cmake framework automatically handles linker scripts for you. However, you can customize memory settings if needed:
# Set chip-specific memory layout
set(STM32_FLASH_SIZE 512K)
set(STM32_RAM_SIZE 128K)If you use STM32CubeMX to generate initialization code:
- In CubeMX, select "Makefile" as project generator
- Generate the code to a subdirectory of your project
- Copy the generated code to your src/ directory
- Add the files to your CMakeLists.txt
- Include the generated paths in your include directories
To properly configure memory sections:
# Add flash sections configuration
target_compile_definitions(${PROJECT_NAME} PRIVATE
-DFLASH_BANK_NUMBER=1
-DRAM_SIZE=128K
-DFLASH_SIZE=512K
)stm32-project/
├── .vscode/
│ ├── settings.json
│ ├── launch.json
│ └── tasks.json
├── board_sdk/ # Auto-generated by FetchContent
│ └── stm32-cmake/
├── build/ # Build output
├── include/ # Header files
│ └── main.h
├── src/ # Source files
│ ├── main.c
│ ├── stm32f4xx_hal_msp.c
│ ├── stm32f4xx_it.c
│ └── system_stm32f4xx.c
└── CMakeLists.txt # Main build file
- stm32-cmake Documentation
- stm32-cmake Examples
- CMSIS-HAL Example
- STM32 Reference Manuals
- ARM GCC Toolchain
- CMake Documentation
- VS Code Cortex-Debug Extension
- STM32CubeMX - GUI for STM32 configuration
- STM32CubeIDE - Alternative IDE based on Eclipse
- OpenOCD - Open-source debugging for ARM