FreeRTOS is a lightweight embeddable multi-task real-time operating system. The produced kernel may require only a few kB of ROM and even less of RAM on the target microcontrolers, hence it is adapted for constrained embedded systems. The kernel source is developed in C language, thus reuse of existing C source code may be easily integrated. As a real-time OS, FreeRTOS focuses on multi-task scheduling and offers the following features:
For a complete documentation on FreeRTOS, don't hesitate to visit the FreeRTOS official website.
The scheduling works with priorities. The running process will always be the one available with the highest priority. There are two scheduling schemes that may be implemented. The first is preemptive, which means that a running process may be interrupted to let an other one be executed. The second is cooperative, which means that each process decides where in its execution it may be interrupted. When a process change is to be made, the scheduler chooses the one with the highest priority amongst those ready.
A task is a process with its context. Therefore, several tasks can coexist in an application, but do not interfere with each other, they’re independent. Since only one task can execute at a time, that is using the CPU, the scheduler swaps in and out the different tasks according to their priorities and the scheduling mode. A task states block diagram may be found in the figure below.
A task may be in different states. Here is a brief description of these states:
The task priority is used by the scheduler to define which task to execute (put in the Running state). When several tasks are in the Ready state, it chooses the one with the highest priority. A task with a lower priority than another one will have to wait until this one enters the Blocked state to be executed.
A task is implemented as a function containing an infinite loop. This function should never return.
There is a task that is systematically created in every application: the idle task. Defined with the lowest priority, it will be executed only when there is no other task in the Ready state. It is possible to define a function that will be called every time the idle task is entered (the idle task hook), which may be interesting for putting the device in a low power mode.
Co-routines are a different type of process that may be used for a real time application. The main differences with tasks are that co-routines use prioritized cooperative scheduling, which means they decide when another co-routine may be executed and they share a single stack which reduces the global RAM usage, but requires some special care. Their states diagram is found in the figure below.
A co-routine may be in the following states:
The co-routines of an application are prioritized, that is when the scheduler have to choose a co-routine to execute it will take the one in the Ready state with the highest priority. When tasks and co-routines are use simultaneously in an application, any task will always take priority over co-routines.
Queues are the generic form of intertask communications. They are FIFO buffers that may be accessed (read from and written to) by tasks. The items placed in the queues are of fixed size, defined with the maximum number of elements when the queue is created.
Reads and writes to the queue are done by copy, therefore the larger the elements are, the more memory will be required by the queue. It is possible to place pointers in the queue, care must then be taken not to modify the data before it is read.
Tasks can block on queues, waiting for an item to be inserted, or space to be made. It is a way to synchronize different tasks between them, or with interrupts.
Semaphores are directly derived from queues, and contains only one element. The queue can be full or empty and what’s contained in the queue doesn’t matter.
A task can ’take’ a semaphore, that is emptying the queue. If the queue wasn’t full, it will block. An interrupt can ’give’ the semaphore, that is filling the queue, hence allowing the task to continue.
Semaphores are therefore mainly designed to offer synchronization between tasks and interrupts.
Mutexes (for MUTual EXclusion) are a form of binary semaphores used to prevent several Tasks to access the same resource at the same time. A mutex is created for a shared resource and it’s associated queue is full. When a task wants to access the resource, it ’takes’ the mutex. When the task is done with the resource, it gives the mutex back, so another task can use it. If a task tries to take a mutex that is already taken, it will block until it’s given back.
The official FreeRTOS archive contains a port for the MSP430F449 microcontroller on the ES449 prototyping board. It is possible to use it as-is, just choose this target in your project configuration file. For a detailed explanation on what changes, what doesn't, and how to adapt the given Demo project to the WSN430 platform, read porting FreeRTOS on the WSN430 hardware platform. You can also download a port archive in the download section below.
When using FreeRTOS as the OS for an application on the WSN430 platform, a few points need special attention.
The real time aspect of the OS is based on counting ticks. An MMCU internal timer is used to generate an interrupt periodically allowing the OS to count time, to preempt tasks and offer a delay mechanism. The default configuration of the OS is to use the timerA of the msp430, configured with the ACLK source clock (assumed to be 32768Hz). In the FreeRTOSConfig.h file required in every application, the desired tick rate is specified. If a tick rate of 1kHz is wanted, the timerA CCR register will be set to 32768/1000 = 32, therefore the real tick rate will be 1024Hz.
To sum up, when using FreeRTOS:
The FreeRTOS kernal allocates memory dynamically when a task or a queue (queue, semaphore …) is created. To achieve this, FreeRTOS creates a large byte array at compile time from which will be taken the memory for future allocation. This array is called the heap, and its size is defined in the FreeRTOSConfig.h file, with the configTOTAL_HEAP_SIZE constant. The total RAM space of the MSP430F1611 is 10k bytes. The heap will contain the stacks of the different tasks as well as the queues created during execution, it can therefore be adjusted to an application.
The remaining RAM space will hold all the global and static declared variables of the project. If the heap is defined too big, the program won’t compile if there are some big global variables, but if the heap is too small, the program will compile but the tasks’ stacks won’t be allocated at runtime, and the application will not run normally.
On the WSN430 platform, several peripherals communicate with the msp430 using shared buses. There are two communication buses on the msp430f1611: USART0 and USART1. On the USART0 are the TSL2550 light sensor using I2 C and a serial link with a PC (or equivalent) using UART. On the USART1 are the CC1100 radio chip using SPI, the M25P80 flash memory using SPI, the DS1722 temperature sensor using SPI and a serial link with a PC (or equivalent) using UART.
Each of these USARTs can be configured for on type of link at a time (UART/SPI/I2 C), thus if you want to switch between several peripherals on the same port, you have to reconfigure it before using it (calling the xxx_init() function of the driver might do the trick). On the USART1, three peripherals can be accessed using SPI. With this type of bus, the different devices share the same communication link, but are enabled/disabled using separate lines. When communicating with a device, the other devices on the same SPI bus must be disabled, that is: only one device should be enabled at a time. To do this, the spi1.h driver disable the other two SPI peripherals when enabling one.
In a multitask environment, if two tasks want to access two different devices on the same SPI bus, care must be taken in order to not interrupt a given transfer, and a mutex (mutual exclusion semaphore) should be used for this. FreeRTOS offers a simple way of using them.
Here are some archive files concerning FreeRTOS on the WSN430 platform: