DEV Community

Cover image for CMake on STM32 | Episode 1: the beginning
Pierre Gradot
Pierre Gradot

Posted on • Updated on

CMake on STM32 | Episode 1: the beginning

This article is the first of a series dealing with CMake and STM32 microcontrollers. 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:

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 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 with the 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 and arm-none-eabi-gcc toolchain. Both software must be available from the PATH :

λ 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.
Enter fullscreen mode Exit fullscreen mode

Other software that you may need or find useful:

  • mingw64 is a Windows port of the GNU Compiler Collection as the GNU Binutils. It is helpful to build Makefiles.
  • STM32CubeMX 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) for the Nucleo board.
  • STM32 ST-LINK utility 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 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

Click Start Project on the top right corner of the screen and accept the default modes:

stm32cubemx_default_modes

Don't modify the MCU configuration, simply go the Project Manager to configure the Project Settings :

stm32cubemx_project_manager

Finally, click on Generate code :

stm32cubemx_output

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

λ 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
Enter fullscreen mode Exit fullscreen mode

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:

int main(void)
{
  // ...

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

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
Enter fullscreen mode Exit fullscreen mode

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_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)
Enter fullscreen mode Exit fullscreen mode

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:

$<$<CONFIG:Debug>:-Og>
Enter fullscreen mode Exit fullscreen mode

This is a generator-expression. 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!

Toolchain file

CMake assumes it is cross-compiling when a toolchain file is specified. For ARM GCC, I used this file:

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)
Enter fullscreen mode Exit fullscreen mode

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 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:

λ mkdir cmake-build-debug
λ cd cmake-build-debug
Enter fullscreen mode Exit fullscreen mode

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).
λ 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
Enter fullscreen mode Exit fullscreen mode

Finally, we build the application :

λ 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'
Enter fullscreen mode Exit fullscreen mode

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:

λ 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'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Top comments (24)

Collapse
 
elmot profile image
Ilia Motornyi

I have just run into this article. Awesome one, I really like your approach.
But I have a question - why don't you use our STM32CubeMX support? Most of your CMakeLists.txt can be written automatically with that.

Collapse
 
pgradot profile image
Pierre Gradot • Edited

Thanks!

The reason is because this serie is dedicated to CMake on MCU and I try to be as agnostic as possible from other tools than CMake itself.

The purpose is for people to understand how to write the CMakeLists.txt. Asking a tool to generate it is not exactly the best approach in my opinion.

Furthermore, CLion is not a free software (even if EAP might be a possible workaround). Even if I talked about it in my second episode, I didn't want it to a central piece of my articles.

Also, I talked about STM32 because I had to choose one MCU to write code examples. Using a tool that is dedicated to these families seemed to be even less agnostic.

To be honest, I haven't tried the STM32CubeMX support since it has been integrated directly in CLion. We used the 3rd party plugin, a long time ago. But I remember we didn't used the generated CMakeLists.txt for our project.

Collapse
 
elmot profile image
Ilia Motornyi

Ok, thanks, I fully understand your reasons.
In fact that generated CMakeLists.txt is quite close to your one, the same ideas behind. And it's possible that "3rd party plugin" was my old hobby project :-).

Some small notes about CMakeLists.txt:

  • Toolchain file looks a bit overcomplicated thingy, I prefer(ed) to keep toolchain information in the same CMakeLists.txt(important note: above project clause). On the other hand, nowadays we have CMake presets feature, which may make the toolchain file a bit more user-friendly

  • You do not need to use arm-none-eabi-size utility, the linker can print the same information, CMake clause add_link_options(-Wl,--print-memory-usage) does the trick

  • If, at some point, you want to make a project with dual-core STM32H7xx, I have a template for CMakeLists.txt for this case, not tied to CLion, but extracting project information directly from STM32CubeMX files.

  • I like your 3rd episode, that's a great idea to hide all of false-positive compiler warnings coming from libraries, that helps to keep actual project code error-free.

  • I also like your C++ episodes, embedded software have to move towards stricter and safer languages.

Thread Thread
 
pgradot profile image
Pierre Gradot

I guess it was! Thanks a lot for this plugin, it was the key to move from Eclipse to CLion. And moving to CMake & CLion was one of the greatest choices we made on this project!

About these small notes :)

  • Using a tool chain seems to be the CMake way to cross-compile, that's why I show it here. If your project is solely made to generate a firmware for a MCU, then setting the toolchain directly in the CMakeLists.txt seems indeed simpler and as efficient. But once you want to also generate a PC-based software, for instance for unit tests and simulators, the toolchain file seems the obvious way. I wanted to make an episode about PC-executed unit tests but never took the time (for the moment?). I wasn't aware of CMake presets, I am going to check them! Thanks!

  • You have missed the PS in my article ;) I wasn't aware of this option when I wrote the article. Must simpler indeed! And in the end it's bad for this article: it shows how to call an external software as a post-build step.

  • Thanks for sharing. I hope I will use such a MCU one day.

  • Thanks!

  • Nice to see other people willing for the embedded world to move to C++. I have been using only C++ for the last 5 years. I will never go back to raw C. I have been looking for the Rust a little, but didn't try to run it on MCU for the moment. Maybe one day XD

Thread Thread
 
elmot profile image
Ilia Motornyi • Edited

Rust

Feel free to use my prototype for that. It worked 2 yrs ago:)

I wanted to make an episode about PC-executed

And I am now thinking about an article about emulator-running unit tests. A prototype works. One day.... You know :)

Thread Thread
 
pgradot profile image
Pierre Gradot

I will have a look at it, thank you :)

Yes, I know...

I have tried out CMake presets and they are amazing. I still have a problem to load MVSC presets in CLion. I have a configure preset like this one:

     {
            "name": "mvsc",
            "inherits": "base",
            "description": "Base config for simulator with MSVC",
            "hidden": true,
            "generator": "CodeBlocks - NMake Makefiles"
        },
Enter fullscreen mode Exit fullscreen mode

From the command-line, I must call vcvarsall.bat to configure the compiler before calling cmake --preset=a-preset-that-inherits-from-this-one.

In CLion, do you know how I can call this script when I use the action "Load CMake presets"?

Thread Thread
 
elmot profile image
Ilia Motornyi

The answer from my colleague:

You actually don't need to run vcvarsall.bat manually. CLion will take care of it. You need to:
configure Visual Studio toolchain in CLion
specify this toolchain in your preset using this JSON fragment:

"vendor": {
    "jetbrains.com/clion": {
      "toolchain": "<toolchain-name>"
    }
}
Enter fullscreen mode Exit fullscreen mode

Here's a more detailed instruction: jetbrains.com/help/clion/cmake-pre...

Thread Thread
 
pgradot profile image
Pierre Gradot • Edited

That's even better than asking the support directly! Thanks 😁

It does work if I add the key to the derived preset, but it does work on the base preset. It seems that the "vendor" map is not inherited.

When I read the documentation, it looks like a bug to me:

vendor

An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, it should follow the same conventions as the root-level vendor field. If vendors use their own per-preset vendor field, they should implement inheritance in a sensible manner when appropriate.

Thread Thread
 
elmot profile image
Ilia Motornyi

Yeah, you are right. Now there is a ticket about it
youtrack.jetbrains.com/issue/CPP-2...

Thread Thread
 
pgradot profile image
Pierre Gradot

That's perfect :)

Collapse
 
gberthiaume profile image
G. Berthiaume

I was not expecting to see such a high quality embedded post on dev.to.
Your Cmake is great:
[X] Using the target_* API
[X] Using toolchain file
[X] Almost no set() variable
[X] No glob
[X] Generator expression
[X] Support windows and posix
[X] add_custom_command

