FreeRTOS Intertask Coordination

FreeRTOS has a range of primitives which you can use for task synchronization, communication, and notification. These primitives are:

Semaphores and Mutexes

The FreeRTOS kernel provides binary semaphores, counting semaphores, and mutexes for both mutual exclusion and synchronization purposes. Binary semaphores can only have two values. They are a good choice for implementing synchronization (either between tasks or between tasks and an interrupt). Counting semaphores take more than two values. They allow many tasks to share resources or perform more complex synchronization operations. Mutexes are binary semaphores that include a priority inheritance mechanism. This means that if a high priority task blocks while attempting to obtain a mutex that is currently held by a lower priority task, the priority of the task holding the token is temporarily raised to that of the blocking task. This mechanism is designed to ensure the higher priority task is kept in the Blocked state for the shortest time possible, to minimize the priority inversion that has occurred. (Source: aws)

Reference: https://www.freertos.org/Embedded-RTOS-Binary-Semaphores.html

Queues

Queues are the primary form of intertask communication. They can be used to send messages between tasks and between interrupts and tasks. In most cases, they are used as thread-safe, First In First Out (FIFO) buffers with new data being sent to the back of the queue. (Data can also be sent to the front of the queue.) Messages are sent through queues by copy, meaning the data (which can be a pointer to larger buffers) is itself copied into the queue rather than simply storing a reference to the data. Queue APIs permit a block time to be specified. When a task attempts to read from an empty queue, the task is placed into the Blocked state until data becomes available on the queue or the block time elapses. Tasks in the Blocked state do not consume any CPU time, allowing other tasks to run. Similarly, when a task attempts to write to a full queue, the task is placed into the Blocked state until space becomes available in the queue or the block time elapses. If more than one task blocks on the same queue, the task with the highest priority is unblocked first. Other FreeRTOS primitives, such as direct-to-task notifications and stream and message buffers, offer lightweight alternatives to queues in many common design scenarios. (Source: aws)

Reference: https://www.freertos.org/Embedded-RTOS-Queues.html

Direct-to-task notifications

Task notifications allow tasks to interact with other tasks, and to synchronize with interrupt service routines (ISRs), without the need for a separate communication object like a semaphore. Each RTOS task has a 32-bit notification value that is used to store the content of the notification, if any. An RTOS task notification is an event sent directly to a task that can unblock the receiving task and optionally update the receiving task's notification value. RTOS task notifications can be used as a faster and lightweight alternative to binary and counting semaphores and, in some cases, queues. Task notifications have both speed and RAM footprint advantages over other FreeRTOS features that can be used to perform equivalent functionality. However, task notifications can only be used when there is only one task that can be the recipient of the event. (Source: aws)

Reference: https://www.freertos.org/RTOS-task-notifications.html

Stream buffers

Stream buffers allow a stream of bytes to be passed from an interrupt service routine to a task, or from one task to another. A byte stream can be of arbitrary length and does not necessarily have a beginning or an end. Any number of bytes can be written at one time, and any number of bytes can be read at one time. You enable stream buffer functionality by including the stream_buffer.c source file in your project. Stream buffers assume there is only one task or interrupt that writes to the buffer (the writer), and only one task or interrupt that reads from the buffer (the reader). It is safe for the writer and reader to be different tasks or interrupt service routines, but it is not safe to have multiple writers or readers. The stream buffer implementation uses direct-to-task notifications. Therefore, calling a stream buffer API that places the calling task into the Blocked state can change the calling task's notification state and value. (Source: aws)

Reference: https://www.freertos.org/RTOS-stream-message-buffers.html

Message buffers

Message buffers allow variable-length discrete messages to be passed from an interrupt service routine to a task, or from one task to another. For example, messages of length 10, 20, and 123 bytes can all be written to, and read from, the same message buffer. A 10-byte message can only be read as a 10-byte message, not as individual bytes. Message buffers are built on top of stream buffer implementation. You can enable message buffer functionality by including the stream_buffer.c source file in your project.

Message buffers assume there is only one task or interrupt that writes to the buffer (the writer), and only one task or interrupt that reads from the buffer (the reader). It is safe for the writer and reader to be different tasks or interrupt service routines, but it is not safe to have multiple writers or readers.

The message buffer implementation uses direct-to-task notifications. Therefore, calling a stream buffer API that places the calling task into the Blocked state can change the calling task's notification state and value.

To enable message buffers to handle variable-sized messages, the length of each message is written into the message buffer before the message itself. The length is stored in a variable of type size_t, which is typically 4 bytes on a 32-byte architecture. Therefore, writing a 10-byte message into a message buffer actually consumes 14 bytes of buffer space. Likewise, writing a 100-byte message into a message buffer actually uses 104 bytes of buffer space. (Source: aws)

Reference: https://www.freertos.org/RTOS-stream-message-buffers.html

Software timers

A software timer allows a function to be executed at a set time in the future. The function executed by the timer is called the timer’s callback function. The time between a timer being started and its callback function being executed is called the timer’s period. The FreeRTOS kernel provides an efficient software timer implementation because:

  • It does not execute timer callback functions from an interrupt context.

  • It does not consume any processing time unless a timer has actually expired.

  • It does not add any processing overhead to the tick interrupt.

  • It does not walk any link list structures while interrupts are disabled.

(Source: aws)

Reference: https://www.freertos.org/RTOS-software-timer.html

Event Bits/ Groups

Event bits are used to indicate if an event has occurred or not. Event bits are often referred to as event flags. For example, an application may:

  • Define a bit (or flag) that means “A message has been received and is ready for processing” when it is set to 1, and “there are no messages waiting to be processed” when it is set to 0.
  • Define a bit (or flag) that means “The application has queued a message that is ready to be sent to a network” when it is set to 1, and “there are no messages queued ready to be sent to the network” when it is set to 0.
  • Define a bit (or flag) that means “It is time to send a heartbeat message onto a network” when it is set to 1, and “it is not yet time to send another heartbeat message” when it is set to 0.

An event group is a set of event bits. Individual event bits within an event group are referenced by a bit number. Expanding the example provided above:

  • The event bit that means “A message has been received and is ready for processing” might be bit number 0 within an event group.
  • The event bit that means “The application has queued a message that is ready to be sent to a network" might be bit number 1 within the same event group.
  • The event bit that means “It is time to send a heartbeat message onto a network” might be bit number 2 within the same event group.

(Source freertos.org)