CMake on STM32 | Episode 1: the beginning
Tags: cmake, embedded, microcontrollers, cpp
Published: 2020-04-28 15:35:25

This article is the first of a series dealing with [CMake](https://cmake.org/) and [STM32 microcontrollers](https://www.st.com/en/microcontrollers-microprocessors/stm32-32-bit-arm-cortex-mcus.html). In fact, it is more about CMake on MCUs but I had to pick one for the examples.

## Why CMake? Why STM32?

CMake is becoming the *de-facto* standard tool to build C and C++ projects. It is becoming so popular that it will probably be [the build tool for Qt6](https://www.qt.io/blog/2019/08/07/technical-vision-qt-6):

> For Qt 6, we aim to use CMake as a standard 3rd party build system to build Qt itself. CMake is by far the most widely used build system in the C++ world, and better integration with it is sorely needed.

STM32 is a family of 32-bit processors by ST-Microelectronics, based on ARM Cortex-M designs. I have been using MCUs from this family for years now, which is a good reason to use them here. Another good reason is because ST provides cheap and efficient demo boards. In the following, I will use the [NUCLEO-F413ZH](https://www.st.com/en/evaluation-tools/nucleo-f413zh.html) demo board (which costs about 25 €).

You can use other demo boards from ST (with few modifications). In fact you can use any board from any vendor (with more modifications). For instance, a few years ago, [I used CMake](https://github.com/Bktero/Planteur/tree/master/launchpad) with the [MSP-EXP430G2](http://www.ti.com/tool/MSP-EXP430G2) LaunchPad Development kit from Texas Instruments.

I will do my best to:

* to use modern CMake techniques but I don't consider myself as a CMake guru
* to explain each piece of my CMake scripts but I will assume that you know the basics

*Let the journey begin!*

## Software to install

You will of course need [CMake](https://cmake.org/download/) and [arm-none-eabi-gcc toolchain](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads). Both software must be available from the PATH :

```bat
λ cmake --version
cmake version 3.15.3

CMake suite maintained and supported by Kitware (kitware.com/cmake).

λ arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Tools for Arm Embedded Processors 9-2019-q4-major) 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599]
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
```

Other software that you may need or find useful:

* [mingw64](http://mingw-w64.org/doku.php) is a Windows port of the GNU Compiler Collection as the GNU Binutils. It is helpful to build Makefiles.
* [STM32CubeMX](https://www.st.com/en/development-tools/stm32cubemx.html) is a "graphical tool that allows a very easy configuration of STM32 microcontrollers and microprocessors, as well as the generation of the corresponding initialization C code" provided by ST. This is the easy way to get a working BSP ([board support package](https://en.wikipedia.org/wiki/Board_support_package)) for the Nucleo board.
* [STM32 ST-LINK utility](https://www.st.com/en/development-tools/stsw-link004.html) is a "full-featured software interface for programming STM32 microcontrollers" provided by ST. Give it an hex or bin file, it will program the Nucleo board with it.
* [λ Cmder](https://cmder.net/) is a "portable console emulator for Windows". I have been using it for years to have a correct command-line on Windows.

## Create the BSP

Here is how to use STM32CubeMX to create a working BSP for the Nucleo board.

On the home, go _File_, _New Project_ and to select _Nucleo-F413ZH_ in the _Board Selector_ tab:

![stm32cubemx_select_board](https://dev-to-uploads.s3.amazonaws.com/i/ipvsq8abuuylzn7m0pzb.png)

Click _Start Project_ on the top right corner of the screen and accept the default modes:

![stm32cubemx_default_modes](https://dev-to-uploads.s3.amazonaws.com/i/6te8p652dug0wvmi7ly8.png)

Don't modify the MCU configuration, simply go the _Project Manager_ to configure the _Project Settings_ :

![stm32cubemx_project_manager](https://dev-to-uploads.s3.amazonaws.com/i/5468e8kzq0yuziqaook6.png)

Finally, click on _Generate code_ :

![stm32cubemx_output](https://dev-to-uploads.s3.amazonaws.com/i/kcwga1p7cljrtc8s2kgo.png)

This is complete application and we can build it with `make`:

```bat
λ cd D:\cmake_stm32\BSP

λ mingw32-make.exe
mkdir build
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/main.d" -Wa,-a,-ad,-alms=build/main.lst Src/main.c -o build/main.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_it.d" -Wa,-a,-ad,-alms=build/stm32f4xx_it.lst Src/stm32f4xx_it.c -o build/stm32f4xx_it.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_msp.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_msp.lst Src/stm32f4xx_hal_msp.c -o build/stm32f4xx_hal_msp.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_tim.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_tim.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c -o build/stm32f4xx_hal_tim.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_tim_ex.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_tim_ex.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c -o build/stm32f4xx_hal_tim_ex.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_uart.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_uart.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c -o build/stm32f4xx_hal_uart.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_rcc.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_rcc.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c -o build/stm32f4xx_hal_rcc.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_rcc_ex.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_rcc_ex.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c -o build/stm32f4xx_hal_rcc_ex.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_flash.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_flash.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c -o build/stm32f4xx_hal_flash.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_flash_ex.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_flash_ex.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c -o build/stm32f4xx_hal_flash_ex.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_flash_ramfunc.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_flash_ramfunc.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c -o build/stm32f4xx_hal_flash_ramfunc.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_gpio.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_gpio.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c -o build/stm32f4xx_hal_gpio.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_dma_ex.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_dma_ex.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c -o build/stm32f4xx_hal_dma_ex.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_dma.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_dma.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c -o build/stm32f4xx_hal_dma.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_pwr.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_pwr.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c -o build/stm32f4xx_hal_pwr.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_pwr_ex.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_pwr_ex.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c -o build/stm32f4xx_hal_pwr_ex.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_cortex.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_cortex.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c -o build/stm32f4xx_hal_cortex.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c -o build/stm32f4xx_hal.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_exti.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_exti.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_exti.c -o build/stm32f4xx_hal_exti.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_pcd.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_pcd.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c -o build/stm32f4xx_hal_pcd.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_hal_pcd_ex.d" -Wa,-a,-ad,-alms=build/stm32f4xx_hal_pcd_ex.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c -o build/stm32f4xx_hal_pcd_ex.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/stm32f4xx_ll_usb.d" -Wa,-a,-ad,-alms=build/stm32f4xx_ll_usb.lst Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c -o build/stm32f4xx_ll_usb.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/system_stm32f4xx.d" -Wa,-a,-ad,-alms=build/system_stm32f4xx.lst Src/system_stm32f4xx.c -o build/system_stm32f4xx.o
arm-none-eabi-gcc -x assembler-with-cpp -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DUSE_HAL_DRIVER -DSTM32F413xx -IInc -IDrivers/STM32F4xx_HAL_Driver/Inc -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F4xx/Include -IDrivers/CMSIS/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/startup_stm32f413xx.d" startup_stm32f413xx.s -o build/startup_stm32f413xx.o
arm-none-eabi-gcc build/main.o build/stm32f4xx_it.o build/stm32f4xx_hal_msp.o build/stm32f4xx_hal_tim.o build/stm32f4xx_hal_tim_ex.o build/stm32f4xx_hal_uart.o build/stm32f4xx_hal_rcc.o build/stm32f4xx_hal_rcc_ex.o build/stm32f4xx_hal_flash.o build/stm32f4xx_hal_flash_ex.o build/stm32f4xx_hal_flash_ramfunc.o build/stm32f4xx_hal_gpio.o build/stm32f4xx_hal_dma_ex.o build/stm32f4xx_hal_dma.o build/stm32f4xx_hal_pwr.o build/stm32f4xx_hal_pwr_ex.o build/stm32f4xx_hal_cortex.o build/stm32f4xx_hal.o build/stm32f4xx_hal_exti.o build/stm32f4xx_hal_pcd.o build/stm32f4xx_hal_pcd_ex.o build/stm32f4xx_ll_usb.o build/system_stm32f4xx.o build/startup_stm32f413xx.o -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -specs=nano.specs -TSTM32F413ZHTx_FLASH.ld  -lc -lm -lnosys  -Wl,-Map=build/BSP.map,--cref -Wl,--gc-sections -o build/BSP.elf
arm-none-eabi-size build/BSP.elf
   text	   data	    bss	    dec	    hex	filename
   7564	     20	   2668	  10252	   280c	build/BSP.elf
arm-none-eabi-objcopy -O ihex build/BSP.elf build/BSP.hex
arm-none-eabi-objcopy -O binary -S build/BSP.elf build/BSP.bin
```

You can program the board using ST-Link Utility with `BSP.hex` or `BSP.bin` but this application does nothing, as you can see in `main.c`:

```c
int main(void)
{
  // ...

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

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

The purpose of this article is just to compile the same application using CMake. Don't worry: later in this series we will blink some LEDs ;)

## Create a minimalist CMake project

Do you know what's great with the Makefile generated by STM32CubeMX? It gives use all the information we need to create a minimalist `CMakeLists.txt` to build the same firmware for the Nucleo board: list of files to compile, macros definitions, compiler and linker options. Everything.

Well, in fact, the console output is enough to recreate a `CMakeLists.txt` that does the job:

```cmake
cmake_minimum_required(VERSION 3.15.3)

project(nucleo-f413zh)

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


set(STM32CUBEMX_GENERATED_FILES

        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_exti.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c
        BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c

        BSP/Inc/main.h
        BSP/Inc/stm32f4xx_hal_conf.h
        BSP/Inc/stm32f4xx_it.h

        BSP/Src/main.c
        BSP/Src/stm32f4xx_hal_msp.c
        BSP/Src/stm32f4xx_it.c
        BSP/Src/system_stm32f4xx.c

        BSP/startup_stm32f413xx.s)

set(EXECUTABLE ${PROJECT_NAME}.out)

add_executable(${EXECUTABLE} ${STM32CUBEMX_GENERATED_FILES})

target_compile_definitions(${EXECUTABLE} PRIVATE
        -DUSE_HAL_DRIVER
        -DSTM32F413xx
        )

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

target_compile_options(${EXECUTABLE} PRIVATE
        -mcpu=cortex-m4
        -mthumb
        -mfpu=fpv4-sp-d16
        -mfloat-abi=hard

        -fdata-sections
        -ffunction-sections

        -Wall

        $<$<CONFIG:Debug>:-Og>
        )

target_link_options(${EXECUTABLE} PRIVATE
        -T${CMAKE_SOURCE_DIR}/BSP/STM32F413ZHTx_FLASH.ld
        -mcpu=cortex-m4
        -mthumb
        -mfpu=fpv4-sp-d16
        -mfloat-abi=hard
        -specs=nano.specs
        -lc
        -lm
        -lnosys
        -Wl,-Map=${PROJECT_NAME}.map,--cref
        -Wl,--gc-sections
        )

# Print executable size
add_custom_command(TARGET ${EXECUTABLE}
        POST_BUILD
        COMMAND arm-none-eabi-size ${EXECUTABLE})

# Create hex file
add_custom_command(TARGET ${EXECUTABLE}
        POST_BUILD
        COMMAND arm-none-eabi-objcopy -O ihex ${EXECUTABLE} ${PROJECT_NAME}.hex
        COMMAND arm-none-eabi-objcopy -O binary ${EXECUTABLE} ${PROJECT_NAME}.bin)
```

Here is quick summary:

* Initialize project
* Enable C and ASM and require to use C99 standard
* Create a variable with the list of generated source files
* `add_executable()` to create an executable with this list
* `target_compile_definitions()` to add macro definitions
* `target_include_directories()` to configure the compiler's include path
* `target_compile_options()` to set the compiler's options
* `target_link_options()` to set the linker's options
* `add_custom_command()` to add two post-build actions:
  1. Print the size of the executable with `arm-none-eabi-size`
  2. Create hex and bin files with `arm-none-eabi-objcopy`

Everything is pretty self-explanatory, except maybe:

```cmake
$<$<CONFIG:Debug>:-Og>
```

This is a [generator-expression](https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html). Generator expressions are as ugly (IMHO) as they are powerful. What does this one mean? If `CONFIG` is `Debug`, then add option `-Og`, otherwise add nothing. [Noice!](https://www.youtube.com/watch?v=VLNxvl3-CpA)

## Toolchain file

CMake assumes it is [cross-compiling when a toolchain file is specified](https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling). For ARM GCC, I used this file:

```cmake
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR ARM)

if(MINGW OR CYGWIN OR WIN32)
    set(UTIL_SEARCH_CMD where)
elseif(UNIX OR APPLE)
    set(UTIL_SEARCH_CMD which)
endif()

set(TOOLCHAIN_PREFIX arm-none-eabi-)

execute_process(
  COMMAND ${UTIL_SEARCH_CMD} ${TOOLCHAIN_PREFIX}gcc
  OUTPUT_VARIABLE BINUTILS_PATH
  OUTPUT_STRIP_TRAILING_WHITESPACE
)

get_filename_component(ARM_TOOLCHAIN_DIR ${BINUTILS_PATH} DIRECTORY)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)

set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc)
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++)

set(CMAKE_OBJCOPY ${ARM_TOOLCHAIN_DIR}/${TOOLCHAIN_PREFIX}objcopy CACHE INTERNAL "objcopy tool")
set(CMAKE_SIZE_UTIL ${ARM_TOOLCHAIN_DIR}/${TOOLCHAIN_PREFIX}size CACHE INTERNAL "size tool")

set(CMAKE_FIND_ROOT_PATH ${BINUTILS_PATH})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
```

Save this code to a file named `arm-none-eabi-gcc.cmake`, next to your `CMakeLists.txt`.

## Build project from the command line

Let's now build this CMake project.

["Out-of-source" builds](https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#what-is-an-out-of-source-build) are probably the best way to work with CMake because it allows you to have several configurations at once (debug and release for instance).

I generally create a subdirectory in my project to generate CMake files:

```bat
λ mkdir cmake-build-debug
λ cd cmake-build-debug
```

We will ask CMake to generate MinGW Makefiles and :

* to use the `arm-none-eabi-gcc` toolchain file so that compiler for ARM is used (otherwise Windows compiler would be selected).
* to create a DEBUG configuration (otherwise some [weird default build type is used](https://blog.kitware.com/cmake-and-the-default-build-type/)).

```bat
λ cmake -G "MinGW Makefiles" -DCMAKE_TOOLCHAIN_FILE=../arm-none-eabi-gcc.cmake -DCMAKE_BUILD_TYPE=Debug ..
-- The C compiler identification is GNU 9.2.1
-- The CXX compiler identification is GNU 9.2.1
-- Check for working C compiler: C:/Program Files (x86)/GNU Tools ARM Embedded/9 2019-q4-major/bin/arm-none-eabi-gcc.exe
-- Check for working C compiler: C:/Program Files (x86)/GNU Tools ARM Embedded/9 2019-q4-major/bin/arm-none-eabi-gcc.exe -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: C:/Program Files (x86)/GNU Tools ARM Embedded/9 2019-q4-major/bin/arm-none-eabi-g++.exe
-- Check for working CXX compiler: C:/Program Files (x86)/GNU Tools ARM Embedded/9 2019-q4-major/bin/arm-none-eabi-g++.exe -- works
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- The ASM compiler identification is GNU
-- Found assembler: C:/Program Files (x86)/GNU Tools ARM Embedded/9 2019-q4-major/bin/arm-none-eabi-gcc.exe
-- Configuring done
-- Build files have been written to: D:/cmake_stm32//cmake-build-debug

λ ls
cmake_install.cmake  CMakeCache.txt  CMakeFiles/  Makefile
```

Finally, we build the application :

```bat
λ cmake --build . -- -j 4
mingw32-make[1]: Entering directory 'D:/cmake_stm32//cmake-build-debug'
mingw32-make[2]: Entering directory 'D:/cmake_stm32//cmake-build-debug'
Scanning dependencies of target nucleo-f413zh.out
mingw32-make[2]: Leaving directory 'D:/cmake_stm32//cmake-build-debug'
mingw32-make[2]: Entering directory 'D:/cmake_stm32//cmake-build-debug'
[  4%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c.obj
[  8%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c.obj
[ 12%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c.obj
[ 16%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c.obj
[ 20%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c.obj
[ 24%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c.obj
[ 28%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c.obj
[ 32%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c.obj
[ 36%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c.obj
[ 40%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c.obj
[ 44%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c.obj
[ 48%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c.obj
[ 52%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c.obj
[ 56%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c.obj
[ 60%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c.obj
[ 64%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_exti.c.obj
[ 68%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c.obj
[ 72%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c.obj
[ 76%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c.obj
[ 80%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Src/main.c.obj
[ 84%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Src/stm32f4xx_hal_msp.c.obj
[ 88%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Src/stm32f4xx_it.c.obj
[ 92%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Src/system_stm32f4xx.c.obj
[ 96%] Building ASM object CMakeFiles/nucleo-f413zh.out.dir/BSP/startup_stm32f413xx.s.obj
[100%] Linking C executable nucleo-f413zh.out
   text    data     bss     dec     hex filename
   7564      20    2668   10252    280c nucleo-f413zh.out
mingw32-make[2]: Leaving directory 'D:/cmake_stm32//cmake-build-debug'
[100%] Built target nucleo-f413zh.out
mingw32-make[1]: Leaving directory 'D:/cmake_stm32//cmake-build-debug'
```

*Et voilà*! `arm-none-eabi-size` outputs the same size as ST's generated Makefile. Hence it is pretty sure that both projects generate the same firmware.

Nevertheless the bin files generated by CMake and by STM32CubeMX's Makefile are not the same even if they have the same sizes:

```bat
λ diff BSP\build\BSP.bin cmake-build-debug\nucleo-f413zh.bin
Binary files BSP\build\BSP.bin and cmake-build-debug\nucleo-f413zh.bin differ

λ ls -l BSP\build\BSP.bin
-rw-r--r-- 1 z19100018 1049089 7584 avr.  11 16:26 'BSP\build\BSP.bin'

λ ls -l cmake-build-debug\nucleo-f413zh.bin
-rw-r--r-- 1 z19100018 1049089 7584 avr.  11 16:11 'cmake-build-debug\nucleo-f413zh.bin'
```

Explanation? Sections are probably laid out differently by the linker because we don't control the order of the files.

The same applies to the hex files.

## Conclusion

In this article we used ST's tools to generated the BSP for the NUCLEO-F413ZH and we create a CMake project to build the firmware.

We know have everything we know to continue our journey!

What's next? Well, we may play with C++, use an IDE such as Clion that supports CMake out-of-the-box, or have some unit tests for our code!

## P.S.

Since I wrote this article, I have discovered a great linker flag: `-Wl,--print-memory-usage`. Here is an example of its output, followed by the output of `arm-none-eabi-size`:

```
[ 14%] Linking CXX executable a.out
Memory region         Used Size  Region Size  %age Used
             RAM:      200384 B       320 KB     61.15%
           FLASH:      498468 B       640 KB     76.06%
   text	   data	    bss	    dec	    hex	filename
 496308	   2152	 198360	 696820	  aa1f4	a.out
```