FreeRTOS Implementations On Newer Arduino Boards

I have Arduino boards with the ability to test FreeRTOS in five microcontroller architectures: Microchip/Atmel ATmega328P (and similar), ATmega4809, SAMD21, Renesas RA4M1, and Espressif ESP32-S3. The previous post discussed the scheduler in the 328P FreeRTOS port. This post covers the differences among the ports that makes may cause difficulties moving between boards.

ATmega328P Baseline

This architecture was tested in the preceding post, FreeRTOS Scheduling in the Arduino Uno R3. Several thing can differ, so lets consider a few things about an application using FreeRTOS on the 328P or other AVR microcontroller but the ATmega4809, say on an Arduino Uno R3 or Nano.

  • The Library Manager in the Arduino IDE is used to install the FreeRTOS library. The library is included in the application as Arduino_FreeRTOS.h.
  • As part of the library there are a number of example programs. Strangely, the example programs are different for the different FreeRTOS ports and are generally not portable without making changes!
  • The time slice is roughly 15ms. It isn’t accurate because the watchdog timer is used and that was never intended to be accurate. Most of the other ports have a 1ms time slice. The frequency of the time slicing is defined as configTICK_RATE_HZ. Only this port is designed to allow setting different (and longer) time slices. Another constant, portTICK_PERIOD_MS, gives the period length in milliseconds rather than frequency in Hertz.
  • When the FreeRTOS library is included, the initVariant function, which is called in main() just before calling setup(), itself calls setup() and then vTaskStartScheduler() to start the RTOS running. This function never returns. The function vApplicationIdleHook is called repeatedly in the (hidden) Idle Task, and this function calls loop(). This means that the loop() function continues to operate and runs when there are no other runnable tasks.

ATmega4809

In the case of the Arduino Nano Every with the ATmega4809, modifications must be made to the FreeRTOS library. See The Continuing Adventure of FreeRTOS. Once the changes are made to the library, it works identically to the 328P version but for the time slice clock. For the 4809 it runs at 64Hz, or about 15.6ms time slice.

SAMD21

This version applies to the Arduino Zero, Nano 33 IoT, all of the MKR boards, and to other boards using the SAMD21 such as the Seeedstudio XIAO. It requires installing the FreeRTOS_SAMD21 library which is included in the application as FreeRTOS_SAMD21.h.

The time slice is 1ms. The function vTaskStartScheduler must be called to start the scheduler. This call is typically placed as the last statement in the setup function. vTaskStartScheduler does not return.

Code in the library (in file idle_hook.c) calls loop() repeatedly when the idle task runs.

Renesas RA4M1

This version applies to the two Uno R4 boards.  The Arduino library for these boards includes the code for FreeRTOS, so rather than being in Arduino directory it is found in Arduino15/packages/arduino/hardware/renesas_uno/1.0.1/libraries/Arduino_FreeRTOS.  The file to include is Arduino_FreeRTOS.h, but keep in mind that because of different search paths this is a different file than that of the ATmega328P version.

The time slice is 1ms. However this version (and only this version) has set the configuration option configUSE_TIME_SLICING to 0. This means that a running task will not yield at the end of a time slice. It will keep running until it explicitly yields the processor or a higher priority task becomes runnable.

The call to vTaskStartScheduler must be added at the end of  setup().

The (hidden) idle task sleeps the processor to save power. This is nice, but the loop function never runs unless you make a modification. There are two general approaches to make the loop function operative. You can hoop the idle task so that it calls loop. Just add the function:

void vApplicationIdleHook(void) {
    loop();
 }

The example program, there is only one for this port,  takes a different approach and adds an additional task at lowest priority which calls the loop function repeatedly. If you use this approach then if you have nothing to do in the loop function you can do a vTaskDelay or taskYIELD to sleep this task and allow the real idle task to run (which will sleep the processor). I won’t repeat the code here as you can see it in the example program.

ESP32-S3

Expressif did this port and the source code is not provided — it is precompiled. This make it the only port for which USE_TIME_SLICING is 1 and cannot be changed. No #include directive is necessary. No call of vTaskStartScheduler is necessary. The loop function is automatically called from the idle task. 

The most important difference is that the ESP32-S3 has two processor cores, and by default the core is assigned to a task randomly and two tasks can run simultaneously.

Here there is no explicit yield in the three tasks, so they each run until the end of a time slice. Note that execution overlaps allowing two tasks to run simultaneously. The idle task gets to run when both processor cores are not needed for the higher priority tasks.

Expressif performed modifications and additions to the FreeRTOS distribution, which can be found in their documentation: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/freertos.html