I'd like to see more advanced compiler flag configuration. Also how about adding unit tests? Or supporting gdb?

Merci et bonne journée!

Collapse
 
pgradot profile image
Pierre Gradot • Edited

Woh! This comment made my day! ❤️

For more advanced compiler flags: have you read other episodes in the series? See the table at the beginning of this article.

I have planned to write another episode about running unit tests on your computer. I don't know when I will do it.

I have not planned to talk about GDB:

  • I don't see any particular reason to call it from the command line with CMake in particular (it would be simply a target to debug the software)
  • CLion allows to debug the application with GDB.

Bonne journée :)

Collapse
 
jerrylogansquare profile image
Jerry Morrow

thanks for posting this.
we're going to use cmake for SiLabs ARM project

I upgraded Qt to version 6, and have loved that it uses cmakelists.txt as its project file. That has been awesome

Collapse
 
wastus profile image
Bernhard Weller

Thanks for this, I tried to follow other tutorials, but those were focused on a lot of other things than compiling and linking so this was a very good start to finally get something built.

I use globbing, but that comes down to personal preference (it has its pros and cons).

Not quite sure why that happens, but I got an extension of .elf so I used:
set_target_properties(${EXECUTABLE} PROPERTIES
SUFFIX ".out"
)
To actually get an .out file.

Now I just have to adjust some things, but it's looking much better.

Collapse
 
pgradot profile image
Pierre Gradot

You're welcome :)

Globbing has its advantages, no doubts! My experience proved that you end up excluding some files from the result list and you get back to list your files by hand ^^

Nevertheless, they are good for instance to get a set of generated files when these numbers of files and their names is not known in advance.

I believe *.elf is the default extension for GCC generated software. Nice trick ;)

Collapse
 
dancollins_171 profile image
Dan Collins

This post was awesome! I've made many STM32 projects with SCons and with GNU Make, and this shows how easy it could be for me to transition!

