Merge branch 'feature/ringbuffer_static_allocation' into 'master'

Ringbuffers: Add static allocation, refactor, and update API reference

Closes IDFGH-639 and IDFGH-902

See merge request idf/esp-idf!4426
This commit is contained in:
Angus Gratton 2019-06-27 13:20:01 +08:00
commit e6d229d301
4 changed files with 497 additions and 247 deletions

View file

@ -16,7 +16,7 @@
#define FREERTOS_RINGBUF_H
#ifndef INC_FREERTOS_H
#error "include FreeRTOS.h" must appear in source files before "include ringbuf.h"
#error "include FreeRTOS.h" must appear in source files before "include ringbuf.h"
#endif
#ifdef __cplusplus
@ -33,27 +33,52 @@ extern "C" {
typedef void * RingbufHandle_t;
typedef enum {
/**
* No-split buffers will only store an item in contiguous memory and will
* never split an item. Each item requires an 8 byte overhead for a header
* and will always internally occupy a 32-bit aligned size of space.
*/
RINGBUF_TYPE_NOSPLIT = 0,
/**
* Allow-split buffers will split an item into two parts if necessary in
* order to store it. Each item requires an 8 byte overhead for a header,
* splitting incurs an extra header. Each item will always internally occupy
* a 32-bit aligned size of space.
*/
RINGBUF_TYPE_ALLOWSPLIT,
/**
* Byte buffers store data as a sequence of bytes and do not maintain separate
* items, therefore byte buffers have no overhead. All data is stored as a
* sequence of byte and any number of bytes can be sent or retrieved each
* time.
*/
RINGBUF_TYPE_BYTEBUF
} ringbuf_type_t;
/**
* No-split buffers will only store an item in contiguous memory and will
* never split an item. Each item requires an 8 byte overhead for a header
* and will always internally occupy a 32-bit aligned size of space.
*/
RINGBUF_TYPE_NOSPLIT = 0,
/**
* Allow-split buffers will split an item into two parts if necessary in
* order to store it. Each item requires an 8 byte overhead for a header,
* splitting incurs an extra header. Each item will always internally occupy
* a 32-bit aligned size of space.
*/
RINGBUF_TYPE_ALLOWSPLIT,
/**
* Byte buffers store data as a sequence of bytes and do not maintain separate
* items, therefore byte buffers have no overhead. All data is stored as a
* sequence of byte and any number of bytes can be sent or retrieved each
* time.
*/
RINGBUF_TYPE_BYTEBUF,
RINGBUF_TYPE_MAX,
} RingbufferType_t;
/**
* @brief Struct that is equivalent in size to the ring buffer's data structure
*
* The contents of this struct are not meant to be used directly. This
* structure is meant to be used when creating a statically allocated ring
* buffer where this struct is of the exact size required to store a ring
* buffer's control data structure.
*
* @note The CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION option must be enabled for
* this structure to be available.
*/
#if ( configSUPPORT_STATIC_ALLOCATION == 1)
typedef struct xSTATIC_RINGBUFFER {
/** @cond */ //Doxygen command to hide this structure from API Reference
size_t xDummy1[2];
UBaseType_t uxDummy2;
BaseType_t xDummy3;
void *pvDummy4[10];
StaticSemaphore_t xDummy5[2];
portMUX_TYPE muxDummy;
/** @endcond */
} StaticRingbuffer_t;
#endif
/**
* @brief Create a ring buffer
@ -66,7 +91,7 @@ typedef enum {
*
* @return A handle to the created ring buffer, or NULL in case of error.
*/
RingbufHandle_t xRingbufferCreate(size_t xBufferSize, ringbuf_type_t xBufferType);
RingbufHandle_t xRingbufferCreate(size_t xBufferSize, RingbufferType_t xBufferType);
/**
* @brief Create a ring buffer of type RINGBUF_TYPE_NOSPLIT for a fixed item_size
@ -81,11 +106,36 @@ RingbufHandle_t xRingbufferCreate(size_t xBufferSize, ringbuf_type_t xBufferType
*/
RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum);
/**
* @brief Create a ring buffer but manually provide the required memory
*
* @param[in] xBufferSize Size of the buffer in bytes.
* @param[in] xBufferType Type of ring buffer, see documentation
* @param[in] pucRingbufferStorage Pointer to the ring buffer's storage area.
* Storage area must of the same size as specified by xBufferSize
* @param[in] pxStaticRingbuffer Pointed to a struct of type StaticRingbuffer_t
* which will be used to hold the ring buffer's data structure
*
* @note The CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION option must be enabled
* for this to be available
*
* @note xBufferSize of no-split/allow-split buffers MUST be 32-bit aligned.
*
* @return A handle to the created ring buffer
*/
#if ( configSUPPORT_STATIC_ALLOCATION == 1)
RingbufHandle_t xRingbufferCreateStatic(size_t xBufferSize,
RingbufferType_t xBufferType,
uint8_t *pucRingbufferStorage,
StaticRingbuffer_t *pxStaticRingbuffer);
#endif
/**
* @brief Insert an item into the ring buffer
*
* Attempt to insert an item into the ring buffer. This function will block until
* enough free space is available or until it timesout.
* enough free space is available or until it times out.
*
* @param[in] xRingbuffer Ring buffer to insert the item into
* @param[in] pvItem Pointer to data to insert. NULL is allowed if xItemSize is 0.
@ -101,7 +151,10 @@ RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum);
* - pdTRUE if succeeded
* - pdFALSE on time-out or when the data is larger than the maximum permissible size of the buffer
*/
BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, TickType_t xTicksToWait);
BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer,
const void *pvItem,
size_t xItemSize,
TickType_t xTicksToWait);
/**
* @brief Insert an item into the ring buffer in an ISR
@ -123,13 +176,16 @@ BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size
* - pdTRUE if succeeded
* - pdFALSE when the ring buffer does not have space.
*/
BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, BaseType_t *pxHigherPriorityTaskWoken);
BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer,
const void *pvItem,
size_t xItemSize,
BaseType_t *pxHigherPriorityTaskWoken);
/**
* @brief Retrieve an item from the ring buffer
*
* Attempt to retrieve an item from the ring buffer. This function will block
* until an item is available or until it timesout.
* until an item is available or until it times out.
*
* @param[in] xRingbuffer Ring buffer to retrieve the item from
* @param[out] pxItemSize Pointer to a variable to which the size of the retrieved item will be written.
@ -168,7 +224,7 @@ void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize)
* Attempt to retrieve a split item from an allow-split ring buffer. If the item
* is not split, only a single item is retried. If the item is split, both parts
* will be retrieved. This function will block until an item is available or
* until it timesout.
* until it times out.
*
* @param[in] xRingbuffer Ring buffer to retrieve the item from
* @param[out] ppvHeadItem Double pointer to first part (set to NULL if no items were retrieved)
@ -184,7 +240,12 @@ void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize)
* - pdTRUE if an item (split or unsplit) was retrieved
* - pdFALSE when no item was retrieved
*/
BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize, TickType_t xTicksToWait);
BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer,
void **ppvHeadItem,
void **ppvTailItem,
size_t *pxHeadItemSize,
size_t *pxTailItemSize,
TickType_t xTicksToWait);
/**
* @brief Retrieve a split item from an allow-split ring buffer in an ISR
@ -207,14 +268,18 @@ BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer, void **ppvHeadIt
* - pdTRUE if an item (split or unsplit) was retrieved
* - pdFALSE when no item was retrieved
*/
BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize);
BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer,
void **ppvHeadItem,
void **ppvTailItem,
size_t *pxHeadItemSize,
size_t *pxTailItemSize);
/**
* @brief Retrieve bytes from a byte buffer, specifying the maximum amount of bytes to retrieve
*
* Attempt to retrieve data from a byte buffer whilst specifying a maximum number
* of bytes to retrieve. This function will block until there is data available
* for retrieval or until it timesout.
* for retrieval or until it times out.
*
* @param[in] xRingbuffer Ring buffer to retrieve the item from
* @param[out] pxItemSize Pointer to a variable to which the size of the retrieved item will be written.
@ -230,7 +295,10 @@ BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer, void **pp
* the length of the item.
* - NULL on timeout, *pxItemSize is untouched in that case.
*/
void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait, size_t xMaxSize);
void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer,
size_t *pxItemSize,
TickType_t xTicksToWait,
size_t xMaxSize);
/**
* @brief Retrieve bytes from a byte buffer, specifying the maximum amount of
@ -281,6 +349,10 @@ void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, Bas
* @brief Delete a ring buffer
*
* @param[in] xRingbuffer Ring buffer to delete
*
* @note This function will not deallocate any memory if the ring buffer was
* created using xRingbufferCreateStatic(). Deallocation must be done
* manually be the user.
*/
void vRingbufferDelete(RingbufHandle_t xRingbuffer);
@ -292,6 +364,12 @@ void vRingbufferDelete(RingbufHandle_t xRingbuffer);
*
* @param[in] xRingbuffer Ring buffer to query
*
* @note The max item size for a no-split buffer is limited to
* ((buffer_size/2)-header_size). This limit is imposed so that an item
* of max item size can always be sent to the an empty no-split buffer
* regardless of the internal positions of the buffer's read/write/free
* pointers.
*
* @return Maximum size, in bytes, of an item that can be placed in a ring buffer.
*/
size_t xRingbufferGetMaxItemSize(RingbufHandle_t xRingbuffer);
@ -307,6 +385,10 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t xRingbuffer);
* the same ring buffer, it is the application's responsibility to
* ensure atomic access to this API and the subsequent Send
*
* @note An empty no-split buffer has a max current free size for an item
* that is limited to ((buffer_size/2)-header_size). See API reference
* for xRingbufferGetMaxItemSize().
*
* @param[in] xRingbuffer Ring buffer to query
*
* @return Current free size, in bytes, available for an entry
@ -373,7 +455,11 @@ BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t xRingbuffer, QueueS
* @param[out] uxWrite Pointer use to store write pointer position
* @param[out] uxItemsWaiting Pointer use to store number of items (bytes for byte buffer) waiting to be retrieved
*/
void vRingbufferGetInfo(RingbufHandle_t xRingbuffer, UBaseType_t *uxFree, UBaseType_t *uxRead, UBaseType_t *uxWrite, UBaseType_t *uxItemsWaiting);
void vRingbufferGetInfo(RingbufHandle_t xRingbuffer,
UBaseType_t *uxFree,
UBaseType_t *uxRead,
UBaseType_t *uxWrite,
UBaseType_t *uxItemsWaiting);
/**
* @brief Debugging function to print the internal pointers in the ring buffer
@ -382,30 +468,9 @@ void vRingbufferGetInfo(RingbufHandle_t xRingbuffer, UBaseType_t *uxFree, UBaseT
*/
void xRingbufferPrintInfo(RingbufHandle_t xRingbuffer);
/* -------------------------------- Deprecated Functions --------------------------- */
/* ------------------------------- Deprecated ------------------------------- */
/** @cond */ //Doxygen command to hide deprecated function from API Reference
/*
* Deprecated as function is not thread safe and does not check if an item is
* actually available for retrieval. Use xRingbufferReceiveSplit() instead for
* thread safe method of retrieve a split item.
*/
bool xRingbufferIsNextItemWrapped(RingbufHandle_t xRingbuffer) __attribute__((deprecated));
/*
* Deprecated as queue sets are not meant to be used for writing to buffers. Adding
* the ring buffer write semaphore to a queue set will break queue set usage rules,
* as every read of a semaphore must be preceded by a call to xQueueSelectFromSet().
* QueueSetWrite no longer supported.
*/
BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet) __attribute__((deprecated));
/*
* Deprecated as queue sets are not meant to be used for writing to buffers.
* QueueSetWrite no longer supported.
*/
BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet) __attribute__((deprecated));
/** @endcond */
typedef RingbufferType_t ringbuf_type_t __attribute__((deprecated));
#ifdef __cplusplus
}

