CMake on STM32 | Episode 4: get ready for C++
Tags: embedded, cpp, microcontrollers, cmake
Published: 2020-05-02 15:49:43

I have been using C++ for the last 4 years... there is no way I get back to C! This episode is not about explaining why I think C++ is better than C. It is simply about running C++ on STM32 with CMake. By the way: we will make LEDs blink!

## Adding user code in code generated by STM32CubeMX 

If we change the MCU configuration with STM32CubeMX and generate the code again, the previous files are overwritten. How can we create our own program if everything is erased each time the code is generated?

Let's have look at the `main()` function in `main.c`. After board initialization,   it runs the classic infinite loop that (almost?) all embedded systems have:

```c
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
```

Notice the comments: they are tags for the code generator. Code between consecutive **USER CODE BEGIN xxx** and **USER CODE END xxx** tags is preserved during code generation.

You will find tags like these in many places in the generated code.

For instance `stm32f4xx_it.c` (which contains interrupt service routines) contains many of theses tags. For example:

```c
void HardFault_Handler(void)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */

  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}
```

## Blink LEDs in C++

Currently the application does nothing because the while loop is empty. Let's create a basic application to toggle LEDs using C++.

First, let's create `source/application.hpp` to declare two functions in a pure Arduino style, [setup()](https://www.arduino.cc/reference/en/language/structure/sketch/setup) and [loop() ](https://www.arduino.cc/reference/en/language/structure/sketch/loop):

```cpp
#pragma once

#ifdef __cplusplus
extern "C"
{
#endif
	
void setup();
void loop();

#ifdef __cplusplus
}
#endif
```

Then, let's create `source/application.cpp` to define these functions:

```cpp
#include "application.hpp"

#include "stm32f4xx_ll_gpio.h"
#include "main.h"

extern "C" {

void setup() {
    // For future episodes ;)
}

void loop() {
	LL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin);
	LL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
	LL_GPIO_TogglePin(LD3_GPIO_Port, LD3_Pin);

	HAL_Delay(200U);
}

}
```

We can now call these functions from `main()`. First, we have to include our file in `main.c` between **USER CODE** tags:

```c
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "application.hpp"
/* USER CODE END Includes */
```

Then we modify the infinite loop:

```c
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  setup();

  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    loop();
  }
  /* USER CODE END 3 */
```

You can open `BSP/BSP.ioc` with STM32CubeMX and generate the code again to check that the modifications are kept.

## Compile C++ code

Let's modify `CMakeLists.txt` to build our C++ application.

### Language settings

C++ language is enabled by default but it is good to configure the version of the standard. ARM GCC 9 supports C++17 so this is the one to use:

```cmake
enable_language(CXX C ASM)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
```

### Target configuration

We have new files so we have to add them to `add_executable()`:

```cmake
add_executable(${EXECUTABLE} ${STM32CUBEMX_GENERATED_FILES}
        source/application.cpp
        source/application.hpp)
```

We have a new directory with headers to we have to modify the include path:

```cmake
target_include_directories(${EXECUTABLE} PRIVATE
        BSP/Inc
        BSP/Drivers/STM32F4xx_HAL_Driver/Inc
        BSP/Drivers/CMSIS/Device/ST/STM32F4xx/Include
        BSP/Drivers/CMSIS/Include
        source/
        )
```

### Linker options

Let's build... and get some weird errors from the linker:

```bash
[  3%] Linking CXX executable nucleo-f413zh.out
c:/progra~2/gnutoo~1/92019-~1/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld.exe: c:/progra~2/gnutoo~1/92019-~1/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard\libg_nano.a(lib_a-abort.o): in function `abort':
abort.c:(.text.abort+0xa): undefined reference to `_exit'
c:/progra~2/gnutoo~1/92019-~1/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld.exe: c:/progra~2/gnutoo~1/92019-~1/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard\libg_nano.a(lib_a-signalr.o): in function `_kill_r':
signalr.c:(.text._kill_r+0xe): undefined reference to `_kill'
c:/progra~2/gnutoo~1/92019-~1/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld.exe: c:/progra~2/gnutoo~1/92019-~1/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard\libg_nano.a(lib_a-signalr.o): in function `_getpid_r':
signalr.c:(.text._getpid_r+0x0): undefined reference to `_getpid'
collect2.exe: error: ld returned 1 exit status
mingw32-make[3]: Leaving directory 'D:/cmake_stm32/cmake-build-debug'
mingw32-make[3]: *** [CMakeFiles\nucleo-f413zh.out.dir\build.make:435: nucleo-f413zh.out] Error 1
mingw32-make[2]: *** [CMakeFiles\Makefile2:75: CMakeFiles/nucleo-f413zh.out.dir/all] Error 2
mingw32-make[2]: Leaving directory 'D:/cmake_stm32/cmake-build-debug'
mingw32-make[1]: Leaving directory 'D:/cmake_stm32/cmake-build-debug'
mingw32-make[1]: *** [CMakeFiles\Makefile2:82: CMakeFiles/nucleo-f413zh.out.dir/rule] Error 2
mingw32-make: *** [makefile:117: nucleo-f413zh.out] Error 2
```

We can see in the error messages that the problem comes from `libg_nano.a`, the C++ standard library used by our toolchain. Internet gave two solutions:

1. Add `-fno-exceptions` to the compiler options.
2. Change `-specs=nano.specs` to `-specs=nosys.specs` in the linker option.

I must admit that my knowledge reaches its limits here, especially on _specs_ files... For this reason, I prefer to use the first solution because I clearly understand its ins and outs and because I don't use exceptions in embedded C++. Note that [Google C++ Style Guide discourages exceptions](https://google.github.io/styleguide/cppguide.html#Exceptions) in general.

## Conclusion

Hurray! LEDs are blinking thanks to C++!

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hp2dulrcx92jvuuk008c.jpg)

There wasn't a lot to do: create our files, add them to the build, configure the C++ standard. OK, we had a little linker issue but disabling exceptions was enough to solve it.

In a further episode, we will improve our C++ configuration by adding C++ specific options to the compiler.