
1. Basic Concepts
Stream buffers are a type of memory buffer specifically designed for FreeRTOS to transmit byte stream data. It is a FIFO (First In, First Out) buffer, but unlike message queues, stream buffers transmit data continuously in bytes rather than as discrete “messages”.
Design Purpose
- To solve the problem of passing variable-length byte data between tasks or between interrupts and tasks.
- Suitable for buffering byte stream data for communication interfaces such as UART, I2C, and SPI.
- Provides a simple and efficient mechanism for synchronizing data writing and reading.
2. Working Principle
Stream buffers are essentially a circular buffer:
- It has a fixed-size array to store byte data.
- Maintains two pointers/indices:
- Write pointer (write index): points to the next position for writing data.
- Read pointer (read index): points to the next position for reading data.
- When the write pointer catches up to the read pointer, it indicates that the buffer is full.
- When the read pointer catches up to the write pointer, it indicates that the buffer is empty.
Data Writing
- When writing data, if there is free space in the buffer, the data is written sequentially into the buffer.
- If there is insufficient space, the write operation will block (waiting for space to be freed) or return the number of bytes written.
- The write pointer moves forward based on the amount of data written.
Data Reading
- When reading, if there is data in the buffer, it is read sequentially.
- If the buffer is empty, the read operation will block (waiting for data to arrive) or return immediately.
- The read pointer moves forward based on the amount of data read.
Trigger Threshold
Stream buffers support the concept of a trigger threshold:
- A trigger threshold (triggerLevel) can be set, which wakes up tasks waiting on the buffer when the number of bytes in the buffer reaches or exceeds this threshold.
- This can reduce frequent context switching and improve efficiency.
3. Interface Definitions
<1>. Create and Delete
StreamBufferHandle_t xStreamBufferCreate(size_t xBufferSizeBytes, size_t xTriggerLevelBytes);
void vStreamBufferDelete(StreamBufferHandle_t xStreamBuffer);
<span>xBufferSizeBytes</span>: Size of the buffer in bytes.<span>xTriggerLevelBytes</span>: Trigger threshold, usually set to 1 or a larger value.- Returns a handle (pointer) to the stream buffer.
Setting the Trigger Threshold
When a task reads data from the stream buffer by calling <span>xStreamBufferReceive()</span>, if there is insufficient data in the current buffer, it will enter a blocking wait state. The trigger threshold determines the minimum amount of data required to wake up the waiting task.
Example Explanation
- If the trigger threshold is 1, then when there is at least 1 byte of data in the stream buffer, the blocking waiting task will be awakened.
- If the trigger threshold is 10, the task will remain blocked until there are at least 10 bytes of data in the buffer, or the task’s wait time times out.
- If the wait times out and the data is below the threshold, the task will still be awakened and return the available data.
Special Cases
- If the trigger threshold is set to 0, it will automatically adjust to 1 (ensuring a minimum threshold of 1).
- The trigger threshold cannot exceed the size of the buffer.
Timing for Setting the Trigger Threshold
- The trigger threshold can be specified when creating the stream buffer.
- It can be dynamically modified using
<span>xStreamBufferSetTriggerLevel()</span>for an existing stream buffer.
<2>. Sending Data
size_t xStreamBufferSend(StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait);
- Writes data to the stream buffer.
<span>pvTxData</span>: Pointer to the data to be written.<span>xDataLengthBytes</span>: Number of bytes to write.<span>xTicksToWait</span>: Maximum time to wait if the buffer is full.- Returns the number of bytes written.
<3>. Receiving Data from the Stream Buffer
size_t xStreamBufferReceive(StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait);
- Reads data from the stream buffer.
<span>pvRxData</span>: Pointer to the receive buffer.<span>xBufferLengthBytes</span>: Maximum number of bytes to read.<span>xTicksToWait</span>: Maximum time to wait if the buffer is empty.- Returns the actual number of bytes read.
<4>. Read/Write in Interrupt Service Routines
BaseType_t xStreamBufferSendFromISR(StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t xDataLengthBytes, BaseType_t *pxHigherPriorityTaskWoken);
size_t xStreamBufferReceiveFromISR(StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t xBufferLengthBytes);
- Allows safe writing or reading of the stream buffer from an interrupt.
<span>pxHigherPriorityTaskWoken</span>is used to notify whether a higher priority task needs to be switched immediately.
4. Application Example
Assume there is a UART receive interrupt that writes received data into the stream buffer; then a task reads data from the stream buffer and processes it:
// Create stream buffer
StreamBufferHandle_t xStreamBuffer = xStreamBufferCreate(128, 1);
// UART interrupt receive function
void USART_IRQHandler(void)
{
uint8_t data = USART_ReadData(); // Read UART data
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xStreamBufferSendFromISR(xStreamBuffer, &data, 1, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
// Task to read data
void vTaskProcessData(void *pvParameters)
{
uint8_t rxData[20];
for(;;)
{
size_t received = xStreamBufferReceive(xStreamBuffer, rxData, sizeof(rxData), portMAX_DELAY);
if(received > 0)
{
// Process received data
}
}
}
Comparison of Solutions
| Feature | Stream Buffer | Message Queue |
|---|---|---|
| Transmission Unit | Byte Stream | Independent Messages (Fixed Size) |
| Suitable Scenarios | Continuous data stream transmission such as UART | Event, command, structure data transmission |
| Blocking Behavior | Supports blocking, timeout | Supports blocking, timeout |
| Data Arrangement | FIFO byte array | FIFO message queue |
| Interrupt Safety | Supports read/write from ISR | Supports read/write from ISR |
Usage Recommendations
- The size of the stream buffer should be set reasonably based on the maximum data amount in the application.
- The trigger threshold is generally set to 1 or slightly larger to avoid frequent task awakenings.
- When writing, if the buffer is full, consider an appropriate wait time or data discard strategy.
- Stream buffers are suitable for byte stream scenarios and not for cases requiring acknowledgment of individual messages.

Previous Recommendations
RECOMMEND

[1]. 【Linux Basics】 How to Analyze CPU Performance in Embedded Systems
[2]. Can FreeRTOS be Low Power? Yes, Just Change These Configurations!
[3]. 【FreeRTOS Development】 Understand the Semaphore Mechanism Thoroughly
[4]. 【FreeRTOS Development】 Understand Event Groups Thoroughly: A Powerful Tool for Multi-Task Synchronization
I am Aike, an embedded software engineer.
Follow me for more embedded insights.
Remember to like, share, and click to see more,
Your encouragement is my greatest motivation to continue sharing!
See you next time.
Looking forward to your
sharing
likes
views
NEWS
WeChat ID|aike_eureka
Baijiahao|Master Embedded Systems