freertos/Demo/Common/include/FreeRTOS/StreamBuffer.hpp

566 lines
23 KiB
C++
Raw Permalink Normal View History

2024-07-13 16:33:13 +00:00
/*
* FreeRTOS-Cpp
* Copyright (C) 2021 Jon Enz. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* https://github.com/jonenz/FreeRTOS-Cpp
*/
#ifndef FREERTOS_STREAMBUFFER_HPP
#define FREERTOS_STREAMBUFFER_HPP
#include "FreeRTOS.h"
#include "stream_buffer.h"
namespace FreeRTOS {
/**
* @class StreamBufferBase StreamBuffer.hpp <FreeRTOS/StreamBuffer.hpp>
*
* @brief Base class that provides the standard stream buffer interface to
* FreeRTOS::StreamBuffer and FreeRTOS::StaticStreamBuffer.
*
* @note This class is not intended to be instantiated by the user. Use
* FreeRTOS::StreamBuffer or FreeRTOS::StaticStreamBuffer.
*
* @warning Uniquely among FreeRTOS objects, the stream buffer implementation
* (so also the message buffer implementation, as message buffers are built on
* top of stream buffers) assumes there is only one task or interrupt that will
* write to the buffer (the writer), and only one task or interrupt that will
* read from the buffer (the reader). It is safe for the writer and reader to
* be different tasks or interrupts, but, unlike other FreeRTOS objects, it is
* not safe to have multiple different writers or multiple different readers. If
* there are to be multiple different writers then the application writer must
* place each call to a writing API function (such as send()) inside a critical
* section and set the send block time to 0. Likewise, if there are to be
* multiple different readers then the application writer must place each call
* to a reading API function (such as read()) inside a critical section and set
* the receive block time to 0.
*/
class StreamBufferBase {
public:
friend class StreamBuffer;
template <size_t>
friend class StaticStreamBuffer;
StreamBufferBase(const StreamBufferBase&) = delete;
StreamBufferBase& operator=(const StreamBufferBase&) = delete;
static void* operator new(size_t, void* ptr) { return ptr; }
static void* operator new[](size_t, void* ptr) { return ptr; }
static void* operator new(size_t) = delete;
static void* operator new[](size_t) = delete;
/**
* StreamBuffer.hpp
*
* @brief Function that checks if the underlying stream buffer handle is not
* NULL. This should be used to ensure a stream buffer has been created
* correctly.
*
* @retval true If the handle is not NULL.
* @retval false If the handle is NULL.
*/
inline bool isValid() const { return (handle != NULL); }
/**
* StreamBuffer.hpp
*
* @brief Function that calls <tt>size_t xStreamBufferSend(
* StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t
* xDataLengthBytes, TickType_t xTicksToWait )</tt>
*
* @see <https://www.freertos.org/xStreamBufferSend.html>
*
* Sends bytes to a stream buffer. The bytes are copied into the stream
* buffer.
*
* Use send() to write to a stream buffer from a task. Use sendFromISR() to
* write to a stream buffer from an interrupt service routine (ISR).
*
* @param data A pointer to the buffer that holds the bytes to be copied into
* the stream buffer.
* @param length The maximum number of bytes to copy from data into the stream
* buffer.
* @param ticksToWait The maximum amount of time the task should remain in the
* Blocked state to wait for enough space to become available in the stream
* buffer, should the stream buffer contain too little space to hold the
* another length bytes. The block time is specified in tick periods, so the
* absolute time it represents is dependent on the tick frequency. The macro
* pdMS_TO_TICKS() can be used to convert a time specified in milliseconds
* into a time specified in ticks. Setting ticksToWait to portMAX_DELAY will
* cause the task to wait indefinitely (without timing out), provided
* INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h. If a task times out
* before it can write all length into the buffer it will still write as many
* bytes as possible. A task does not use any CPU time when it is in the
* blocked state.
* @return size_t The number of bytes written to the stream buffer. If a task
* times out before it can write all length into the buffer it will still
* write as many bytes as possible.
*
* <b>Example Usage</b>
* @include StreamBuffer/send.cpp
*/
inline size_t send(const void* data, const size_t length,
const TickType_t ticksToWait = portMAX_DELAY) const {
return xStreamBufferSend(handle, data, length, ticksToWait);
}
/**
* StreamBuffer.hpp
*
* @brief Function that calls <tt>size_t xStreamBufferSendFromISR(
* StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t
* xDataLengthBytes, BaseType_t *pxHigherPriorityTaskWoken )</tt>
*
* @see <https://www.freertos.org/xStreamBufferSendFromISR.html>
*
* Interrupt safe version of the API function that sends a stream of bytes to
* the stream buffer.
*
* Use send() to write to a stream buffer from a task. Use sendFromISR() to
* write to a stream buffer from an interrupt service routine (ISR).
*
* @param higherPriorityTaskWoken It is possible that a stream buffer will
* have a task blocked on it waiting for data. Calling sendFromISR() can make
* data available, and so cause a task that was waiting for data to leave the
* Blocked state. If calling sendFromISR() causes a task to leave the Blocked
* state, and the unblocked task has a priority higher than the currently
* executing task (the task that was interrupted), then, internally,
* sendFromISR() will set higherPriorityTaskWoken to true. If sendFromISR()
* sets this value to true, then normally a context switch should be performed
* before the interrupt is exited. This will ensure that the interrupt
* returns directly to the highest priority Ready state task.
* higherPriorityTaskWoken should be set to false before it is passed into the
* function. See the example code below for an example.
* @param data A pointer to the buffer that holds the bytes to be copied into
* the stream buffer.
* @param length The maximum number of bytes to copy from data into the stream
* buffer.
* @return size_t The number of bytes written to the stream buffer. If a task
* times out before it can write all length into the buffer it will still
* write as many bytes as possible.
*
* <b>Example Usage</b>
* @include StreamBuffer/sendFromISR.cpp
*/
inline size_t sendFromISR(bool& higherPriorityTaskWoken, const void* data,
const size_t length) const {
BaseType_t taskWoken = pdFALSE;
size_t result = xStreamBufferSendFromISR(handle, data, length, &taskWoken);
if (taskWoken == pdTRUE) {
higherPriorityTaskWoken = true;
}
return result;
}
/**
* StreamBuffer.hpp
*
* @brief Function that calls <tt>size_t xStreamBufferSendFromISR(
* StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t
* xDataLengthBytes, BaseType_t *pxHigherPriorityTaskWoken )</tt>
*
* @see <https://www.freertos.org/xStreamBufferSendFromISR.html>
*
* @overload
*/
inline size_t sendFromISR(const void* data, const size_t length) const {
return xStreamBufferSendFromISR(handle, data, length, NULL);
}
/**
* StreamBuffer.hpp
*
* @brief Function that calls <tt>size_t xStreamBufferReceive(
* StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t
* xBufferLengthBytes, TickType_t xTicksToWait )</tt>
*
* @see <https://www.freertos.org/xStreamBufferReceive.html>
*
* Receives bytes from a stream buffer.
*
* Use receive() to read from a stream buffer from a task. Use
* receiveFromISR() to read from a stream buffer from an interrupt service
* routine (ISR).
*
* @param buffer A pointer to the buffer into which the received bytes will be
* copied.
* @param bufferLength The length of the buffer pointed to by the data
* parameter. This sets the maximum number of bytes to receive in one call.
* receive() will return as many bytes as possible up to a maximum set by
* length.
* @param ticksToWait The maximum amount of time the task should remain in the
* Blocked state to wait for data to become available if the stream buffer is
* empty. receive() will return immediately if ticksToWait is zero. The block
* time is specified in tick periods, so the absolute time it represents is
* dependent on the tick frequency. The macro pdMS_TO_TICKS() can be used to
* convert a time specified in milliseconds into a time specified in ticks.
* Setting ticksToWait to portMAX_DELAY will cause the task to wait
* indefinitely (without timing out), provided INCLUDE_vTaskSuspend is set to
* 1 in FreeRTOSConfig.h. A task does not use any CPU time when it is in the
* Blocked state.
* @return size_t The number of bytes read from the stream buffer. This will
* be the number of bytes available up to a maximum of length.
*
* <b>Example Usage</b>
* @include StreamBuffer/receive.cpp
*/
inline size_t receive(void* buffer, const size_t bufferLength,
const TickType_t ticksToWait = portMAX_DELAY) const {
return xStreamBufferReceive(handle, buffer, bufferLength, ticksToWait);
}
/**
* StreamBuffer.hpp
*
* @brief Function that calls <tt>size_t xStreamBufferReceiveFromISR(
* StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t
* xBufferLengthBytes, BaseType_t *pxHigherPriorityTaskWoken )</tt>
*
* @see <https://www.freertos.org/xStreamBufferReceiveFromISR.html>
*
* An interrupt safe version of the API function that receives bytes from a
* stream buffer.
*
* Use receive() to read from a stream buffer from a task. Use
* receiveFromISR() to read from a stream buffer from an interrupt service
* routine (ISR).
*
* @param higherPriorityTaskWoken It is possible that a stream buffer will
* have a task blocked on it waiting for space to become available. Calling
* receiveFromISR() can make space available, and so cause a task that is
* waiting for space to leave the Blocked state. If calling receiveFromISR()
* causes a task to leave the Blocked state, and the unblocked task has a
* priority higher than the currently executing task (the task that was
* interrupted), then, internally, receiveFromISR() will set
* higherPriorityTaskWoken to true. If receiveFromISR() sets this value to
* true, then normally a context switch should be performed before the
* interrupt is exited. That will ensure the interrupt returns directly to the
* highest priority Ready state task. higherPriorityTaskWoken should be set to
* false before it is passed into the function. See the code example below for
* an example.
* @param buffer A pointer to the buffer into which the received bytes will be
* copied.
* @param bufferLength The length of the buffer pointed to by the buffer
* parameter. This sets the maximum number of bytes to receive in one call.
* receive() will return as many bytes as possible up to a maximum set by
* length.
* @return size_t The number of bytes read from the stream buffer, if any.
*
* <b>Example Usage</b>
* @include StreamBuffer/receiveFromISR.cpp
*/
inline size_t receiveFromISR(bool& higherPriorityTaskWoken, void* buffer,
const size_t bufferLength) const {
BaseType_t taskWoken = pdFALSE;
size_t result =
xStreamBufferReceiveFromISR(handle, buffer, bufferLength, &taskWoken);
if (taskWoken == pdTRUE) {
higherPriorityTaskWoken = true;
}
return result;
}
/**
* StreamBuffer.hpp
*
* @brief Function that calls <tt>size_t xStreamBufferReceiveFromISR(
* StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t
* xBufferLengthBytes, BaseType_t *pxHigherPriorityTaskWoken )</tt>
*
* @see <https://www.freertos.org/xStreamBufferReceiveFromISR.html>
*
* @overload
*/
inline size_t receiveFromISR(void* buffer, const size_t bufferLength) const {
return xStreamBufferReceiveFromISR(handle, buffer, bufferLength, NULL);
}
/**
* StreamBuffer.hpp
*
* @brief Function that calls <tt>size_t xStreamBufferBytesAvailable(
* StreamBufferHandle_t xStreamBuffer )</tt>
*
* @see <https://www.freertos.org/xStreamBufferBytesAvailable.html>
*
* Queries the stream buffer to see how much data it contains, which is equal
* to the number of bytes that can be read from the stream buffer before the
* stream buffer would be empty.
*
* @return size_t The number of bytes that can be read from the stream buffer
* before the stream buffer would be empty.
*/
inline size_t bytesAvailable() const {
return xStreamBufferBytesAvailable(handle);
}
/**
* StreamBuffer.hpp
*
* @brief Function that calls <tt>size_t xStreamBufferSpacesAvailable(
* StreamBufferHandle_t xStreamBuffer )</tt>
*
* @see <https://www.freertos.org/xStreamBufferSpacesAvailable.html>
*
* Queries a stream buffer to see how much free space it contains, which is
* equal to the amount of data that can be sent to the stream buffer before it
* is full.
*
* @return size_t The number of bytes that can be written to the stream buffer
* before the stream buffer would be full.
*/
inline size_t spacesAvailable() const {
return xStreamBufferSpacesAvailable(handle);
}
/**
* StreamBuffer.hpp
*
* @brief Function that calls <tt>BaseType_t xStreamBufferSetTriggerLevel(
* StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel )</tt>
*
* @see <https://www.freertos.org/xStreamBufferSetTriggerLevel.html>
*
* A stream buffer's trigger level is the number of bytes that must be in the
* stream buffer before a task that is blocked on the stream buffer to wait
* for data is moved out of the blocked state. For example, if a task is
* blocked on a read of an empty stream buffer that has a trigger level of 1
* then the task will be unblocked when a single byte is written to the buffer
* or the task's block time expires. As another example, if a task is blocked
* on a read of an empty stream buffer that has a trigger level of 10 then the
* task will not be unblocked until the stream buffer contains at least 10
* bytes or the task's block time expires. If a reading task's block time
* expires before the trigger level is reached then the task will still
* receive however many bytes are actually available. Setting a trigger level
* of 0 will result in a trigger level of 1 being used. It is not valid to
* specify a trigger level that is greater than the buffer size.
*
* @param triggerLevel The new trigger level for the stream buffer.
* @retval true If triggerLevel was less than or equal to the stream buffer's
* length then the trigger level was updated.
* @retval false Otherwise.
*/
inline bool setTriggerLevel(const size_t triggerLevel = 0) const {
return (xStreamBufferSetTriggerLevel(handle, triggerLevel) == pdTRUE);
}
/**
* StreamBuffer.hpp
*
* @brief Function that calls <tt>BaseType_t xStreamBufferReset(
* StreamBufferHandle_t xStreamBuffer )</tt>
*
* @see <https://www.freertos.org/xStreamBufferReset.html>
*
* Resets a stream buffer to its initial, empty, state. Any data that was in
* the stream buffer is discarded. A stream buffer can only be reset if there
* are no tasks blocked waiting to either send to or receive from the stream
* buffer.
*
* @return true If the stream buffer is reset.
* @return false If there was a task blocked waiting to send to or read from
* the stream buffer then the stream buffer was not reset.
*/
inline bool reset() const { return (xStreamBufferReset(handle) == pdPASS); }
/**
* StreamBuffer.hpp
*
* @brief Function that calls <tt>BaseType_t xStreamBufferIsEmpty(
* StreamBufferHandle_t xStreamBuffer )</tt>
*
* @see <https://www.freertos.org/xStreamBufferIsEmpty.html>
*
* Queries a stream buffer to see if it is empty. A stream buffer is empty if
* it does not contain any data.
*
* @return true If the stream buffer is empty.
* @return false Otherwise.
*/
inline bool isEmpty() const {
return (xStreamBufferIsEmpty(handle) == pdTRUE);
}
/**
* StreamBuffer.hpp
*
* @brief Function that calls <tt>BaseType_t xStreamBufferIsFull(
* StreamBufferHandle_t xStreamBuffer )</tt>
*
* @see <https://www.freertos.org/xStreamBufferIsFull.html>
*
* Queries a stream buffer to see if it is full. A stream buffer is full if it
* does not have any free space, and therefore cannot accept any more data.
*
* @return true If the stream buffer is full.
* @return false Otherwise.
*/
inline bool isFull() const { return (xStreamBufferIsFull(handle) == pdTRUE); }
private:
StreamBufferBase() = default;
/**
* StreamBuffer.hpp
*
* @brief Destroy the StreamBufferBase object by calling
* <tt>void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer )</tt>
*
* @see <https://www.freertos.org/vStreamBufferDelete.html>
*
* Deletes a stream buffer and free the allocated memory.
*/
~StreamBufferBase() { vStreamBufferDelete(this->handle); }
StreamBufferBase(StreamBufferBase&&) noexcept = default;
StreamBufferBase& operator=(StreamBufferBase&&) noexcept = default;
StreamBufferHandle_t handle = NULL;
};
#if (configSUPPORT_DYNAMIC_ALLOCATION == 1)
/**
* @class StreamBuffer StreamBuffer.hpp <FreeRTOS/StreamBuffer.hpp>
*
* @brief Class that encapsulates the functionality of a FreeRTOS stream buffer.
*
* A stream buffer using dynamically allocated memory from the FreeRTOS heap.
* See FreeRTOS::StaticStreamBuffer for a version that uses statically allocated
* memory (memory that is allocated at compile time).
*/
class StreamBuffer : public StreamBufferBase {
public:
/**
* StreamBuffer.hpp
*
* @brief Construct a new StreamBuffer object by calling
* <tt>StreamBufferHandle_t xStreamBufferCreate( size_t xBufferSizeBytes,
* size_t xTriggerLevelBytes )</tt>
*
* @see <https://www.freertos.org/xStreamBufferCreate.html>
*
* @warning The user should call isValid() on this object to verify that the
* stream buffer was created successfully in case the memory required to
* create the message buffer could not be allocated.
*
* @param size The total number of bytes the stream buffer will be able to
* hold at any one time.
* @param triggerLevel The number of bytes that must be in the stream
* buffer before a task that is blocked on the stream buffer to wait for data
* is moved out of the blocked state. For example, if a task is blocked on a
* read of an empty stream buffer that has a trigger level of 1 then the task
* will be unblocked when a single byte is written to the buffer or the task's
* block time expires. As another example, if a task is blocked on a read of
* an empty stream buffer that has a trigger level of 10 then the task will
* not be unblocked until the stream buffer contains at least 10 bytes or the
* task's block time expires. If a reading task's block time expires before
* the trigger level is reached then the task will still receive however many
* bytes are actually available. Setting a trigger level of 0 will result in a
* trigger level of 1 being used. It is not valid to specify a trigger level
* that is greater than the buffer size.
*
* <b>Example Usage</b>
* @include StreamBuffer/streamBuffer.cpp
*/
explicit StreamBuffer(const size_t size, const size_t triggerLevel = 0) {
this->handle = xStreamBufferCreate(size, triggerLevel);
}
~StreamBuffer() = default;
StreamBuffer(const StreamBuffer&) = delete;
StreamBuffer& operator=(const StreamBuffer&) = delete;
StreamBuffer(StreamBuffer&&) noexcept = default;
StreamBuffer& operator=(StreamBuffer&&) noexcept = default;
};
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
#if (configSUPPORT_STATIC_ALLOCATION == 1)
/**
* @class StaticStreamBuffer StreamBuffer.hpp <FreeRTOS/StreamBuffer.hpp>
*
* @brief Class that encapsulates the functionality of a FreeRTOS stream buffer.
*
* If a stream buffer is created using this class then the RAM is provided by
* the application writer as part of the object instance and allows the RAM to
* be statically allocated at compile time.
*
* @tparam N The size, in bytes, of the storage buffer for the stream buffer.
*/
template <size_t N>
class StaticStreamBuffer : public StreamBufferBase {
public:
/**
* StreamBuffer.hpp
*
* @brief Construct a new StaticStreamBuffer object by calling
* <tt>StreamBufferHandle_t xStreamBufferCreateStatic( size_t
* xBufferSizeBytes, size_t xTriggerLevelBytes, uint8_t
* *pucStreamBufferStorageArea, StaticStreamBuffer_t *pxStaticStreamBuffer
* )</tt>
*
* @see <https://www.freertos.org/xStreamBufferCreateStatic.html>
*
* @param triggerLevel The number of bytes that must be in the stream
* buffer before a task that is blocked on the stream buffer to wait for data
* is moved out of the blocked state. For example, if a task is blocked on a
* read of an empty stream buffer that has a trigger level of 1 then the task
* will be unblocked when a single byte is written to the buffer or the task's
* block time expires. As another example, if a task is blocked on a read of
* an empty stream buffer that has a trigger level of 10 then the task will
* not be unblocked until the stream buffer contains at least 10 bytes or the
* task's block time expires. If a reading task's block time expires before
* the trigger level is reached then the task will still receive however many
* bytes are actually available. Setting a trigger level of 0 will result in a
* trigger level of 1 being used. It is not valid to specify a trigger level
* that is greater than the buffer size.
*
* <b>Example Usage</b>
* @include StreamBuffer/staticStreamBuffer.cpp
*/
explicit StaticStreamBuffer(const size_t triggerLevel = 0) {
this->handle = xStreamBufferCreateStatic(sizeof(storage), triggerLevel,
storage, &staticStreamBuffer);
}
~StaticStreamBuffer() = default;
StaticStreamBuffer(const StaticStreamBuffer&) = delete;
StaticStreamBuffer& operator=(const StaticStreamBuffer&) = delete;
StaticStreamBuffer(StaticStreamBuffer&&) noexcept = default;
StaticStreamBuffer& operator=(StaticStreamBuffer&&) noexcept = default;
private:
StaticStreamBuffer_t staticStreamBuffer;
uint8_t storage[N];
};
#endif /* configSUPPORT_STATIC_ALLOCATION */
} // namespace FreeRTOS
#endif // FREERTOS_STREAMBUFFER_HPP