Serious Business STM32 Development

Last updated: July 3, 2024, 10:20 p.m.


This blog post is meant to cover a bunch of different things about developing large, complex and sophisticated but highly reliable applications on STM32 microcontrollers. Lots of the stuff I'm writing about will have applications on other embedded systems which use FreeRTOS but I'm sticking to the ecosystem I know best. I've done this stuff several times over for various employers over the last decade but it's all locked away in private repositories and each time I feel like I'm reinventing the wheel so I'm going to try and build it all up from scratch on my own time without reference to any proprietary code.

The code I'm writing in this post will all be run on an STM32F429I-DISC1. This board is getting on a bit now but it's still available pretty cheaply and it has an external SDRAM adding 8MB of memory to the system which adds a bit more flexibility and extra requirements for memory segmentation.

Getting started

To start off with I've used the latest version of STM32 CubeMX (6.12) and I've used the board selector option to start with the default configuration for the Discovery board I'm using. I've made a few tweaks to the project generated from this template:

  1. I've disabled the LTDC peripheral (not using it for this project) and un-assigned the pins
  2. Similarly I've disabled I2C3 which is the touch screen controller
  3. I've disabled the USB Host middleware and the USB_OTG_HS peripheral
  4. I've configured the clock to run at 180MHz sysclk, basically everything at max speed
  5. In "Middleware and Software Packs" I've disabled the FREERTOS component, there are a bunch of issues with the FreeRTOS that ships from ST and it's easiest to just leave it out entirely
  6. Finally I set the Toolchain/IDE to the new CMake option in the Project Manager tab

Running generate code will make the project framework and provide all the necessary linker scripts and assembly code startup stuff.

Once I had the framework generated I checked it actually built using CMake and committed those base files to the repository, to see this first step see the first commit on GitHub.

Adding FreeRTOS

Now I'm going to add FreeRTOS to the project. Earlier I specifically didn't use the one from STM32CubeMX, that's because it won't allow us to use the Memory Protection Unit features that I want to enable. The STM23Cube bindings use the CMSIS OS bindings which don't support the MPU port of FreeRTOS. There's an option for enabling the MPU but it's permanently disabled in CubeMX.

First of all I add a submodule to the project to pull in the FreeRTOS kernel from the GitHub repository. (If the project is going to have long term support requirements consider making a clone of the FreeRTOS repo in a private Git server that can be maintained even if github goes away or the source is pulled in future.)

git submodule add https://github.com/FreeRTOS/FreeRTOS-Kernel.git libs/freertos
cd libs/freertos
git checkout V11.1.0
cd ../../

Now I've got a submodule referencing a specific tagged release of the FreeRTOS kernel. Next we need to configure the project. From the checkout I copied examples/template_configuration/FreeRTOSConfig.h to application/inc/FreeRTOSConfig.h in my source tree. Now my project has a copy of this config file I can tweak to suit my needs.

A few things that definitely need setting in this new config file are:

#define configCPU_CLOCK_HZ  ( ( unsigned long ) 180000000 ) // we set the CPU clock in CubeMX earlier

#define configTICK_RATE_HZ  1000  // with a faster CPU this seems reasonable

#define configTICK_TYPE_WIDTH_IN_BITS  TICK_TYPE_WIDTH_32_BITS  // systick is 32 bit native on Cortex-M4

I replaced the Interrupt nesting behaviour configuration section with this section taken from a CubeMX generated project which defines the bits based on the NVIC configuration:

/* Cortex-M specific definitions. */
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS         __NVIC_PRIO_BITS
#define configPRIO_BITS         4

/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */

/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
PRIORITY THAN THIS! (higher priorities are lower numeric values. */

/* Interrupt priorities used by the kernel port layer itself.  These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */

Finally I removed all the sections about SMP (Symmetric multi-processing) since this is a single core project and the sections about ARM v8-M processors (the Cortex-M4 is an ARM V7-M so these bits aren't relevant).

Finally in the top-level CMakeLists.txt in my project I added a section to include the FreeRTOS source:

# -----------------------------
# Add FreeRTOS from submodule
add_library(freertos_config INTERFACE)

target_include_directories(freertos_config SYSTEM INTERFACE

# ------------------------------

Now running CMake I see it builds (not actually invoking it in the code yet so it's not useful, but it does build).



Posting comments is not currently possible. If you want to discuss this article you can reach me on twitter or via email.


Email: nathan@nathandumont.com

Mastodon: @hairymnstr@mastodon.social