FreeRTOS on Kinetis E Cortex M0+ : easy porting tutorial

FreeRTOS is a popular, open-source operating system that can run on a variety of microcontrollers. This post shows how to make a minimal working setup with two tasks on a new MCU without starting from a complete demo code or code generators (like Processor Expert) on an inexpensive development board FRDM-KE06Z from NXP. All examples use static memory allocation. Most of the procedures and tips mentioned here apply equally well to all Cortex-M microcontrollers.

I will assume that you have good understanding of C, installed Kinetis Design Studio, some experience with Cortex-M microcontrollers and that you know why you want an RTOS. πŸ™‚

From programmer’s point of view FreeRTOS makes several C function appear as they would be executed all at once. It does it by switching between them many times a second. Each of those functions is called a task. Switching happens within a timer interrupt (in case of Cortex-M it is the systick).

Creating a new KDS project

Let’s begin by creating blank project targeted for Kinetis KE06Z. Don’t enable Processor Expert.

Default project structure


The project contains MCU headers and CMSIS headers. CMSIS headers and provided mostly by ARM – they provide a common programming interface to the CPU core. FreeRTOS uses them to access systick and do some interrupt control. As the KE06Z is a Cortex-M0+ the headers for M4 and M7 can be removed.

Project after removing unnecessary files

Adding FreeRTOS files

The number of directories and files in FreeRTOS release package can be frightening, but only a very small part is necessary.

Let’s have a look at FreeRTOS v9.0.0 release directory structure:

FreeRTOS-Plus directory and demos can be totally left out of scope (it is handy to look at the demos – to check if the particulr Cortex type is supported). All important parts are in FreeRTOS directory.

FreeRTOS uses basically two kinds of source files:

  • portable – common to all chips and architectures
  • port files – specific to the target chip (ideally just two files)

All portable .c and .h files from FreeRTOS/Source have to be added to the project (don’t add anything from the “portable” directory yet – naming is a little confusing as the “portable” directory contains files that are CPU-specific).

Finding the right port for your CPU

While the portable files are written in plain C and are easy to follow, the port itself is written in assembly and essentially does low-level black magic (from C programmer’s point of view). Fortunately the port is dependent only on the CPU core and not on the whole microcontroller, so for example if MCU with Cortex M0+ from Atmel has a port, then it can also be used with an MCU from ST or Silicon Labs that has the same core. Port files should match the core (example: Cortex M4F is different than Cortex M4, but Cortex M0 and M0+ use the same code – YMMV).

In this particular case: NXP Kinetis KE06 has a Cortex M0+ and the port files are in FreeRTOS/Source/portable/GCC/ARM_CM0.

Portable and port files have to be added to Kinetis project.

That is how the project may look like:

FreeRTOSConfig.h

This is the only file that has to be customized for your project. I have taken one from the CORTEX_M0+_Atmel_SAMD20_XPlained demo (as it matches the core type of my MCU).

First thing to specify is the CPU clock speed: #include <asf.h> has to be removed (it is an Atmel library) and instead MKE06Z4.h has to be included. The configCPU_CLOCK_HZ macro has to be defined simply as DEFAULT_SYSTEM_CLOCK.

Second, a bunch of options (in my case) have to be tweaked. My example uses only static memory allocation.

  • configUSE_TICK_HOOK is zero
  • configGENERATE_RUN_TIME_STATS is zero (and accompanying vMainConfigureTimerForRunTimeStats, ulMainGetRunTimeCounterValue, portCONFIGURE_TIMER_FOR_RUN_TIME_STATS, portGET_RUN_TIME_COUNTER_VALUE next to it can be removed)
  • configUSE_TIMERS is 0
  • configCHECK_FOR_STACK_OVERFLOW is zero (we will get back to it later)
  • remote configTOTAL_HEAP_SIZE (we will be using only static memory allocation)
  • add #define configSUPPORT_STATIC_ALLOCATION 1
  • add #define configSUPPORT_DYNAMIC_ALLOCATION 0
  • change configMINIMAL_STACK_SIZE to 128 (it is CPU-native words, in case of this ARM: uint32_t)

At this point the project will not build yet.

main.c

