RTOS Stream Buffers
[Stream Buffers and Message Buffers]
- Getting started
- Blocking reads and Trigger Levels
- Blocking Writes
- Send and Receive Complete Macros (for multicore use)
Unlike most other FreeRTOS communications primitives, stream buffers are optimised for single reader single writer scenarios, such as passing data from an interrupt service routine to a task, or from one microcontroller core to another on a dual core CPU.
Stream buffer functionality is enabled by including the FreeRTOS/source/stream_buffer.c source file in the build.
The stream buffer implementation uses direct to task notifications. Therefore, calling a stream buffer API function that places the calling task into the Blocked state can change the calling task’s notification state and value.
IMPORTANT NOTE: Uniquely among FreeRTOS objects, the stream buffer implementation (so also the message buffer implementation, as message buffers are built on top of stream buffers) assumes there is only one task or interrupt that will write to the buffer (the writer), and only one task or interrupt that will read from the buffer (the reader). It is safe for the writer and reader to be different tasks or interrupts, but, unlike other FreeRTOS objects, it is not safe to have multiple different writers or multiple different readers. If there are to be multiple different writers then the application writer must place each call to a writing API function (such as xStreamBufferSend()) inside a critical section and use a send block time of 0. Likewise, if there are to be multiple different readers then the application writer must place each call to a reading API function (such as xStreamBufferReceive()) inside a critical section and use a receive block time of 0.
See the stream buffer section of the user documentation for a list of stream buffer related API functions, in many cases including code snippets that demonstrate the functions being used.
xStreamBufferReceive() allows a block time to be specified. If a non zero block time is specified when a task uses xStreamBufferReceive() to read from a stream buffer that happens to be empty the task will be placed into the Blocked state (so it is not consuming any CPU time and other tasks can run) until either a specified amount of data becomes available in the stream buffer, or the block time expires. The amount of data that must be in the stream buffer before a task that is waiting for data is removed from the blocked state is called the stream buffer’s Trigger Level. For example:
If a task is blocked on a read of an empty stream buffer that has a
trigger level of 1 then the task will be unblocked when a single byte is
written to the buffer or the task’s block time expires.
- If a task is blocked on a read of an empty stream buffer that has a trigger level of 10 then the task will not be unblocked until the stream buffer contains at least 10 bytes or the task’s block time expires.
It is not valid to set the trigger level to 0. Attempting to set the
trigger level to 0 will result result in a trigger level of 1 being used.
It is also not valid to specify a trigger level that is greater than the
stream buffer’s size.
If a non zero block time is specified when a task uses xStreamBufferSend() to write to a stream buffer that happens to be full the task will be placed into the Blocked state (so it is not consuming any CPU time and other tasks can run) until either space becomes available in the stream buffer, or the block time expires.
sbSEND_COMPLETED() (and sbSEND_COMPLETED_FROM_ISR())sbSEND_COMPLETED() is a macro that is called (internally within the FreeRTOS API functions) when data is written to a stream buffer. It takes a single parameter, which is the handle of the stream buffer that was updated.
sbSEND_COMPLETED() checks to see if there is a task blocked on the stream buffer to wait for data, and if so, removes the task from the Blocked state.
It is possible for the application writer to change this default behaviour by providing their own implementation of sbSEND_COMPLETED() in FreeRTOSConfig.h. That is useful when a stream buffer is used to pass data between cores on a multicore processor. In that scenario, sbSEND_COMPLETED() can be implemented to generate an interrupt in the other CPU core, and the interrupt’s service routine can then use the xStreamBufferSendCompletedFromISR() API function to check, and if necessary unblock, a task that was waiting for the data. The FreeRTOS/Demo/Common/Minimal/MessageBufferAMP.c source file provides a heavily commented example of exactly that scenario.
sbRECEIVE_COMPLETED() (and sbRECEIVE_COMPLETED_FROM_ISR())sbRECEIVE_COMPLETED() is the receive equivalent of sbSEND_COMPLETED(). It is called (internally within the FreeRTOS API functions) when data is read from a stream buffer. The macro checks to see if there is a task blocked on the stream buffer to wait for space to become available within the buffer, and if so, removes the task from the Blocked state. Just as with sbSEND_COMPLETED(), the default behaviour of sbRECEIVE_COMPLETED() by providing an alternative implementation in FreeRTOSConfig.h