/* * 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_MESSAGEBUFFER_HPP #define FREERTOS_MESSAGEBUFFER_HPP #include "FreeRTOS.h" #include "message_buffer.h" namespace FreeRTOS { /** * @class MessageBufferBase MessageBuffer.hpp * * @brief Base class that provides the standard message buffer interface to * FreeRTOS::MessageBuffer and FreeRTOS::StaticMessageBuffer. * * @note This class is not intended to be instantiated by the user. Use * FreeRTOS::MessageBuffer or FreeRTOS::StaticMessageBuffer. * * @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 MessageBufferBase { public: friend class MessageBuffer; template friend class StaticMessageBuffer; MessageBufferBase(const MessageBufferBase&) = delete; MessageBufferBase& operator=(const MessageBufferBase&) = 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; /** * MessageBuffer.hpp * * @brief Function that checks if the underlying message buffer handle is not * NULL. This should be used to ensure a message 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); } /** * MessageBuffer.hpp * * @brief Function that calls size_t xMessageBufferSend( * MessageBufferHandle_t xMessageBuffer, const void *pvTxData, size_t * xDataLengthBytes, TickType_t xTicksToWait ) * * @see * * Sends a discrete message to the message buffer. The message can be any * length that fits within the buffer's free space, and is copied into the * buffer. * * Use send() to write to a message buffer from a task. Use sendFromISR() to * write to a message buffer from an interrupt service routine (ISR). * * @param data A pointer to the message that is to be copied into the message * buffer. * @param length The length of the message. That is, the number of bytes to * copy from data into the message buffer. When a message is written to the * message buffer an additional sizeof( size_t ) bytes are also written to * store the message's length. sizeof( size_t ) is typically 4 bytes on a * 32-bit architecture, so on most 32-bit architecture setting length to 20 * will reduce the free space in the message buffer by 24 bytes (20 bytes of * message data and 4 bytes to hold the message length). * @param ticksToWait The maximum amount of time the calling task should * remain in the Blocked state to wait for enough space to become available in * the message buffer, should the message buffer have insufficient space when * send() is called. The calling task will never block 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. Tasks do not use any * CPU time when they are in the Blocked state. * @return size_t The number of bytes written to the message buffer. If the * call to send() times out before there was enough space to write the message * into the message buffer then zero is returned. If the call did not time * out then length is returned. * * Example Usage * @include MessageBuffer/send.cpp */ inline size_t send(const void* data, const size_t length, const TickType_t ticksToWait = portMAX_DELAY) const { return xMessageBufferSend(handle, data, length, ticksToWait); } /** * MessageBuffer.hpp * * @brief Function that calls size_t xMessageBufferSendFromISR( * MessageBufferHandle_t xMessageBuffer, const void *pvTxData, size_t * xDataLengthBytes, BaseType_t *pxHigherPriorityTaskWoken ) * * @see * * Interrupt safe version of the API function that sends a discrete message to * the message buffer. The message can be any length that fits within the * buffer's free space, and is copied into the buffer. * * Use send() to write to a message buffer from a task. Use sendFromISR() to * write to a message buffer from an interrupt service routine (ISR). * * @param higherPriorityTaskWoken It is possible that a message 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 code * example below for an example. * @param data A pointer to the message that is to be copied into the message * buffer. * @param length The length of the message. That is, the number of bytes to * copy from data into the message buffer. When a message is written to the * message buffer an additional sizeof( size_t ) bytes are also written to * store the message's length. sizeof( size_t ) is typically 4 bytes on a * 32-bit architecture, so on most 32-bit architecture setting length to 20 * will reduce the free space in the message buffer by 24 bytes (20 bytes of * message data and 4 bytes to hold the message length). * @return size_t The number of bytes actually written to the message buffer. * If the message buffer didn't have enough free space for the message to be * stored then 0 is returned, otherwise length is returned. * * Example Usage * @include MessageBuffer/sendFromISR.cpp */ inline size_t sendFromISR(bool& higherPriorityTaskWoken, const void* data, const size_t length) const { BaseType_t taskWoken = pdFALSE; size_t result = xMessageBufferSendFromISR(handle, data, length, &taskWoken); if (taskWoken == pdTRUE) { higherPriorityTaskWoken = true; } return result; } /** * MessageBuffer.hpp * * @brief Function that calls size_t xMessageBufferSendFromISR( * MessageBufferHandle_t xMessageBuffer, const void *pvTxData, size_t * xDataLengthBytes, BaseType_t *pxHigherPriorityTaskWoken ) * * @see * * @overload */ inline size_t sendFromISR(const void* data, const size_t length) const { return xMessageBufferSendFromISR(handle, data, length, NULL); } /** * MessageBuffer.hpp * * @brief Function that calls size_t xMessageBufferReceive( * MessageBufferHandle_t xMessageBuffer, void *pvRxData, size_t * xBufferLengthBytes, TickType_t xTicksToWait ) * * @see * * Use receive() to read from a message buffer from a task. * UsereceiveFromISR() to read from a message buffer from an interrupt service * routine (ISR). * * @param buffer A pointer to the buffer into which the received message is to * be copied. * @param bufferLength The length of the buffer pointed to by the buffer * parameter. This sets the maximum length of the message that can be * received. If bufferLength is too small to hold the next message then the * message will be left in the message buffer and 0 will be returned. * @param ticksToWait The maximum amount of time the task should remain in the * Blocked state to wait for a message, should the message buffer be empty. * receive() will return immediately if ticksToWait is zero and the message * buffer is empty. 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. Tasks do not use any * CPU time when they are in the Blocked state. * @return size_t The length, in bytes, of the message read from the message * buffer, if any. If receive() times out before a message became available * then zero is returned. If the length of the message is greater than * bufferLength then the message will be left in the message buffer and zero * is returned. * * Example Usage * @include MessageBuffer/receive.cpp */ inline size_t receive(void* buffer, const size_t bufferLength, const TickType_t ticksToWait = portMAX_DELAY) const { return xMessageBufferReceive(handle, buffer, bufferLength, ticksToWait); } /** * MessageBuffer.hpp * * @brief Function that calls size_t xMessageBufferReceiveFromISR( * MessageBufferHandle_t xMessageBuffer, void *pvRxData, size_t * xBufferLengthBytes, BaseType_t *pxHigherPriorityTaskWoken ) * * @see * * Use receive() to read from a message buffer from a task. * UsereceiveFromISR() to read from a message buffer from an interrupt service * routine (ISR). * * @param higherPriorityTaskWoken It is possible that a message 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 message is to * be copied. * @param bufferLength The length of the buffer pointed to by the buffer * parameter. This sets the maximum length of the message that can be * received. If bufferLength is too small to hold the next message then the * message will be left in the message buffer and 0 will be returned. * @return size_t The length, in bytes, of the message read from the message * buffer, if any. * * Example Usage * @include MessageBuffer/receiveFromISR.cpp */ inline size_t receiveFromISR(bool& higherPriorityTaskWoken, void* buffer, const size_t bufferLength) const { BaseType_t taskWoken = pdFALSE; size_t result = xMessageBufferReceiveFromISR(handle, buffer, bufferLength, &taskWoken); if (taskWoken == pdTRUE) { higherPriorityTaskWoken = true; } return result; } /** * MessageBuffer.hpp * * @brief Function that calls size_t xMessageBufferReceiveFromISR( * MessageBufferHandle_t xMessageBuffer, void *pvRxData, size_t * xBufferLengthBytes, BaseType_t *pxHigherPriorityTaskWoken ) * * @see * * @overload */ inline size_t receiveFromISR(void* buffer, const size_t bufferLength) const { return xMessageBufferReceiveFromISR(handle, buffer, bufferLength, NULL); } /** * MessageBuffer.hpp * * @brief Function that calls size_t xMessageBufferSpacesAvailable( * MessageBufferHandle_t xMessageBuffer ) * * @see * * Queries a message buffer to see how much free space it contains, which is * equal to the amount of data that can be sent to the message buffer before * it is full. The returned value is 4 bytes larger than the maximum message * size that can be sent to the message buffer. * * @return size_t The number of bytes that can be written to the message * buffer before the message buffer would be full. When a message is written * to the message buffer an additional sizeof( size_t ) bytes are also written * to store the message's length. sizeof( size_t ) is typically 4 bytes on a * 32-bit architecture, so if spacesAvailable() returns 10, then the size of * the largest message that can be written to the message buffer is 6 bytes. */ inline size_t spacesAvailable() const { return xMessageBufferSpacesAvailable(handle); } /** * MessageBuffer.hpp * * @brief Function that calls BaseType_t xMessageBufferReset( * MessageBufferHandle_t xMessageBuffer ) * * @see * * Resets a message buffer to its initial, empty, state. Any data that was in * the message buffer is discarded. A message buffer can only be reset if * there are no tasks blocked waiting to either send to or receive from the * message buffer. * * @retval true If the message buffer is reset. * @retval false If there was a task blocked waiting to send to or read from * the message buffer then the message buffer will not be reset. */ inline bool reset() const { return (xMessageBufferReset(handle) == pdPASS); } /** * MessageBuffer.hpp * * @brief Function that calls BaseType_t xMessageBufferIsEmpty( * MessageBufferHandle_t xMessageBuffer ) * * @see * * Queries a message buffer to see if it is empty. A message buffer is empty * if it does not contain any messages. * * @retval true If the message buffer is empty. * @retval false Otherwise. */ inline bool isEmpty() const { return (xMessageBufferIsEmpty(handle) == pdTRUE); } /** * MessageBuffer.hpp * * @brief Function that calls BaseType_t xMessageBufferIsFull( * MessageBufferHandle_t xMessageBuffer ) * * @see * * Queries a message buffer to see if it is full. A message buffer is full if * it cannot accept any more messages, of any size, until space is made * available by a message being removed from the message buffer. * * @retval true If the message buffer is full. * @retval false Otherwise. */ inline bool isFull() const { return (xMessageBufferIsFull(handle) == pdTRUE); } private: MessageBufferBase() = default; /** * MessageBuffer.hpp * * @brief Destroy the MessageBufferBase object by calling void * vMessageBufferDelete( MessageBufferHandle_t xMessageBuffer ) * * @see * * Delete a queue - freeing all the memory allocated for storing of items * placed on the queue. */ ~MessageBufferBase() { vMessageBufferDelete(this->handle); } MessageBufferBase(MessageBufferBase&&) noexcept = default; MessageBufferBase& operator=(MessageBufferBase&&) noexcept = default; MessageBufferHandle_t handle = NULL; }; #if (configSUPPORT_DYNAMIC_ALLOCATION == 1) /** * @class MessageBuffer MessageBuffer.hpp * * @brief Class that encapsulates the functionality of a FreeRTOS message * buffer. * * A message buffer using dynamically allocated memory from the FreeRTOS heap. * See FreeRTOS::StaticMessageBuffer for a version that uses statically * allocated memory (memory that is allocated at compile time). */ class MessageBuffer : public MessageBufferBase { public: /** * MessageBuffer.hpp * * @brief Construct a new MessageBuffer object by calling * MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes * ) * * @see * * @warning The user should call isValid() on this object to verify that the * message 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 (not messages) the message buffer * will be able to hold at any one time. When a message is written to the * message buffer an additional sizeof( size_t ) bytes are also written to * store the message's length. sizeof( size_t ) is typically 4 bytes on a * 32-bit architecture, so on most 32-bit architectures a 10 byte message will * take up 14 bytes of message buffer space. * * Example Usage * @include MessageBuffer/messageBuffer.cpp */ explicit MessageBuffer(size_t size) { this->handle = xMessageBufferCreate(size); } ~MessageBuffer() = default; MessageBuffer(const MessageBuffer&) = delete; MessageBuffer& operator=(const MessageBuffer&) = delete; MessageBuffer(MessageBuffer&&) noexcept = default; MessageBuffer& operator=(MessageBuffer&&) noexcept = default; }; #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ #if (configSUPPORT_STATIC_ALLOCATION == 1) /** * @class StaticMessageBuffer MessageBuffer.hpp * * @brief Class that encapsulates the functionality of a FreeRTOS message * buffer. * * If a message 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 for the message buffer. */ template class StaticMessageBuffer : public MessageBufferBase { public: /** * MessageBuffer.hpp * * @brief Construct a new StaticMessageBuffer object by calling * MessageBufferHandle_t xMessageBufferCreateStatic( size_t * xBufferSizeBytes, uint8_t *pucMessageBufferStorageArea, * StaticMessageBuffer_t *pxStaticMessageBuffer ) * * @see * * @warning This class contains the storage buffer for the message buffer, so * the user should create this object as a global object or with the static * storage specifier so that the object instance is not on the stack. * * Example Usage * @include MessageBuffer/staticMessageBuffer.cpp */ StaticMessageBuffer() : MessageBufferBase() { this->handle = xMessageBufferCreateStatic(sizeof(storage), storage, &staticMessageBuffer); } ~StaticMessageBuffer() = default; StaticMessageBuffer(const StaticMessageBuffer&) = delete; StaticMessageBuffer& operator=(const StaticMessageBuffer&) = delete; StaticMessageBuffer(StaticMessageBuffer&&) noexcept = default; StaticMessageBuffer& operator=(StaticMessageBuffer&&) noexcept = default; private: StaticMessageBuffer_t staticMessageBuffer; uint8_t storage[N] = {0}; }; #endif /* configSUPPORT_STATIC_ALLOCATION */ } // namespace FreeRTOS #endif // FREERTOS_MESSAGEBUFFER_HPP