FreeRTOS requires one task to be always ready to execute (not blocked) – this is the idle task. Idle task (as every task) needs its own stack. Because we are using static memory allocation, vApplicationGetIdleTaskMemory has to be implemented. Static stacks for tasks are simply regular arrays. Very basic main.c:

At this point the project should build cleanly and can be flashed to the microcontroller. It does totally nothing (not even blink a LED!), but runs and should not crash (inspect by pausing and restarting with the debugger). Impressive! πŸ™‚

Blinking a LED the RTOS way

To get anything done at least one task has to be specified. It must have an endless loop and can’t return (however it can terminate itself via FreeRTOS API call). Of course a separate stack has to be allocated for the blink task. Example:

Blinking two LEDs

What is better than a blinking LED? Two blinking LEDs!

This example has two tasks that blink separate LEDs with different timings, so it is easy to observe that both loops are executed “at the same time” (in reality – chopped and executed alternatively), which is the main point of an operating system. πŸ™‚

KinetisE FreeRTOS KDS project files

Common issues and what to do next

Stack size unit

Remember that the stack size everywhere in FreeRTOS is specified in words, for a 32-bit ARM that is 32 bits (so stack size 1 = 4 bytes).

Preemptivness

Even though FreeRTOS is advertized as supporting preemptive multitasking, the meaning of “preemptive” is very different to what it means in “full” operating systems (Linux/Windows/etc.). Preemptive multitasking on a PC means that each task will get its “fair share” of time to execute – task switch no matter what. On FreeRTOS it means that a task will run until another task with a higher priority is ready. In practice if two tasks with identical priority have endless loops – only one of them will effectively run, the other will starve. Tasks must regularly call vTaskDelay to give CPU time to each other.

Stack overflow handling

Memory management (especially with multiple stacks) is an issue in all resource-constrained operating systems. Stack overflows can manifest themselves as “random data corruption somewhere” (stack has “hit” other variables above and changed their values). Fortunately FreeRTOS supports stack instrumentation. configCHECK_FOR_STACK_OVERFLOW has to be defined (to 1 or 2) in FreeRTOSConfig.h to enable vApplicationStackOverflowHook. This function has to be defined somewhere and it is up to the developer to do something about the problem.

Simple example that I use during development:

When a stack overflow happens this function simply calls a breakpoint. With a debugger attached the CPU will stop, without a debugger connected the breakpoint instruction will trigger system reset. In production grade code I would add storage of some diagnostic information, maybe a controlled system shutdown/cleanup and restart. When any of the stacks overflows and the microcontroller has no MPU or MMU – global data and program state can not be trusted anymore (global data could be corrupted and hold literally anything), so a system reset is mandatory.

Hardware sharing between tasks

is difficult. Whenever accessing global data (hardware registers can be regarded as such) you have to assume that an interrupt (or task switch) can happen at any time. All read-modify-write operations can be interrupted. For example GPIO initilization in sample code would not be safe to carry out in two separate tasks. Simplest solution is a critical section – disabling interrupts for a short while. Another approach is to use mutexes or design a bigger driver that can handle multiple tasks, like an SPI bus driver that supports two devices (eg. flash and accelerometer) on a single bus that can be used by two tasks – the driver must take care to properly multiplex access to the shared bus.

What to do when there is nothing to do – idle hook

What does FreeRTOS do when all tasks are blocked and are waiting for something? It calls the idle hook!
Idle hook is enabled by defining configUSE_IDLE_HOOK in the configuration file. It is a regular function that will be called whenever there is nothing better to do (so it can also be never called, depending on the circumstances). Typical usage: put the CPU into sleep mode and/or kick the watchdog (to guarantee that CPU utilization is below 100%):

CPU will sleep until next interrupt happens (either systick or another peripheral). This will reduce total power consumption.

Crashes and hard faults

Most of the faults will be caused by stack management or too small stack sizes. It is also important to give proper (~1KB) stack to the idle task (it also does some FreeRTOS housekeeping). When something bad happens it may be hard to figure out which task caused the problem – my way of debugging is to disable/enable tasks or pieces of code until I can find the tipping point that leads to a crash.

5/5 - (2 votes)