STM32CubeIDE for Visual Studio Code Documentation

Table Of Contents

- STM32CubeIDE for Visual Studio Code
- Introduction
  - Overview
  - System requirements
  - Release note
- Basic Concepts
  - User interface
  - Command-line tools
  - Visual Studio Code extension packs
  - Bundles
  - CMake
  - Toolchains
  - STMicroelectronics Arm Clang Toolchain for STM32
- Getting Started
  - Installation
  - First project creation
- Workspace and extension workflow
  - Workspace overview
  - Extension commands
  - Project files
  - Multi-project workspaces
- Tutorials
  - Migrating from STM32CubeIDE for Visual Studio Code extension 2.x to 3.x
  - Dual-core STM32 projects
  - Power management
  - RTOS-based STM32 projects
  - Firmware security and integrity
- Development
  - Projects
  - Editing
  - Build
  - Debug
  - API manual
  - Integration with external tools
  - Testing and validation
- Troubleshooting
  - Frequently asked questions
  - Known issues
  - Support

### On this page

- Why learn CMake for STM32 development?
  - Further reading
- Create a CMake project
  - STM32CubeMX generated files
- Generated files
  - How to create a configuration preset
- `txt`
- How to add source files
  - Syntax:
  - Example:
  - Steps:
- How to add include files and folders
  - Example:
  - Notes:
- How to add symbols
  - Syntax
  - Example
  - Explanation
  - Steps
- How to add libraries
  - Syntax
  - Example
  - Explanation
  - Steps
- Toolchain files
  - Purpose:
  - Common toolchain files:
  - Project setup:
  - Example snippet (GCC toolchain):
  - Usage:
- Compiler settings
- Build output suffix
- Cortex settings
- Build optimization settings
- ASM configuration
- Linker configuration
- STM32CubeMX generated project structure

# CMake

Efficient and reliable build systems are essential in embedded development to accelerate product development and maintain code quality. For STM32 developers, mastering the build process is fundamental to fully leveraging STM32 microcontrollers and their extensive ecosystem.

CMake is a modern, versatile, and widely adopted build system generator that addresses many challenges faced by embedded developers. Unlike traditional integrated development environment (IDE)-specific project formats, CMake provides a unified, flexible, and portable approach to managing builds across different platforms, toolchains, and environments.

This page introduces STM32 developers to the basics of CMake, focusing on practical use cases that simplify the learning curve and help avoid common pitfalls during onboarding.

## Why learn CMake for STM32 development?

1. IDE independence and choice

STM32CubeIDE and Eclipse/CDT are popular development environments. However, relying solely on graphical user interface (GUI)-driven projects can limit flexibility. CMake enables developers to generate project files for multiple IDEs or build directly from the command line, offering freedom to select the best tools for each task.

2. Improved project portability and scalability

CMake uses simple, text-based configuration files ( `CMakeLists.txt`) that are easy to version control, share, and maintain. This contrasts with IDE-specific project files that are often bulky and difficult to manage. As STM32 projects grow in complexity, CMake’s modular approach helps maintain a clean and scalable build system.

3. Compiler abstraction

CMake toolchain abstraction enables developers to switch seamlessly between toolchains without rewriting build scripts. This flexibility allows STM32 developers to port projects between toolchains more easily.

4. Automation and continuous integration ready

Automated builds and testing pipelines are key to maintaining code quality and accelerating delivery in embedded development. CMake’s command-line driven workflow integrates naturally with continuous integration (CI) systems, enabling STM32 teams to automate builds, run tests, and deploy firmware efficiently.

5. Software component and library integration

Many STM32 software components, middleware, and libraries provide native CMake support or integration examples. Learning CMake empowers developers to take full advantage of these resources, reducing integration effort and avoiding vendor lock-in with specific IDEs.

By understanding and adopting CMake, STM32 developers gain a powerful skillset that enhances productivity, fosters collaboration, and future-proofs projects. This page guides readers step-by-step through essential CMake concepts and practical examples tailored to STM32 development, ensuring a smooth onboarding experience.

### Further reading

For more detailed information on CMake, see the official CMake online documentation.

For more information on CMake integration and usage, see the CMake integration in STM32CubeMX and usage in STM32CubeIDE for Visual Studio Code community article.

## Create a CMake project

To create a new CMake project and open it in Visual Studio Code, follow the Import STM32CubeMX project guide.

After the project is created and imported into Visual Studio Code, revisit this page for additional CMake information.

### STM32CubeMX generated files

STM32CubeMX generates two types of CMake files:

1. Tool-owned files - These files must not be modified. CubeMX overwrites them.

2. User-owned files - These files can be modified by the user to manage builds.

- `CMakeLists.txt`: Managed by CubeMX. Do not modify it.

