Series / STM32 Development with CMake and VSCode / Why Use CMake and VSCode for STM32 Development?

Why Use CMake and VSCode for STM32 Development?

A practical look at why this site uses CMake and VSCode for STM32 firmware instead of making STM32CubeIDE the main project workflow.

On this page

STM32CubeIDE is a useful tool. It can generate projects, configure peripherals, build firmware, flash a board, and start a debug session from one application. For many projects, that is enough.

This site uses a different default workflow: CMake for the build system and VSCode for editing and debugging.

That choice is not about avoiding ST tools completely. It is about keeping the firmware project explicit, portable, and easy to explain. When the goal is to learn embedded development from the build system down to startup code, registers, drivers, and applications, the project structure matters. The tooling should make the moving parts visible instead of hiding them inside an IDE-managed project.

The target board for this path is the STM32 Nucleo L433RC-P, but the reasoning applies to many STM32 projects.

The Workflow This Site Will Use

The STM32 articles on this site will use a workflow built around a few separate tools:

  • CMake describes how the firmware is built.
  • An Arm cross-compiler produces the firmware binary.
  • A linker script describes the target memory layout.
  • Startup code prepares the runtime before main().
  • A flashing/debugging tool talks to the board through ST-LINK.
  • VSCode provides editing, navigation, tasks, and debugger integration.

The important part is that VSCode does not own the project. The project is still a normal source tree with build files that can be inspected, versioned, and run outside the editor.

That means the same project can be built from a terminal, a CI job, or VSCode. If the editor changes later, the firmware project does not have to be rebuilt around a new IDE format.

The Problem With IDE-Owned Projects

IDE-owned embedded projects are convenient at the beginning. You click through a wizard, pick a board, enable a peripheral, and get a buildable project. That is a good way to get an LED blinking quickly.

The friction usually appears later.

Generated project files can become hard to review. Build settings may be spread across IDE dialogs, XML files, generated folders, and tool-specific metadata. Two developers can have the same source files but different local IDE settings. A clean command-line build may not exist at all, or may not behave exactly like the IDE build.

That is a problem for educational content too. If an article says "open the project settings and enable this checkbox," the real build rule is hidden from the reader. If the next article needs startup code, linker sections, compiler flags, or include paths, the explanation depends on the IDE instead of the underlying build.

For this site, that is the wrong tradeoff. The goal is not only to produce firmware. The goal is to understand the firmware project well enough to modify it deliberately.

What CMake Gives an STM32 Project

CMake gives the project an explicit build description.

It can define the target microcontroller, source files, include paths, compile options, linker options, and generated outputs in text files that live with the code. Those files can be reviewed in a pull request, copied into another project, or built from a terminal.

A CMake-based STM32 project can make important build decisions visible:

  • Which compiler is used.
  • Which CPU and FPU flags are passed.
  • Which source files are part of the firmware.
  • Which include directories are required.
  • Which linker script controls memory layout.
  • Which post-build steps create .elf, .bin, or .hex files.

That visibility matters when learning embedded systems. A microcontroller project is not just C files. It is also the startup file, the linker script, the target memory map, the compiler options, and the debug artifacts. CMake gives those pieces a place in the project instead of leaving them as IDE state.

CMake also makes it easier to grow a project. A simple blink example can become a project with application code, reusable drivers, board support files, generated vendor code, and testable host-side utilities without changing the core workflow.

What VSCode Gives Without Owning the Project

VSCode is useful because it is a good front end for a workflow that already exists.

It can provide code navigation, syntax highlighting, formatting, tasks, terminal access, and debugger integration. It can run the same CMake commands that work outside the editor. It can launch a debug session against the same firmware artifact produced by the command-line build.

That is the right relationship between editor and project.

The editor should help you work with the project. It should not be the only place where the project makes sense.

This distinction becomes important when debugging or documenting embedded code. If flashing and debugging are driven by scripts or task definitions, the commands can be shown in an article. A reader can see which file is flashed, which debugger is launched, and which target is selected. If something fails, there is a concrete command to inspect instead of a hidden IDE action.

Where STM32CubeIDE and CubeMX Still Fit

Using CMake and VSCode does not mean every ST tool is useless.

STM32CubeIDE can still be helpful as a reference environment. STM32CubeMX can still be useful for exploring pin assignments, clock trees, peripheral initialization, and generated driver code. Vendor examples are still worth reading. ST's HAL and LL drivers can still be used when they are the right tradeoff.

The boundary is this: CubeMX may generate code, but it should not define the project workflow.

In this site's STM32 content, the source tree, build system, and editor setup should remain understandable without opening STM32CubeIDE. If generated code is used, it should be treated as source input to a CMake project, not as a reason to hand control of the build back to the IDE.

That approach keeps the project teachable. It also makes it easier to compare HAL-based code with bare-metal code because the build and debug workflow stays the same.

The Tradeoffs

CMake and VSCode are not magic. The workflow has costs.

You have to install and connect more pieces yourself. You need an Arm toolchain, CMake, a build backend such as Ninja, a flashing/debugging tool, and VSCode configuration. The first setup takes longer than clicking through an IDE wizard.

You also have to understand more of the project. That is the point, but it can feel slower at the start. Linker scripts, startup files, compiler flags, and debugger configuration are real parts of embedded firmware. A CMake workflow makes them visible earlier.

The payoff is that the project becomes less mysterious. When a build fails, there is a command to rerun. When a source file is missing, there is a build file to check. When memory placement matters, there is a linker script to inspect. When the debugger launches, the connection is described by configuration rather than by an IDE button.

For a learning-focused site, that tradeoff is worth it.

What This Series Will Build Toward

This series will build the STM32 workflow in layers.

First, it will install the required tools on macOS. Then it will create a minimal CMake project, add the linker script and startup code, build the firmware, flash it to the Nucleo board, and run it under a VSCode debugger.

After that, it will show how generated STM32CubeMX code can be brought into a CMake project without making STM32CubeIDE the main environment. Finally, it will organize the project structure so later articles can focus on firmware instead of rebuilding the tooling from scratch.

The intended result is simple: every STM32 article can start from a workflow that is already explicit, reproducible, and under version control.

Next Steps

The next article sets up the STM32 CMake toolchain on macOS.

Once the toolchain is installed, the series can move from motivation to a working project: source files, build files, startup code, linker script, flashing, and debugging. That foundation will support the later STM32 starter articles, the bare-metal series, and the display projects that build on this workflow.