Circular buffer

Hi,
I have a stream buffer that receives one byte at a time from UART interrupt. I would like the buffer to behave as a circular buffer, i.e. the oldest character should be automatically overwritten when the buffer if full. Is it possible to do this with FreeRTOS stream buffer and how? Would a queue be better option?
Thanks.

FreeRTOS doesn’t directly have a buffer that works that way. You could implment one by checking for an error when adding the new character to the queue, and if that fails, remove a character (which will be the oldest) then you can add a new one.

Since StreamBuffers only allow a single thing to be extracting off the buffer, you likely need to switch to using a queue. The issue being that the receiving task might have started the procedure to get a character from the buffer. Since StreamBuffers aren’t designed to handle contention, that operation doesn’t need to be protected, but will be done in a critica section on a queue, so you can’t get an interrupt in the middle of that operation.

StreamBuffer assumes only one task will write to the buffer and only one task will read from the buffer. However, if all buffer operations are encapsulated inside critical sections, and all operations are non-blocking, then multiple readers and multiple writers may use the same stream buffer.

Consequentially, this requirement also prevents direct to task notification to be performed by stream_buffer.c. So, blocking read and non-blocking write will requires bookkeeping.

Possible implementation, assumes only the ISR and the reader access the stream buffer:

#define BUFFER_LEN ( ( size_t ) 64 )
/* must be at least 1 in order to block on an empty queue  */
#define TRIGGER_LEVEL ( ( size_t ) 8 )
/* Note, stream_buffer.c validates but ignores TRIGGER_LEVEL when non-blocking */
StreamBufferHandle_t buffer = xStreamBufferCreate( BUFFER_LEN, TRIGGER_LEVEL );
volatile TaskHandle_t readerTask = NULL;

/* Writer */
size_t bytesAvailable = xStreamBufferBytesAvailable( buffer );
/* must be initalized to false */
BaseType_t higherPriorityTaskWoken = pdFALSE;
uint8_t data = readUART();
if( bytesAvailable == BUFFER_LEN )
{
    /* the reader must not wait on a full buffer */
    configASSERT( readerTask == NULL );
    uint8_t discard;
    xStreamBufferReceiveFromISR( buffer, &discard, 1, &higherPriorityTaskWoken );
}
else if( ( readerTask != NULL ) && ( bytesAvailable + 1 >= TRIGGER_LEVEL ) )
{
    xTaskNotifyGiveFromISR( readerTask, &higherPriorityTaskWoken );
    readerTask = NULL;
}

xStreamBufferSendFromISR( buffer, &data, 1, &higherPriorityTaskWoken );

/* Note: refer to the port's portmacro.h to determine how to yield from an ISR */
portYIELD_FROM_ISR( higherPriorityTaskWoken );

/* Reader */
uint8_t data[ BUFFER_LEN ];
size_t bytesReceived;

/* Note: refer to the port to determine whether specific interrupts can be masked */
taskENTER_CRITICAL();
if( xStreamBufferBytesAvailable( buffer ) < TRIGGER_LEVEL )
{
    /* a reader must not be already waiting on the buffer */
    configASSERT( readerTask == NULL );

    /* ensure task is not already pending a notification */
    ( void ) xTaskNotifyStateClear( NULL );
    readerTask = xTaskGetCurrentTaskHandle();
    
    /* cannot block from within a critical section */
    taskEXIT_CRITICAL();
    
    xTaskNotifyWait( portMAX_DELAY );
    taskENTER_CRITICAL();
}
bytesReceived = xStreamBufferReceive( buffer, data, sizeof( data ), ( TickType_t ) 0 );
taskEXIT_CRITICAL();

configASSERT( bytesReceived >= TRIGGER_LEVEL );

/* handle received UART data */