How to build a firmware using Makefile?

How to build a firmware using Makefile?

Source: Dev.to

In embedded programming, we trust IDEs like STM32CubeIDE, Keil or so on. These IDEs give us just some buttons and UIs to build, flash, and debug the source code that we've written for a MCU. But, I think that every engineer that works on this field have to know that how these steps are made by hand. In this article, I will show building a firmware written for STM32F446RE MCU with just using a single Makefile. Below is the project directory: Firstly, which libraries do we need to have? arm-none-eabi-* tools I've put the all required libraries/scripts into /drivers directory. Please download these using: In here, HAL library is used to program the peripherals and CMSIS for ARM-Cortex itself. These are the most important libraries that we have to get. Apart from that, we also need some special files. First is the linker script. It includes the memory layout of microcontroller. Sometimes, it comes from above command. But if you cannot find it there, please install it externally. Another file is the startup code. It's written in Assembly language. As its name suggests, it's the first code that runs in microcontroller. Basically, it includes the Reset_Handler and a branch that jumps into main() function in your source code. Also system code includes the some generic and general definitions. After downloaded and explained the required ones, Let's write the Makefile step by step: 1. Toolchain definitions 2. MCU-specific definitions 3. Compiler and linker flags In here, we've defined some flags: -mthumb means that the firmware uses thumb (16-bit) instruction set. -mcpu=cortex-m4 means the ARM-Cortex series itself. -mfpu=fpv4-sp-d16 and -mfloat-abi=hard means that floating-point operations can be done in the firmware. In here, we specified the header files that are used in the source code. 5. Source and special files 6. Sorthands and build definitions In here, I mostly did the string operations and manipulations. But some points are interesting and need some explanations: Firstly, I've grouped the sources and libraries and converted the source file suffixes into object file formats. Also, I've created three total flags that will be used for compiling, assembling, and linking phases. In linker flags, I put the linker script. As you noticed, this step will be done after the compiling the source files. I also linked the some libraries. -lc is the standard C library. In firmware, we use often string manipulation functions (strlen(), strcat(), strcpy() or so on), snprint(), memset(), or similar ones. -lm is the math library as you know. -lnosys means that I'm just implementing the bare-metal firmware, there is no such as kernel or fully OS. Generally, we use the ELF file when flashing and debugging. ELF executable includes the both machine code, metadata, debug information, symbol table or similar stuffs. In contrast, BIN executable just includes the machine code. If you look at the size of both, you will use the ELF executable is much bigger! Lastly, run the Makefile: That's it ๐Ÿฅณ๐Ÿฅณ๐Ÿฅณ. You've built the both firmware.elf and firmware.bin. You are ready to flash the built firmware into microcontroller with this or similar command (over ST-Link connection): Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse COMMAND_BLOCK:
$ tree
.
โ”œโ”€โ”€ bin/
โ”œโ”€โ”€ drivers/
โ”‚ โ”œโ”€โ”€ CMSIS/
โ”‚ โ”œโ”€โ”€ STM32F446RETX_FLASH.ld
โ”‚ โ””โ”€โ”€ STM32F4xx_HAL_Driver/
โ”œโ”€โ”€ lib/
โ”œโ”€โ”€ Makefile
โ””โ”€โ”€ src/ โ”œโ”€โ”€ it.c โ”œโ”€โ”€ main.c โ”œโ”€โ”€ main.h โ””โ”€โ”€ peripheral.c Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
$ tree
.
โ”œโ”€โ”€ bin/
โ”œโ”€โ”€ drivers/
โ”‚ โ”œโ”€โ”€ CMSIS/
โ”‚ โ”œโ”€โ”€ STM32F446RETX_FLASH.ld
โ”‚ โ””โ”€โ”€ STM32F4xx_HAL_Driver/
โ”œโ”€โ”€ lib/
โ”œโ”€โ”€ Makefile
โ””โ”€โ”€ src/ โ”œโ”€โ”€ it.c โ”œโ”€โ”€ main.c โ”œโ”€โ”€ main.h โ””โ”€โ”€ peripheral.c COMMAND_BLOCK:
$ tree
.
โ”œโ”€โ”€ bin/
โ”œโ”€โ”€ drivers/
โ”‚ โ”œโ”€โ”€ CMSIS/
โ”‚ โ”œโ”€โ”€ STM32F446RETX_FLASH.ld
โ”‚ โ””โ”€โ”€ STM32F4xx_HAL_Driver/
โ”œโ”€โ”€ lib/
โ”œโ”€โ”€ Makefile
โ””โ”€โ”€ src/ โ”œโ”€โ”€ it.c โ”œโ”€โ”€ main.c โ”œโ”€โ”€ main.h โ””โ”€โ”€ peripheral.c COMMAND_BLOCK:
$ git clone --recurse-submodules https://github.com/STMicroelectronics/STM32CubeF4.git
$ sudo apt install gcc-arm-none-eabi Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
$ git clone --recurse-submodules https://github.com/STMicroelectronics/STM32CubeF4.git
$ sudo apt install gcc-arm-none-eabi COMMAND_BLOCK:
$ git clone --recurse-submodules https://github.com/STMicroelectronics/STM32CubeF4.git
$ sudo apt install gcc-arm-none-eabi CODE_BLOCK:
CC := arm-none-eabi-gcc
OBJCOPY := arm-none-eabi-objcopy
SIZE := arm-none-eabi-size
RM := rm -f
FILE := file Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
CC := arm-none-eabi-gcc
OBJCOPY := arm-none-eabi-objcopy
SIZE := arm-none-eabi-size
RM := rm -f
FILE := file CODE_BLOCK:
CC := arm-none-eabi-gcc
OBJCOPY := arm-none-eabi-objcopy
SIZE := arm-none-eabi-size
RM := rm -f
FILE := file CODE_BLOCK:
DEVICE_FAMILY := STM32F4xx
DEVICE_MODEL := STM32F446xx
DEVICE_VARIANT := STM32F446RETx Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
DEVICE_FAMILY := STM32F4xx
DEVICE_MODEL := STM32F446xx
DEVICE_VARIANT := STM32F446RETx CODE_BLOCK:
DEVICE_FAMILY := STM32F4xx
DEVICE_MODEL := STM32F446xx
DEVICE_VARIANT := STM32F446RETx CODE_BLOCK:
CORTEX_FLAGS := -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard
COMMON_FLAGS := -g3 -Os -ffunction-sections -fdata-sections -Wall
AS_FLAGS := -x assembler-with-cpp Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
CORTEX_FLAGS := -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard
COMMON_FLAGS := -g3 -Os -ffunction-sections -fdata-sections -Wall
AS_FLAGS := -x assembler-with-cpp CODE_BLOCK:
CORTEX_FLAGS := -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard
COMMON_FLAGS := -g3 -Os -ffunction-sections -fdata-sections -Wall
AS_FLAGS := -x assembler-with-cpp CODE_BLOCK:
MAIN_INC := ./src
CMSIS_INC := ./drivers/CMSIS/Include
CMSIS_DEV_INC := ./drivers/CMSIS/Device/ST/STM32F4xx/Include
HAL_INC := ./drivers/STM32F4xx_HAL_Driver/Inc
CMSIS_DSP_INC := ./drivers/CMSIS/DSP/Include Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
MAIN_INC := ./src
CMSIS_INC := ./drivers/CMSIS/Include
CMSIS_DEV_INC := ./drivers/CMSIS/Device/ST/STM32F4xx/Include
HAL_INC := ./drivers/STM32F4xx_HAL_Driver/Inc
CMSIS_DSP_INC := ./drivers/CMSIS/DSP/Include CODE_BLOCK:
MAIN_INC := ./src
CMSIS_INC := ./drivers/CMSIS/Include
CMSIS_DEV_INC := ./drivers/CMSIS/Device/ST/STM32F4xx/Include
HAL_INC := ./drivers/STM32F4xx_HAL_Driver/Inc
CMSIS_DSP_INC := ./drivers/CMSIS/DSP/Include CODE_BLOCK:
MAIN_SRC := $(wildcard ./src/*.c)
HAL_SRC := $(wildcard ./drivers/STM32F4xx_HAL_Driver/Src/*.c)
SYSTEM_SRC := ./drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c
STARTUP_CODE := ./drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc/startup_stm32f446xx.S
LINKER_SCRIPT := ./drivers/STM32F446RETX_FLASH.ld Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
MAIN_SRC := $(wildcard ./src/*.c)
HAL_SRC := $(wildcard ./drivers/STM32F4xx_HAL_Driver/Src/*.c)
SYSTEM_SRC := ./drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c
STARTUP_CODE := ./drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc/startup_stm32f446xx.S
LINKER_SCRIPT := ./drivers/STM32F446RETX_FLASH.ld CODE_BLOCK:
MAIN_SRC := $(wildcard ./src/*.c)
HAL_SRC := $(wildcard ./drivers/STM32F4xx_HAL_Driver/Src/*.c)
SYSTEM_SRC := ./drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c
STARTUP_CODE := ./drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc/startup_stm32f446xx.S
LINKER_SCRIPT := ./drivers/STM32F446RETX_FLASH.ld CODE_BLOCK:
DEFINES := -D$(DEVICE_FAMILY) -D$(DEVICE_MODEL) -D$(DEVICE_VARIANT) \ -DUSE_HAL_DRIVER SOURCES := $(MAIN_SRC) $(HAL_SRC) $(SYSTEM_SRC)
OBJECTS := $(notdir $(patsubst %.c,%.o,$(SOURCES))) startup_stm32f446xx.o
INCLUDES := -I$(MAIN_INC) -I$(CMSIS_DEV_INC) -I$(CMSIS_INC) -I$(HAL_INC) \ -I$(CMSIS_DSP_INC) CFLAGS := $(CORTEX_FLAGS) $(COMMON_FLAGS) $(DEFINES) $(INCLUDES)
AFLAGS := $(CORTEX_FLAGS) $(AS_FLAGS) $(DEFINES) $(INCLUDES)
LDFLAGS := $(CORTEX_FLAGS) -T $(LINKER_SCRIPT) \ -Wl,--gc-sections,--relax --specs=nano.specs --specs=nosys.specs \ -Wl,--start-group -lc -lm -lnosys -Wl,--end-group Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
DEFINES := -D$(DEVICE_FAMILY) -D$(DEVICE_MODEL) -D$(DEVICE_VARIANT) \ -DUSE_HAL_DRIVER SOURCES := $(MAIN_SRC) $(HAL_SRC) $(SYSTEM_SRC)
OBJECTS := $(notdir $(patsubst %.c,%.o,$(SOURCES))) startup_stm32f446xx.o
INCLUDES := -I$(MAIN_INC) -I$(CMSIS_DEV_INC) -I$(CMSIS_INC) -I$(HAL_INC) \ -I$(CMSIS_DSP_INC) CFLAGS := $(CORTEX_FLAGS) $(COMMON_FLAGS) $(DEFINES) $(INCLUDES)
AFLAGS := $(CORTEX_FLAGS) $(AS_FLAGS) $(DEFINES) $(INCLUDES)
LDFLAGS := $(CORTEX_FLAGS) -T $(LINKER_SCRIPT) \ -Wl,--gc-sections,--relax --specs=nano.specs --specs=nosys.specs \ -Wl,--start-group -lc -lm -lnosys -Wl,--end-group CODE_BLOCK:
DEFINES := -D$(DEVICE_FAMILY) -D$(DEVICE_MODEL) -D$(DEVICE_VARIANT) \ -DUSE_HAL_DRIVER SOURCES := $(MAIN_SRC) $(HAL_SRC) $(SYSTEM_SRC)
OBJECTS := $(notdir $(patsubst %.c,%.o,$(SOURCES))) startup_stm32f446xx.o
INCLUDES := -I$(MAIN_INC) -I$(CMSIS_DEV_INC) -I$(CMSIS_INC) -I$(HAL_INC) \ -I$(CMSIS_DSP_INC) CFLAGS := $(CORTEX_FLAGS) $(COMMON_FLAGS) $(DEFINES) $(INCLUDES)
AFLAGS := $(CORTEX_FLAGS) $(AS_FLAGS) $(DEFINES) $(INCLUDES)
LDFLAGS := $(CORTEX_FLAGS) -T $(LINKER_SCRIPT) \ -Wl,--gc-sections,--relax --specs=nano.specs --specs=nosys.specs \ -Wl,--start-group -lc -lm -lnosys -Wl,--end-group CODE_BLOCK:
FIRMWARE_ELF := firmware.elf
FIRMWARE_BIN := firmware.bin Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
FIRMWARE_ELF := firmware.elf
FIRMWARE_BIN := firmware.bin CODE_BLOCK:
FIRMWARE_ELF := firmware.elf
FIRMWARE_BIN := firmware.bin CODE_BLOCK:
.PHONY: all all: @echo "-------------------------------------" @echo "----- Building the source files -----" @echo "-------------------------------------" @$(CC) $(AFLAGS) -c $(STARTUP_CODE) @$(CC) $(CFLAGS) -c $(SOURCES) @echo "\n------------------------------------" @echo "----- Linking the object files -----" @echo "------------------------------------" @$(CC) $(LDFLAGS) $(OBJECTS) -o $(FIRMWARE_ELF) @$(OBJCOPY) -O binary $(FIRMWARE_ELF) $(FIRMWARE_BIN) @$(RM) $(OBJECTS) @echo "\n-------------------------------------" @echo "----- The firmware memory usage -----" @echo "-------------------------------------" @$(SIZE) $(FIRMWARE_ELF) @echo "\n-------------------------------------" @echo "----- The firmware binary format ----" @echo "-------------------------------------" @$(FILE) $(FIRMWARE_ELF) Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
.PHONY: all all: @echo "-------------------------------------" @echo "----- Building the source files -----" @echo "-------------------------------------" @$(CC) $(AFLAGS) -c $(STARTUP_CODE) @$(CC) $(CFLAGS) -c $(SOURCES) @echo "\n------------------------------------" @echo "----- Linking the object files -----" @echo "------------------------------------" @$(CC) $(LDFLAGS) $(OBJECTS) -o $(FIRMWARE_ELF) @$(OBJCOPY) -O binary $(FIRMWARE_ELF) $(FIRMWARE_BIN) @$(RM) $(OBJECTS) @echo "\n-------------------------------------" @echo "----- The firmware memory usage -----" @echo "-------------------------------------" @$(SIZE) $(FIRMWARE_ELF) @echo "\n-------------------------------------" @echo "----- The firmware binary format ----" @echo "-------------------------------------" @$(FILE) $(FIRMWARE_ELF) CODE_BLOCK:
.PHONY: all all: @echo "-------------------------------------" @echo "----- Building the source files -----" @echo "-------------------------------------" @$(CC) $(AFLAGS) -c $(STARTUP_CODE) @$(CC) $(CFLAGS) -c $(SOURCES) @echo "\n------------------------------------" @echo "----- Linking the object files -----" @echo "------------------------------------" @$(CC) $(LDFLAGS) $(OBJECTS) -o $(FIRMWARE_ELF) @$(OBJCOPY) -O binary $(FIRMWARE_ELF) $(FIRMWARE_BIN) @$(RM) $(OBJECTS) @echo "\n-------------------------------------" @echo "----- The firmware memory usage -----" @echo "-------------------------------------" @$(SIZE) $(FIRMWARE_ELF) @echo "\n-------------------------------------" @echo "----- The firmware binary format ----" @echo "-------------------------------------" @$(FILE) $(FIRMWARE_ELF) COMMAND_BLOCK:
$ make Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
$ openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "program firmware.elf verify reset exit" Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
$ openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "program firmware.elf verify reset exit" COMMAND_BLOCK:
$ openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "program firmware.elf verify reset exit" - HAL library
- CMSIS library
- Linker script
- Startup/system code
- arm-none-eabi-* tools - -mthumb means that the firmware uses thumb (16-bit) instruction set.
- -mcpu=cortex-m4 means the ARM-Cortex series itself.
- -mfpu=fpv4-sp-d16 and -mfloat-abi=hard means that floating-point operations can be done in the firmware. - Firstly, I've grouped the sources and libraries and converted the source file suffixes into object file formats.
- Also, I've created three total flags that will be used for compiling, assembling, and linking phases.
- In linker flags, I put the linker script. As you noticed, this step will be done after the compiling the source files. I also linked the some libraries. -lc is the standard C library. In firmware, we use often string manipulation functions (strlen(), strcat(), strcpy() or so on), snprint(), memset(), or similar ones. -lm is the math library as you know. -lnosys means that I'm just implementing the bare-metal firmware, there is no such as kernel or fully OS.