- `gcc-arm-none-eabi.cmake`: Contains toolchain settings. Users can modify this file.

- `CMakeLists.txt`: Contains high-level project definition. Users can modify this file.

- `CMakePresets.json`: Contains CMake presets. Users can modify this file.

## Generated files

### How to create a configuration preset

Create a configuration and build preset in the `CMakePresets.json` file. The following is an example of preset configuration:

    "name": Machine-friendly name of the preset
    "hidden": Specifies whether a preset is hidden
    "inherits": Preset name to inherit from
    "condition": Condition to use this preset
    "vendor": Vendor name
    "displayName": Human friendly name
    "generator": Generator to use for the preset
    "toolchainFile": Path to the toolchain file
    "cacheVariables": {
        "CMAKE_BUILD_TYPE": Cache variable embedding the machine-friendly name of  the preset
    }

For more detailed information, see the official CMake presets documentation.

After creating the configuration preset, create a build preset in the same file. An example is shown below:

To use this configuration preset, select it from the Project Status tab of the CMake extension interface or use the command line with `--preset=example`:

You can also use the `CMAKE_BUILD_TYPE` as a condition to build or include files.

## `CMakeLists.txt`

`CMakeLists.txt` is generated once. Any user modification is preserved if STM32CubeMX or other ST tools regenerate assets.

Two `CMakeLists.txt` files are available in the project:

- A generic `CMakeLists.txt` in the main project folder

- An MX-Generated `CMakeLists.txt` included by the generic file, containing STM32CubeMX project configuration.

## How to add source files

Use relative paths intead of absolute paths to facilitate project sharing across development environments.

Use the target_sources() command to add source files to a target.

### Syntax:

    target_sources(<target> <scope>
      <source1>
      <source2>
      ...
    )

- <target>: Target name, such as an executable or library

- <scope>: PRIVATE, PUBLIC, or INTERFACE

- <source>: Source file paths relative to the `CMakeLists.txt` file

### Example:

    target_sources(my_project PRIVATE
      src/main.c
      src/stm32c0xx_it.c
      Drivers/STM32C0xx_HAL_Driver/Src/stm32c0xx_hal_gpio.c
      startup/startup_stm32c0l1xx.s
    )

### Steps:

1. Identify the target name.

2. List source files with correct relative paths.

3. Add the files using target_sources() with the appropriate scope.

4. Run CMake to configure and build the project.

## How to add include files and folders

Add source files with target_sources() and include directories with target_include_directories() .

1. Add source files:

    target_sources(<target> <scope>
      <source1>
      <source2>
      ...
    )

2. Add include directories:

    target_include_directories(<target> <scope>
      <include_dir1>
      <include_dir2>
      ...
    )

### Example:

    target_sources(stm32cubemx INTERFACE
      ../../Core/Src/main.c
      ../../Drivers/STM32C0xx_HAL_Driver/Src/stm32c0xx_hal_gpio.c
      ../../startup_stm32c0l1xx.s
    )

    target_include_directories(stm32cubemx INTERFACE
      ../../Core/Inc
      ../../Drivers/STM32C0xx_HAL_Driver/Inc
      ../../Drivers/CMSIS/Include
    )

### Notes:

- Use PRIVATE for internal use, INTERFACE to propagate to dependents.

- Paths are relative to the `CMakeLists.txt` file location.

- Include directories specify header file locations for compilation.

## How to add symbols

Use the target_compile_definitions() command to add preprocessor macros (compile-time symbols) to a target.

### Syntax

    target_compile_definitions(<target> <scope>
      <definition1>
      <definition2>
      ...
    )

- <target>: Target name (executable or library).

- <scope>: PRIVATE, PUBLIC, or INTERFACE.

- <definition>: Preprocessor definitions (macro), optionally with a value or generator expression.

### Example

    target_compile_definitions(stm32cubemx INTERFACE
      USE_HAL_DRIVER
      STM32C011xx
      $<$<CONFIG:Debug>:DEBUG>
    )

### Explanation

- USE_HAL_DRIVER and STM32C011xx are macros defined for all builds.

- $<$<CONFIG:Debug>:DEBUG> defines the DEBUG macro only for Debug configuration builds.

- Definitions added with the INTERFACE scope propagate to targets that link to stm32cubemx.

### Steps

1. Identify the target to add definitions to.

2. Choose the appropriate scope:

   - PRIVATE: For the target only.

   - PUBLIC: For the target and dependents.

   - INTERFACE: For dependents only.

3. List the macros to define.

4. Add conditional macros using generator expressions if required.

5. Run CMake to apply the changes.

## How to add libraries

Use the target_link_libraries() command to specify libraries to link to a target.