View file

@ -21,18 +21,28 @@
//32-bit alignment macros
#define rbALIGN_SIZE( xSize ) ( ( xSize + portBYTE_ALIGNMENT_MASK ) & ~portBYTE_ALIGNMENT_MASK )
#define rbCHECK_ALIGNED( pvPtr ) ( ( ( UBaseType_t ) pvPtr & portBYTE_ALIGNMENT_MASK ) == 0 )
#define rbCHECK_ALIGNED( pvPtr ) ( ( ( UBaseType_t ) ( pvPtr ) & portBYTE_ALIGNMENT_MASK ) == 0 )
//Ring buffer flags
#define rbALLOW_SPLIT_FLAG ( ( UBaseType_t ) 1 ) //The ring buffer allows items to be split
#define rbBYTE_BUFFER_FLAG ( ( UBaseType_t ) 2 ) //The ring buffer is a byte buffer
#define rbBUFFER_FULL_FLAG ( ( UBaseType_t ) 4 ) //The ring buffer is currently full (write pointer == free pointer)
#define rbBUFFER_STATIC_FLAG ( ( UBaseType_t ) 8 ) //The ring buffer is statically allocated
//Item flags
#define rbITEM_FREE_FLAG ( ( UBaseType_t ) 1 ) //Item has been retrieved and returned by application, free to overwrite
#define rbITEM_DUMMY_DATA_FLAG ( ( UBaseType_t ) 2 ) //Data from here to end of the ring buffer is dummy data. Restart reading at start of head of the buffer
#define rbITEM_SPLIT_FLAG ( ( UBaseType_t ) 4 ) //Valid for RINGBUF_TYPE_ALLOWSPLIT, indicating that rest of the data is wrapped around
//Static allocation related
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
#define rbGET_TX_SEM_HANDLE( pxRingbuffer ) ( (SemaphoreHandle_t) &(pxRingbuffer->xTransSemStatic) )
#define rbGET_RX_SEM_HANDLE( pxRingbuffer ) ( (SemaphoreHandle_t) &(pxRingbuffer->xRecvSemStatic) )
#else
#define rbGET_TX_SEM_HANDLE( pxRingbuffer ) ( pxRingbuffer->xTransSemHandle )
#define rbGET_RX_SEM_HANDLE( pxRingbuffer ) ( pxRingbuffer->xRecvSemHandle )
#endif
typedef struct {
//This size of this structure must be 32-bit aligned
size_t xItemLen;
@ -40,7 +50,7 @@ typedef struct {
} ItemHeader_t;
#define rbHEADER_SIZE sizeof(ItemHeader_t)
typedef struct Ringbuffer_t Ringbuffer_t;
typedef struct RingbufferDefinition Ringbuffer_t;
typedef BaseType_t (*CheckItemFitsFunction_t)(Ringbuffer_t *pxRingbuffer, size_t xItemSize);
typedef void (*CopyItemFunction_t)(Ringbuffer_t *pxRingbuffer, const uint8_t *pcItem, size_t xItemSize);
typedef BaseType_t (*CheckItemAvailFunction_t) (Ringbuffer_t *pxRingbuffer);
@ -48,10 +58,10 @@ typedef void *(*GetItemFunction_t)(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsS
typedef void (*ReturnItemFunction_t)(Ringbuffer_t *pxRingbuffer, uint8_t *pvItem);
typedef size_t (*GetCurMaxSizeFunction_t)(Ringbuffer_t *pxRingbuffer);
struct Ringbuffer_t {
typedef struct RingbufferDefinition {
size_t xSize; //Size of the data storage
UBaseType_t uxRingbufferFlags; //Flags to indicate the type and status of ring buffer
size_t xMaxItemSize; //Maximum item size
UBaseType_t uxRingbufferFlags; //Flags to indicate the type and status of ring buffer
CheckItemFitsFunction_t xCheckItemFits; //Function to check if item can currently fit in ring buffer
CopyItemFunction_t vCopyItem; //Function to copy item to ring buffer
@ -66,11 +76,37 @@ struct Ringbuffer_t {
uint8_t *pucTail; //Pointer to the end of the ring buffer storage area
BaseType_t xItemsWaiting; //Number of items/bytes(for byte buffers) currently in ring buffer that have not yet been read
SemaphoreHandle_t xFreeSpaceSemaphore; //Binary semaphore, wakes up writing threads when more free space becomes available or when another thread times out attempting to write
SemaphoreHandle_t xItemsBufferedSemaphore; //Binary semaphore, indicates there are new packets in the circular buffer. See remark.
/*
* TransSem: Binary semaphore used to indicate to a blocked transmitting tasks
* that more free space has become available or that the block has
* timed out.
*
* RecvSem: Binary semaphore used to indicate to a blocked receiving task that
* new data/item has been written to the ring buffer.
*
* Note - When static allocation is enabled, the two semaphores are always
* statically stored in the ring buffer's control structure
* regardless of whether the ring buffer is allocated dynamically or
* statically. When static allocation is disabled, the two semaphores
* are allocated dynamically and their handles stored instead, thus
* making the ring buffer's control structure slightly smaller when
* static allocation is disabled.
*/
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
StaticSemaphore_t xTransSemStatic;
StaticSemaphore_t xRecvSemStatic;
#else
SemaphoreHandle_t xTransSemHandle;
SemaphoreHandle_t xRecvSemHandle;
#endif
portMUX_TYPE mux; //Spinlock required for SMP
};
} Ringbuffer_t;
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
#if __GNUC_PREREQ(4, 6)
_Static_assert(sizeof(StaticRingbuffer_t) == sizeof(Ringbuffer_t), "StaticRingbuffer_t != Ringbuffer_t");
#endif
#endif
/*
Remark: A counting semaphore for items_buffered_sem would be more logical, but counting semaphores in
FreeRTOS need a maximum count, and allocate more memory the larger the maximum count is. Here, we
@ -78,13 +114,20 @@ would need to set the maximum to the maximum amount of times a null-byte unit fi
which is quite high and so would waste a fair amount of memory.
*/
/* ------------------------------------------------ Static Declarations ------------------------------------------ */
/* --------------------------- Static Declarations -------------------------- */
/*
* WARNING: All of the following static functions (except generic functions)
* ARE NOT THREAD SAFE. Therefore they should only be called within a critical
* section (using spin locks)
*/
//Initialize a ring buffer after space has been allocated for it
static void prvInitializeNewRingbuffer(size_t xBufferSize,
RingbufferType_t xBufferType,
Ringbuffer_t *pxNewRingbuffer,
uint8_t *pucRingbufferStorage);
//Calculate current amount of free space (in bytes) in the ring buffer
static size_t prvGetFreeSize(Ringbuffer_t *pxRingbuffer);
@ -107,10 +150,16 @@ static void prvCopyItemAllowSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *puc
static void prvCopyItemByteBuf(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize);
//Retrieve item from no-split/allow-split ring buffer. *pxIsSplit is set to pdTRUE if the retrieved item is split
static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsSplit, size_t xUnusedParam, size_t *pxItemSize);
static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer,
BaseType_t *pxIsSplit,
size_t xUnusedParam,
size_t *pxItemSize);
//Retrieve data from byte buffer. If xMaxSize is 0, all continuous data is retrieved
static void *prvGetItemByteBuf(Ringbuffer_t *pxRingbuffer, BaseType_t *pxUnusedParam ,size_t xMaxSize, size_t *pxItemSize);
static void *prvGetItemByteBuf(Ringbuffer_t *pxRingbuffer,
BaseType_t *pxUnusedParam,
size_t xMaxSize,
size_t *pxItemSize);
//Return an item to a split/no-split ring buffer
static void prvReturnItemDefault(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem);
@ -133,12 +182,73 @@ static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer);
* a split item will be retrieved. xMaxSize will only take effect if called on
* byte buffers.
*/
static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer, void **pvItem1, void **pvItem2, size_t *xItemSize1, size_t *xItemSize2, size_t xMaxSize, TickType_t xTicksToWait);
static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
void **pvItem1,
void **pvItem2,
size_t *xItemSize1,
size_t *xItemSize2,
size_t xMaxSize,
TickType_t xTicksToWait);
//Generic function used to retrieve an item/data from ring buffers in an ISR
static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer, void **pvItem1, void **pvItem2, size_t *xItemSize1, size_t *xItemSize2, size_t xMaxSize);
static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer,
void **pvItem1,
void **pvItem2,
size_t *xItemSize1,
size_t *xItemSize2,
size_t xMaxSize);
/* ------------------------------------------------ Static Definitions ------------------------------------------- */
/* --------------------------- Static Definitions --------------------------- */
static void prvInitializeNewRingbuffer(size_t xBufferSize,
RingbufferType_t xBufferType,
Ringbuffer_t *pxNewRingbuffer,
uint8_t *pucRingbufferStorage)
{
//Initialize values
pxNewRingbuffer->xSize = xBufferSize;
pxNewRingbuffer->pucHead = pucRingbufferStorage;
pxNewRingbuffer->pucTail = pucRingbufferStorage + xBufferSize;
pxNewRingbuffer->pucFree = pucRingbufferStorage;
pxNewRingbuffer->pucRead = pucRingbufferStorage;
pxNewRingbuffer->pucWrite = pucRingbufferStorage;
pxNewRingbuffer->xItemsWaiting = 0;
pxNewRingbuffer->uxRingbufferFlags = 0;
//Initialize type dependent values and function pointers
if (xBufferType == RINGBUF_TYPE_NOSPLIT) {
pxNewRingbuffer->xCheckItemFits = prvCheckItemFitsDefault;
pxNewRingbuffer->vCopyItem = prvCopyItemNoSplit;
pxNewRingbuffer->pvGetItem = prvGetItemDefault;
pxNewRingbuffer->vReturnItem = prvReturnItemDefault;
/*
* Worst case scenario is when the read/write/free pointers are all
* pointing to the halfway point of the buffer.
*/
pxNewRingbuffer->xMaxItemSize = rbALIGN_SIZE(pxNewRingbuffer->xSize / 2) - rbHEADER_SIZE;
pxNewRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeNoSplit;
} else if (xBufferType == RINGBUF_TYPE_ALLOWSPLIT) {
pxNewRingbuffer->uxRingbufferFlags |= rbALLOW_SPLIT_FLAG;
pxNewRingbuffer->xCheckItemFits = prvCheckItemFitsDefault;
pxNewRingbuffer->vCopyItem = prvCopyItemAllowSplit;
pxNewRingbuffer->pvGetItem = prvGetItemDefault;
pxNewRingbuffer->vReturnItem = prvReturnItemDefault;
//Worst case an item is split into two, incurring two headers of overhead
pxNewRingbuffer->xMaxItemSize = pxNewRingbuffer->xSize - (sizeof(ItemHeader_t) * 2);
pxNewRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeAllowSplit;
} else { //Byte Buffer
pxNewRingbuffer->uxRingbufferFlags |= rbBYTE_BUFFER_FLAG;
pxNewRingbuffer->xCheckItemFits = prvCheckItemFitsByteBuffer;
pxNewRingbuffer->vCopyItem = prvCopyItemByteBuf;
pxNewRingbuffer->pvGetItem = prvGetItemByteBuf;
pxNewRingbuffer->vReturnItem = prvReturnItemByteBuf;
//Byte buffers do not incur any overhead
pxNewRingbuffer->xMaxItemSize = pxNewRingbuffer->xSize;
pxNewRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeByteBuf;
}
xSemaphoreGive(rbGET_TX_SEM_HANDLE(pxNewRingbuffer));
vPortCPUInitializeMutex(&pxNewRingbuffer->mux);
}
static size_t prvGetFreeSize(Ringbuffer_t *pxRingbuffer)
{
@ -333,7 +443,10 @@ static BaseType_t prvCheckItemAvail(Ringbuffer_t *pxRingbuffer)
}
}
static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsSplit, size_t xUnusedParam, size_t *pxItemSize)
static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer,
BaseType_t *pxIsSplit,
size_t xUnusedParam,
size_t *pxItemSize)
{
//Check arguments and buffer state
ItemHeader_t *pxHeader = (ItemHeader_t *)pxRingbuffer->pucRead;
@ -371,7 +484,10 @@ static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsSplit
return (void *)pcReturn;
}
static void *prvGetItemByteBuf(Ringbuffer_t *pxRingbuffer, BaseType_t *pxUnusedParam ,size_t xMaxSize, size_t *pxItemSize)
static void *prvGetItemByteBuf(Ringbuffer_t *pxRingbuffer,
BaseType_t *pxUnusedParam,
size_t xMaxSize,
size_t *pxItemSize)
{
//Check arguments and buffer state
configASSERT((pxRingbuffer->xItemsWaiting > 0) && ((pxRingbuffer->pucRead != pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG))); //Check there are items to be read
@ -552,7 +668,13 @@ static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer)
return xFreeSize;
}
static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer, void **pvItem1, void **pvItem2, size_t *xItemSize1, size_t *xItemSize2, size_t xMaxSize, TickType_t xTicksToWait)
static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
void **pvItem1,
void **pvItem2,
size_t *xItemSize1,
size_t *xItemSize2,
size_t xMaxSize,
TickType_t xTicksToWait)
{
BaseType_t xReturn = pdFALSE;
BaseType_t xReturnSemaphore = pdFALSE;
@ -560,7 +682,7 @@ static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer, void **pvItem1,
TickType_t xTicksRemaining = xTicksToWait;
while (xTicksRemaining <= xTicksToWait) { //xTicksToWait will underflow once xTaskGetTickCount() > ticks_end
//Block until more free space becomes available or timeout
if (xSemaphoreTake(pxRingbuffer->xItemsBufferedSemaphore, xTicksRemaining) != pdTRUE) {
if (xSemaphoreTake(rbGET_RX_SEM_HANDLE(pxRingbuffer), xTicksRemaining) != pdTRUE) {
xReturn = pdFALSE; //Timed out attempting to get semaphore
break;
}
@ -606,12 +728,17 @@ static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer, void **pvItem1,
}
if (xReturnSemaphore == pdTRUE) {
xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore); //Give semaphore back so other tasks can retrieve
xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer)); //Give semaphore back so other tasks can retrieve
}
return xReturn;
}
static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer, void **pvItem1, void **pvItem2, size_t *xItemSize1, size_t *xItemSize2, size_t xMaxSize)
static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer,
void **pvItem1,
void **pvItem2,
size_t *xItemSize1,
size_t *xItemSize2,
size_t xMaxSize)
{
BaseType_t xReturn = pdFALSE;
BaseType_t xReturnSemaphore = pdFALSE;
@ -644,95 +771,54 @@ static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer, void **pv
portEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
if (xReturnSemaphore == pdTRUE) {
xSemaphoreGiveFromISR(pxRingbuffer->xItemsBufferedSemaphore, NULL); //Give semaphore back so other tasks can retrieve
xSemaphoreGiveFromISR(rbGET_RX_SEM_HANDLE(pxRingbuffer), NULL); //Give semaphore back so other tasks can retrieve
}
return xReturn;
}
/* ------------------------------------------------- Public Definitions -------------------------------------------- */
/* --------------------------- Public Definitions --------------------------- */
RingbufHandle_t xRingbufferCreate(size_t xBufferSize, ringbuf_type_t xBufferType)
RingbufHandle_t xRingbufferCreate(size_t xBufferSize, RingbufferType_t xBufferType)
{
configASSERT(xBufferSize > 0);
configASSERT(xBufferType < RINGBUF_TYPE_MAX);
//Allocate memory
Ringbuffer_t *pxRingbuffer = calloc(1, sizeof(Ringbuffer_t));
if (pxRingbuffer == NULL) {
goto err;
}
if (xBufferType != RINGBUF_TYPE_BYTEBUF) {
xBufferSize = rbALIGN_SIZE(xBufferSize); //xBufferSize is rounded up for no-split/allow-split buffers
}
pxRingbuffer->pucHead = malloc(xBufferSize);
if (pxRingbuffer->pucHead == NULL) {
Ringbuffer_t *pxNewRingbuffer = calloc(1, sizeof(Ringbuffer_t));
uint8_t *pucRingbufferStorage = malloc(xBufferSize);
if (pxNewRingbuffer == NULL || pucRingbufferStorage == NULL) {
goto err;
}
//Initialize values
pxRingbuffer->xSize = xBufferSize;
pxRingbuffer->pucTail = pxRingbuffer->pucHead + xBufferSize;
pxRingbuffer->pucFree = pxRingbuffer->pucHead;
pxRingbuffer->pucRead = pxRingbuffer->pucHead;
pxRingbuffer->pucWrite = pxRingbuffer->pucHead;
pxRingbuffer->xItemsWaiting = 0;
pxRingbuffer->xFreeSpaceSemaphore = xSemaphoreCreateBinary();
pxRingbuffer->xItemsBufferedSemaphore = xSemaphoreCreateBinary();
pxRingbuffer->uxRingbufferFlags = 0;
//Initialize type dependent values and function pointers
if (xBufferType == RINGBUF_TYPE_NOSPLIT) {
pxRingbuffer->xCheckItemFits = prvCheckItemFitsDefault;
pxRingbuffer->vCopyItem = prvCopyItemNoSplit;
pxRingbuffer->pvGetItem = prvGetItemDefault;
pxRingbuffer->vReturnItem = prvReturnItemDefault;
/*
* Buffer lengths are always aligned. No-split buffer (read/write/free)
* pointers are also always aligned. Therefore worse case scenario is
* the write pointer is at the most aligned halfway point.
*/
pxRingbuffer->xMaxItemSize = rbALIGN_SIZE(pxRingbuffer->xSize / 2) - rbHEADER_SIZE;
pxRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeNoSplit;
} else if (xBufferType == RINGBUF_TYPE_ALLOWSPLIT) {
pxRingbuffer->uxRingbufferFlags |= rbALLOW_SPLIT_FLAG;
pxRingbuffer->xCheckItemFits = prvCheckItemFitsDefault;
pxRingbuffer->vCopyItem = prvCopyItemAllowSplit;
pxRingbuffer->pvGetItem = prvGetItemDefault;
pxRingbuffer->vReturnItem = prvReturnItemDefault;
//Worst case an item is split into two, incurring two headers of overhead
pxRingbuffer->xMaxItemSize = pxRingbuffer->xSize - (sizeof(ItemHeader_t) * 2);
pxRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeAllowSplit;
} else if (xBufferType == RINGBUF_TYPE_BYTEBUF) {
pxRingbuffer->uxRingbufferFlags |= rbBYTE_BUFFER_FLAG;
pxRingbuffer->xCheckItemFits = prvCheckItemFitsByteBuffer;
pxRingbuffer->vCopyItem = prvCopyItemByteBuf;
pxRingbuffer->pvGetItem = prvGetItemByteBuf;
pxRingbuffer->vReturnItem = prvReturnItemByteBuf;
//Byte buffers do not incur any overhead
pxRingbuffer->xMaxItemSize = pxRingbuffer->xSize;
pxRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeByteBuf;
} else {
//Unsupported type
configASSERT(0);
}
if (pxRingbuffer->xFreeSpaceSemaphore == NULL || pxRingbuffer->xItemsBufferedSemaphore == NULL) {
//Initialize Semaphores
#if ( configSUPPORT_STATIC_ALLOCATION == 1)
//We don't use the handles for static semaphores, and xSemaphoreCreateBinaryStatic will never fail thus no need to check static case
xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xTransSemStatic));
xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xRecvSemStatic));
#else
pxNewRingbuffer->xTransSemHandle = xSemaphoreCreateBinary();
pxNewRingbuffer->xRecvSemHandle = xSemaphoreCreateBinary();
if (pxNewRingbuffer->xTransSemHandle == NULL || pxNewRingbuffer->xRecvSemHandle == NULL) {
if (pxNewRingbuffer->xTransSemHandle != NULL) {
vSemaphoreDelete(pxNewRingbuffer->xTransSemHandle);
}
if (pxNewRingbuffer->xRecvSemHandle != NULL) {
vSemaphoreDelete(pxNewRingbuffer->xRecvSemHandle);
}
goto err;
}
xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore);
vPortCPUInitializeMutex(&pxRingbuffer->mux);
#endif
return (RingbufHandle_t)pxRingbuffer;
prvInitializeNewRingbuffer(xBufferSize, xBufferType, pxNewRingbuffer, pucRingbufferStorage);
return (RingbufHandle_t)pxNewRingbuffer;
err:
//Some error has happened. Free/destroy all allocated things and return NULL.
if (pxRingbuffer) {
free(pxRingbuffer->pucHead);
if (pxRingbuffer->xFreeSpaceSemaphore) {
vSemaphoreDelete(pxRingbuffer->xFreeSpaceSemaphore);
}
if (pxRingbuffer->xItemsBufferedSemaphore) {
vSemaphoreDelete(pxRingbuffer->xItemsBufferedSemaphore);
}
}
free(pxRingbuffer);
//An error has occurred, Free memory and return NULL
free(pxNewRingbuffer);
free(pucRingbufferStorage);
return NULL;
}
@ -741,7 +827,34 @@ RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum)
return xRingbufferCreate((rbALIGN_SIZE(xItemSize) + rbHEADER_SIZE) * xItemNum, RINGBUF_TYPE_NOSPLIT);
}
BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, TickType_t xTicksToWait)
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
RingbufHandle_t xRingbufferCreateStatic(size_t xBufferSize,
RingbufferType_t xBufferType,
uint8_t *pucRingbufferStorage,
StaticRingbuffer_t *pxStaticRingbuffer)
{
//Check arguments
configASSERT(xBufferSize > 0);
configASSERT(xBufferType < RINGBUF_TYPE_MAX);
configASSERT(pucRingbufferStorage != NULL && pxStaticRingbuffer != NULL);
if (xBufferType != RINGBUF_TYPE_BYTEBUF) {
//No-split/allow-split buffer sizes must be 32-bit aligned
configASSERT(rbCHECK_ALIGNED(xBufferSize));
}
Ringbuffer_t *pxNewRingbuffer = (Ringbuffer_t *)pxStaticRingbuffer;
xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xTransSemStatic));
xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xRecvSemStatic));
prvInitializeNewRingbuffer(xBufferSize, xBufferType, pxNewRingbuffer, pucRingbufferStorage);
pxNewRingbuffer->uxRingbufferFlags |= rbBUFFER_STATIC_FLAG;
return (RingbufHandle_t)pxNewRingbuffer;
}
#endif
BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer,
const void *pvItem,
size_t xItemSize,
TickType_t xTicksToWait)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
@ -761,7 +874,7 @@ BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size
TickType_t xTicksRemaining = xTicksToWait;
while (xTicksRemaining <= xTicksToWait) { //xTicksToWait will underflow once xTaskGetTickCount() > ticks_end
//Block until more free space becomes available or timeout
if (xSemaphoreTake(pxRingbuffer->xFreeSpaceSemaphore, xTicksRemaining) != pdTRUE) {
if (xSemaphoreTake(rbGET_TX_SEM_HANDLE(pxRingbuffer), xTicksRemaining) != pdTRUE) {
xReturn = pdFALSE;
break;
}
@ -791,15 +904,18 @@ BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size
if (xReturn == pdTRUE) {
//Indicate item was successfully sent
xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore);
xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer));
}
if (xReturnSemaphore == pdTRUE) {
xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore); //Give back semaphore so other tasks can send
xSemaphoreGive(rbGET_TX_SEM_HANDLE(pxRingbuffer)); //Give back semaphore so other tasks can send
}
return xReturn;
}
BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, BaseType_t *pxHigherPriorityTaskWoken)
BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer,
const void *pvItem,
size_t xItemSize,
BaseType_t *pxHigherPriorityTaskWoken)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
@ -830,10 +946,10 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer, const void *pvIte
if (xReturn == pdTRUE) {
//Indicate item was successfully sent
xSemaphoreGiveFromISR(pxRingbuffer->xItemsBufferedSemaphore, pxHigherPriorityTaskWoken);
xSemaphoreGiveFromISR(rbGET_RX_SEM_HANDLE(pxRingbuffer), pxHigherPriorityTaskWoken);
}
if (xReturnSemaphore == pdTRUE) {
xSemaphoreGiveFromISR(pxRingbuffer->xFreeSpaceSemaphore, pxHigherPriorityTaskWoken); //Give back semaphore so other tasks can send
xSemaphoreGiveFromISR(rbGET_TX_SEM_HANDLE(pxRingbuffer), pxHigherPriorityTaskWoken); //Give back semaphore so other tasks can send
}
return xReturn;
}
@ -876,7 +992,12 @@ void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize)
}
}
BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize, TickType_t xTicksToWait)
BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer,
void **ppvHeadItem,
void **ppvTailItem,
size_t *pxHeadItemSize,
size_t *pxTailItemSize,
TickType_t xTicksToWait)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
@ -911,7 +1032,11 @@ BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer, void **ppvHeadIt
}
}
BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize)
BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer,
void **ppvHeadItem,
void **ppvTailItem,
size_t *pxHeadItemSize,
size_t *pxTailItemSize)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
@ -945,7 +1070,10 @@ BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer, void **pp
}
}
void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait, size_t xMaxSize)
void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer,
size_t *pxItemSize,
TickType_t xTicksToWait,
size_t xMaxSize)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
@ -1000,7 +1128,7 @@ void vRingbufferReturnItem(RingbufHandle_t xRingbuffer, void *pvItem)
portENTER_CRITICAL(&pxRingbuffer->mux);
pxRingbuffer->vReturnItem(pxRingbuffer, (uint8_t *)pvItem);
portEXIT_CRITICAL(&pxRingbuffer->mux);
xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore);
xSemaphoreGive(rbGET_TX_SEM_HANDLE(pxRingbuffer));
}
void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, BaseType_t *pxHigherPriorityTaskWoken)
@ -1012,7 +1140,7 @@ void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, Bas
portENTER_CRITICAL_ISR(&pxRingbuffer->mux);
pxRingbuffer->vReturnItem(pxRingbuffer, (uint8_t *)pvItem);
portEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
xSemaphoreGiveFromISR(pxRingbuffer->xFreeSpaceSemaphore, pxHigherPriorityTaskWoken);
xSemaphoreGiveFromISR(rbGET_TX_SEM_HANDLE(pxRingbuffer), pxHigherPriorityTaskWoken);
}
void vRingbufferDelete(RingbufHandle_t xRingbuffer)
@ -1020,15 +1148,16 @@ void vRingbufferDelete(RingbufHandle_t xRingbuffer)
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
if (pxRingbuffer) {
free(pxRingbuffer->pucHead);
if (pxRingbuffer->xFreeSpaceSemaphore) {
vSemaphoreDelete(pxRingbuffer->xFreeSpaceSemaphore);
}
if (pxRingbuffer->xItemsBufferedSemaphore) {
vSemaphoreDelete(pxRingbuffer->xItemsBufferedSemaphore);
}
vSemaphoreDelete(rbGET_TX_SEM_HANDLE(pxRingbuffer));
vSemaphoreDelete(rbGET_RX_SEM_HANDLE(pxRingbuffer));
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_STATIC_FLAG) {
//Ring buffer was statically allocated, no need to free
return;
}
#endif
free(pxRingbuffer->pucHead);
free(pxRingbuffer);
}
@ -1059,11 +1188,11 @@ BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHan
BaseType_t xReturn;
portENTER_CRITICAL(&pxRingbuffer->mux);
//Cannot add semaphore to queue set if semaphore is not empty. Temporarily hold semaphore
BaseType_t xHoldSemaphore = xSemaphoreTake(pxRingbuffer->xItemsBufferedSemaphore, 0);
xReturn = xQueueAddToSet(pxRingbuffer->xItemsBufferedSemaphore, xQueueSet);
BaseType_t xHoldSemaphore = xSemaphoreTake(rbGET_RX_SEM_HANDLE(pxRingbuffer), 0);
xReturn = xQueueAddToSet(rbGET_RX_SEM_HANDLE(pxRingbuffer), xQueueSet);
if (xHoldSemaphore == pdTRUE) {
//Return semaphore if temporarily held
configASSERT(xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore) == pdTRUE);
configASSERT(xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer)) == pdTRUE);
}
portEXIT_CRITICAL(&pxRingbuffer->mux);
return xReturn;
@ -1074,7 +1203,7 @@ BaseType_t xRingbufferCanRead(RingbufHandle_t xRingbuffer, QueueSetMemberHandle_
//Check if the selected queue set member is the ring buffer's read semaphore
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
return (pxRingbuffer->xItemsBufferedSemaphore == xMember) ? pdTRUE : pdFALSE;
return (rbGET_RX_SEM_HANDLE(pxRingbuffer) == xMember) ? pdTRUE : pdFALSE;
}
BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet)
@ -1085,17 +1214,21 @@ BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t xRingbuffer, QueueS
BaseType_t xReturn;
portENTER_CRITICAL(&pxRingbuffer->mux);
//Cannot remove semaphore from queue set if semaphore is not empty. Temporarily hold semaphore
BaseType_t xHoldSemaphore = xSemaphoreTake(pxRingbuffer->xItemsBufferedSemaphore, 0);
xReturn = xQueueRemoveFromSet(pxRingbuffer->xItemsBufferedSemaphore, xQueueSet);
BaseType_t xHoldSemaphore = xSemaphoreTake(rbGET_RX_SEM_HANDLE(pxRingbuffer), 0);
xReturn = xQueueRemoveFromSet(rbGET_RX_SEM_HANDLE(pxRingbuffer), xQueueSet);
if (xHoldSemaphore == pdTRUE) {
//Return semaphore if temporarily held
configASSERT(xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore) == pdTRUE);
configASSERT(xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer)) == pdTRUE);
}
portEXIT_CRITICAL(&pxRingbuffer->mux);
return xReturn;
}
void vRingbufferGetInfo(RingbufHandle_t xRingbuffer, UBaseType_t *uxFree, UBaseType_t *uxRead, UBaseType_t *uxWrite, UBaseType_t *uxItemsWaiting)
void vRingbufferGetInfo(RingbufHandle_t xRingbuffer,
UBaseType_t *uxFree,
UBaseType_t *uxRead,
UBaseType_t *uxWrite,
UBaseType_t *uxItemsWaiting)
{
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
@ -1127,58 +1260,3 @@ void xRingbufferPrintInfo(RingbufHandle_t xRingbuffer)
pxRingbuffer->pucWrite - pxRingbuffer->pucHead);
}
/* --------------------------------- Deprecated Functions ------------------------------ */
//Todo: Remove the following deprecated functions in next release
bool xRingbufferIsNextItemWrapped(RingbufHandle_t xRingbuffer)
{
//This function is deprecated, use xRingbufferReceiveSplit() instead
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
bool is_wrapped;
portENTER_CRITICAL(&pxRingbuffer->mux);
ItemHeader_t *xHeader = (ItemHeader_t *)pxRingbuffer->pucRead;
is_wrapped = xHeader->uxItemFlags & rbITEM_SPLIT_FLAG;
portEXIT_CRITICAL(&pxRingbuffer->mux);
return is_wrapped;
}
BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet)
{
//This function is deprecated. QueueSetWrite no longer supported
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
BaseType_t xReturn;
portENTER_CRITICAL(&pxRingbuffer->mux);
//Cannot add semaphore to queue set if semaphore is not empty. Temporary hold semaphore
BaseType_t xHoldSemaphore = xSemaphoreTake(pxRingbuffer->xFreeSpaceSemaphore, 0);
xReturn = xQueueAddToSet(pxRingbuffer->xFreeSpaceSemaphore, xQueueSet);
if (xHoldSemaphore == pdTRUE) {
//Return semaphore is temporarily held
configASSERT(xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore) == pdTRUE);
}
portEXIT_CRITICAL(&pxRingbuffer->mux);
return xReturn;
}
BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet)
{
//This function is deprecated. QueueSetWrite no longer supported
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
BaseType_t xReturn;
portENTER_CRITICAL(&pxRingbuffer->mux);
//Cannot remove semaphore from queue set if semaphore is not empty. Temporary hold semaphore
BaseType_t xHoldSemaphore = xSemaphoreTake(pxRingbuffer->xFreeSpaceSemaphore, 0);
xReturn = xQueueRemoveFromSet(pxRingbuffer->xFreeSpaceSemaphore, xQueueSet);
if (xHoldSemaphore == pdTRUE) {
//Return semaphore is temporarily held
configASSERT(xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore) == pdTRUE);
}
portEXIT_CRITICAL(&pxRingbuffer->mux);
return xReturn;
}

View file

@ -6,6 +6,7 @@
#include "freertos/semphr.h"
#include "freertos/ringbuf.h"
#include "driver/timer.h"
#include "esp_heap_caps.h"
#include "esp_spi_flash.h"
#include "unity.h"
#include "test_utils.h"
@ -165,7 +166,7 @@ static void receive_check_and_return_item_byte_buffer(RingbufHandle_t handle, co
* 4) Receive and check the wrapped item
*/
TEST_CASE("Test ring buffer No-Split", "[freertos]")
TEST_CASE("Test ring buffer No-Split", "[esp_ringbuf]")
{
//Create buffer
RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_NOSPLIT);
@ -196,7 +197,7 @@ TEST_CASE("Test ring buffer No-Split", "[freertos]")
vRingbufferDelete(buffer_handle);
}
TEST_CASE("Test ring buffer Allow-Split", "[freertos]")
TEST_CASE("Test ring buffer Allow-Split", "[esp_ringbuf]")
{
//Create buffer
RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_ALLOWSPLIT);
@ -227,7 +228,7 @@ TEST_CASE("Test ring buffer Allow-Split", "[freertos]")
vRingbufferDelete(buffer_handle);
}
TEST_CASE("Test ring buffer Byte Buffer", "[freertos]")
TEST_CASE("Test ring buffer Byte Buffer", "[esp_ringbuf]")
{
//Create buffer
RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_BYTEBUF);
@ -304,7 +305,7 @@ static void queue_set_receiving_task(void *queue_set_handle)
vTaskDelete(NULL);
}
TEST_CASE("Test ring buffer with queue sets", "[freertos]")
TEST_CASE("Test ring buffer with queue sets", "[esp_ringbuf]")
{
QueueSetHandle_t queue_set = xQueueCreateSet(NO_OF_RB_TYPES);
done_sem = xSemaphoreCreateBinary();
@ -421,7 +422,7 @@ static void cleanup_timer()
esp_intr_free(ringbuffer_isr_handle);
}
TEST_CASE("Test ring buffer ISR", "[freertos]")
TEST_CASE("Test ring buffer ISR", "[esp_ringbuf]")
{
for (int i = 0; i < NO_OF_RB_TYPES; i++) {
buffer_handles[i] = xRingbufferCreate(BUFFER_SIZE, i);
@ -461,11 +462,12 @@ static const char continuous_data[] = {"A_very_long_string_that_will_be_split_in
"be_increased_over_multiple_iterations_in_this"
"_test"};
#define CONT_DATA_LEN sizeof(continuous_data)
#define CONT_DATA_TEST_BUFF_LEN (CONT_DATA_LEN/2) //This will guarantee that the buffer must do a wrap around at some point
//32-bit aligned size that guarantees a wrap around at some point
#define CONT_DATA_TEST_BUFF_LEN (((CONT_DATA_LEN/2) + 0x03) & ~0x3)
typedef struct {
RingbufHandle_t buffer;
ringbuf_type_t type;
RingbufferType_t type;
} task_args_t;
static SemaphoreHandle_t tasks_done;
@ -494,7 +496,7 @@ static void send_to_buffer(RingbufHandle_t buffer, size_t max_item_size)
}
}
static void read_from_buffer(RingbufHandle_t buffer, ringbuf_type_t buf_type, size_t max_rec_size)
static void read_from_buffer(RingbufHandle_t buffer, RingbufferType_t buf_type, size_t max_rec_size)
{
for (int iter = 0; iter < SMP_TEST_ITERATIONS; iter++) {
size_t bytes_rec = 0; //Number of data bytes received in this iteration
@ -568,20 +570,33 @@ static void rec_task(void *args)
vTaskDelete(NULL);
}
TEST_CASE("Test ring buffer SMP", "[freertos]")
static void setup()
{
ets_printf("size of buf %d\n", CONT_DATA_LEN);
ets_printf("Size of test data: %d\n", CONT_DATA_LEN);
tx_done = xSemaphoreCreateBinary(); //Semaphore to indicate send is done for a particular iteration
rx_done = xSemaphoreCreateBinary(); //Semaphore to indicate receive is done for a particular iteration
tasks_done = xSemaphoreCreateBinary(); //Semaphore used to to indicate send and receive tasks completed running
tasks_done = xSemaphoreCreateBinary(); //Semaphore used to to indicate send and receive tasks completed running
srand(SRAND_SEED); //Seed RNG
}
static void cleanup()
{
//Cleanup
vSemaphoreDelete(tx_done);
vSemaphoreDelete(rx_done);
vSemaphoreDelete(tasks_done);
}
TEST_CASE("Test ring buffer SMP", "[esp_ringbuf]")
{
setup();
//Iterate through buffer types (No split, split, then byte buff)
for (ringbuf_type_t buf_type = 0; buf_type <= RINGBUF_TYPE_BYTEBUF; buf_type++) {
for (RingbufferType_t buf_type = 0; buf_type < RINGBUF_TYPE_MAX; buf_type++) {
//Create buffer
task_args_t task_args;
task_args.buffer = xRingbufferCreate(CONT_DATA_TEST_BUFF_LEN, buf_type); //Create buffer of selected type
task_args.type = buf_type;
TEST_ASSERT_MESSAGE(task_args.buffer != NULL, "Failed to create ring buffer");
for (int prior_mod = -1; prior_mod < 2; prior_mod++) { //Test different relative priorities
//Test every permutation of core affinity
@ -600,13 +615,60 @@ TEST_CASE("Test ring buffer SMP", "[freertos]")
vRingbufferDelete(task_args.buffer);
vTaskDelay(10);
}
//Cleanup
vSemaphoreDelete(tx_done);
vSemaphoreDelete(rx_done);
vSemaphoreDelete(tasks_done);
cleanup();
}
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
TEST_CASE("Test static ring buffer SMP", "[esp_ringbuf]")
{
setup();
//Iterate through buffer types (No split, split, then byte buff)
for (RingbufferType_t buf_type = 0; buf_type < RINGBUF_TYPE_MAX; buf_type++) {
StaticRingbuffer_t *buffer_struct;
uint8_t *buffer_storage;
//Allocate memory and create semaphores
#if CONFIG_SPIRAM_USE_CAPS_ALLOC //When SPIRAM can only be allocated using heap_caps_malloc()
buffer_struct = (StaticRingbuffer_t *)heap_caps_malloc(sizeof(StaticRingbuffer_t), MALLOC_CAP_SPIRAM);
buffer_storage = (uint8_t *)heap_caps_malloc(sizeof(uint8_t)*CONT_DATA_TEST_BUFF_LEN, MALLOC_CAP_SPIRAM);
#else //Case where SPIRAM is disabled or when SPIRAM is allocatable through malloc()
buffer_struct = (StaticRingbuffer_t *)malloc(sizeof(StaticRingbuffer_t));
buffer_storage = (uint8_t *)malloc(sizeof(uint8_t)*CONT_DATA_TEST_BUFF_LEN);
#endif
TEST_ASSERT(buffer_struct != NULL && buffer_storage != NULL);
//Create buffer
task_args_t task_args;
task_args.buffer = xRingbufferCreateStatic(CONT_DATA_TEST_BUFF_LEN, buf_type, buffer_storage, buffer_struct); //Create buffer of selected type
task_args.type = buf_type;
TEST_ASSERT_MESSAGE(task_args.buffer != NULL, "Failed to create ring buffer");
for (int prior_mod = -1; prior_mod < 2; prior_mod++) { //Test different relative priorities
//Test every permutation of core affinity
for (int send_core = 0; send_core < portNUM_PROCESSORS; send_core++) {
for (int rec_core = 0; rec_core < portNUM_PROCESSORS; rec_core ++) {
ets_printf("Type: %d, PM: %d, SC: %d, RC: %d\n", buf_type, prior_mod, send_core, rec_core);
xTaskCreatePinnedToCore(send_task, "send tsk", 2048, (void *)&task_args, 10 + prior_mod, NULL, send_core);
xTaskCreatePinnedToCore(rec_task, "rec tsk", 2048, (void *)&task_args, 10, NULL, rec_core);
xSemaphoreTake(tasks_done, portMAX_DELAY);
vTaskDelay(5); //Allow idle to clean up
}
}
}
//Delete ring buffer
vRingbufferDelete(task_args.buffer);
//Deallocate memory
free(buffer_storage);
free(buffer_struct);
vTaskDelay(10);
}
cleanup();
}
#endif
/* -------------------------- Test ring buffer IRAM ------------------------- */
static IRAM_ATTR __attribute__((noinline)) bool iram_ringbuf_test()
{
bool result = true;
@ -621,7 +683,7 @@ static IRAM_ATTR __attribute__((noinline)) bool iram_ringbuf_test()
return result;
}
TEST_CASE("Test ringbuffer functions work with flash cache disabled", "[freertos]")
TEST_CASE("Test ringbuffer functions work with flash cache disabled", "[esp_ringbuf]")
{
TEST_ASSERT( iram_ringbuf_test() );
}

View file

@ -323,6 +323,51 @@ The following example demonstrates queue set usage with ring buffers.
...
}
Ring Buffers with Static Allocation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The :cpp:func:`xRingbufferCreateStatic` can be used to create ring buffers with specific memory requirements (such as a ring buffer being allocated in external RAM). All blocks of memory used by a ring buffer must be manually allocated beforehand then passed to the :cpp:func:`xRingbufferCreateStatic` to be initialized as a ring buffer. These blocks include the following:
- The ring buffer's data structure of type :cpp:type:`StaticRingbuffer_t`
- The ring buffer's storage area of size ``xBufferSize``. Note that ``xBufferSize`` must be 32-bit aligned for no-split/allow-split buffers.
The manner in which these blocks are allocated will depend on the users requirements (e.g. all blocks being statically declared, or dynamically allocated with specific capabilities such as external RAM).
.. note::
The :ref:`CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION` option must be enabled in `menuconfig` for statically allocated ring buffers to be available.
.. note::
When deleting a ring buffer created via :cpp:func:`xRingbufferCreateStatic`,
the function :cpp:func:`vRingbufferDelete` will not free any of the memory blocks. This must be done manually by the user after :cpp:func:`vRingbufferDelete` is called.
The code snippet below demonstrates a ring buffer being allocated entirely in external RAM.
.. code-block:: c
#include "freertos/ringbuf.h"
#include "freertos/semphr.h"
#include "esp_heap_caps.h"
#define BUFFER_SIZE 400 //32-bit aligned size
#define BUFFER_TYPE RINGBUF_TYPE_NOSPLIT
...
//Allocate ring buffer data structure and storage area into external RAM
StaticRingbuffer_t *buffer_struct = (StaticRingbuffer_t *)heap_caps_malloc(sizeof(StaticRingbuffer_t), MALLOC_CAP_SPIRAM);
uint8_t *buffer_storage = (uint8_t *)heap_caps_malloc(sizeof(uint8_t)*BUFFER_SIZE, MALLOC_CAP_SPIRAM);
//Create a ring buffer with manually allocated memory
RingbufHandle_t handle = xRingbufferCreateStatic(BUFFER_SIZE, BUFFER_TYPE, buffer_storage, buffer_struct);
...
//Delete the ring buffer after used
vRingbufferDelete(handle);
//Manually free all blocks of memory
free(buffer_struct);
free(buffer_storage);
Ring Buffer API Reference
-------------------------