Merge branch 'feature/xRingBufferCanRead_pr1672' into 'master'
freeRTOS/Re-factor ring buffers and add xRingbufferCanRead, xRingbufferCanWrite See merge request idf/esp-idf!2214
This commit is contained in:
commit
21e085d235
17 changed files with 2624 additions and 1146 deletions
|
@ -1,3 +1,17 @@
|
|||
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef FREERTOS_RINGBUF_H
|
||||
#define FREERTOS_RINGBUF_H
|
||||
|
||||
|
@ -11,71 +25,48 @@ extern "C" {
|
|||
|
||||
#include <freertos/queue.h>
|
||||
|
||||
//An opaque handle for a ringbuff object.
|
||||
/**
|
||||
* Type by which ring buffers are referenced. For example, a call to xRingbufferCreate()
|
||||
* returns a RingbufHandle_t variable that can then be used as a parameter to
|
||||
* xRingbufferSend(), xRingbufferReceive(), etc.
|
||||
*/
|
||||
typedef void * RingbufHandle_t;
|
||||
|
||||
/**
|
||||
* @brief The various types of buffer
|
||||
*
|
||||
* A ringbuffer instantiated by these functions essentially acts like a
|
||||
* FreeRTOS queue, with the difference that it's strictly FIFO and with
|
||||
* the main advantage that you can put in randomly-sized items. The capacity,
|
||||
* accordingly, isn't measured in the amount of items, but the amount of
|
||||
* memory that is used for storing the items. Dependent on the size of
|
||||
* the items, more or less of them will fit in the ring buffer.
|
||||
*
|
||||
* This ringbuffer tries to be efficient with memory: when inserting an item,
|
||||
* the item data will be copied to the ringbuffer memory. When retrieving
|
||||
* an item, however, a reference to ringbuffer memory will be returned.
|
||||
* The returned memory is guaranteed to be 32-bit aligned and contiguous.
|
||||
* The application can use this memory, but as long as it does, ringbuffer
|
||||
* writes that would write to this bit of memory will block.
|
||||
*
|
||||
* The requirement for items to be contiguous is slightly problematic when
|
||||
* the only way to place the next item would involve a wraparound from the end
|
||||
* to the beginning of the ringbuffer. This can be solved (or not) in a few ways,
|
||||
* see descriptions of possible ringbuf_type_t types below.
|
||||
*
|
||||
* The maximum size of an item will be affected by ringbuffer type.
|
||||
* When split items are allowed, it is acceptable to push items of
|
||||
* (buffer_size)-16 bytes into the buffer.
|
||||
* When it's not allowed, the maximum size is (buffer_size/2)-8 bytes.
|
||||
* The bytebuf can fill the entire buffer with data, it has no overhead.
|
||||
*/
|
||||
typedef enum {
|
||||
/** The insertion code will leave the room at the end of the ringbuffer
|
||||
* unused and instead will put the entire item at the start of the ringbuffer,
|
||||
* as soon as there is enough free space.
|
||||
/**
|
||||
* 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,
|
||||
/** The insertion code will split the item in two items; one which fits
|
||||
* in the space left at the end of the ringbuffer, one that contains
|
||||
* the remaining data which is placed in the beginning.
|
||||
* Two xRingbufferReceive calls will be needed to retrieve the data.
|
||||
/**
|
||||
* 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,
|
||||
/** This is your conventional byte-based ringbuffer. It does have no
|
||||
* overhead, but it has no item contiguousness either: a read will just
|
||||
* give you the entire written buffer space, or the space up to the end
|
||||
* of the buffer, and writes can be broken up in any way possible.
|
||||
* Note that this type cannot do a 2nd read before returning the memory
|
||||
* of the 1st.
|
||||
/**
|
||||
* 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;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a ring buffer
|
||||
*
|
||||
* @param buf_length Length of circular buffer, in bytes. Each entry will
|
||||
* take up its own length, plus a header that at the moment
|
||||
* is equal to sizeof(size_t).
|
||||
* @param type Type of ring buffer, see ringbuf_type_t.
|
||||
* @param[in] xBufferSize Size of the buffer in bytes. Note that items require
|
||||
* space for overhead in no-split/allow-split buffers
|
||||
* @param[in] xBufferType Type of ring buffer, see documentation.
|
||||
*
|
||||
* @return A RingbufHandle_t handle to the created ringbuffer, or NULL in case of error.
|
||||
* @note xBufferSize of no-split/allow-split buffers will be rounded up to the nearest 32-bit aligned size.
|
||||
*
|
||||
* @return A handle to the created ring buffer, or NULL in case of error.
|
||||
*/
|
||||
RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type);
|
||||
RingbufHandle_t xRingbufferCreate(size_t xBufferSize, ringbuf_type_t xBufferType);
|
||||
|
||||
/**
|
||||
* @brief Create a ring buffer of type RINGBUF_TYPE_NOSPLIT for a fixed item_size
|
||||
|
@ -83,255 +74,338 @@ RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type);
|
|||
* This API is similar to xRingbufferCreate(), but it will internally allocate
|
||||
* additional space for the headers.
|
||||
*
|
||||
* @param item_size Size of each item to be put into the ring buffer
|
||||
* @param num_item Maximum number of items the buffer needs to hold simultaneously
|
||||
* @param[in] xItemSize Size of each item to be put into the ring buffer
|
||||
* @param[in] xItemNum Maximum number of items the buffer needs to hold simultaneously
|
||||
*
|
||||
* @return A RingbufHandle_t handle to the created ringbuffer, or NULL in case of error.
|
||||
* @return A RingbufHandle_t handle to the created ring buffer, or NULL in case of error.
|
||||
*/
|
||||
RingbufHandle_t xRingbufferCreateNoSplit(size_t item_size, size_t num_item);
|
||||
|
||||
/**
|
||||
* @brief Delete a ring buffer
|
||||
*
|
||||
* @param ringbuf Ring buffer to delete
|
||||
*/
|
||||
void vRingbufferDelete(RingbufHandle_t ringbuf);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get maximum size of an item that can be placed in the ring buffer
|
||||
*
|
||||
* @param ringbuf Ring buffer to query
|
||||
*
|
||||
* @return Maximum size, in bytes, of an item that can be placed in a ring buffer.
|
||||
*/
|
||||
size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf);
|
||||
|
||||
/**
|
||||
* @brief Get current free size available in the buffer
|
||||
*
|
||||
* This gives the real time free space available in the ring buffer. So basically,
|
||||
* this will be the maximum size of the entry that can be sent into the buffer.
|
||||
*
|
||||
* @note This API is not thread safe. So, if multiple threads are accessing the same
|
||||
* ring buffer, it is the application's responsibility to ensure atomic access to this
|
||||
* API and the subsequent Send
|
||||
*
|
||||
* @param ringbuf - Ring buffer to query
|
||||
*
|
||||
* @return Current free size, in bytes, available for an entry
|
||||
*/
|
||||
size_t xRingbufferGetCurFreeSize(RingbufHandle_t ringbuf);
|
||||
|
||||
/**
|
||||
* @brief Check if the next item is wrapped
|
||||
*
|
||||
* This API tells if the next item that is available for a Receive is wrapped
|
||||
* or not. This is valid only if the ring buffer type is RINGBUF_TYPE_ALLOWSPLIT
|
||||
*
|
||||
* @note This API is not thread safe. So, if multiple threads are accessing the same
|
||||
* ring buffer, it is the application's responsibility to ensure atomic access to this
|
||||
* API and the subsequent Receive
|
||||
*
|
||||
* @param ringbuf - Ring buffer to query
|
||||
*
|
||||
* @return true if the next item is wrapped around
|
||||
* @return false if the next item is not wrapped
|
||||
*/
|
||||
bool xRingbufferIsNextItemWrapped(RingbufHandle_t ringbuf);
|
||||
RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum);
|
||||
|
||||
/**
|
||||
* @brief Insert an item into the ring buffer
|
||||
*
|
||||
* @param ringbuf Ring buffer to insert the item into
|
||||
* @param data Pointer to data to insert. NULL is allowed if data_size is 0.
|
||||
* @param data_size Size of data to insert. A value of 0 is allowed.
|
||||
* @param ticks_to_wait Ticks to wait for room in the ringbuffer.
|
||||
* Attempt to insert an item into the ring buffer. This function will block until
|
||||
* enough free space is available or until it timesout.
|
||||
*
|
||||
* @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.
|
||||
* @param[in] xItemSize Size of data to insert.
|
||||
* @param[in] xTicksToWait Ticks to wait for room in the ring buffer.
|
||||
*
|
||||
* @note For no-split/allow-split ring buffers, the actual size of memory that
|
||||
* the item will occupy will be rounded up to the nearest 32-bit aligned
|
||||
* size. This is done to ensure all items are always stored in 32-bit
|
||||
* aligned fashion.
|
||||
*
|
||||
* @return
|
||||
* - pdTRUE if succeeded
|
||||
* - pdFALSE on time-out or when the buffer is larger than indicated
|
||||
* by xRingbufferGetMaxItemSize(ringbuf).
|
||||
* - pdFALSE on time-out or when the data is larger than the maximum permissible size of the buffer
|
||||
*/
|
||||
BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t data_size, TickType_t ticks_to_wait);
|
||||
|
||||
BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, TickType_t xTicksToWait);
|
||||
|
||||
/**
|
||||
* @brief Insert an item into the ring buffer from an ISR
|
||||
* @brief Insert an item into the ring buffer in an ISR
|
||||
*
|
||||
* @param ringbuf Ring buffer to insert the item into
|
||||
* @param data Pointer to data to insert. NULL is allowed if data_size is 0.
|
||||
* @param data_size Size of data to insert. A value of 0 is allowed.
|
||||
* @param[out] higher_prio_task_awoken Value pointed to will be set to pdTRUE
|
||||
* if the push woke up a higher priority task.
|
||||
* Attempt to insert an item into the ring buffer from an ISR. This function
|
||||
* will return immediately if there is insufficient free space in the buffer.
|
||||
*
|
||||
* @return pdTRUE if succeeded, pdFALSE when the ring buffer does not have space.
|
||||
* @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.
|
||||
* @param[in] xItemSize Size of data to insert.
|
||||
* @param[out] pxHigherPriorityTaskWoken Value pointed to will be set to pdTRUE if the function woke up a higher priority task.
|
||||
*
|
||||
* @note For no-split/allow-split ring buffers, the actual size of memory that
|
||||
* the item will occupy will be rounded up to the nearest 32-bit aligned
|
||||
* size. This is done to ensure all items are always stored in 32-bit
|
||||
* aligned fashion.
|
||||
*
|
||||
* @return
|
||||
* - pdTRUE if succeeded
|
||||
* - pdFALSE when the ring buffer does not have space.
|
||||
*/
|
||||
BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t data_size, BaseType_t *higher_prio_task_awoken);
|
||||
BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, BaseType_t *pxHigherPriorityTaskWoken);
|
||||
|
||||
/**
|
||||
* @brief Retrieve an item from the ring buffer
|
||||
*
|
||||
* @note A call to vRingbufferReturnItem() is required after this to free up
|
||||
* the data received.
|
||||
* Attempt to retrieve an item from the ring buffer. This function will block
|
||||
* until an item is available or until it timesout.
|
||||
*
|
||||
* @param ringbuf Ring buffer to retrieve the item from
|
||||
* @param[out] item_size Pointer to a variable to which the size of the
|
||||
* retrieved item will be written.
|
||||
* @param ticks_to_wait Ticks to wait for items in the ringbuffer.
|
||||
* @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.
|
||||
* @param[in] xTicksToWait Ticks to wait for items in the ring buffer.
|
||||
*
|
||||
* @note A call to vRingbufferReturnItem() is required after this to free the item retrieved.
|
||||
*
|
||||
* @return
|
||||
* - pointer to the retrieved item on success; *item_size filled with
|
||||
* the length of the item.
|
||||
* - NULL on timeout, *item_size is untouched in that case.
|
||||
* - Pointer to the retrieved item on success; *pxItemSize filled with the length of the item.
|
||||
* - NULL on timeout, *pxItemSize is untouched in that case.
|
||||
*/
|
||||
void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait);
|
||||
|
||||
void *xRingbufferReceive(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait);
|
||||
|
||||
/**
|
||||
* @brief Retrieve an item from the ring buffer from an ISR
|
||||
* @brief Retrieve an item from the ring buffer in an ISR
|
||||
*
|
||||
* @note A call to vRingbufferReturnItemFromISR() is required after this to
|
||||
* free up the data received
|
||||
* Attempt to retrieve an item from the ring buffer. This function returns immediately
|
||||
* if there are no items available for retrieval
|
||||
*
|
||||
* @param ringbuf Ring buffer to retrieve the item from
|
||||
* @param[out] item_size Pointer to a variable to which the size of the
|
||||
* @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.
|
||||
*
|
||||
* @return
|
||||
* - Pointer to the retrieved item on success; *item_size filled with
|
||||
* the length of the item.
|
||||
* - NULL when the ringbuffer is empty, *item_size is untouched in that case.
|
||||
*/
|
||||
void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve bytes from a ByteBuf type of ring buffer,
|
||||
* specifying the maximum amount of bytes to return
|
||||
*
|
||||
* @note A call to vRingbufferReturnItem() is required after this to free up
|
||||
* the data received.
|
||||
*
|
||||
* @param ringbuf Ring buffer to retrieve the item from
|
||||
* @param[out] item_size Pointer to a variable to which the size
|
||||
* of the retrieved item will be written.
|
||||
* @param ticks_to_wait Ticks to wait for items in the ringbuffer.
|
||||
* @param wanted_size Maximum number of bytes to return.
|
||||
* @note A call to vRingbufferReturnItemFromISR() is required after this to free the item retrieved.
|
||||
* @note Byte buffers do not allow multiple retrievals before returning an item
|
||||
*
|
||||
* @return
|
||||
* - Pointer to the retrieved item on success; *item_size filled with
|
||||
* the length of the item.
|
||||
* - NULL on timeout, *item_size is untouched in that case.
|
||||
* - Pointer to the retrieved item on success; *pxItemSize filled with the length of the item.
|
||||
* - NULL when the ring buffer is empty, *pxItemSize is untouched in that case.
|
||||
*/
|
||||
void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size);
|
||||
|
||||
void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize);
|
||||
|
||||
/**
|
||||
* @brief Retrieve bytes from a ByteBuf type of ring buffer,
|
||||
* specifying the maximum amount of bytes to return. Call this from an ISR.
|
||||
* @brief Retrieve a split item from an allow-split ring buffer
|
||||
*
|
||||
* @note A call to vRingbufferReturnItemFromISR() is required after this
|
||||
* to free up the data received.
|
||||
* 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.
|
||||
*
|
||||
* @param ringbuf Ring buffer to retrieve the item from
|
||||
* @param[out] item_size Pointer to a variable to which the size of the
|
||||
* retrieved item will be written.
|
||||
* @param wanted_size Maximum number of bytes to return.
|
||||
* @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)
|
||||
* @param[out] ppvTailItem Double pointer to second part (set to NULL if item is not split)
|
||||
* @param[out] pxHeadItemSize Pointer to size of first part (unmodified if no items were retrieved)
|
||||
* @param[out] pxTailItemSize Pointer to size of second part (unmodified if item is not split)
|
||||
* @param[in] xTicksToWait Ticks to wait for items in the ring buffer.
|
||||
*
|
||||
* @note Call(s) to vRingbufferReturnItem() is required after this to free up the item(s) retrieved.
|
||||
* @note This function should only be called on allow-split buffers
|
||||
*
|
||||
* @return
|
||||
* - Pointer to the retrieved item on success; *item_size filled with
|
||||
* - 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);
|
||||
|
||||
/**
|
||||
* @brief Retrieve a split item from an allow-split ring buffer in an ISR
|
||||
*
|
||||
* 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 returns immediately if there are no items
|
||||
* available for retrieval
|
||||
*
|
||||
* @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)
|
||||
* @param[out] ppvTailItem Double pointer to second part (set to NULL if item is not split)
|
||||
* @param[out] pxHeadItemSize Pointer to size of first part (unmodified if no items were retrieved)
|
||||
* @param[out] pxTailItemSize Pointer to size of second part (unmodified if item is not split)
|
||||
*
|
||||
* @note Calls to vRingbufferReturnItemFromISR() is required after this to free up the item(s) retrieved.
|
||||
* @note This function should only be called on allow-split buffers
|
||||
*
|
||||
* @return
|
||||
* - 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);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
* @param[in] xTicksToWait Ticks to wait for items in the ring buffer.
|
||||
* @param[in] xMaxSize Maximum number of bytes to return.
|
||||
*
|
||||
* @note A call to vRingbufferReturnItem() is required after this to free up the data retrieved.
|
||||
* @note This function should only be called on byte buffers
|
||||
* @note Byte buffers do not allow multiple retrievals before returning an item
|
||||
*
|
||||
* @return
|
||||
* - Pointer to the retrieved item on success; *pxItemSize filled with
|
||||
* the length of the item.
|
||||
* - NULL when the ringbuffer is empty, *item_size is untouched in that case.
|
||||
* - NULL on timeout, *pxItemSize is untouched in that case.
|
||||
*/
|
||||
void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size);
|
||||
|
||||
|
||||
void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait, size_t xMaxSize);
|
||||
|
||||
/**
|
||||
* @brief Return a previously-retrieved item to the ringbuffer
|
||||
* @brief Retrieve bytes from a byte buffer, specifying the maximum amount of
|
||||
* bytes to retrieve. Call this from an ISR.
|
||||
*
|
||||
* @param ringbuf Ring buffer the item was retrieved from
|
||||
* @param item Item that was received earlier
|
||||
* Attempt to retrieve bytes from a byte buffer whilst specifying a maximum number
|
||||
* of bytes to retrieve. This function will return immediately if there is no data
|
||||
* available for retrieval.
|
||||
*
|
||||
* @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.
|
||||
* @param[in] xMaxSize Maximum number of bytes to return.
|
||||
*
|
||||
* @note A call to vRingbufferReturnItemFromISR() is required after this to free up the data received.
|
||||
* @note This function should only be called on byte buffers
|
||||
* @note Byte buffers do not allow multiple retrievals before returning an item
|
||||
*
|
||||
* @return
|
||||
* - Pointer to the retrieved item on success; *pxItemSize filled with
|
||||
* the length of the item.
|
||||
* - NULL when the ring buffer is empty, *pxItemSize is untouched in that case.
|
||||
*/
|
||||
void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item);
|
||||
|
||||
|
||||
void *xRingbufferReceiveUpToFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize, size_t xMaxSize);
|
||||
|
||||
/**
|
||||
* @brief Return a previously-retrieved item to the ringbuffer from an ISR
|
||||
* @brief Return a previously-retrieved item to the ring buffer
|
||||
*
|
||||
* @param ringbuf Ring buffer the item was retrieved from
|
||||
* @param item Item that was received earlier
|
||||
* @param[out] higher_prio_task_awoken Value pointed to will be set to pdTRUE
|
||||
* if the push woke up a higher priority task.
|
||||
* @param[in] xRingbuffer Ring buffer the item was retrieved from
|
||||
* @param[in] pvItem Item that was received earlier
|
||||
*
|
||||
* @note If a split item is retrieved, both parts should be returned by calling this function twice
|
||||
*/
|
||||
void vRingbufferReturnItemFromISR(RingbufHandle_t ringbuf, void *item, BaseType_t *higher_prio_task_awoken);
|
||||
|
||||
void vRingbufferReturnItem(RingbufHandle_t xRingbuffer, void *pvItem);
|
||||
|
||||
/**
|
||||
* @brief Add the ringbuffer to a queue set.
|
||||
* @brief Return a previously-retrieved item to the ring buffer from an ISR
|
||||
*
|
||||
* This specifically adds the semaphore that indicates more space
|
||||
* has become available in the ringbuffer.
|
||||
* @param[in] xRingbuffer Ring buffer the item was retrieved from
|
||||
* @param[in] pvItem Item that was received earlier
|
||||
* @param[out] pxHigherPriorityTaskWoken Value pointed to will be set to pdTRUE
|
||||
* if the function woke up a higher priority task.
|
||||
*
|
||||
* @param ringbuf Ring buffer to add to the queue set
|
||||
* @param xQueueSet Queue set to add the ringbuffer to
|
||||
* @note If a split item is retrieved, both parts should be returned by calling this function twice
|
||||
*/
|
||||
void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, BaseType_t *pxHigherPriorityTaskWoken);
|
||||
|
||||
/**
|
||||
* @brief Delete a ring buffer
|
||||
*
|
||||
* @param[in] xRingbuffer Ring buffer to delete
|
||||
*/
|
||||
void vRingbufferDelete(RingbufHandle_t xRingbuffer);
|
||||
|
||||
/**
|
||||
* @brief Get maximum size of an item that can be placed in the ring buffer
|
||||
*
|
||||
* This function returns the maximum size an item can have if it was placed in
|
||||
* an empty ring buffer.
|
||||
*
|
||||
* @param[in] xRingbuffer Ring buffer to query
|
||||
*
|
||||
* @return Maximum size, in bytes, of an item that can be placed in a ring buffer.
|
||||
*/
|
||||
size_t xRingbufferGetMaxItemSize(RingbufHandle_t xRingbuffer);
|
||||
|
||||
/**
|
||||
* @brief Get current free size available for an item/data in the buffer
|
||||
*
|
||||
* This gives the real time free space available for an item/data in the ring
|
||||
* buffer. This represents the maximum size an item/data can have if it was
|
||||
* currently sent to the ring buffer.
|
||||
*
|
||||
* @warning This API is not thread safe. So, if multiple threads are accessing
|
||||
* the same ring buffer, it is the application's responsibility to
|
||||
* ensure atomic access to this API and the subsequent Send
|
||||
*
|
||||
* @param[in] xRingbuffer Ring buffer to query
|
||||
*
|
||||
* @return Current free size, in bytes, available for an entry
|
||||
*/
|
||||
size_t xRingbufferGetCurFreeSize(RingbufHandle_t xRingbuffer);
|
||||
|
||||
/**
|
||||
* @brief Add the ring buffer's read semaphore to a queue set.
|
||||
*
|
||||
* The ring buffer's read semaphore indicates that data has been written
|
||||
* to the ring buffer. This function adds the ring buffer's read semaphore to
|
||||
* a queue set.
|
||||
*
|
||||
* @param[in] xRingbuffer Ring buffer to add to the queue set
|
||||
* @param[in] xQueueSet Queue set to add the ring buffer's read semaphore to
|
||||
*
|
||||
* @return
|
||||
* - pdTRUE on success, pdFALSE otherwise
|
||||
*/
|
||||
BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet);
|
||||
BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Add the ringbuffer to a queue set.
|
||||
* @brief Check if the selected queue set member is the ring buffer's read semaphore
|
||||
*
|
||||
* This specifically adds the semaphore that indicates something has been
|
||||
* written into the ringbuffer.
|
||||
* This API checks if queue set member returned from xQueueSelectFromSet()
|
||||
* is the read semaphore of this ring buffer. If so, this indicates the ring buffer
|
||||
* has items waiting to be retrieved.
|
||||
*
|
||||
* @param ringbuf Ring buffer to add to the queue set
|
||||
* @param xQueueSet Queue set to add the ringbuffer to
|
||||
* @param[in] xRingbuffer Ring buffer which should be checked
|
||||
* @param[in] xMember Member returned from xQueueSelectFromSet
|
||||
*
|
||||
* @return pdTRUE on success, pdFALSE otherwise
|
||||
* @return
|
||||
* - pdTRUE when semaphore belongs to ring buffer
|
||||
* - pdFALSE otherwise.
|
||||
*/
|
||||
BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet);
|
||||
|
||||
BaseType_t xRingbufferCanRead(RingbufHandle_t xRingbuffer, QueueSetMemberHandle_t xMember);
|
||||
|
||||
/**
|
||||
* @brief Remove the ringbuffer from a queue set.
|
||||
* @brief Remove the ring buffer's read semaphore from a queue set.
|
||||
*
|
||||
* This specifically removes the semaphore that indicates more space
|
||||
* has become available in the ringbuffer.
|
||||
* This specifically removes a ring buffer's read semaphore from a queue set. The
|
||||
* read semaphore is used to indicate when data has been written to the ring buffer
|
||||
*
|
||||
* @param ringbuf Ring buffer to remove from the queue set
|
||||
* @param xQueueSet Queue set to remove the ringbuffer from
|
||||
* @param[in] xRingbuffer Ring buffer to remove from the queue set
|
||||
* @param[in] xQueueSet Queue set to remove the ring buffer's read semaphore from
|
||||
*
|
||||
* @return pdTRUE on success, pdFALSE otherwise
|
||||
* @return
|
||||
* - pdTRUE on success
|
||||
* - pdFALSE otherwise
|
||||
*/
|
||||
BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet);
|
||||
|
||||
BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet);
|
||||
|
||||
/**
|
||||
* @brief Remove the ringbuffer from a queue set.
|
||||
* @brief Get information about ring buffer status
|
||||
*
|
||||
* This specifically removes the semaphore that indicates something
|
||||
* has been written to the ringbuffer.
|
||||
* Get information of the a ring buffer's current status such as
|
||||
* free/read/write pointer positions, and number of items waiting to be retrieved.
|
||||
* Arguments can be set to NULL if they are not required.
|
||||
*
|
||||
* @param ringbuf Ring buffer to remove from the queue set
|
||||
* @param xQueueSet Queue set to remove the ringbuffer from
|
||||
*
|
||||
* @return pdTRUE on success, pdFALSE otherwise
|
||||
* @param[in] xRingbuffer Ring buffer to remove from the queue set
|
||||
* @param[out] uxFree Pointer use to store free pointer position
|
||||
* @param[out] uxRead Pointer use to store read pointer position
|
||||
* @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
|
||||
*/
|
||||
BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet);
|
||||
|
||||
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
|
||||
*
|
||||
* @param ringbuf Ring buffer to show
|
||||
* @param xRingbuffer Ring buffer to show
|
||||
*/
|
||||
void xRingbufferPrintInfo(RingbufHandle_t ringbuf);
|
||||
void xRingbufferPrintInfo(RingbufHandle_t xRingbuffer);
|
||||
|
||||
/* -------------------------------- Deprecated Functions --------------------------- */
|
||||
|
||||
/** @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 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,227 +1,606 @@
|
|||
/*
|
||||
Test for multicore FreeRTOS ringbuffer.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "rom/ets_sys.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/ringbuf.h"
|
||||
#include "freertos/xtensa_api.h"
|
||||
#include "driver/timer.h"
|
||||
#include "unity.h"
|
||||
#include "soc/uart_reg.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
|
||||
//Definitions used in multiple test cases
|
||||
#define TIMEOUT_TICKS 10
|
||||
#define NO_OF_RB_TYPES 3
|
||||
#define ITEM_HDR_SIZE 8
|
||||
#define SMALL_ITEM_SIZE 8
|
||||
#define LARGE_ITEM_SIZE (2 * SMALL_ITEM_SIZE)
|
||||
#define BUFFER_SIZE 160 //4Byte aligned size
|
||||
|
||||
static RingbufHandle_t rb;
|
||||
typedef enum {
|
||||
TST_MOSTLYFILLED,
|
||||
TST_MOSTLYEMPTY,
|
||||
TST_INTTOTASK,
|
||||
TST_TASKTOINT,
|
||||
} testtype_t;
|
||||
static const uint8_t small_item[SMALL_ITEM_SIZE] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
|
||||
static const uint8_t large_item[LARGE_ITEM_SIZE] = { 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
|
||||
static RingbufHandle_t buffer_handles[NO_OF_RB_TYPES];
|
||||
static SemaphoreHandle_t done_sem;
|
||||
|
||||
static volatile testtype_t testtype;
|
||||
|
||||
intr_handle_t s_intr_handle;
|
||||
|
||||
static void task1(void *arg)
|
||||
static void send_item_and_check(RingbufHandle_t handle, const uint8_t *item, size_t item_size, TickType_t ticks_to_wait, bool in_isr)
|
||||
{
|
||||
testtype_t oldtest;
|
||||
char buf[100];
|
||||
int i = 0;
|
||||
int x, r;
|
||||
while (1) {
|
||||
oldtest = testtype;
|
||||
if (testtype == TST_MOSTLYFILLED || testtype == TST_MOSTLYEMPTY) {
|
||||
for (x = 0; x < 10; x++) {
|
||||
sprintf(buf, "This is test %d item %d.", (int)testtype, i++);
|
||||
ets_printf("TSK w");
|
||||
xRingbufferPrintInfo(rb);
|
||||
r = xRingbufferSend(rb, buf, strlen(buf) + 1, 2000 / portTICK_PERIOD_MS);
|
||||
if (!r) {
|
||||
printf("Test %d: Timeout on send!\n", (int)testtype);
|
||||
BaseType_t ret;
|
||||
if (in_isr) {
|
||||
ret = xRingbufferSendFromISR(handle, (void *)item, item_size, NULL);
|
||||
} else {
|
||||
ret = xRingbufferSend(handle, (void *)item, item_size, ticks_to_wait);
|
||||
}
|
||||
if (testtype == TST_MOSTLYEMPTY) {
|
||||
vTaskDelay(300 / portTICK_PERIOD_MS);
|
||||
TEST_ASSERT_MESSAGE(ret == pdTRUE, "Failed to send item");
|
||||
}
|
||||
|
||||
static void receive_check_and_return_item_no_split(RingbufHandle_t handle, const uint8_t *expected_data, size_t expected_size, TickType_t ticks_to_wait, bool in_isr)
|
||||
{
|
||||
//Receive item from no-split buffer
|
||||
size_t item_size;
|
||||
uint8_t *item;
|
||||
if (in_isr) {
|
||||
item = (uint8_t *)xRingbufferReceiveFromISR(handle, &item_size);
|
||||
} else {
|
||||
item = (uint8_t *)xRingbufferReceive(handle, &item_size, ticks_to_wait);
|
||||
}
|
||||
TEST_ASSERT_MESSAGE(item != NULL, "Failed to receive item");
|
||||
TEST_ASSERT_MESSAGE(item_size == expected_size, "Item size is incorrect");
|
||||
//Check data of received item
|
||||
for (int i = 0; i < item_size; i++) {
|
||||
TEST_ASSERT_MESSAGE(item[i] == expected_data[i], "Item data is invalid");
|
||||
}
|
||||
//Send NULL event to stop other side.
|
||||
r = xRingbufferSend(rb, NULL, 0, 10000 / portTICK_PERIOD_MS);
|
||||
//Return item
|
||||
if (in_isr) {
|
||||
vRingbufferReturnItemFromISR(handle, (void *)item, NULL);
|
||||
} else {
|
||||
vRingbufferReturnItem(handle, (void *)item);
|
||||
}
|
||||
while (oldtest == testtype) {
|
||||
vTaskDelay(300 / portTICK_PERIOD_MS);
|
||||
|
||||
}
|
||||
|
||||
static void receive_check_and_return_item_allow_split(RingbufHandle_t handle, const uint8_t *expected_data, size_t expected_size, TickType_t ticks_to_wait, bool in_isr)
|
||||
{
|
||||
//Receive item
|
||||
size_t item_size1, item_size2;
|
||||
uint8_t *item1, *item2;
|
||||
BaseType_t ret;
|
||||
if (in_isr) {
|
||||
ret = xRingbufferReceiveSplitFromISR(handle, (void**)&item1, (void **)&item2, &item_size1, &item_size2);
|
||||
} else {
|
||||
ret = xRingbufferReceiveSplit(handle, (void**)&item1, (void **)&item2, &item_size1, &item_size2, ticks_to_wait);
|
||||
}
|
||||
//= xRingbufferReceiveSplit(handle, (void**)&item1, (void **)&item2, &item_size1, &item_size2, ticks_to_wait);
|
||||
TEST_ASSERT_MESSAGE(ret == pdTRUE, "Failed to receive item");
|
||||
TEST_ASSERT_MESSAGE(item1 != NULL, "Failed to receive item");
|
||||
|
||||
//Check data of received item(s) and return them
|
||||
if (item2 == NULL) {
|
||||
TEST_ASSERT_MESSAGE(item_size1 == expected_size, "Item size is incorrect");
|
||||
for (int i = 0; i < item_size1; i++) {
|
||||
TEST_ASSERT_MESSAGE(item1[i] == expected_data[i], "Item data is invalid");
|
||||
}
|
||||
//Return item
|
||||
if (in_isr) {
|
||||
vRingbufferReturnItemFromISR(handle, (void *)item1, NULL);
|
||||
} else {
|
||||
vRingbufferReturnItem(handle, (void *)item1);
|
||||
}
|
||||
} else {
|
||||
//Item was split
|
||||
TEST_ASSERT_MESSAGE(item_size1 + item_size2 == expected_size, "Total item size is incorrect");
|
||||
for (int i = 0; i < item_size1; i++) {
|
||||
TEST_ASSERT_MESSAGE(item1[i] == expected_data[i], "Head item data is invalid");
|
||||
}
|
||||
for (int i = 0; i < item_size2; i++) {
|
||||
TEST_ASSERT_MESSAGE(item2[i] == expected_data[item_size1 + i], "Head item data is invalid");
|
||||
}
|
||||
//Return Items
|
||||
if (in_isr) {
|
||||
vRingbufferReturnItemFromISR(handle, (void *)item1, NULL);
|
||||
vRingbufferReturnItemFromISR(handle, (void *)item2, NULL);
|
||||
} else {
|
||||
vRingbufferReturnItem(handle, (void *)item1);
|
||||
vRingbufferReturnItem(handle, (void *)item2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void task2(void *arg)
|
||||
static void receive_check_and_return_item_byte_buffer(RingbufHandle_t handle, const uint8_t *expected_data, size_t expected_size, TickType_t ticks_to_wait, bool in_isr)
|
||||
{
|
||||
testtype_t oldtest;
|
||||
char *buf;
|
||||
size_t len;
|
||||
while (1) {
|
||||
oldtest = testtype;
|
||||
if (testtype == TST_MOSTLYFILLED || testtype == TST_MOSTLYEMPTY) {
|
||||
while (1) {
|
||||
ets_printf("TSK r");
|
||||
xRingbufferPrintInfo(rb);
|
||||
buf = xRingbufferReceive(rb, &len, 2000 / portTICK_PERIOD_MS);
|
||||
if (buf == NULL) {
|
||||
printf("Test %d: Timeout on recv!\n", (int)testtype);
|
||||
} else if (len == 0) {
|
||||
printf("End packet received.\n");
|
||||
vRingbufferReturnItem(rb, buf);
|
||||
break;
|
||||
//Receive item
|
||||
size_t item_size;
|
||||
uint8_t *item;
|
||||
if (in_isr) {
|
||||
item = (uint8_t *)xRingbufferReceiveUpToFromISR(handle, &item_size, expected_size);
|
||||
} else {
|
||||
printf("Received: %s (%d bytes, %p)\n", buf, len, buf);
|
||||
vRingbufferReturnItem(rb, buf);
|
||||
item = (uint8_t *)xRingbufferReceiveUpTo(handle, &item_size, ticks_to_wait, expected_size); //Limit amount of bytes returned to the size of one item
|
||||
}
|
||||
if (testtype == TST_MOSTLYFILLED) {
|
||||
vTaskDelay(300 / portTICK_PERIOD_MS);
|
||||
TEST_ASSERT_MESSAGE(item != NULL, "Failed to receive item");
|
||||
|
||||
//Check data of received item
|
||||
for (int i = 0; i < item_size; i++) {
|
||||
TEST_ASSERT_MESSAGE(item[i] == expected_data[i], "Item data is invalid");
|
||||
}
|
||||
//Return item
|
||||
if (in_isr) {
|
||||
vRingbufferReturnItemFromISR(handle, (void *)item, NULL);
|
||||
} else {
|
||||
vRingbufferReturnItem(handle, (void *)item);
|
||||
}
|
||||
|
||||
//Check if item wrapped around
|
||||
if (item_size < expected_size) {
|
||||
//Item is wrapped, receive second portion
|
||||
size_t item_size2;
|
||||
uint8_t *item2;
|
||||
if (in_isr) {
|
||||
item2 = (uint8_t *)xRingbufferReceiveUpToFromISR(handle, &item_size2, expected_size - item_size);
|
||||
} else {
|
||||
item2 = (uint8_t *)xRingbufferReceiveUpTo(handle, &item_size2, ticks_to_wait, expected_size - item_size);
|
||||
}
|
||||
while (oldtest == testtype) {
|
||||
vTaskDelay(300 / portTICK_PERIOD_MS);
|
||||
//= (uint8_t *)xRingbufferReceiveUpTo(handle, &item_size2, ticks_to_wait, expected_size - item_size);
|
||||
TEST_ASSERT_MESSAGE(item2 != NULL, "Failed to receive item");
|
||||
TEST_ASSERT_MESSAGE(item_size + item_size2 == expected_size, "Total item size is incorrect");
|
||||
for (int i = 0; i < item_size2; i++) {
|
||||
TEST_ASSERT_MESSAGE(item2[i] == expected_data[item_size + i], "Item data is invalid");
|
||||
}
|
||||
if (in_isr) {
|
||||
vRingbufferReturnItemFromISR(handle, (void *)item2, NULL);
|
||||
} else {
|
||||
vRingbufferReturnItem(handle, (void *)item2);
|
||||
}
|
||||
} else {
|
||||
TEST_ASSERT_MESSAGE(item_size == expected_size, "Item size is incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------- Basic ring buffer behavior tests cases --------------------
|
||||
* The following test cases will test basic send, receive, and wrap around
|
||||
* behavior of each type of ring buffer. Each test case will do the following
|
||||
* 1) Send multiple items (nearly fill the buffer)
|
||||
* 2) Receive and check the sent items (also prepares the buffer for a wrap around
|
||||
* 3) Send a final item that causes a wrap around
|
||||
* 4) Receive and check the wrapped item
|
||||
*/
|
||||
|
||||
|
||||
static void uartIsrHdl(void *arg)
|
||||
TEST_CASE("Test ring buffer No-Split", "[freertos]")
|
||||
{
|
||||
char c;
|
||||
char buf[50];
|
||||
char *item;
|
||||
int r;
|
||||
size_t len;
|
||||
BaseType_t xHigherPriorityTaskWoken;
|
||||
SET_PERI_REG_MASK(UART_INT_CLR_REG(0), UART_RXFIFO_FULL_INT_CLR);
|
||||
while (READ_PERI_REG(UART_STATUS_REG(0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) {
|
||||
c = READ_PERI_REG(UART_FIFO_REG(0));
|
||||
if (c == 'r') {
|
||||
ets_printf("ISR r");
|
||||
xRingbufferPrintInfo(rb);
|
||||
item = xRingbufferReceiveFromISR(rb, &len);
|
||||
if (item == NULL) {
|
||||
ets_printf("ISR recv fail!\n");
|
||||
} else if (len == 0) {
|
||||
ets_printf("ISR recv NULL!\n");
|
||||
vRingbufferReturnItemFromISR(rb, item, &xHigherPriorityTaskWoken);
|
||||
//Create buffer
|
||||
RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_NOSPLIT);
|
||||
TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer");
|
||||
//Calculate number of items to send. Aim to almost fill buffer to setup for wrap around
|
||||
int no_of_items = (BUFFER_SIZE - (ITEM_HDR_SIZE + SMALL_ITEM_SIZE)) / (ITEM_HDR_SIZE + SMALL_ITEM_SIZE);
|
||||
|
||||
//Test sending items
|
||||
for (int i = 0; i < no_of_items; i++) {
|
||||
send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
|
||||
}
|
||||
//Test receiving items
|
||||
for (int i = 0; i < no_of_items; i++) {
|
||||
receive_check_and_return_item_no_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
|
||||
}
|
||||
|
||||
//Write pointer should be near the end, test wrap around
|
||||
uint32_t write_pos_before, write_pos_after;
|
||||
vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL);
|
||||
//Send large item that causes wrap around
|
||||
send_item_and_check(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
|
||||
//Receive wrapped item
|
||||
receive_check_and_return_item_no_split(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
|
||||
vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_after, NULL);
|
||||
TEST_ASSERT_MESSAGE(write_pos_after < write_pos_before, "Failed to wrap around");
|
||||
|
||||
//Cleanup
|
||||
vRingbufferDelete(buffer_handle);
|
||||
}
|
||||
|
||||
TEST_CASE("Test ring buffer Allow-Split", "[freertos]")
|
||||
{
|
||||
//Create buffer
|
||||
RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_ALLOWSPLIT);
|
||||
TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer");
|
||||
//Calculate number of items to send. Aim to almost fill buffer to setup for wrap around
|
||||
int no_of_items = (BUFFER_SIZE - (ITEM_HDR_SIZE + SMALL_ITEM_SIZE)) / (ITEM_HDR_SIZE + SMALL_ITEM_SIZE);
|
||||
|
||||
//Test sending items
|
||||
for (int i = 0; i < no_of_items; i++) {
|
||||
send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
|
||||
}
|
||||
//Test receiving items
|
||||
for (int i = 0; i < no_of_items; i++) {
|
||||
receive_check_and_return_item_allow_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
|
||||
}
|
||||
|
||||
//Write pointer should be near the end, test wrap around
|
||||
uint32_t write_pos_before, write_pos_after;
|
||||
vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL);
|
||||
//Send large item that causes wrap around
|
||||
send_item_and_check(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
|
||||
//Receive wrapped item
|
||||
receive_check_and_return_item_allow_split(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
|
||||
vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_after, NULL);
|
||||
TEST_ASSERT_MESSAGE(write_pos_after < write_pos_before, "Failed to wrap around");
|
||||
|
||||
//Cleanup
|
||||
vRingbufferDelete(buffer_handle);
|
||||
}
|
||||
|
||||
TEST_CASE("Test ring buffer Byte Buffer", "[freertos]")
|
||||
{
|
||||
//Create buffer
|
||||
RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_BYTEBUF);
|
||||
TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer");
|
||||
//Calculate number of items to send. Aim to almost fill buffer to setup for wrap around
|
||||
int no_of_items = (BUFFER_SIZE - SMALL_ITEM_SIZE) / SMALL_ITEM_SIZE;
|
||||
|
||||
//Test sending items
|
||||
for (int i = 0; i < no_of_items; i++) {
|
||||
send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
|
||||
}
|
||||
//Test receiving items
|
||||
for (int i = 0; i < no_of_items; i++) {
|
||||
receive_check_and_return_item_byte_buffer(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
|
||||
}
|
||||
|
||||
//Write pointer should be near the end, test wrap around
|
||||
uint32_t write_pos_before, write_pos_after;
|
||||
vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL);
|
||||
//Send large item that causes wrap around
|
||||
send_item_and_check(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
|
||||
//Receive wrapped item
|
||||
receive_check_and_return_item_byte_buffer(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
|
||||
vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_after, NULL);
|
||||
TEST_ASSERT_MESSAGE(write_pos_after < write_pos_before, "Failed to wrap around");
|
||||
|
||||
//Cleanup
|
||||
vRingbufferDelete(buffer_handle);
|
||||
}
|
||||
|
||||
/* ----------------------- Ring buffer queue sets test ------------------------
|
||||
* The following test case will test receiving from ring buffers that have been
|
||||
* added to a queue set. The test case will do the following...
|
||||
* 1) Ring buffer of each type is created and added to the queue set
|
||||
* 2) A receiving task is created to select from the queue set and read from the appropriate ring buffer
|
||||
*/
|
||||
|
||||
static void queue_set_receiving_task(void *queue_set_handle)
|
||||
{
|
||||
QueueSetHandle_t queue_set = (QueueSetHandle_t)queue_set_handle;
|
||||
|
||||
//Receive multiple items via queue set
|
||||
BaseType_t done = pdFALSE;
|
||||
int no_of_items = BUFFER_SIZE / SMALL_ITEM_SIZE;
|
||||
int items_rec_count[NO_OF_RB_TYPES] = {0};
|
||||
while (done != pdTRUE) {
|
||||
xQueueSetMemberHandle member = xQueueSelectFromSet(queue_set, TIMEOUT_TICKS);
|
||||
//Read from selected ring buffer
|
||||
if (xRingbufferCanRead(buffer_handles[0], member) == pdTRUE) {
|
||||
//No-split buffer
|
||||
receive_check_and_return_item_no_split(buffer_handles[0], small_item, SMALL_ITEM_SIZE, 0, false);
|
||||
items_rec_count[0] ++;
|
||||
} else if (xRingbufferCanRead(buffer_handles[1], member) == pdTRUE) {
|
||||
//Allow-split buffer
|
||||
receive_check_and_return_item_allow_split(buffer_handles[1], small_item, SMALL_ITEM_SIZE, 0, false);
|
||||
items_rec_count[1] ++;
|
||||
} else if (xRingbufferCanRead(buffer_handles[2], member) == pdTRUE){
|
||||
//Byte buffer
|
||||
receive_check_and_return_item_byte_buffer(buffer_handles[2], small_item, SMALL_ITEM_SIZE, 0, false);
|
||||
items_rec_count[2] ++;
|
||||
} else {
|
||||
ets_printf("ISR recv '%s' (%d bytes, %p)\n", buf, len, buf);
|
||||
vRingbufferReturnItemFromISR(rb, item, &xHigherPriorityTaskWoken);
|
||||
TEST_ASSERT_MESSAGE( false, "Error with queue set member");
|
||||
}
|
||||
|
||||
//Check for completion
|
||||
if (items_rec_count[0] == no_of_items &&
|
||||
items_rec_count[1] == no_of_items &&
|
||||
items_rec_count[2] == no_of_items) {
|
||||
done = pdTRUE;
|
||||
}
|
||||
}
|
||||
|
||||
xSemaphoreGive(done_sem);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("Test ring buffer with queue sets", "[freertos]")
|
||||
{
|
||||
QueueSetHandle_t queue_set = xQueueCreateSet(NO_OF_RB_TYPES);
|
||||
done_sem = xSemaphoreCreateBinary();
|
||||
|
||||
//Create ring buffer of each type, then add them to a queue set
|
||||
for (int i = 0; i < NO_OF_RB_TYPES; i++) {
|
||||
buffer_handles[i] = xRingbufferCreate(BUFFER_SIZE, i);
|
||||
TEST_ASSERT_MESSAGE(buffer_handles[i] != NULL, "Failed to create ring buffer");
|
||||
TEST_ASSERT_MESSAGE(xRingbufferAddToQueueSetRead(buffer_handles[i], queue_set) == pdPASS, "Failed to add to read queue set");
|
||||
}
|
||||
//Create a task to send items to each ring buffer
|
||||
int no_of_items = BUFFER_SIZE / SMALL_ITEM_SIZE;
|
||||
xTaskCreatePinnedToCore(queue_set_receiving_task, "rec tsk", 2048, (void *)queue_set, UNITY_FREERTOS_PRIORITY + 1 , NULL, 0);
|
||||
|
||||
//Send multiple items to each type of ring buffer
|
||||
for (int i = 0; i < no_of_items; i++) {
|
||||
for (int j = 0; j < NO_OF_RB_TYPES; j++) {
|
||||
send_item_and_check(buffer_handles[j], small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
|
||||
}
|
||||
}
|
||||
|
||||
xSemaphoreTake(done_sem, portMAX_DELAY);
|
||||
vSemaphoreDelete(done_sem);
|
||||
//Remove and delete ring buffers from queue sets
|
||||
for (int i = 0; i < NO_OF_RB_TYPES; i++) {
|
||||
TEST_ASSERT_MESSAGE(xRingbufferRemoveFromQueueSetRead(buffer_handles[i], queue_set) == pdTRUE, "Failed to remove from read queue set");
|
||||
vRingbufferDelete(buffer_handles[i]);
|
||||
}
|
||||
vQueueDelete(queue_set);
|
||||
}
|
||||
|
||||
/* -------------------------- Test ring buffer ISR -----------------------------
|
||||
* The following test case tests ring buffer ISR API. A timer is used to trigger
|
||||
* the ISR. The test case will do the following
|
||||
* 1) ISR will be triggered periodically by timer
|
||||
* 2) The ISR will iterate through all ring buffer types where each iteration
|
||||
* will send then receive an item to a ring buffer.
|
||||
*/
|
||||
|
||||
#define TIMER_GROUP 0
|
||||
#define TIMER_NUMBER 0
|
||||
#define ISR_ITERATIONS ((BUFFER_SIZE / SMALL_ITEM_SIZE) * 2)
|
||||
|
||||
intr_handle_t ringbuffer_isr_handle;
|
||||
static int buf_type;
|
||||
static int iterations;
|
||||
|
||||
static void ringbuffer_isr(void *arg)
|
||||
{
|
||||
//Clear timer interrupt
|
||||
TIMERG0.int_clr_timers.t0 = 1;
|
||||
TIMERG0.hw_timer[xPortGetCoreID()].config.alarm_en = 1;
|
||||
|
||||
//Test sending to buffer from ISR from ISR
|
||||
if (buf_type < NO_OF_RB_TYPES) {
|
||||
send_item_and_check(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
|
||||
}
|
||||
|
||||
//Receive item from ISR
|
||||
if (buf_type == RINGBUF_TYPE_NOSPLIT) {
|
||||
//Test receive from ISR for no-split buffer
|
||||
receive_check_and_return_item_no_split(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
|
||||
buf_type++;
|
||||
} else if (buf_type == RINGBUF_TYPE_ALLOWSPLIT) {
|
||||
//Test send from ISR to allow-split buffer
|
||||
receive_check_and_return_item_allow_split(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
|
||||
buf_type++;
|
||||
} else if (buf_type == RINGBUF_TYPE_BYTEBUF) {
|
||||
//Test receive from ISR for byte buffer
|
||||
receive_check_and_return_item_byte_buffer(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
|
||||
buf_type++;
|
||||
} else if (buf_type == NO_OF_RB_TYPES) {
|
||||
//Check if all iterations complete
|
||||
if (iterations < ISR_ITERATIONS) {
|
||||
iterations++;
|
||||
buf_type = 0; //Reset and iterate through each buffer type again
|
||||
return;
|
||||
} else {
|
||||
sprintf(buf, "UART: %c", c);
|
||||
ets_printf("ISR w");
|
||||
xRingbufferPrintInfo(rb);
|
||||
r = xRingbufferSendFromISR(rb, buf, strlen(buf) + 1, &xHigherPriorityTaskWoken);
|
||||
if (!r) {
|
||||
ets_printf("ISR send fail\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (xHigherPriorityTaskWoken) {
|
||||
//Signal complete
|
||||
BaseType_t task_woken = pdFALSE;
|
||||
xSemaphoreGiveFromISR(done_sem, &task_woken);
|
||||
if (task_woken == pdTRUE) {
|
||||
buf_type++;
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void uartRxInit()
|
||||
static void setup_timer()
|
||||
{
|
||||
WRITE_PERI_REG(UART_CONF1_REG(0), 1 << UART_RXFIFO_FULL_THRHD_S);
|
||||
CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(0), UART_TXFIFO_EMPTY_INT_ENA | UART_RXFIFO_TOUT_INT_ENA);
|
||||
SET_PERI_REG_MASK(UART_INT_ENA_REG(0), UART_RXFIFO_FULL_INT_ENA);
|
||||
|
||||
ESP_ERROR_CHECK(esp_intr_alloc(ETS_UART0_INTR_SOURCE, 0, &uartIsrHdl, NULL, &s_intr_handle));
|
||||
//Setup timer for ISR
|
||||
int timer_group = TIMER_GROUP;
|
||||
int timer_idx = TIMER_NUMBER;
|
||||
timer_config_t config;
|
||||
config.alarm_en = 1;
|
||||
config.auto_reload = 1;
|
||||
config.counter_dir = TIMER_COUNT_UP;
|
||||
config.divider = 10000;
|
||||
config.intr_type = TIMER_INTR_LEVEL;
|
||||
config.counter_en = TIMER_PAUSE;
|
||||
timer_init(timer_group, timer_idx, &config); //Configure timer
|
||||
timer_pause(timer_group, timer_idx); //Stop timer counter
|
||||
timer_set_counter_value(timer_group, timer_idx, 0x00000000ULL); //Load counter value
|
||||
timer_set_alarm_value(timer_group, timer_idx, 20); //Set alarm value
|
||||
timer_enable_intr(timer_group, timer_idx); //Enable timer interrupt
|
||||
timer_set_auto_reload(timer_group, timer_idx, 1); //Auto Reload
|
||||
timer_isr_register(timer_group, timer_idx, ringbuffer_isr, NULL, 0, &ringbuffer_isr_handle); //Set ISR handler
|
||||
}
|
||||
|
||||
static void uartRxDeinit()
|
||||
static void cleanup_timer()
|
||||
{
|
||||
esp_intr_free(s_intr_handle);
|
||||
timer_disable_intr(TIMER_GROUP, TIMER_NUMBER);
|
||||
esp_intr_free(ringbuffer_isr_handle);
|
||||
}
|
||||
|
||||
static void testRingbuffer(int type, bool arbitrary)
|
||||
TEST_CASE("Test ring buffer ISR", "[freertos]")
|
||||
{
|
||||
TaskHandle_t th[2];
|
||||
int i;
|
||||
/* Arbitrary Length means buffer length which is not a multiple of 4 */
|
||||
if (arbitrary) {
|
||||
rb = xRingbufferCreate(31 * 3, type);
|
||||
for (int i = 0; i < NO_OF_RB_TYPES; i++) {
|
||||
buffer_handles[i] = xRingbufferCreate(BUFFER_SIZE, i);
|
||||
}
|
||||
done_sem = xSemaphoreCreateBinary();
|
||||
buf_type = 0;
|
||||
iterations = 0;
|
||||
setup_timer();
|
||||
//Start timer to trigger ISR
|
||||
timer_start(TIMER_GROUP, TIMER_NUMBER);
|
||||
//Wait for ISR to complete multiple iterations
|
||||
xSemaphoreTake(done_sem, portMAX_DELAY);
|
||||
|
||||
//Cleanup
|
||||
cleanup_timer();
|
||||
vSemaphoreDelete(done_sem);
|
||||
for (int i = 0; i < NO_OF_RB_TYPES; i++) {
|
||||
vRingbufferDelete(buffer_handles[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------- Test ring buffer SMP ---------------------------
|
||||
* The following test case tests each type of ring buffer in an SMP fashion. A
|
||||
* sending task and a receiving task is created. The sending task will split
|
||||
* a continuous piece of data into items of random length and send it to a ring
|
||||
* buffer. The receiving task will receive and check those items.
|
||||
* Every permutation of core pinning of the sending and receiving task will be
|
||||
* tested.
|
||||
*/
|
||||
|
||||
#define SRAND_SEED 3 //Arbitrarily chosen srand() seed
|
||||
#define SMP_TEST_ITERATIONS 4
|
||||
|
||||
static const char continuous_data[] = {"A_very_long_string_that_will_be_split_into_"
|
||||
"items_of_random_lengths_and_sent_to_the_ring_"
|
||||
"buffer._The_maximum_random_length_will_also_"
|
||||
"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
|
||||
|
||||
typedef struct {
|
||||
RingbufHandle_t buffer;
|
||||
ringbuf_type_t type;
|
||||
} task_args_t;
|
||||
|
||||
static SemaphoreHandle_t tasks_done;
|
||||
static SemaphoreHandle_t tx_done;
|
||||
static SemaphoreHandle_t rx_done;
|
||||
|
||||
static void send_to_buffer(RingbufHandle_t buffer, size_t max_item_size)
|
||||
{
|
||||
for (int iter = 0; iter < SMP_TEST_ITERATIONS; iter++) {
|
||||
size_t bytes_sent = 0; //Number of data bytes sent in this iteration
|
||||
size_t next_item_size; //Size of next item to send
|
||||
|
||||
while (bytes_sent < CONT_DATA_LEN) {
|
||||
//Get size of next item
|
||||
next_item_size = rand() % (max_item_size + 1);
|
||||
if (next_item_size + bytes_sent > CONT_DATA_LEN) {
|
||||
next_item_size = CONT_DATA_LEN - bytes_sent;
|
||||
}
|
||||
|
||||
//Send item
|
||||
TEST_ASSERT_MESSAGE(xRingbufferSend(buffer, (void *)&(continuous_data[bytes_sent]), next_item_size, TIMEOUT_TICKS) == pdTRUE, "Failed to send an item");
|
||||
bytes_sent += next_item_size;
|
||||
}
|
||||
xSemaphoreGive(tx_done);
|
||||
xSemaphoreTake(rx_done, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
static void read_from_buffer(RingbufHandle_t buffer, ringbuf_type_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
|
||||
while (bytes_rec < CONT_DATA_LEN) {
|
||||
size_t item_size, item_size2; //Possible for allow split buffers to receive two items
|
||||
char *item_data, *item_data2;
|
||||
|
||||
//Select appropriate receive function for type of ring buffer
|
||||
if (buf_type == RINGBUF_TYPE_NOSPLIT) {
|
||||
item_data = (char *)xRingbufferReceive(buffer, &item_size, TIMEOUT_TICKS);
|
||||
} else if (buf_type == RINGBUF_TYPE_ALLOWSPLIT) {
|
||||
BaseType_t ret = xRingbufferReceiveSplit(buffer, (void **)&item_data, (void **)&item_data2, &item_size, &item_size2, TIMEOUT_TICKS);
|
||||
TEST_ASSERT_MESSAGE(ret == pdTRUE, "Failed to receive any item");
|
||||
} else {
|
||||
rb = xRingbufferCreate(32 * 3, type);
|
||||
item_data = (char *)xRingbufferReceiveUpTo(buffer, &item_size, TIMEOUT_TICKS, max_rec_size);
|
||||
}
|
||||
|
||||
testtype = TST_MOSTLYFILLED;
|
||||
|
||||
xTaskCreatePinnedToCore(task1, "tskone", 2048, NULL, 3, &th[0], 0);
|
||||
xTaskCreatePinnedToCore(task2, "tsktwo", 2048, NULL, 3, &th[1], 0);
|
||||
uartRxInit();
|
||||
|
||||
printf("Press 'r' to read an event in isr, any other key to write one.\n");
|
||||
printf("Test: mostlyfilled; putting 10 items in ringbuff ASAP, reading 1 a second\n");
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
printf("Test: mostlyempty; putting 10 items in ringbuff @ 1/sec, reading as fast as possible\n");
|
||||
testtype = TST_MOSTLYEMPTY;
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
|
||||
//Shut down all the tasks
|
||||
for (i = 0; i < 2; i++) {
|
||||
vTaskDelete(th[i]);
|
||||
//Check received item and return it
|
||||
TEST_ASSERT_MESSAGE(item_data != NULL, "Failed to receive an item");
|
||||
if (buf_type == RINGBUF_TYPE_BYTEBUF) {
|
||||
TEST_ASSERT_MESSAGE(item_size <= max_rec_size, "Received data exceeds max size");
|
||||
}
|
||||
vRingbufferDelete(rb);
|
||||
uartRxDeinit();
|
||||
}
|
||||
|
||||
// TODO: split this thing into separate orthogonal tests
|
||||
TEST_CASE("FreeRTOS ringbuffer test, no splitting items", "[freertos]")
|
||||
{
|
||||
testRingbuffer(0, false);
|
||||
}
|
||||
|
||||
TEST_CASE("FreeRTOS ringbuffer test, w/ splitting items", "[freertos]")
|
||||
{
|
||||
testRingbuffer(1, false);
|
||||
}
|
||||
|
||||
TEST_CASE("FreeRTOS ringbuffer test, no splitting items, arbitrary length buffer", "[freertos]")
|
||||
{
|
||||
testRingbuffer(0, true);
|
||||
}
|
||||
|
||||
TEST_CASE("FreeRTOS ringbuffer test, w/ splitting items, arbitrary length buffer", "[freertos]")
|
||||
{
|
||||
testRingbuffer(1, true);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("FreeRTOS ringbuffer test, check if zero-length items are handled correctly", "[freertos]")
|
||||
{
|
||||
rb = xRingbufferCreate(32, 0);
|
||||
int r;
|
||||
void *v;
|
||||
size_t sz;
|
||||
for (int x=0; x<128; x++) {
|
||||
if (x!=127) {
|
||||
//Send an item
|
||||
r = xRingbufferSend(rb, NULL, 0, 10000 / portTICK_PERIOD_MS);
|
||||
assert(r==pdTRUE);
|
||||
for (int i = 0; i < item_size; i++) {
|
||||
//Check item_data is valid
|
||||
TEST_ASSERT_MESSAGE(item_data[i] == continuous_data[bytes_rec + i], "Received data is corrupted");
|
||||
}
|
||||
if (x!=0) {
|
||||
//Receive an item
|
||||
v=xRingbufferReceive(rb, &sz, 10000 / portTICK_PERIOD_MS);
|
||||
assert(sz==0);
|
||||
vRingbufferReturnItem(rb, v); //actually not needed for NULL data...
|
||||
bytes_rec += item_size;
|
||||
vRingbufferReturnItem(buffer, item_data);
|
||||
if (buf_type == RINGBUF_TYPE_ALLOWSPLIT && item_data2 != NULL) {
|
||||
//Check item_data2 is valid
|
||||
for (int i = 0; i < item_size2; i++) {
|
||||
TEST_ASSERT_MESSAGE(item_data2[i] == continuous_data[bytes_rec + i], "Received split data is corrupted");
|
||||
}
|
||||
bytes_rec += item_size2;
|
||||
vRingbufferReturnItem(buffer, item_data2);
|
||||
}
|
||||
}
|
||||
vRingbufferDelete(rb);
|
||||
TEST_ASSERT_MESSAGE(bytes_rec == CONT_DATA_LEN, "Total length of received data is incorrect");
|
||||
xSemaphoreGive(rx_done);
|
||||
xSemaphoreTake(tx_done, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
static void send_task(void *args)
|
||||
{
|
||||
RingbufHandle_t buffer = ((task_args_t *)args)->buffer;
|
||||
size_t max_item_len = xRingbufferGetMaxItemSize(buffer);
|
||||
|
||||
//Test sending short length items
|
||||
send_to_buffer(buffer, 1);
|
||||
//Test sending mid length items
|
||||
send_to_buffer(buffer, max_item_len/2);
|
||||
//Test sending long length items
|
||||
send_to_buffer(buffer, max_item_len);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void rec_task(void *args)
|
||||
{
|
||||
RingbufHandle_t buffer = ((task_args_t *)args)->buffer;
|
||||
size_t max_rec_len = xRingbufferGetMaxItemSize(buffer);
|
||||
|
||||
//Test receiving short length items
|
||||
read_from_buffer(buffer, ((task_args_t *)args)->type, 1);
|
||||
//Test receiving mid length items
|
||||
read_from_buffer(buffer, ((task_args_t *)args)->type, max_rec_len/2);
|
||||
//Test receiving long length items
|
||||
read_from_buffer(buffer, ((task_args_t *)args)->type, max_rec_len);
|
||||
|
||||
xSemaphoreGive(tasks_done);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("Test ring buffer SMP", "[freertos]")
|
||||
{
|
||||
ets_printf("size of buf %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
|
||||
srand(SRAND_SEED); //Seed RNG
|
||||
|
||||
//Iterate through buffer types (No split, split, then byte buff)
|
||||
for (ringbuf_type_t buf_type = 0; buf_type <= RINGBUF_TYPE_BYTEBUF; 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;
|
||||
|
||||
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);
|
||||
vTaskDelay(10);
|
||||
}
|
||||
|
||||
//Cleanup
|
||||
vSemaphoreDelete(tx_done);
|
||||
vSemaphoreDelete(rx_done);
|
||||
vSemaphoreDelete(tasks_done);
|
||||
}
|
||||
|
|
30
docs/_static/diagrams/ring-buffer/ring_buffer_read_ret_byte_buf.diag
vendored
Normal file
30
docs/_static/diagrams/ring-buffer/ring_buffer_read_ret_byte_buf.diag
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
#Diagram demonstrating reading and returning an item in a byte buffer
|
||||
#Buffer of 128 bytes, with 68 bytes occupied but wrapped. All data is read
|
||||
|
||||
packetdiag ring_buffer_read_ret_byte_buf {
|
||||
node_width = 6
|
||||
node_height = 24
|
||||
default_fontsize = 12
|
||||
colwidth = 128
|
||||
|
||||
#Initial
|
||||
0-29: 30 [color = lightyellow];
|
||||
30-89: 60 Free
|
||||
90-127: 38 [color = lightyellow];
|
||||
|
||||
#Read all continuous data
|
||||
128-157: 30 [color = lightyellow];
|
||||
158-217: 60 Free
|
||||
218-255: 38 [color = pink];
|
||||
|
||||
#Return data
|
||||
256-285: 30 [color = lightyellow];
|
||||
286-383: 98 Free
|
||||
|
||||
#Read remaining data
|
||||
384-413: 30 [color = pink];
|
||||
414-511: 98 Free
|
||||
|
||||
#Return data
|
||||
512-639: 128 Free
|
||||
}
|
86
docs/_static/diagrams/ring-buffer/ring_buffer_read_ret_non_byte_buf.diag
vendored
Normal file
86
docs/_static/diagrams/ring-buffer/ring_buffer_read_ret_non_byte_buf.diag
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
#Diagram demonstrating reading and returning an item in a No-Split/Allow-Split ring buffer
|
||||
#Buffer of 128 bytes, with 4 items of 16, 20, 8 and 24 bytes. First 3 items are read and returned
|
||||
|
||||
packetdiag ring_buffer_read_ret_non_byte_buf {
|
||||
node_width = 6
|
||||
node_height = 24
|
||||
default_fontsize = 12
|
||||
colwidth = 128
|
||||
|
||||
#Initial
|
||||
0-7: 8 [color = lightblue];
|
||||
8-23: 16 [color = lightyellow];
|
||||
24-31: 8 [color = lightblue];
|
||||
32-51: 20 [color = lightyellow];
|
||||
52-59: 8 [color = lightblue];
|
||||
60-67: 8 [color = lightyellow];
|
||||
68-75: 8 [color = lightblue];
|
||||
76-99: 24 [color = lightyellow];
|
||||
100-127: 28 Free
|
||||
|
||||
#Read item 1
|
||||
128-135: 8 [color = pink];
|
||||
136-151: 16 [color = pink];
|
||||
152-159: 8 [color = lightblue];
|
||||
160-179: 20 [color = lightyellow];
|
||||
180-187: 8 [color = lightblue];
|
||||
188-195: 8 [color = lightyellow];
|
||||
196-203: 8 [color = lightblue];
|
||||
204-227: 24 [color = lightyellow];
|
||||
228-255: 28 Free
|
||||
|
||||
#Read item 2
|
||||
256-263: 8 [color = pink];
|
||||
264-279: 16 [color = pink];
|
||||
280-287: 8 [color = pink];
|
||||
288-307: 20 [color = pink];
|
||||
308-315: 8 [color = lightblue];
|
||||
316-323: 8 [color = lightyellow];
|
||||
324-331: 8 [color = lightblue];
|
||||
332-355: 24 [color = lightyellow];
|
||||
356-383: 28 Free
|
||||
|
||||
#Read item 3
|
||||
384-391: 8 [color = pink];
|
||||
392-407: 16 [color = pink];
|
||||
408-415: 8 [color = pink];
|
||||
416-435: 20 [color = pink];
|
||||
436-443: 8 [color = pink];
|
||||
444-451: 8 [color = pink];
|
||||
452-459: 8 [color = lightblue];
|
||||
460-483: 24 [color = lightyellow];
|
||||
484-511: 28 Free
|
||||
|
||||
#Return item 2
|
||||
512-519: 8 [color = pink];
|
||||
520-535: 16 [color = pink];
|
||||
536-563: Ret [color = lightgrey];
|
||||
564-571: 8 [color = pink];
|
||||
572-579: 8 [color = pink];
|
||||
580-587: 8 [color = lightblue];
|
||||
588-611: 24 [color = lightyellow];
|
||||
612-639: 28 Free
|
||||
|
||||
#Return item 3
|
||||
640-647: 8 [color = pink];
|
||||
648-663: 16 [color = pink];
|
||||
664-691: Ret [color = lightgrey];
|
||||
692-707: Ret [color = lightgrey];
|
||||
708-715: 8 [color = lightblue];
|
||||
716-739: 24 [color = lightyellow];
|
||||
740-767: 28 Free
|
||||
|
||||
#Return item 1
|
||||
768-791: Ret [color = lightgrey];
|
||||
792-819: Ret [color = lightgrey];
|
||||
820-835: Ret [color = lightgrey];
|
||||
836-843: 8 [color = lightblue];
|
||||
844-867: 24 [color = lightyellow];
|
||||
868-895: 28 Free
|
||||
|
||||
#End state
|
||||
896-963: 68 Free
|
||||
964-971: 8 [color = lightblue];
|
||||
972-995: 24 [color = lightyellow];
|
||||
996-1023: 28 Free
|
||||
}
|
21
docs/_static/diagrams/ring-buffer/ring_buffer_send_byte_buf.diag
vendored
Normal file
21
docs/_static/diagrams/ring-buffer/ring_buffer_send_byte_buf.diag
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
#Diagram demonstrating sending in byte buffer
|
||||
#Buffer of 128 bytes, and 3 items of size 18, 3, and 27 bytes sent
|
||||
|
||||
packetdiag ring_buffer_send_byte_buf {
|
||||
node_width = 6
|
||||
node_height = 24
|
||||
default_fontsize = 12
|
||||
colwidth = 128
|
||||
|
||||
#Add 18 byte item
|
||||
0-17: 18 [color = lightyellow];
|
||||
18-127: 110 Free
|
||||
|
||||
#Add 3 byte item
|
||||
128-148: 21 [color = lightyellow];
|
||||
149-255: 107 Free
|
||||
|
||||
#Add 27 byte item
|
||||
256-303: 48 [color = lightyellow];
|
||||
304-383: 80 Free
|
||||
}
|
30
docs/_static/diagrams/ring-buffer/ring_buffer_send_non_byte_buf.diag
vendored
Normal file
30
docs/_static/diagrams/ring-buffer/ring_buffer_send_non_byte_buf.diag
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
#Diagram demonstrating sending in a No-Split/Allow-Split ring buffer
|
||||
#Buffer of 128 bytes, and 3 items of size 18, 3, and 27 bytes sent
|
||||
|
||||
packetdiag ring_buffer_send_non_byte_buf {
|
||||
node_width = 6
|
||||
node_height = 24
|
||||
default_fontsize = 12
|
||||
colwidth = 128
|
||||
|
||||
#Add 18 byte item
|
||||
0-7: 8 [color = lightblue];
|
||||
8-27: 20 [color = lightyellow];
|
||||
28-127: 100 Free
|
||||
|
||||
#Add 3 byte item
|
||||
128-135: 8 [color = lightblue];
|
||||
136-155: 20 [color = lightyellow];
|
||||
156-163: 8 [color = lightblue];
|
||||
164-167: 4 [color = lightyellow];
|
||||
168-255: 88 Free
|
||||
|
||||
#Add 27 byte item
|
||||
256-263: 8 [color = lightblue];
|
||||
264-283: 20 [color = lightyellow];
|
||||
284-291: 8 [color = lightblue];
|
||||
292-295: 4 [color = lightyellow];
|
||||
296-303: 8 [color = lightblue];
|
||||
304-331: 28 [color = lightyellow];
|
||||
332-383: 52 Free
|
||||
}
|
37
docs/_static/diagrams/ring-buffer/ring_buffer_wrap_allow_split.diag
vendored
Normal file
37
docs/_static/diagrams/ring-buffer/ring_buffer_wrap_allow_split.diag
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
#Diagram demonstrating wrap around in a Allow-Split ring buffer
|
||||
#Buffer of 128 bytes, with 56 bytes free, and 28 bytes sent
|
||||
|
||||
packetdiag ring_buffer_wrap_allow_split {
|
||||
node_width = 6
|
||||
node_height = 24
|
||||
default_fontsize = 12
|
||||
colwidth = 128
|
||||
|
||||
#Initial state
|
||||
0-39: 40 Free
|
||||
40-47: 8 [color = lightblue];
|
||||
48-63: 16 [color = lightyellow];
|
||||
64-71: 8 [color = lightblue];
|
||||
72-111: 40 [color = lightyellow];
|
||||
112-127: 16 Free
|
||||
|
||||
#Send first part
|
||||
128-167: 40 Free
|
||||
168-175: 8 [color = lightblue];
|
||||
176-191: 16 [color = lightyellow];
|
||||
192-199: 8 [color = lightblue];
|
||||
200-239: 40 [color = lightyellow];
|
||||
240-247: 8 [color = lightblue];
|
||||
248-255: 8 [color = lightyellow];
|
||||
|
||||
#Send second part
|
||||
256-263: 8 [color = lightblue];
|
||||
264-283: 20 [color = lightyellow];
|
||||
284-295: 12 Free
|
||||
296-303: 8 [color = lightblue];
|
||||
304-319: 16 [color = lightyellow];
|
||||
320-327: 8 [color = lightblue];
|
||||
328-367: 40 [color = lightyellow];
|
||||
368-375: 8 [color = lightblue];
|
||||
376-383: 8 [color = lightyellow];
|
||||
}
|
23
docs/_static/diagrams/ring-buffer/ring_buffer_wrap_byte_buf.diag
vendored
Normal file
23
docs/_static/diagrams/ring-buffer/ring_buffer_wrap_byte_buf.diag
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
#Diagram demonstrating wrap around in byte buffer
|
||||
#Buffer of 128 bytes, with 56 bytes free, and 28 bytes sent
|
||||
|
||||
packetdiag ring_buffer_wrap_byte_buf {
|
||||
node_width = 6
|
||||
node_height = 24
|
||||
default_fontsize = 12
|
||||
colwidth = 128
|
||||
|
||||
#Initial state
|
||||
0-39: 40 Free
|
||||
40-111: 72 [color = lightyellow];
|
||||
112-127: 16 Free
|
||||
|
||||
#Fill up free space at the end of the buffer
|
||||
128-167: 40 Free
|
||||
168-255: 88 [color = lightyellow];
|
||||
|
||||
#Wrap around remaining data
|
||||
256-267: 12 [color = lightyellow];
|
||||
268-295: 28 Free
|
||||
296-383: 88 [color = lightyellow];
|
||||
}
|
35
docs/_static/diagrams/ring-buffer/ring_buffer_wrap_no_split.diag
vendored
Normal file
35
docs/_static/diagrams/ring-buffer/ring_buffer_wrap_no_split.diag
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
#Diagram demonstrating wrap around in a No-Split ring buffer
|
||||
#Buffer of 128 bytes, with 56 bytes free, and 28 bytes sent
|
||||
|
||||
packetdiag ring_buffer_wrap_no_split {
|
||||
node_width = 6
|
||||
node_height = 24
|
||||
default_fontsize = 12
|
||||
colwidth = 128
|
||||
|
||||
#Initial state
|
||||
0-39: 40 Free
|
||||
40-47: 8 [color = lightblue];
|
||||
48-63: 16 [color = lightyellow];
|
||||
64-71: 8 [color = lightblue];
|
||||
72-111: 40 [color = lightyellow];
|
||||
112-127: 16 Free
|
||||
|
||||
#Set dummy data
|
||||
128-167: 40 Free
|
||||
168-175: 8 [color = lightblue];
|
||||
176-191: 16 [color = lightyellow];
|
||||
192-199: 8 [color = lightblue];
|
||||
200-239: 40 [color = lightyellow];
|
||||
240-255: Dummy [color = lightgrey];
|
||||
|
||||
#Send wrap around item
|
||||
256-263: 8 [color = lightblue];
|
||||
264-291: 28 [color = lightyellow];
|
||||
292-295: 4 Free
|
||||
296-303: 8 [color = lightblue];
|
||||
304-319: 16 [color = lightyellow];
|
||||
320-327: 8 [color = lightblue];
|
||||
328-367: 40 [color = lightyellow];
|
||||
368-383: Dummy [color = lightgrey];
|
||||
}
|
|
@ -16,6 +16,9 @@ of FreeRTOS v8.2.0. This guide outlines the major differences between vanilla
|
|||
FreeRTOS and ESP-IDF FreeRTOS. The API reference for vanilla FreeRTOS can be
|
||||
found via http://www.freertos.org/a00106.html
|
||||
|
||||
For information regarding features that are exclusive to ESP-IDF FreeRTOS,
|
||||
see :doc:`ESP-IDF FreeRTOS Additions<../api-reference/system/freertos_additions>`.
|
||||
|
||||
:ref:`backported-features`: Although ESP-IDF FreeRTOS is based on the Xtensa
|
||||
port of FreeRTOS v8.2.0, a number of FreeRTOS v9.0.0 features have been backported
|
||||
to ESP-IDF.
|
||||
|
@ -70,10 +73,6 @@ used to free memory pointed to by TLSP. Call
|
|||
:cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback()` to set TLSP and Deletion
|
||||
Callbacks.
|
||||
|
||||
:ref:`FreeRTOS Hooks<hooks_api_reference>`: Vanilla FreeRTOS Hooks were not designed for SMP.
|
||||
ESP-IDF provides its own Idle and Tick Hooks in addition to the Vanilla FreeRTOS
|
||||
hooks. For full details, see the ESP-IDF Hooks API Reference.
|
||||
|
||||
:ref:`esp-idf-freertos-configuration`: Several aspects of ESP-IDF FreeRTOS can be
|
||||
configured using ``make meunconfig`` such as running ESP-IDF in Unicore Mode,
|
||||
or configuring the number of Thread Local Storage Pointers each task will have.
|
||||
|
|
|
@ -6,7 +6,8 @@ Overview
|
|||
|
||||
This section contains documentation of FreeRTOS types, functions, and macros. It is automatically generated from FreeRTOS header files.
|
||||
|
||||
For more information about FreeRTOS features specific to ESP-IDF, see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`.
|
||||
For more information about FreeRTOS features specific to ESP-IDF, see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`
|
||||
and :doc:`ESP-IDF FreeRTOS Additions<freertos_additions>`.
|
||||
|
||||
|
||||
Task API
|
||||
|
@ -35,8 +36,4 @@ Event Group API
|
|||
|
||||
.. include:: /_build/inc/event_groups.inc
|
||||
|
||||
Ringbuffer API
|
||||
--------------
|
||||
|
||||
.. include:: /_build/inc/ringbuf.inc
|
||||
|
||||
|
|
403
docs/en/api-reference/system/freertos_additions.rst
Normal file
403
docs/en/api-reference/system/freertos_additions.rst
Normal file
|
@ -0,0 +1,403 @@
|
|||
FreeRTOS Additions
|
||||
==================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
ESP-IDF FreeRTOS is based on the Xtensa port of FreeRTOS v8.2.0 with significant modifications
|
||||
for SMP compatibility (see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`).
|
||||
However various features specific to ESP-IDF FreeRTOS have been added. The features are as follows:
|
||||
|
||||
:ref:`ring-buffers`: Ring buffers were added to provide a form of buffer that could accept
|
||||
entries of arbitrary lengths.
|
||||
|
||||
:ref:`hooks`: ESP-IDF FreeRTOS hooks provides support for registering extra Idle and
|
||||
Tick hooks at run time. Moreover, the hooks can be asymmetric amongst both CPUs.
|
||||
|
||||
|
||||
.. _ring-buffers:
|
||||
|
||||
Ring Buffers
|
||||
------------
|
||||
|
||||
The ESP-IDF FreeRTOS ring buffer is a strictly FIFO buffer that supports arbitrarily sized items.
|
||||
Ring buffers are a more memory efficient alternative to FreeRTOS queues in situations where the
|
||||
size of items is variable. The capacity of a ring buffer is not measured by the number of items
|
||||
it can store, but rather by the amount of memory used for storing items. Items are sent to
|
||||
ring buffers by copy, however for efficiency reasons **items are retrieved by reference**. As a
|
||||
result, all retrieved items **must also be returned** in order for them to be removed from
|
||||
the ring buffer completely. The ring buffers are split into the three following types:
|
||||
|
||||
**No-Split** buffers will guarantee that an item is stored in contiguous memory and will not
|
||||
attempt to split an item under any circumstances. Use no-split buffers when items must occupy
|
||||
contiguous memory.
|
||||
|
||||
**Allow-Split** buffers will allow an item to be split when wrapping around if doing so will allow
|
||||
the item to be stored. Allow-split buffers are more memory efficient than no-split buffers but
|
||||
can return an item in two parts when retrieving.
|
||||
|
||||
**Byte buffers** do not store data as separate items. All data is stored as a sequence of bytes,
|
||||
and any number of bytes and be sent or retrieved each time. Use byte buffers when separate items
|
||||
do not need to be maintained (e.g. a byte stream).
|
||||
|
||||
.. note::
|
||||
No-split/allow-split buffers will always store items at 32-bit aligned addresses. Therefore when
|
||||
retrieving an item, the item pointer is guaranteed to be 32-bit aligned.
|
||||
|
||||
.. note::
|
||||
Each item stored in no-split/allow-split buffers will **require an additional 8 bytes for a header**.
|
||||
Item sizes will also be rounded up to a 32-bit aligned size (multiple of 4 bytes), however the true
|
||||
item size is recorded within the header. The sizes of no-split/allow-split buffers will also
|
||||
be rounded up when created.
|
||||
|
||||
Usage
|
||||
^^^^^
|
||||
|
||||
The following example demonstrates the usage of :cpp:func:`xRingbufferCreate`
|
||||
and :cpp:func:`xRingbufferSend` to create a ring buffer then send an item to it.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include "freertos/ringbuf.h"
|
||||
static char tx_item[] = "test_item";
|
||||
|
||||
...
|
||||
|
||||
//Create ring buffer
|
||||
RingbufHandle_t buf_handle;
|
||||
buf_handle = xRingbufferCreate(1028, RINGBUF_TYPE_NOSPLIT);
|
||||
if (buf_handle == NULL) {
|
||||
printf("Failed to create ring buffer\n");
|
||||
}
|
||||
|
||||
//Send an item
|
||||
UBaseType_t res = xRingbufferSend(buf_handle, tx_item, sizeof(tx_item), pdMS_TO_TICKS(1000));
|
||||
if (res != pdTRUE) {
|
||||
printf("Failed to send item\n");
|
||||
}
|
||||
|
||||
|
||||
The following example demonstrates retrieving and returning an item from a **no-split ring buffer**
|
||||
using :cpp:func:`xRingbufferReceive` and :cpp:func:`vRingbufferReturnItem`
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
...
|
||||
|
||||
//Receive an item from no-split ring buffer
|
||||
size_t item_size;
|
||||
char *item = (char *)xRingbufferReceive(buf_handle, &item_size, pdMS_TO_TICKS(1000));
|
||||
|
||||
//Check received item
|
||||
if (item != NULL) {
|
||||
//Print item
|
||||
for (int i = 0; i < item_size; i++) {
|
||||
printf("%c", item[i]);
|
||||
}
|
||||
printf("\n");
|
||||
//Return Item
|
||||
vRingbufferReturnItem(buf_handle, (void *)item);
|
||||
} else {
|
||||
//Failed to receive item
|
||||
printf("Failed to receive item\n");
|
||||
}
|
||||
|
||||
|
||||
The following example demonstrates retrieving and returning an item from an **allow-split ring buffer**
|
||||
using :cpp:func:`xRingbufferReceiveSplit` and :cpp:func:`vRingbufferReturnItem`
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
...
|
||||
|
||||
//Receive an item from allow-split ring buffer
|
||||
size_t item_size1, item_size2;
|
||||
char *item1, *item2;
|
||||
BaseType_t ret = xRingbufferReceiveSplit(buf_handle, (void **)&item1, (void **)&item2, &item_size1, &item_size2, pdMS_TO_TICKS(1000));
|
||||
|
||||
//Check received item
|
||||
if (ret == pdTRUE && item1 != NULL) {
|
||||
for (int i = 0; i < item_size1; i++) {
|
||||
printf("%c", item1[i]);
|
||||
}
|
||||
vRingbufferReturnItem(buf_handle, (void *)item1);
|
||||
//Check if item was split
|
||||
if (item2 != NULL) {
|
||||
for (int i = 0; i < item_size2; i++) {
|
||||
printf("%c", item2[i]);
|
||||
}
|
||||
vRingbufferReturnItem(buf_handle, (void *)item2);
|
||||
}
|
||||
printf("\n");
|
||||
} else {
|
||||
//Failed to receive item
|
||||
printf("Failed to receive item\n");
|
||||
}
|
||||
|
||||
|
||||
The following example demonstrates retrieving and returning an item from a **byte buffer**
|
||||
using :cpp:func:`xRingbufferReceiveUpTo` and :cpp:func:`vRingbufferReturnItem`
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
...
|
||||
|
||||
//Receive data from byte buffer
|
||||
size_t item_size;
|
||||
char *item = (char *)xRingbufferReceiveUpTo(buf_handle, &item_size, pdMS_TO_TICKS(1000), sizeof(tx_item));
|
||||
|
||||
//Check received data
|
||||
if (item != NULL) {
|
||||
//Print item
|
||||
for (int i = 0; i < item_size; i++) {
|
||||
printf("%c", item[i]);
|
||||
}
|
||||
printf("\n");
|
||||
//Return Item
|
||||
vRingbufferReturnItem(buf_handle, (void *)item);
|
||||
} else {
|
||||
//Failed to receive item
|
||||
printf("Failed to receive item\n");
|
||||
}
|
||||
|
||||
|
||||
For ISR safe versions of the functions used above, call :cpp:func:`xRingbufferSendFromISR`, :cpp:func:`xRingbufferReceiveFromISR`,
|
||||
:cpp:func:`xRingbufferReceiveSplitFromISR`, :cpp:func:`xRingbufferReceiveUpToFromISR`, and :cpp:func:`vRingbufferReturnItemFromISR`
|
||||
|
||||
|
||||
Sending to Ring Buffer
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following diagrams illustrate the differences between no-split/allow-split buffers
|
||||
and byte buffers with regards to sending items/data. The diagrams assume that three
|
||||
items of sizes **18, 3, and 27 bytes** are sent respectively to a **buffer of 128 bytes**.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_send_non_byte_buf.diag
|
||||
:caption: Sending items to no-split/allow-split ring buffers
|
||||
:align: center
|
||||
|
||||
For no-split/allow-split buffers, a header of 8 bytes precedes every data item. Furthermore, the space
|
||||
occupied by each item is **rounded up to the nearest 32-bit aligned size** in order to maintain overall
|
||||
32-bit alignment. However the true size of the item is recorded inside the header which will be
|
||||
returned when the item is retrieved.
|
||||
|
||||
Referring to the diagram above, the 18, 3, and 27 byte items are **rounded up to 20, 4, and 28 bytes**
|
||||
respectively. An 8 byte header is then added in front of each item.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_send_byte_buf.diag
|
||||
:caption: Sending items to byte buffers
|
||||
:align: center
|
||||
|
||||
Byte buffers treat data as a sequence of bytes and does not incur any overhead
|
||||
(no headers). As a result, all data sent to a byte buffer is merged into a single item.
|
||||
|
||||
Referring to the diagram above, the 18, 3, and 27 byte items are sequentially written to the
|
||||
byte buffer and **merged into a single item of 48 bytes**.
|
||||
|
||||
Wrap around
|
||||
^^^^^^^^^^^
|
||||
|
||||
The following diagrams illustrate the differences between no-split, allow-split, and byte
|
||||
buffers when a sent item requires a wrap around. The diagrams assumes a buffer of **128 bytes**
|
||||
with **56 bytes of free space that wraps around** and a sent item of **28 bytes**.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_no_split.diag
|
||||
:caption: Wrap around in no-split buffers
|
||||
:align: center
|
||||
|
||||
No-split buffers will **only store an item in continuous free space and will not split
|
||||
an item under any circumstances**. When the free space at the tail of the buffer is insufficient
|
||||
to completely store the item and its header, the free space at the tail will be **marked as dummy data**.
|
||||
The buffer will then wrap around and store the item in the free space at the head of the buffer.
|
||||
|
||||
Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is
|
||||
insufficient to store the 28 byte item. Therefore the 16 bytes is marked as dummy data and
|
||||
the item is written to the free space at the head of the buffer instead.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_allow_split.diag
|
||||
:caption: Wrap around in allow-split buffers
|
||||
:align: center
|
||||
|
||||
Allow-split buffers will attempt to **split the item into two parts** when the free space at the tail
|
||||
of the buffer is insufficient to store the item data and its header. Both parts of the
|
||||
split item will have their own headers (therefore incurring an extra 8 bytes of overhead).
|
||||
|
||||
Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is insufficient
|
||||
to store the 28 byte item. Therefore the item is split into two parts (8 and 20 bytes) and written
|
||||
as two parts to the buffer.
|
||||
|
||||
.. note::
|
||||
Allow-split buffers treats the both parts of the split item as two separate items, therefore call
|
||||
:cpp:func:`xRingbufferReceiveSplit` instead of :cpp:func:`xRingbufferReceive` to receive both
|
||||
parts of a split item in a thread safe manner.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_byte_buf.diag
|
||||
:caption: Wrap around in byte buffers
|
||||
:align: center
|
||||
|
||||
Byte buffers will **store as much data as possible into the free space at the tail of buffer**. The remaining
|
||||
data will then be stored in the free space at the head of the buffer. No overhead is incurred when wrapping
|
||||
around in byte buffers.
|
||||
|
||||
Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is insufficient to
|
||||
completely store the 28 bytes of data. Therefore the 16 bytes of free space is filled with data, and the
|
||||
remaining 12 bytes are written to the free space at the head of the buffer. The buffer now contains
|
||||
data in two separate continuous parts, and each part continuous will be treated as a separate item by the
|
||||
byte buffer.
|
||||
|
||||
Retrieving/Returning
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following diagrams illustrates the differences between no-split/allow-split and
|
||||
byte buffers in retrieving and returning data.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_read_ret_non_byte_buf.diag
|
||||
:caption: Retrieving/Returning items in no-split/allow-split ring buffers
|
||||
:align: center
|
||||
|
||||
Items in no-split/allow-split buffers are **retrieved in strict FIFO order** and **must be returned**
|
||||
for the occupied space to be freed. Multiple items can be retrieved before returning, and the items
|
||||
do not necessarily need to be returned in the order they were retrieved. However the freeing of space
|
||||
must occur in FIFO order, therefore not returning the earliest retrieved item will prevent the space
|
||||
of subsequent items from being freed.
|
||||
|
||||
Referring to the diagram above, the **16, 20, and 8 byte items are retrieved in FIFO order**. However the items
|
||||
are not returned in they were retrieved (20, 8, 16). As such, the space is not freed until the first item
|
||||
(16 byte) is returned.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_read_ret_byte_buf.diag
|
||||
:caption: Retrieving/Returning data in byte buffers
|
||||
:align: center
|
||||
|
||||
Byte buffers **do not allow multiple retrievals before returning** (every retrieval must be followed by a return
|
||||
before another retrieval is permitted). When using :cpp:func:`xRingbufferReceive` or
|
||||
:cpp:func:`xRingbufferReceiveFromISR`, all continuous stored data will be retrieved. :cpp:func:`xRingbufferReceiveUpTo`
|
||||
or :cpp:func:`xRingbufferReceiveUpToFromISR` can be used to restrict the maximum number of bytes retrieved. Since
|
||||
every retrieval must be followed by a return, the space will be freed as soon as the data is returned.
|
||||
|
||||
Referring to the diagram above, the 38 bytes of continuous stored data at the tail of the buffer is retrieved,
|
||||
returned, and freed. The next call to :cpp:func:`xRingbufferReceive` or :cpp:func:`xRingbufferReceiveFromISR`
|
||||
then wraps around and does the same to the 30 bytes of continuous stored data at the head of the buffer.
|
||||
|
||||
Ring Buffers with Queue Sets
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Ring buffers can be added to FreeRTOS queue sets using :cpp:func:`xRingbufferAddToQueueSetRead` such that every
|
||||
time a ring buffer receives an item or data, the queue set is notified. Once added to a queue set, every
|
||||
attempt to retrieve an item from a ring buffer should be preceded by a call to :cpp:func:`xQueueSelectFromSet`.
|
||||
To check whether the selected queue set member is the ring buffer, call :cpp:func:`xRingbufferCanRead`.
|
||||
|
||||
The following example demonstrates queue set usage with ring buffers.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/ringbuf.h"
|
||||
|
||||
...
|
||||
|
||||
//Create ring buffer and queue set
|
||||
RingbufHandle_t buf_handle = xRingbufferCreate(1028, RINGBUF_TYPE_NOSPLIT);
|
||||
QueueSetHandle_t queue_set = xQueueCreateSet(3);
|
||||
|
||||
//Add ring buffer to queue set
|
||||
if (xRingbufferAddToQueueSetRead(buf_handle, queue_set) != pdTRUE) {
|
||||
printf("Failed to add to queue set\n");
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
//Block on queue set
|
||||
xQueueSetMemberHandle member = xQueueSelectFromSet(queue_set, pdMS_TO_TICKS(1000));
|
||||
|
||||
//Check if member is ring buffer
|
||||
if (member != NULL && xRingbufferCanRead(buf_handle, member) == pdTRUE) {
|
||||
//Member is ring buffer, receive item from ring buffer
|
||||
size_t item_size;
|
||||
char *item = (char *)xRingbufferReceive(buf_handle, &item_size, 0);
|
||||
|
||||
//Handle item
|
||||
...
|
||||
|
||||
} else {
|
||||
...
|
||||
}
|
||||
|
||||
|
||||
Ring Buffer API Reference
|
||||
-------------------------
|
||||
|
||||
.. note::
|
||||
Ideally, ring buffers can be used with multiple tasks in an SMP fashion where the **highest
|
||||
priority task will always be serviced first.** However due to the usage of binary semaphores
|
||||
in the ring buffer's underlying implementation, priority inversion may occur under very
|
||||
specific circumstances.
|
||||
|
||||
The ring buffer governs sending by a binary semaphore which is given whenever space is
|
||||
freed on the ring buffer. The highest priority task waiting to send will repeatedly take
|
||||
the semaphore until sufficient free space becomes available or until it times out. Ideally
|
||||
this should prevent any lower priority tasks from being serviced as the semaphore should
|
||||
always be given to the highest priority task.
|
||||
|
||||
However in between iterations of acquiring the semaphore, there is a **gap in the critical
|
||||
section** which may permit another task (on the other core or with an even higher priority) to
|
||||
free some space on the ring buffer and as a result give the semaphore. Therefore the semaphore
|
||||
will be given before the highest priority task can re-acquire the semaphore. This will result
|
||||
in the **semaphore being acquired by the second highest priority task** waiting to send, hence
|
||||
causing priority inversion.
|
||||
|
||||
This side effect will not affect ring buffer performance drastically given if the number
|
||||
of tasks using the ring buffer simultaneously is low, and the ring buffer is not operating
|
||||
near maximum capacity.
|
||||
|
||||
.. include:: /_build/inc/ringbuf.inc
|
||||
|
||||
|
||||
.. _hooks:
|
||||
|
||||
Hooks
|
||||
-----
|
||||
|
||||
FreeRTOS consists of Idle Hooks and Tick Hooks which allow for application
|
||||
specific functionality to be added to the Idle Task and Tick Interrupt.
|
||||
ESP-IDF provides its own Idle and Tick Hook API in addition to the hooks
|
||||
provided by Vanilla FreeRTOS. ESP-IDF hooks have the added benefit of
|
||||
being run time configurable and asymmetrical.
|
||||
|
||||
Vanilla FreeRTOS Hooks
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Idle and Tick Hooks in vanilla FreeRTOS are implemented by the user
|
||||
defining the functions ``vApplicationIdleHook()`` and ``vApplicationTickHook()``
|
||||
respectively somewhere in the application. Vanilla FreeRTOS will run the user
|
||||
defined Idle Hook and Tick Hook on every iteration of the Idle Task and Tick
|
||||
Interrupt respectively.
|
||||
|
||||
Vanilla FreeRTOS hooks are referred to as **Legacy Hooks** in ESP-IDF FreeRTOS.
|
||||
To enable legacy hooks, :ref:`CONFIG_FREERTOS_LEGACY_HOOKS`,
|
||||
:ref:`CONFIG_FREERTOS_LEGACY_IDLE_HOOK`, and :ref:`CONFIG_FREERTOS_LEGACY_TICK_HOOK`
|
||||
should all be enabled in ``make menuconfig``.
|
||||
|
||||
Due to vanilla FreeRTOS being designed for single core, ``vApplicationIdleHook()``
|
||||
and ``vApplicationTickHook()`` can only be defined once. However, the ESP32 is dual core
|
||||
in nature, therefore same Idle Hook and Tick Hook are used for both cores (in other words,
|
||||
the hooks are symmetrical for both cores).
|
||||
|
||||
ESP-IDF Idle and Tick Hooks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Due to the the dual core nature of the ESP32, it may be necessary for some
|
||||
applications to have separate hooks for each core. Furthermore, it may
|
||||
be necessary for the Idle Tasks or Tick Interrupts to execute multiple hooks
|
||||
that are configurable at run time. Therefore the ESP-IDF provides it's own hooks
|
||||
API in addition to the legacy hooks provided by Vanilla FreeRTOS.
|
||||
|
||||
The ESP-IDF tick/idle hooks are registered at run time, and each tick/idle hook
|
||||
must be registered to a specific CPU. When the idle task runs/tick Interrupt
|
||||
occurs on a particular CPU, the CPU will run each of its registered idle/tick hooks
|
||||
in turn.
|
||||
|
||||
|
||||
Hooks API Reference
|
||||
-------------------
|
||||
|
||||
.. include:: /_build/inc/esp_freertos_hooks.inc
|
|
@ -1,51 +0,0 @@
|
|||
.. _hooks_api_reference:
|
||||
|
||||
FreeRTOS Hooks
|
||||
==============
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
FreeRTOS consists of Idle Hooks and Tick Hooks which allow for application
|
||||
specific funtiionality to be added to the Idle Task and Tick Interrupt. The
|
||||
ESP32 is dual core in nature, hence the ESP-IDF provides its own Idle and Tick
|
||||
Hooks that are dual core compatible in addition to the hooks provided by Vanilla
|
||||
FreeRTOS.
|
||||
|
||||
Vanilla FreeRTOS Hooks
|
||||
----------------------
|
||||
|
||||
Idle and Tick Hooks in vanilla FreeRTOS are implemented by defining
|
||||
implementations for the functions ``vApplicationIdleHook`` and
|
||||
``vApplicationTickHook`` respectively somewhere in the application. Vanilla
|
||||
FreeRTOS will run the user defined Idle Hook every iteration of the Idle Task,
|
||||
whereas the user defined Tick Hook will run once per tick interrupt (given that
|
||||
there are no pended ticks).
|
||||
|
||||
Due to vanilla FreeRTOS being designed for single core, ``vApplicationIdleHook``
|
||||
and ``vApplicationTickHook`` will be run in both cores on the ESP32. In
|
||||
other words, the same Idle Hook and Tick Hook are used for both cores.
|
||||
|
||||
To enable the vanilla FreeRTOS hooks in ESP-IDF, :ref:`CONFIG_FREERTOS_LEGACY_HOOKS`
|
||||
must be enabled in ``make menuconfig``. :ref:`CONFIG_FREERTOS_LEGACY_IDLE_HOOK`
|
||||
and :ref:`CONFIG_FREERTOS_LEGACY_TICK_HOOK` should also be enabled.
|
||||
|
||||
ESP-IDF Idle and Tick Hooks
|
||||
---------------------------
|
||||
|
||||
Due to the dual core nature of the ESP32, it may be necessary for some
|
||||
applications to have seperate Idle Hooks for each core. Furthermore, it may
|
||||
be necessary for Idle and Tick Hooks to have execute multiple functionalities
|
||||
that are configurable at run time. Therefore the ESP-IDF provides it's own Idle
|
||||
and Tick Hooks in addition to the hooks provided by Vanilla FreeRTOS.
|
||||
|
||||
The ESP-IDF Hooks do not operate in the same way as Vanilla FreeRTOS Hooks
|
||||
where users provide a definition for each of the hooks. Instead, the ESP-IDF
|
||||
Hooks are predefined to call a list of user registered callbacks specific to
|
||||
each core. Users can register and deregister callbacks which are run on the
|
||||
Idle or Tick Hook of a specific core.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include:: /_build/inc/esp_freertos_hooks.inc
|
|
@ -5,7 +5,7 @@ System API
|
|||
:maxdepth: 1
|
||||
|
||||
FreeRTOS <freertos>
|
||||
FreeRTOS Hooks <hooks>
|
||||
FreeRTOS Additions <freertos_additions>
|
||||
Heap Memory Allocation <mem_alloc>
|
||||
Heap Memory Debugging <heap_debug>
|
||||
Interrupt Allocation <intr_alloc>
|
||||
|
|
1
docs/zh_CN/api-reference/system/freertos_additions.rst
Normal file
1
docs/zh_CN/api-reference/system/freertos_additions.rst
Normal file
|
@ -0,0 +1 @@
|
|||
.. include:: ../../../en/api-reference/system/freertos_additions.rst
|
|
@ -1 +0,0 @@
|
|||
.. include:: ../../../en/api-reference/system/hooks.rst
|
Loading…
Reference in a new issue