Overview
Basic Principles

Single Message Code Description
Code to send data to the stream buffer:
xMessageBufferSend(){ /* If a time out is specified and there isn't enough space in the message buffer to send the data, then enter the blocked state to wait for more space. */ if( time out != 0 ) { while( there is insufficient space in the buffer && not timed out waiting ) { Enter the blocked state to wait for space in the buffer } }
if( there is enough space in the buffer ) { write data to buffer sbSEND_COMPLETED() }}
Code to read data from the stream buffer:
xMessageBufferReceive(){ /* If a time out is specified and the buffer doesn't contain any data that can be read, then enter the blocked state to wait for the buffer to contain data. */ if( time out != 0 ) { while( there is no data in the buffer && not timed out waiting ) { Enter the blocked state to wait for data } }
if( there is data in the buffer ) { read data from buffer sbRECEIVE_COMPLETED() }}

As shown by the arrows in the figure, the sending and receiving tasks are located on different MCU cores:
-
The receiving task attempts to read data from an empty message buffer and enters a blocked state to wait for data to arrive.
-
The sending task writes data to the message buffer.
-
sbSEND_COMPLETED() triggers an interrupt in the core where the receiving task is executing.
-
The interrupt service routine calls xMessageBufferSendCompletedFromISR() to unblock the receiving task, which can now read from the buffer since it is no longer empty.
Multi-Message Code Description
However, in cases where there are two or more message buffers, the ISR must first determine which message buffer contains data.
If the number of message buffers is small, there are several methods to achieve this:
-
If hardware permits, each message buffer can use different interrupt lines, maintaining a one-to-one mapping between the interrupt service routine and the message buffer.
-
The interrupt service routine can simply query each message buffer to see if it contains data.
-
Instead of multiple message buffers, a single message buffer can carry metadata (what the message is, what the expected receiver of the message is, etc.) as well as the actual data.
Implementation using sbSEND_COMPLETED():
/* Added to FreeRTOSConfig.h to override the default implementation. */#define sbSEND_COMPLETED( pxStreamBuffer ) vGenerateCoreToCoreInterrupt( pxStreamBuffer )
/* Implemented in a C file. */void vGenerateCoreToCoreInterrupt( MessageBufferHandle_t xUpdatedBuffer ){size_t BytesWritten.
/* Called by the implementation of sbSEND_COMPLETED() in FreeRTOSConfig.h. If this function was called because data was written to any message buffer other than the control message buffer then write the handle of the message buffer that contains data to the control message buffer, then raise an interrupt in the other core. If this function was called because data was written to the control message buffer then do nothing. */ if( xUpdatedBuffer != xControlMessageBuffer ) { BytesWritten = xMessageBufferSend( xControlMessageBuffer, &xUpdatedBuffer, sizeof( xUpdatedBuffer ), 0 );
/* If the bytes could not be written then the control message buffer is too small! */ configASSERT( BytesWritten == sizeof( xUpdatedBuffer );
/* Generate interrupt in the other core (pseudocode). */ GenerateInterrupt(); }}
void InterruptServiceRoutine( void ){MessageBufferHandle_t xUpdatedMessageBuffer;BaseType_t xHigherPriorityTaskWoken = pdFALSE;
/* Receive the handle of the message buffer that contains data from the control message buffer. Ensure to drain the buffer before returning. */ while( xMessageBufferReceiveFromISR( xControlMessageBuffer, &xUpdatedMessageBuffer, sizeof( xUpdatedMessageBuffer ), &xHigherPriorityTaskWoken ) == sizeof( xUpdatedMessageBuffer ) ) { /* Call the API function that sends a notification to any task that is blocked on the xUpdatedMessageBuffer message buffer waiting for data to arrive. */ xMessageBufferSendCompletedFromISR( xUpdatedMessageBuffer, &xHigherPriorityTaskWoken ); }
/* Normal FreeRTOS "yield from interrupt" semantics, where xHigherPriorityTaskWoken is initialised to pdFALSE and will then get set to pdTRUE if the interrupt unblocks a task that has a priority above that of the currently executing task. */ portYIELD_FROM_ISR( xHigherPriorityTaskWoken );}

-
The receiving task attempts to read data from an empty message buffer and enters a blocked state to wait for data to arrive. -
The sending task writes data to the message buffer. -
sbSEND_COMPLETED() sends the handle of the message buffer that now contains data to the control message buffer. -
sbSEND_COMPLETED() triggers an interrupt in the core where the receiving task is executing. -
The interrupt service routine reads the handle of the message buffer containing data from the control message buffer, then passes that handle to the xMessageBufferSendCompletedFromISR() API function to unblock the receiving task, which can now read from the buffer since it is no longer empty.

END
→ Follow us to stay updated ←