### Syntax

    target_link_libraries(<target> <scope>
      <library1>
      <library2>
      ...
    )

- <target>: Target to link libraries to (executable or library).

- <scope>: Optional; can be PRIVATE, PUBLIC, or INTERFACE.

- <library>: Name of the library or target to link.

### Example

    target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
      stm32cubemx
      # Add other user-defined libraries here
    )

### Explanation

- ${CMAKE_PROJECT_NAME} is the current project target.

- stm32cubemx is a library target linked to the project.

- The PRIVATE scope means libraries are linked only for this target.

- Use PUBLIC or INTERFACE to propagate linkage to dependents.

### Steps

1. Identify the target to link libraries to.

2. List the libraries or targets to link.

3. Specify the linkage scope if required.

4. Add the target_link_libraries() command in the CMakeLists.txt file.

5. Run CMake to configure and build the project.

## Toolchain files

Toolchain files configure the compiler, linker, and build environment for cross-compiling or targeting specific platforms.

### Purpose:

- Define system name and processor architecture.

- Specify compiler and toolchain executables.

- Set compiler and linker flags.

- Enable building with different toolchains, for example GCC or LLVM.

### Common toolchain files:

- gcc-arm-none-eabi.cmake: For GCC ARM embedded toolchain.

- starm-clang.cmake: For LLVM/Clang ARM toolchain.

### Project setup:

- When creating a new project, select one of the following options: - GCC toolchain only. - LLVM toolchain only. - Hybrid environment with both GCC and LLVM toolchains.

- Hybrid projects include both toolchain files, allowing flexible use of either compiler.

### Example snippet (GCC toolchain):

    set(CMAKE_SYSTEM_NAME generic)
    set(CMAKE_SYSTEM_PROCESSOR arm)

    set(TOOLCHAIN_PREFIX arm-none-eabi-)
    set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc)
    set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++)
    set(CMAKE_LINKER ${TOOLCHAIN_PREFIX}ld)

    set(CMAKE_C_FLAGS "-mcpu=cortex-m4 -mthumb ...")

### Usage:

1. Specify the toolchain file when configuring CMake:

       cmake -DCMAKE_TOOLCHAIN_FILE=path/to/gcc-arm-none-eabi.cmake ..

2. The toolchain file sets up the environment for cross-compilation automatically.

3. Use different toolchain files to switch between GCC and LLVM compilers.

## Compiler settings

This snippet configures the compiler environment in CMake by setting the system name, processor type, and enforcing the use of GCC compilers. It defines the toolchain prefix and specifies the compiler, assembler, linker, and related tools for ARM cross-compilation.

## Build output suffix

This snippet sets the output file suffix for assembly, C, and C++ executables to .elf in the CMake build configuration.

## Cortex settings

This snippet sets MCU-specific compiler flags for the Cortex-M4 processor, enabling floating-point and specific instruction set options.

## Build optimization settings

The build optimization settings depend on the selected context. By default, only release and debug configurations are available. If additional build contexts are required, you can add more configurations in this area.

The compiler supports the following optimization levels:

- -O (Same as -O1)

- -O0 (no optimization, the default if no optimization level is specified)

- -O1 (minimal optimization, prioritizing compilation time)

- -O2 (more optimization, without speed or size trade-off)

- -O3 (increased optimization, prioritizing speed)

- -Ofast (very aggressive optimization that may break standard compliance and change program behavior)

- -Og (optimizes for debugging experience. -Og enables optimizations that do not interfere with debugging. It is recommended for the standard edit-compile-debug cycle, offering a reasonable level of optimization while maintaining fast compilation and a good debugging experience.)

- -Os (optimizes for size. -Os enables all -O2 optimizations that do not typically increase code size and performs further optimizations to reduse code size. -Os disables the following optimization flags: -falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -freorder-blocks-and-partition -fprefetch-loop-arrays -ftree-vect-loop-version)

## ASM configuration

This snippet sets assembler flags to enable C++ preprocessing and configures C++ compiler flags to disable RTTI, exceptions, and thread-safe statics.

## Linker configuration

This snippet sets linker flags to specify the linker script, memory map output, garbage collection of unused sections, library groups, and memory usage printing for the build.

## STM32CubeMX generated project structure

STM32CubeMX can generate two types of projects; single-context projects and multi-context projects.

A multi-context project is generated for the following cases: - Dual-core products - Flashless products - Secure products

The following figure illustrates the differences between these two types of projects.

For multi-context projects, you will find the same files described above, with additional specificities:

- A top-level `Cmakelists.txt` and `CMakePresets.json` file (all subprojects inherit from these files)

- A specific `Cmakelists.txt` and `CMakePresets.json` for each subproject, where you can add or modify settings for only the related subproject