The quick start guide for FreeRTOS recommends choosing one of the working sample applications as the basis for your application. The result is that your project begins with a bunch of boilerplate code that you now own, when ideally it would all be isolated as a strict dependency.

With the dependency management and library customization features of PlatformIO, we can do better. Specifically, we can build a basic FreeRTOS application starting with just three source files.

The files are

  • platformio.ini - Application build and dependency specification.
  • main.cpp - Application code.
  • FreeRTOSConfig.h - FreeRTOS #define switches, some that are MCU-specific and others that are application-specific.

Can’t wait? Check out the files in my Minimal-FreeRTOS repo.

What is CMSIS?

The minimal build configuration depends on the CMSIS framework. What is that? From the CMSIS repo:

The CMSIS is a vendor-independent hardware abstraction layer for microcontrollers that are based on Arm® Cortex® processors. The CMSIS defines generic tool interfaces and enables consistent device support. It provides simple software interfaces to the processor and the peripherals, simplifying software re-use, reducing the learning curve for microcontroller developers, and reducing the time to market for new devices.

When you refer to a GPIO pin/pad by name, like PA17, a CMSIS header file is where that is defined. In addition to register access, CMSIS provides target-specific startup code, linker scripts, SysTick configuration, standard exception names, and access to special instructions. Layered on top of that core are generic peripheral drivers, as well as support for Neural Networks, DSP, standard debugging, RTOS, and Zone security.

We’re just blinking an LED, so all we need is the GPIO pin and the registers to configure it and set its value.

Minimal platformio.ini in Detail

The platformio.ini file for Minimal-FreeRTOS looks like this:

[env:adafruit_feather_m0]
platform = atmelsam
board = adafruit_feather_m0
framework = cmsis
lib_deps = https://github.com/frankleonrose/CMSIS-FreeRTOS
build_flags =
  -D CMSIS_device_header='"sam.h"'
  -D FREERTOS_HEAP_IMPLEMENTATION=1
  -D FREERTOS_MCU_FAMILY=ARM_CM0
  -D ENV_SAML21_XPRO

This file has a single build environment: adafruit_feather_m0. Every environment requires platform and board in order to build - here we are using the Adafruit Feather M0, which sports an Atmel SAMD21G18A Cortex M0+.

The file directs PlatformIO to build with the CMSIS framework, as discussed above. The majority of the effort making Minimal-FreeRTOS possible was adding support within PlatformIO for building the CMSIS framework with atmelsam platform.

As of 2019-10-30, this update to framework-cmsis has not been incorporated into PlatformIO. In order to use this feature, edit ~/.platformio/platforms/atmelsam/platform.json setting the framework version to the github repo: "{ packages": { "framework-cmsis": { "version": "https://github.com/frankleonrose/framework-cmsis") ...}}}.

The following line starting with lib_deps lists dependencies. Minimal-FreeRTOS uses a lightly forked version the CMSIS-FreeRTOS library provided by Arm®. My version of the library includes library.json and library.py files which configure the PlatformIO build.

Lastly, there are four build flags that define macros used to select library source and include files.

  • CMSIS_device_header='"sam.h"' - This macro is used within the FreeRTOS header files like this: #include CMSIS_device_header and the macro value has to include surrounding quotes. Hence the double quotation of the head file name.
  • FREERTOS_HEAP_IMPLEMENTATION=1 - This one identifies the heap implementation among the ones provided by FreeRTOS. The options are 1 through 5, detailed here. When omitted, the value defaults to 1. If you want to provide your own heap implementation my_heap.c, simply set FREERTOS_HEAP_IMPLEMENTATION=0 (or to any number other than [12345]).
  • FREERTOS_MCU_FAMILY=ARM_CM0 - This one names the subdirectory of https://github.com/ARM-software/CMSIS-FreeRTOS/tree/develop/Source/portable/GCC under which to find the FreeRTOS files port.c and portmacro.h. If this flag is not specified the build will fail with an error.
  • ENV_SAML21_XPRO - This one is used to select board specific LED control code in this specific example main.cpp. It is not a required macro for using the CMSIS_FreeRTOS library as the other macros are.

Running on a Feather M0

  1. git clone https://github.com/frankleonrose/Minimal-FreeRTOS
  2. cd Minimal-FreeRTOS
  3. Connect Feather M0 via USB and double-click reset button to enter upload mode.
  4. pio run -t upload -e adafruit_feather_m0

Running on a SAML21 Xplained Pro

The point of limiting the number of source files is to minimize the number of things to pay attention to when making changes.

To make the simple change of board, we need to update the following files:

  • platformio.ini - Since the SAML21 is still a Cortex M0+, the only change to this file is to set board = saml21_xpro_b
  • main.cpp - The LED pad is PB10, which means we need to specify
    #define LED_INIT() { REG_PORT_DIR1 |= PORT_PB10; }
    #define LED_OFF() { REG_PORT_OUT1 |= PORT_PB10; }
    #define LED_ON() { REG_PORT_OUT1 &= PORT_PB10; }
    
  • FreeRTOSConfig.h - No change.

Pretty simple.

Running on a 1Bitsy

Building this same blink example for the 1Bitsy board with an STM32F415RG on it takes a bit more modification.

  • The platformio.ini environment is slightly more verbose. The 1Bitsy requires some extra code generation flags relating to the FPU found on this Cortex-M4 chip. Also, the 1Bitsy has no built-in programmer, so it requires using the blackmagic probe as an upload tool.
    [env:1bitsy_stm32f415rgt]
    platform = ststm32
    board = 1bitsy_stm32f415rgt
    framework = cmsis
    lib_deps = https://github.com/frankleonrose/CMSIS-FreeRTOS
    build_flags =
      -mfloat-abi=softfp
      -mfpu=fpv4-sp-d16
      -D CMSIS_device_header='"stm32f4xx.h"'
      -D FREERTOS_HEAP_IMPLEMENTATION=1
      -D FREERTOS_MCU_FAMILY=ARM_CM4F
      -D ENV_1BITSY
    upload_tool = blackmagic
    upload_port = /dev/cu.usbmodem7BB0799D1 ; Your modem ID is likely different
    
  • main.cpp requires entirely different register manipulation to control the LED, because it is ST as opposed to Atmel.
    #define LED_INIT() {                                             \
        RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;	/* Enable PortA clock */ \
        GPIOA->MODER |= GPIO_MODER_MODER8_0;	/* Port A.08 output */   \
      }
    #define LED_OFF() { GPIOA->BSRR = (1 << 8); }
    #define LED_ON() { GPIOA->BSRR = (1 << (8 + 16)); }
    
  • FreeRTOSConfig.h - Again, no change.

Conclusion

The upshot is that in order to use different boards we had to make reasonable and relevant changes. To change boards we changed a few lines in a couple files, we didn’t have to swap entire directory structures into our project. In fact, we did swap in new code, but it was well hidden within the CMSIS-FreeRTOS library where it belongs. That is a good thing and makes change and experimentation and nimbleness less costly.

Up Next

My objective is to build a standard application toolbox that I can reuse for multiple projects. I have a bunch of Adafruit Feather M0 boards, so they are going to be part of that toolbox. I want the capabilities of an RTOS to manage complexity and power consumption, so FreeRTOS is going to be part of that toolbox as well.

Blinking at the register level is all well and good to demonstrate correct builds, but to do anything serious requires more driver support. The next step will be to add a framework with drivers that makes it easier to control GPIO and DMA as well as communicate via I2C, SPI, UART, and USB, while still controlling power consumption.

Lastly, this Minimal FreeRTOS application needs to be extended to include a more varied collection of processors, like ones that include multiple cores.