Learning how to use STM32 MCUs by setting up a build environment with CMake, downloading the ARM compiler, exploring the STM32CubeIDE directory structure, and modifying it to suit personal needs and style.
The CMake build automatically downloads the ARM GNU toolchain into .tools/ on
first configure if it is not already present. No manual toolchain setup needed.
Configure:
cmake -B build -G Ninja -DCMAKE_TOOLCHAIN_FILE=cmake/arm-none-eabi-toolchain.cmakeBuild:
ninja -C buildTo target a different MCU (default is STM32U545xx):
cmake -B build -G Ninja -DCMAKE_TOOLCHAIN_FILE=cmake/arm-none-eabi-toolchain.cmake \
-DTARGET_MCU=STM32H503xxSupported MCUs are defined in cmake/MCU.cmake. Add an elseif block there
to support a new device.
Optional: Symlink compile_commands.json to the root directory for
clangd/ccls.
Linux
ln -sfn ./build/compile_commands.json .Windows (requires admin)
New-Item -ItemType SymbolicLink -Path "compile_commands.json" -Target "./build/compile_commands.json"Linux:
docker build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g) -t hellostm32 .Windows:
docker build -t hellostm32 .Run container:
docker run -it --rm -v "$(pwd):/app" hellostm32You can flash your firmware onto the target MCU using various tools. Choose the one that matches your hardware/debug adapter.
Download stlink.
st-flash write build/firmware.bin 0x8000000Windows
If you're downloading through the github release page make sure to copy the
stlink directory under Program Files (x86) in the zip file to the real
Program Files (x86).
And you'll also need to download libusb and put the
dll either in the same path as stlink binaries or expose it in the system
path environment.
openocd -f interface/stlink.cfg -f target/stm32u5x.cfg \
-c "program build/firmware.elf verify reset exit"CMake targets are provided for all common workflows:
cmake --build build --target flash # build and flash firmware
cmake --build build --target debug # start OpenOCD GDB server on :3333
cmake --build build --target gdb # connect arm-none-eabi-gdb to :3333debug and gdb are meant to run in separate terminals — start debug
first, then gdb.
Equivalent manual commands:
openocd -f interface/stlink.cfg -f target/stm32u5x.cfgarm-none-eabi-gdb \
-ex "target remote :3333" \
-ex "load" \
-ex "monitor reset init" \
build/firmware.elfZed's built-in debugger connects to OpenOCD via GDB. Requires a GDB build
with DAP support (GDB 14+) and multi-architecture support. Install
gdb-multiarch for your platform:
Windows — MSYS2 UCRT64:
pacman -S mingw-w64-ucrt-x86_64-gdb-multiarchLinux:
sudo apt install gdb-multiarchmacOS:
brew install gdbCreate .zed/settings.json with the path to the installed binary:
| Platform | Path |
|---|---|
| Windows | C:/msys64/ucrt64/bin/gdb-multiarch.exe |
| Linux | /usr/bin/gdb-multiarch |
| macOS | /opt/homebrew/bin/gdb |
{
"dap": {
"GDB": {
"binary": "<path from table above>",
},
},
}Create .zed/debug.json:
[
{
"label": "Debug firmware (OpenOCD)",
"adapter": "GDB",
"request": "launch",
"program": "$ZED_WORKTREE_ROOT/build/firmware.elf",
"gdb_args": [
"-ex",
"target remote :3333",
"-ex",
"load",
"-ex",
"monitor reset init",
],
},
]Start OpenOCD first, then launch the debugger in Zed:
# Terminal — keep this running
cmake --build build --target debugThen use Run > Start Debugging or the debug panel and select Debug firmware (OpenOCD).
JLinkGDBServer -device STM32U545RET6 -if SWD -speed 4000arm-none-eabi-gdb -ex "target remote localhost:3333" -ex "load" -ex "monitor reset init" -ex "b main" -ex "c" build/firmware.elfFollow the same step as the openocd.
You'll need an account if you want to download anything from ST.
Tutorials
AArch32 bare-metal target (arm-none-eabi)