DEV Community

Cover image for Streamlining STM32 Projects: VS Code, CMake and clangd
Mattia
Mattia

Posted on

Streamlining STM32 Projects: VS Code, CMake and clangd

The STM32CubeIDE software serves as an excellent starting point for STM32 development. However, when it comes to developer experience, the IDE provided by ST can feel sluggish and resource-intensive.

In this article, we’ll explore how to seamlessly transition from STM32CubeIDE to Visual Studio Code for your embedded software development needs. Under the hood, we’ll set up a robust CMake build system, utilize clangd for indexing, formatting and tidy checks, and configure VS Code to efficiently manage the CMake project, clangd, and debugging.

Let’s dive into the details!

Creating a project with STM32CubeIDE

In this chapter, we’ll walk through the process of setting up a C++ project using STM32CubeIDE. If you’ve already configured a project, feel free to skip ahead to the next section.

New STM32 project
Let's create a new project in STM32CubeIDE by clicking on File -> New -> STM32 Project

Image description

Micro-controller choice
In the micro-controller search we are gonna enter the name of the micro-controller that we are using. In my case it's the NUCLEO board with the micro-controller STM32L4A6ZGTX.

Image description

Project name
Let's now enter the name of the project and it's location.

Image description

HAL Configuration
After clicking finish, the configuration view will open up. Here you will be able to configure your micro-controller hardware. For the purpose of this demo, let's configure a led as GPIO Output and give the pin a name that we can later reference in our source files.

Image description

GPIO Label

Code generation settings
As a last step before letting STM32CubeIDE generate the code, select Project Manager -> Project -> Do not generate the main()

And Project Manager -> Project -> Generate peripheral initialization as a pair of '.c/.h' files for peripheral

Configure don't generate main

Configure peripheral initialization as a pair of '.c/.h' files

Generating the HAL code
We are now ready to generate the code. Click save or the generate code symbol.

Generate code symbol

Application folder
We selected to not generate the main so that we can write a cpp main ourselves. Start by creating a source folder in the root directory by right clicking on the project then clicking on New -> Source Folder

Create source folder

Create Application source folder

Creating the main
And create a main.cpp file inside the Application folder:

Image description

Image description

The code for the main.cpp used in this demo is the following:



#include "main.h"
#include "gpio.h"

extern "C"
{
    extern void SystemClock_Config();
}

int main()
{
    HAL_Init();

    SystemClock_Config();

    MX_GPIO_Init();

    while (true)
    {
        HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
        HAL_Delay(1000);
        HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
        HAL_Delay(1000);
    }
}


Enter fullscreen mode Exit fullscreen mode

Compile and debug
We are now ready to compile and debug our project with STM32CubeIDE.

Setting up the CMake project

Let's now close the STM32CubeIDE window and start integrating our STM project in VS code. The first thing to do is to set up the CMake project.

Start by checking if you have CMake and Ninja already installed on your system by typing in the terminal:



cmake --version
ninja --version


Enter fullscreen mode Exit fullscreen mode

If CMake or Ninja are missing, you can install them from your package manager in linux or from the official websites in Windows:

In Windows you will have to add the path of your Ninja executable to the system environment variables. For example, if your executable is located in C:\Ninja\ninja.exe, you can add the path C:\Ninja\ to your system environment variables.

Afterward clone the github folder associated to this article and copy the following files to your project folder:

  • CMakeLists.txt
  • CMakePresets.json
  • gcc-arm-none-eabi.cmake

CMakeLists.txt

Open the CMakeLists.txt file and if needed adapt the name of the micro-controller from STM32L4A6ZGT to the one that you are using. In the CMakeLists.txt file check that your sources are defined in the PROJECT_SOURCES variable and that the correct symbols are defined in the PROJECT_DEFINES variable.

CMake toolchain file

The toolchain file gcc-arm-none-eabi.cmake defines the location of the compiler toolchain that CMake uses.

The TOOLCHAIN_PREFIX variable should point to the location of the compiler included by STM32CubeIDE. In linux it's usually inside /opt/st/ while in Windows it's usually inside C:\ST\.