CMake is great for me, as it can generate outputs for language servers which can then hook into emacs (or Visual Studio Code if that's your thing). I think this will allow me to have a bit more of a modern feel :D

I'll be reading your follow-ups next!

Collapse
 
hiten4612 profile image
hiten4612

I get below errors after apply " cmake --build . -- -j 4"( Win and ubuntu )

D:\Dev-Tools\cmakef4\startup_stm32f413xx.s: Assembler messages:
D:\Dev-Tools\cmakef4\startup_stm32f413xx.s:1: Error: junk at end of line, first unrecognized character is -'
D:\Dev-Tools\cmakef4\startup_stm32f413xx.s:2: Error: bad size 0 in type specifier
D:\Dev-Tools\cmakef4\startup_stm32f413xx.s:2: Error: bad instruction
startup_stm32f413xx.s '
mingw32-make.exe[2]: *** [CMakeFiles/nucleo-f413zh.out.dir/startup_stm32f413xx.s.obj] Error 1
mingw32-make.exe[1]: *** [CMakeFiles/nucleo-f413zh.out.dir/all] Error 2
mingw32-make.exe: *** [all] Error 2

Collapse
 
pgradot profile image
Pierre Gradot

This looks weird.... The errors happen ion lines 1 and 2 of the file.

Is the file correct?
What version of arm-none-eabi-gcc do you use?
Does the Makefile generated by STM32CubeMX work?

You can try the following command cmake --build . --verbose -- -j 4 to get more details from the build system.

Collapse
 
hiten4612 profile image
hiten4612 • Edited

->Yes the file is correct and project generated by stm32cubemx
-> arm-none-eabi-gcc version
arm-none-eabi-gcc.exe (GNU Tools for Arm Embedded Processors 8-2019-q3-update) 8.3.1 20190703 (release) [gcc-8-branch revision 273027]
Copyright (C) 2018 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.

-> cmake --build . --verbose -- -j 4
[ 4%] Building ASM object CMakeFiles/nucleo-f413zh.out.dir/startup_stm32f413xx.s.obj
D:\Dev-Tools\gcc-arm-8\bin\arm-none-eabi-gcc.exe -DSTM32F413xx -DUSE_HAL_DRIVER -ID:\Dev-Tools\cmakef4\Core\Inc -ID:\Dev-Tools\cmakef4\Drivers\STM32F4xx_HAL_Driver\Inc -ID:\Dev-Tools\cmakef4\Drivers\CMSIS\Device\ST\STM32F4xx\Include -ID:\Dev-Tools\cmakef4\Drivers\CMSIS\Include -g -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -fdata-sections -ffunction-sections -Wall -Og -o CMakeFiles\nucleo-f413zh.out.dir\startup_stm32f413xx.s.obj -c D:\Dev-Tools\cmakef4\startup_stm32f413xx.s
D:\Dev-Tools\cmakef4\startup_stm32f413xx.s: Assembler messages:
D:\Dev-Tools\cmakef4\startup_stm32f413xx.s:1: Error: junk at end of line, first unrecognized character is -'
D:\Dev-Tools\cmakef4\startup_stm32f413xx.s:2: Error: bad size 0 in type specifier
D:\Dev-Tools\cmakef4\startup_stm32f413xx.s:2: Error: bad instruction
startup_stm32f413xx.s '
mingw32-make.exe[2]: *** [CMakeFiles/nucleo-f413zh.out.dir/startup_stm32f413xx.s.obj] Error 1
mingw32-make.exe[2]: Leaving directory D:/Dev-Tools/cmakef4/cmake-build-debug'
mingw32-make.exe[1]: *** [CMakeFiles/nucleo-f413zh.out.dir/all] Error 2
mingw32-make.exe[1]: Leaving directory
D:/Dev-Tools/cmakef4/cmake-build-debug'

Thread Thread
 
pgradot profile image
Pierre Gradot

This is quite an old version of GCC.

To be clear: the Makefile generated by stm32cubemx does build the project, right?

Collapse
 
thebruce87m profile image
thebruce87m

Not an expert, but stumbled about some errors in my own project to find some workarounds below.

For anyone else having problems with "undefined reference to ... _close, _lseek" etc, remove the "enable_language(C ASM)" line.

This seems to be an issue of libg_nano.a vs liba_nano.a - with the line in it seems to use libg_nano.a and you get the linker errors.

Alternatively, changing "-specs=nano.specs" to "-specs=nosys.specs".

Collapse
 
pgradot profile image
Pierre Gradot

Errors like these are indeed caused by variations in the standard libraries. I am not familiar with specs files, so fixing them is a bit like coin-flipping for me ;)

Collapse
 
pmarinova profile image
Plamena Marinova

Hello, great series! I'm a bit confused about the usage of CMAKE_FIND_ROOT_PATH inside the toolchain file. It is set to ${BINUTILS_PATH} which points to gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gcc.exe. I would expect to find the toolchain library paths instead?

Collapse
 
pgradot profile image
Pierre Gradot • Edited

Glad you enjoyed the series :)

To be very honnest, I didn't write the toolchain myself. We have been using this file since 2018 and I don't remember how we got it.

Your comment seems pretty legit. I believe the toolchain file is wrong.

After some researches and experiments, I was able to reduce the toolchain file and the projects (both the sample project I am using for this series and the professional projects at my office) keep building successfully.

Here my shrinked file:

set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR ARM)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)

set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_ASM_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
Enter fullscreen mode Exit fullscreen mode

Since I never write toolchain files, I may be missing some points. For instance, I know that CMAKE_SYSTEM_VERSION should be set:

CMAKE_SYSTEM_NAME may be set explicitly when first configuring a new build tree in order to enable cross compiling. In this case the CMAKE_SYSTEM_VERSION variable must also be set explicitly.

But I have no idea of the value I should give it... The official toolchain file samples don't always set it.