FreeRTOS is a popular real-time operating system kernel for embedded devices, that has been ported to more than 35 microcontrollers. It is distributed under the GPL with an optional exception. The exception permits users' proprietary code to remain closed source while maintaining the kernel itself as open source, thereby facilitating the use of FreeRTOS in proprietary applications.
FreeRTOS is designed to be small and simple. The kernel itself consists of only three or four C files. To make the code readable, easy to port, and maintainable, it is written mostly in C, but there are a few assembly functions included where needed (mostly in architecture-specific scheduler routines).
FreeRTOS provides methods for multiple threads or tasks, mutexes, semaphores and software timers. A tick-less mode is provided for low power applications. Thread priorities are supported. In addition there are several schemes of memory.
In OpenWSN FreeRTOS has been encapsulate and hidden behind our very simple scheduler.h interface. In that way our code stays agnostic to the underlaying scheduler.
#ifndef __SCHEDULER_H #define __SCHEDULER_H /** \addtogroup kernel \{ \addtogroup Scheduler \{ */ #include "opendefs.h" //=========================== define ========================================== typedef enum { TASKPRIO_NONE = 0x00, // tasks trigger by the stack rx TASKPRIO_STACK_LOWMAC = 0x01, TASKPRIO_STACK_HIGHMAC = 0x02, TASKPRIO_STACK_6TOP = 0x03, TASKPRIO_STACK_IP = 0x04, TASKPRIO_STACK_ROUTING = 0x05, TASKPRIO_STACK_TRANSPORT = 0x06, // tasks going up the stack - sendone and timers TASKPRIO_SENDDONE_TIMERS_MAC = 0x07, TASKPRIO_SENDDONE_TIMERS_6TOP = 0x08, TASKPRIO_SENDDONE_TIMERS_IP = 0x09, TASKPRIO_SENDDONE_TIMERS_ROUTING = 0x0a, TASKPRIO_SENDDONE_TIMERS_TRANSPORT = 0x0b, //app tasks - down the stack TASKPRIO_APP_HIGH = 0x0c, TASKPRIO_APP_MED = 0x0d, TASKPRIO_APP_LOW = 0x0e, TASKPRIO_MAX = 0x0f, } task_prio_t; #define TASK_LIST_DEPTH 10 //=========================== typedef ========================================= typedef void (*task_cbt)(void); typedef struct task_llist_t { task_cbt cb; task_prio_t prio; void* next; uint16_t counter; } taskList_item_t; //=========================== module variables ================================ typedef struct { taskList_item_t taskBuf[TASK_LIST_DEPTH]; taskList_item_t* task_list; uint8_t numTasksCur; uint8_t numTasksMax; } scheduler_vars_t; typedef struct { uint8_t numTasksCur; uint8_t numTasksMax; } scheduler_dbg_t; //=========================== prototypes ====================================== void scheduler_init(void); void scheduler_start(void); void scheduler_push_task(task_cbt task_cb, task_prio_t prio); /** \} \} */ #endif |
The implementation is still simple, the main idea is to have 3 FreeRTOS tasks handling the operation of the OpenWSN protocol stack.
In OpenWSN, the MACROS DISABLE_INTERRUPTS and ENABLE_INTERRUPTS are used to protect critical sections. When using a RTOS, however, we do not want to disable the operation of the RTOS but instead we want some sort of mutual exclusion to disable task preemption. This is achieved by a global lock semaphore
#define INTERRUPT_DECLARATION() (rtos_sched_v.xStackLock != NULL ? (rtos_sched_v.xStackLock=rtos_sched_v.xStackLock) : ( rtos_sched_v.xStackLock = xSemaphoreCreateMutex())) #define DISABLE_INTERRUPTS() xSemaphoreTakeFromISR( (rtos_sched_v.xStackLock), &globalPriorityTaskWoken ) #define ENABLE_INTERRUPTS() xSemaphoreGiveFromISR( (rtos_sched_v.xStackLock),&globalPriorityTaskWoken ) |
The scheduler operation has been implemented as follows:
void scheduler_push_task(task_cbt cb, task_prio_t prio) { BaseType_t xHigherPriorityTaskWoken; xHigherPriorityTaskWoken = pdFALSE; //=== step 1. insert the task into the task list scheduler_push_task_internal(cb, prio); debugpins_slot_toggle(); //=== step 2. toggle the appropriate semaphore so the corresponding handler takes care of it if (prio < SCHEDULER_STACK_PRIO_BOUNDARY) { xSemaphoreGiveFromISR(rtos_sched_v.xRxSem, &xHigherPriorityTaskWoken); } else if (prio >= SCHEDULER_STACK_PRIO_BOUNDARY && prio < SCHEDULER_SENDDONETIMER_PRIO_BOUNDARY) { xSemaphoreGiveFromISR(rtos_sched_v.xSendDoneSem, &xHigherPriorityTaskWoken); } else if (prio >= SCHEDULER_SENDDONETIMER_PRIO_BOUNDARY && prio <= SCHEDULER_APP_PRIO_BOUNDARY //includes TASKPRIO_MAX ) { xSemaphoreGiveFromISR(rtos_sched_v.xAppSem, &xHigherPriorityTaskWoken); } else { leds_error_blink(); while (1) ; } portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } |
As said the FreeRTOS tasks unlock when a callback is pushed and execute the task that it has been inserted.
/** \brief Handles application packets, brinding them down the stack until they are queued, ready for the lowwer MAC to consume. */ static void vAppTask(void* pvParameters) { taskList_item_t* pThisTask = NULL; xSemaphoreTake(rtos_sched_v.xAppSem, portMAX_DELAY); //take it for the first time so it blocks right after. while (1) { xSemaphoreTake(rtos_sched_v.xAppSem, portMAX_DELAY); debugpins_fsm_toggle(); scheduler_find_next_task_and_execute( SCHEDULER_SENDDONETIMER_PRIO_BOUNDARY, SCHEDULER_APP_PRIO_BOUNDARY, pThisTask); } } |
One interesting thing to do once a RTOS is supported is to have some application level tasks that handle the application activity without constraining the OpenWSN activity. This can be easily achived by letting the application to create a FreeRTOS task.
//=========================== variables ======================================= #ifdef USE_FREERTOS #define tskUDPECHOAPP_PRIORITY configMAX_PRIORITIES - 4 TaskHandle_t xAppUechoHandle; // task //=========================== prototypes ====================================== static void vUechoTask(void* pvParameters); #endif //=========================== public ========================================== void uecho_init() { #ifdef USE_FREERTOS xTaskCreate(vUechoTask, "Uecho", 50, NULL, tskUDPECHOAPP_PRIORITY, &(xAppUechoHandle)); #endif } #ifdef USE_FREERTOS static void vUechoTask(void* pvParameters) { uint8_t count = 0; while (1) { debugpins_fsm_toggle(); vTaskDelay(10000); leds_debug_toggle(); udpecho_sendHello(count++); } } #endif |
In order to enable FreeRTOS kernel in OpenWSN a compilation options have been added to the Scons build option.
$>scons board=OpenMote-CC2538 toolchain=armgcc kernel=freertos oos_openwsn |
By now FreeRTOS is only supported by those boards with enough memory footprint.