At this point you should be ready to let CMake configure and build your project. Open a terminal in the root of your project folder and type the following commands:



cmake --preset Application          # configure project
cmake --build --preset Application  # build project


Enter fullscreen mode Exit fullscreen mode

The output of the last command when the compilation finishes should be something like:



[25/25] Linking CXX executable STM32-CMake-base.elf


Enter fullscreen mode Exit fullscreen mode

Congratulation you compiled your embedded project with CMake and Ninja !

Setting up VS Code

From the github repo associated with this article, copy the .vscode folder to your project folder.

In the .vscode/settings.json file you can adapt define the location of gdb and gdb server to allow debugging. Additionally the location of the gcc compiler for clangd can also be adapted.



{
    "cortex-debug.gdbPath": "/opt/st/stm32cubeide_1.15.0/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.12.3.rel1.linux64_1.0.100.202403111256/tools/bin/arm-none-eabi-gdb",
    "cortex-debug.JLinkGDBServerPath": "/opt/st/stm32cubeide_1.15.0/plugins/com.st.stm32cube.ide.mcu.externaltools.jlink.linux64_2.2.200.202402092224/tools/bin/JLinkGDBServer",
    "clangd.arguments": ["--query-driver=/opt/st/stm32cubeide_1.15.0/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.12.3.rel1.linux64_1.0.100.202403111256/tools/bin/arm-none-eabi-gcc"]
}


Enter fullscreen mode Exit fullscreen mode

The file .vscode/extensions.json specifies the recommended extensions to use. If you open the extension menu in VS Code, you will be able to see and install the extension listed in .vscode/extensions.json.

After installing the recommended extensions, the VS Code CMake and clangd integration will start working. By opening a source file, clangd will index it and you will be able to start using the advanced functionality that the clangd language server provides.

Here an example of the sometimes very helpful checking that clangd performs by default:

Clang will warn if an unsigned if compares against a negative number

You can now compile the project in VS Code by pressing CTRL + SHIFT + B.

Debugging

The file .vscode/launch.json that was copied in the previous chapter from the github repo configures the debugging settings. If you open it, you will see two configurations, one for the SEGGER J-Link debugger and the other for the ST-Link debugger.

In the left bar in VS Code, select the Debug symbol, then select the debug configuration compatible with your debugger and finally press the play button to flash the firmware on your micro-controller and start debugging.

Debug in VS Code

Formatting and tidy checks

To enable formatting of the sources and clang-tidy checks, copy the following files from the cloned github folder to your application folder:

  • Application/.clang-format: This file defines the formatting rules for your C/C++ code.
  • Application/.clang-tidy: Here, you’ll find configuration settings for clang-tidy. This tool analyzes your code for potential issues, such as code smells, bugs, and performance bottlenecks.

Note that clang-format and clang-tidy will be active only at the level of the configuration files. In this scenario, their effects will extend to all files residing inside the Application folder. In this way clangd will not format or perform checks on files generated by STM32CubeIDE.

Now you’re all set to streamline your STM32 development workflow using VS Code, CMake, and clangd. Happy coding!

Feel free to ask me any question in the comments.

Top comments (3)

Collapse
 
nigel447 profile image
nigel447

really appreciate all the work that has gone into this great post

Collapse
 
alan_sub_a544fc88eec12cab profile image
alan sub • Edited

Thank you so much for sharing this valuable guide! Your detailed explanation is incredibly helpful.

I also notice that the STM32 have a VS Code Extension which can assist in creating STM32 CMake projects directly within VS Code, making development even more seamless and efficient.

official information:
stm32-vs-code-extension

How to create projects using the STM32 VS Code Extension

do not use STM32CubeMX 6.12.1, there is an issue
Issue with Linker Script in STM32CubeMX 6.12.1
I use 6.12.0, it works

I have tried it out and looks working, I put the code on github
cmake_stm32f429i_disco_mx

Collapse
 
maxaigner profile image
Max

This is really neat. I'm not a huge fan of the STM32CubeIDE since I'm used to vscode for all of my coding work -- so thanks, I'll give that a try!