You know that for many of the projects we post, we use the fairly simple STM32G0B1RE Nucleo board, and we prepare a template that easily compiles its respective HAL library. However, we understand that not everyone has the same board model. Fear not! In the following steps, we will teach you how to migrate our template project to any STM32 board out there—just follow these easy instructions. Template can be clone from here
$ git clone https://github.com/ModularMX/template-g0.git
First things first, you must understand that the STM32 microcontroller family is divided into several sub-families. For each of these families, ST has developed the respective HAL and LL driver library, called STM32Cube. Locate the microcontroller family of the board you own and download the corresponding Cube library.
The second important thing to keep in mind is the template project structure, which is as follows: There are certain files we need to replace, and some others we need to adjust. Two of the most important directories are cmsisg0
and halg0
, which correspond to the STM32CubeG0 library because we use an STM32G0 part number in our board.
template-g0
├── app
│ ├── bsp.h
│ ├── ints.c
│ ├── main.c
│ ├── msps.c
│ └── stm32g0xx_hal_conf.h
├── cmsisg0
│ ├── core
│ ├── registers
│ └── startups
├── halg0
│ ├── Inc
│ └── Src
├── linker.ld
├── makefile
├── misra.json
├── project.yml
├── README.md
├── STM32G0B1.svd
└── test
├── support
└── test_dummy.c
11 directories, 12 files
Loyal to my philosophy of learning by doing, I’m going to show you how to perform a migration to the Nucleo F446RE board. That’s why I’ve already made a duplicate of my template and renamed it to template-f4
. I also have the corresponding STM32CubeF4 library on my computer. Additionally, I renamed the cmsisg0
folder to cmsisf4
and halg0
to half4
, and I removed all files inside the corresponding subfolders of these directories. Lastly, I removed the files stm32g0xx_hal_conf.h
, linker.ld
, and STM32G0B1.svd
.
template-f4
├── app
│ ├── bsp.h
│ ├── ints.c
│ ├── main.c
│ └── msps.c
├── cmsisf4
│ ├── core (empty directory)
│ ├── registers (empty directory)
│ └── startups (empty directory)
├── half4 (empty directory)
├── makefile
├── misra.json
├── project.yml
├── README.md
└── test
├── support
└── test_dummy.c
Extracting Drivers from the Library
It's time to copy the files we need from the STM32Cube library. But why not use the library as it is? Well, there are just too many files we won't need. To better understand what we're doing, we should clean things up. The simpler we keep it, the easier it is to understand.
CMSIS Files
The first set of files to copy are the macros and definitions that will help us access internal CPU registers and peripherals, such as the NVIC and the SysTick Timer. These files are located in the Core folder and are part of what ARM refers to as CMSIS. From the Cube library, copy all the files from this directory.
STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Core/(all files)
and place them in the template directory.
template-f4/cmsisf4/core
There are certain files you can remove because they aren’t needed for the F4 family ( though this is optional and doesn’t affect anything in the end). For example, all the files starting with "core_
" can be removed, except for core_cm4.h, as it is specific to our microcontroller, which uses a Cortex-M4 CPU. There are a few other files you could remove, but let’s leave it at that.
Next, we’ll move on to the peripheral definition register structures. These are really helpful if, for any reason, you need to access the registers directly. In the following folder, you'll find the register definitions for all the F4 part numbers. From the Cube library, copy all the files from this directory.
STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include/(all files)
and place them in the template directory.
template-f4/cmsisf4/registers
Last but not least, we need to copy the files containing the startup routines to initialize the vector table, memory, and all the necessary code before calling the main
function. Since these files are written in assembly, they are available for three different compilers. We will only select the ones for GCC (if you want to use another compiler, you know what to do). From the Cube library, copy the following files:
STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc/(all files)
STM32Cube_FW_F4_V1.28.0/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c
and place them in the template directory.
template-f4/cmsisf4/startups
HAL Files
Next are the actual pieces of code we will use to configure and control the microcontroller peripherals: the "HAL drivers." From the Cube library, copy the following directories:
STM32Cube_FW_F4_V1.28.0/Drivers/STM32F4xx_HAL_Driver/(both folders, Inc and Src)
and place them in the template directory.
template-f4/half4
Configuration and linker files
Last but not least copy the file stm32f4xx_hal_conf.h
from here
STM32Cube_FW_F4_V1.28.0/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_conf_template.h
STM32Cube_FW_F4_V1.28.0/Projects/STM32F446ZE-Nucleo/Templates/STM32CubeIDE/STM32F446ZETX_FLASH.ld
to here, notice how we rename each file (renaming the linker file is optional, but is mandatory to rename the _tamplate
part form stm32f4xx_hal_conf_template.h
file )
template-f4/app/stm32f4xx_hal_conf.h
template-f4/linker.ld
One more file we need is our part number SVD which is a XML file with all the register information to be later use by our debugger, you can download the corresponding file from here https://www.keil.arm.com/devices/ or here https://github.com/modm-io/cmsis-svd-stm32 i my particular case i need the STM32F446.svd
The makefile
There are several things we need to change because we are compiling a complete (but similar) set of files for a different machine. Locate the line where it says 'Files to compile.' Here, you'll find the minimum set of files you need to compile to simply blink a single LED, which serves as the starting point for most of your projects. Just change them from the G0 family to the one you are using ( F4 in my particular case ). The startup file is more or less an exception since you also need to specify the closest file name for the chip you're using. Just take a look at the startup routine files in the directory template-f4/cmsisf4/startups
# Files to compile
SRCS = main.c ints.c msps.c startup_stm32f446xx.s system_stm32f4xx.c
SRCS += stm32f4xx_hal.c stm32f4xx_hal_cortex.c stm32f4xx_hal_rcc.c stm32f4xx_hal_flash.c
SRCS += stm32f4xx_hal_gpio.c
We need to specify a global define to set the part number we are using. This will help the HAL library activate certain code that is only applicable to the part number we're using. For instance, in my case, the symbol is STM32F446xx
, and our part number is STM32F446ZE
# Global symbols (#defines)
SYMBOLS = -DSTM32H573xx -DUSE_HAL_DRIVER
To find the information you can take a look at the corresponding STM32Cube library user manual or look the define that most approach our part number, or you can see at template-f4/cmsisf4/registers/stm32f4xx.h
file
# directories with source files to compiler (.c y .s)
SRC_PATHS = app
SRC_PATHS += cmsisf4/startups
SRC_PATHS += half4/Src
# directories with header files
INC_PATHS = app
INC_PATHS += cmsisf4/core
INC_PATHS += cmsisf4/registers
INC_PATHS += half4/Inc
One of the most important parts is that the compiler's -mcpu
flag indicates the CPU to compile our code for. You need to figure out which ARM CPU corresponds to the microcontroller you're using. In my case, the F4 family comes with a Cortex-M4 CPU and also includes a floating-point unit, so I can switch from soft to hard with the -mfloat-abi
flag. You can find more information about this and other ARM flags here ARM Options (Using the GNU Compiler Collection (GCC))
TOOLCHAIN = arm-none-eabi
CPU = -mcpu=cortex-m4 -mthumb -mfloat-abi=hard
The following targets are optional and depends on which debugger you are using, but in case you are planning using OpenOCD and gdb through commands or even later to do some testing with python you need to change the following lines according to family or part number your board comes with. This is well explained in here Enter OpenOCD
#---flash the image into the mcu-------------------------------------------------------------------
flash :
openocd -f board/st_nucleo_f4.cfg -c "program Build/$(TARGET).hex verify reset" -c shutdown
#---open a debug server conection------------------------------------------------------------------
open :
openocd -f board/st_nucleo_f4.cfg
# JLinkGDBServer -if SWD -device stm32f446ze -nogui
Last but not least in the file just change the line below form bsp.h file according to the stm32 familly tyou need, in my case is the F4, then I change to
#include <stm32f4xx.h>
To test everything is working just build with make
$ make