Post

STM32 CMake Template

A short overview and some documentation of my CMake build script for working with STM32.

Preface

As I move more into the embedded side of programming, I thought it prevalent that I start to move away from tools like the Arduino IDE and PlatformIO and step into the world of Makefile and CMake. Due to the wealth of information available on the internet surrounding CMake and STM32, I will only provide a brief overview here along with some of the problems I encountered during the setup process and how I solved them.

ARM-GCC

In order to compile ARM code on an x86 windows machine, it is necessary to use the arm toolchain for windows and the arm-none-eabi-gcc linker. Since a this linker must be used with the standard GCC compiler, a CMake configuration file must be created in order to tell CMake to use a the correct toolchains. In my configuration, I called this file gcc-arm-none-eabi.cmake. The contents of the file are listed below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# ARM GCC Toolchain
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(TOOLCHAIN_PREFIX arm-none-eabi-)
## CFLAGS
set(CMAKE_CFLAGS
"-fdata-sections -ffunction-sections -Wl,--gc-sections")

set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc ${CMAKE_CFLAGS} ${CMAKE_LDFLAGS})
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy)
set(CMAKE_SIZE ${TOOLCHAIN_PREFIX}size)
set(CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}objdump)

set(CMAKE_EXECUTABLE_SUFFIX_ASM ".elf")
set(CMAKE_EXECUTABLE_SUFFIX_C ".elf")
set(CMAKE_EXECUTABLE_SUFFIX_CXX ".elf")

set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)

The most important part of this file is the last line. This tells CMake that the binary produced by the linker is not designed for, and will therefor not run, on the host system. When this line is not included, CMake will not compile the code, claiming that the compiler is broken. This was one of the bigger headaches when trying to set up this environment so do not forget to include it.

CMake Configuration

Most of the CMake configuration is the same as if it were compiling a standard x86 executable. Where the ARM CMake file differs is in the compile options. In order to correctly compile the binary for the correct ARM processor, the MCU compilation parameters must be set up.

1
2
3
4
5
6
7
8
9
set(MCU_FAMILY STM32F4xx)
set(MCU_MODEL STM32F446xx)
set(CPU_PARAMETERS
    -mcpu=cortex-m4
    -mthumb
    # FPU enable code must be written before it can be used
    #-mfpu=fpv4-sp-d16
    #-mfloat-abi=hard
    )

Note that in this file, the compiler parameters for the floating point unit are commented out. This is because if floating point numbers are used with these parameters enabled, but the FPU has not been turned on within the RCC register, the CPU will crash. The rest of the compile and linker options are fairly standard for ARM Cortex-M4 processors. The only thing that differs in my configuration is that I removed nearly all of the compilation optimization, and added an option to include printf functionality for floats.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
target_compile_options(${EXECUTABLE} PRIVATE
    ${CPU_PARAMETERS}
    -Wall
    -Wextra
    -g
    -fmessage-length=0
    -ffunction-sections
    -fdata-sections
    --specs=nano.specs 
    )

target_link_options(${EXECUTABLE} PRIVATE
    -T${MCU_LINKER_SCRIPT}
    ${CPU_PARAMETERS}
    #-nostartfiles
    -Wall
    --specs=nano.specs
    -lgcc
    -Wl,--gc-sections
    -Wl,-L./ld
    # Printf floats can be removed in production code
    -Wl,-u_printf_float
    -Wl,-Map=${PROJECT_NAME}.map
    -Wl,--print-memory-usage
    )

The last step in the CMake configuration file is to add a command to generate the hex and binary files used by the firmware upload tool. They can be added via the following lines:

1
2
3
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
    COMMAND ${CMAKE_OBJCOPY} -O ihex $<TARGET_FILE:${EXECUTABLE}> firmware.hex
    COMMAND ${CMAKE_OBJCOPY} -O binary $<TARGET_FILE:${EXECUTABLE}> firmware.bin)

Uploading + Makefile

In order to upload the code, I chose to use a Makefile to simplify the commands needed. This way, its both easier to type them, and there’s no risk of forgetting them. In my Makefile, I included two possible upload methods: ST-Flash and GDB. ST-Flash uses ST’s proprietary command line tool to interface with their ST-Link in order to upload the code. GDB is used when uploading with GDB compatible debuggers like the BlackMagic probe. I’ve included the two commands below:

1
2
3
4
5
6
7
8
9
10
11
# must run "run" command or restart target to start program
BlackMagic-flash:
	gdb -ex 'target extended-remote $(COMPORT)' \
	-ex 'set mem inaccessible-by-default off' \
	-ex 'monitor swdp' \
	-ex 'attach 1' \
	-ex 'load firmware.elf' \
	-q

st-flash: build/firmware.bin
	./Tools/ST-LINK_CLI.exe -P build/firmware.bin 0x08000000 -V -RST

It is important to note that this Makefile will only work for windows users, and assumes that the ST-Link upload tool exists inside of a tools folder within the project directory.

Required Tools

In order to build the CMake file on your machine, you will need to install the following tools:

  • GNU GCC
  • ARM Toolchain
  • make
  • VS Code (or other IDE)
    • CMake Extension

Future Revisions

Because the current revision of this template requires all contributors to set up their own development environment, future revisions could move to using a docker container to alleviate this problem.

Source File

The source files is available to clone from my Gentry (Embedded C) GitHub Repository

This post is licensed under CC BY 4.0 by the author.