/* * 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_QUEUE_HPP #define FREERTOS_QUEUE_HPP #include #include "FreeRTOS.h" #include "queue.h" namespace FreeRTOS { /** * @class QueueBase Queue.hpp * * @brief Base class that provides the standard queue interface to * FreeRTOS::Queue and FreeRTOS::StaticQueue. * * @note This class is not intended to be instantiated by the user. Use * FreeRTOS::Queue or FreeRTOS::StaticQueue. * * @tparam T Type to be stored in the queue. */ template class QueueBase { public: template friend class Queue; template friend class StaticQueue; QueueBase(const QueueBase&) = delete; QueueBase& operator=(const QueueBase&) = 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; /** * Queue.hpp * * @brief Function that checks if the underlying queue handle is not NULL. * This should be used to ensure a queue has been created correctly. * * @retval true the handle is not NULL. * @retval false the handle is NULL. */ inline bool isValid() const { return (handle != NULL); } /** * Queue.hpp * * @brief Function that calls xQueueSendToBack( xQueue, pvItemToQueue, * xTicksToWait ) * * @see * * Post an item to the back of a queue. The item is queued by copy, not by * reference. This function must not be called from an interrupt service * routine. See FreeRTOS::Queue::sendToBackFromISR() for an alternative which * may be used in an ISR. * * @param item A reference to the item that is to be placed on the queue. * @param ticksToWait The maximum amount of time the task should block waiting * for space to become available on the queue, should it already be full. The * call will return immediately if this is set to 0 and the queue is full. The * time is defined in tick periods so the constant portTICK_PERIOD_MS should * be used to convert to real time if this is required. * @retval true if the item was successfully posted. * @retval false otherwise. * * Example Usage * @include Queue/sendToBack.cpp */ inline bool sendToBack(const T& item, const TickType_t ticksToWait = portMAX_DELAY) const { return (xQueueSendToBack(handle, &item, ticksToWait) == pdTRUE); } /** * Queue.hpp * * @brief Function that calls xQueueSendToBackFromISR( xQueue, * pvItemToQueue, pxHigherPriorityTaskWoken ) * * @see * * @param higherPriorityTaskWoken A reference that will be set to true if * sending to the queue caused a task to unblock, and the unblocked task has a * priority higher than the currently running task. * @param item A reference to the item that is to be placed on the queue. * @retval true if the item was successfully posted * @retval false otherwise. * * Example Usage * @include Queue/sendToBackFromISR.cpp */ inline bool sendToBackFromISR(bool& higherPriorityTaskWoken, const T& item) const { BaseType_t taskWoken = pdFALSE; bool result = (xQueueSendToBackFromISR(handle, &item, &taskWoken) == pdPASS); if (taskWoken == pdTRUE) { higherPriorityTaskWoken = true; } return result; } /** * Queue.hpp * * @brief Function that calls xQueueSendToBackFromISR( xQueue, * pvItemToQueue, pxHigherPriorityTaskWoken ) * * @see * * @overload */ inline bool sendToBackFromISR(const T& item) const { return (xQueueSendToBackFromISR(handle, &item, NULL) == pdPASS); } /** * Queue.hpp * * @brief Function that calls xQueueSendToFront( xQueue, pvItemToQueue, * xTicksToWait ) * * @see * * @param item A reference to the item that is to be placed on the queue. * @param ticksToWait The maximum amount of time the task should block waiting * for space to become available on the queue, should it already be full. The * call will return immediately if this is set to 0 and the queue is full. The * time is defined in tick periods so the constant portTICK_PERIOD_MS should * be used to convert to real time if this is required. * @retval true if the item was successfully posted. * @retval false otherwise. * * Example Usage * @include Queue/sendToFront.cpp */ inline bool sendToFront(const T& item, const TickType_t ticksToWait = portMAX_DELAY) const { return (xQueueSendToFront(handle, &item, ticksToWait) == pdTRUE); } /** * Queue.hpp * * @brief Function that calls xQueueSendToFrontFromISR( xQueue, * pvItemToQueue, pxHigherPriorityTaskWoken ) * * @see * * @param higherPriorityTaskWoken A reference that will be set to true if * sending to the queue caused a task to unblock, and the unblocked task has a * priority higher than the currently running task. * @param item A reference to the item that is to be placed on the queue. * @retval true if the item was successfully posted * @retval false otherwise. * * Example Usage * @include Queue/sendToFrontFromISR.cpp */ inline bool sendToFrontFromISR(bool& higherPriorityTaskWoken, const T& item) const { BaseType_t taskWoken = pdFALSE; bool result = (xQueueSendToFrontFromISR(handle, &item, &taskWoken) == pdPASS); if (taskWoken == pdTRUE) { higherPriorityTaskWoken = true; } return result; } /** * Queue.hpp * * @brief Function that calls xQueueSendToFrontFromISR( xQueue, * pvItemToQueue, pxHigherPriorityTaskWoken ) * * @see * * @overload */ inline bool sendToFrontFromISR(const T& item) const { return (xQueueSendToFrontFromISR(handle, &item, NULL) == pdPASS); } /** * Queue.hpp * * @brief Function that calls BaseType_t xQueueReceive( QueueHandle_t * xQueue, void *pvBuffer, TickType_t xTicksToWait ) * * @see * * Receive an item from a queue. This function must not be used in an * interrupt service routine. See receiveFromISR() for an alternative that * can. * * @param ticksToWait The maximum amount of time the task should block waiting * for an item to receive should the queue be empty at the time of the call. * Setting ticksToWait to 0 will cause the function to return immediately if * the queue is empty. The time is defined in tick periods so the constant * portTICK_PERIOD_MS should be used to convert to real time if this is * required. * @return std::optional Object from the queue. User should check that the * value is present. * * Example Usage * @include Queue/receive.cpp */ inline std::optional receive( const TickType_t ticksToWait = portMAX_DELAY) const { T buffer; return (xQueueReceive(handle, &buffer, ticksToWait) == pdTRUE) ? std::optional(buffer) : std::nullopt; } /** * Queue.hpp * * @brief Function that calls BaseType_t xQueueReceiveFromISR( * QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxHigherPriorityTaskWoken * ) * * @see * * Receive an item from a queue. It is safe to use this function from within * an interrupt service routine. * * @param higherPriorityTaskWoken A reference that will be set to true if * sending to the queue caused a task to unblock, and the unblocked task has a * priority higher than the currently running task. * @return std::optional Object from the queue. User should check that the * value is present. * * Example Usage * @include Queue/receiveFromISR.cpp */ inline std::optional receiveFromISR(bool& higherPriorityTaskWoken) const { T buffer; BaseType_t taskWoken = pdFALSE; bool result = (xQueueReceiveFromISR(handle, &buffer, &taskWoken) == pdTRUE); if (taskWoken == pdTRUE) { higherPriorityTaskWoken = true; } return result ? std::optional(buffer) : std::nullopt; } /** * Queue.hpp * * @brief Function that calls BaseType_t xQueueReceiveFromISR( * QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxHigherPriorityTaskWoken * ) * * @see * * @overload */ inline std::optional receiveFromISR() const { T buffer; return (xQueueReceiveFromISR(handle, &buffer, NULL) == pdTRUE) ? std::optional(buffer) : std::nullopt; } /** * Queue.hpp * * @brief Function that calls UBaseType_t uxQueueMessagesWaiting( * QueueHandle_t xQueue ) * * @see * * Return the number of messages stored in a queue. * * @retval UBaseType_t The number of messages available in the queue. */ inline UBaseType_t messagesWaiting() const { return uxQueueMessagesWaiting(handle); } /** * Queue.hpp * * @brief Function that calls UBaseType_t uxQueueMessagesWaitingFromISR( * QueueHandle_t xQueue ) * * @see * * A version of messagesWaiting() that can be called from an ISR. Return the * number of messages stored in a queue. * * @retval UBaseType_t The number of messages available in the queue. */ inline UBaseType_t messagesWaitingFromISR() const { return uxQueueMessagesWaitingFromISR(handle); } /** * Queue.hpp * * @brief Function that calls UBaseType_t uxQueueSpacesAvailable( * QueueHandle_t xQueue ) * * @see * * Return the number of free spaces in a queue. * * @retval UBaseType_t The number of free spaces available in the queue. */ inline UBaseType_t spacesAvailable() const { return uxQueueSpacesAvailable(handle); } /** * Queue.hpp * * @brief Function that calls BaseType_t xQueueReset( QueueHandle_t xQueue * ) * * @see * * Resets a queue to its original empty state. */ inline void reset() const { xQueueReset(handle); } /** * Queue.hpp * * @brief Function that calls BaseType_t xQueueOverwrite( QueueHandle_t * xQueue, const void * pvItemToQueue ) * * @see * * Only for use with queues that have a length of one - so the queue is either * empty or full. * * Post an item on a queue. If the queue is already full then overwrite the * value held in the queue. The item is queued by copy, not by reference. * * This function must not be called from an interrupt service routine. See * overwriteFromISR() for an alternative which may be used in an ISR. * * @param item A reference to the item that is to be placed on the queue. * * Example Usage * @include Queue/overwrite.cpp */ inline void overwrite(const T& item) const { xQueueOverwrite(handle, &item); } /** * Queue.hpp * * @brief Function that calls BaseType_t xQueueOverwriteFromISR( * QueueHandle_t xQueue, const void * pvItemToQueue, BaseType_t * *pxHigherPriorityTaskWoken ) * * @see * * A version of overwrite() that can be used in an interrupt service routine * (ISR). * * Only for use with queues that can hold a single item - so the queue is * either empty or full. * * Post an item on a queue. If the queue is already full then overwrite the * value held in the queue. The item is queued by copy, not by reference. * * @param higherPriorityTaskWoken A reference that will be set to true if * sending to the queue caused a task to unblock, and the unblocked task has a * priority higher than the currently running task. * @param item A reference to the item that is to be placed on the queue. * * Example Usage * @include Queue/overwriteFromISR.cpp */ inline void overwriteFromISR(bool& higherPriorityTaskWoken, const T& item) const { BaseType_t taskWoken = pdFALSE; xQueueOverwriteFromISR(handle, &item, &taskWoken); if (taskWoken == pdTRUE) { higherPriorityTaskWoken = true; } } /** * Queue.hpp * * @brief Function that calls BaseType_t xQueueOverwriteFromISR( * QueueHandle_t xQueue, const void * pvItemToQueue, BaseType_t * *pxHigherPriorityTaskWoken ) * * @see * * @overload */ inline void overwriteFromISR(const T& item) const { xQueueOverwriteFromISR(handle, &item, NULL); } /** * Queue.hpp * * @brief Function that calls BaseType_t xQueuePeek( QueueHandle_t xQueue, * void * const pvBuffer, TickType_t xTicksToWait ) * * @see * * Receive an item from a queue without removing the item from the queue. * * Successfully received items remain on the queue so will be returned again * by the next call, or a call to receive(). * * This function must not be used in an interrupt service routine. See * peekFromISR() for an alternative that can be called from an interrupt * service routine. * * @param ticksToWait The maximum amount of time the task should block waiting * for an item to receive should the queue be empty at the time of the call. * Setting ticksToWait to 0 will cause the function to return immediately if * the queue is empty. The time is defined in tick periods so the constant * portTICK_PERIOD_MS should be used to convert to real time if this is * required. * @return std::optional Object from the queue. User should check that the * value is present. * * Example Usage * @include Queue/peek.cpp */ inline std::optional peek( const TickType_t ticksToWait = portMAX_DELAY) const { T buffer; return (xQueuePeek(handle, &buffer, ticksToWait) == pdTRUE) ? std::optional(buffer) : std::nullopt; } /** * Queue.hpp * * @brief Function that calls BaseType_t xQueuePeekFromISR( QueueHandle_t * xQueue, void *pvBuffer ) * * @see * * A version of peek() that can be called from an interrupt service routine * (ISR). * * Receive an item from a queue without removing the item from the queue. * * Successfully received items remain on the queue so will be returned again * by the next call, or a call to receive(). * * @return std::optional Object from the queue. User should check that the * value is present. */ inline std::optional peekFromISR() const { T buffer; return (xQueuePeekFromISR(handle, &buffer) == pdTRUE) ? std::optional(buffer) : std::nullopt; } /** * Queue.hpp * * @brief Function that calls void vQueueAddToRegistry( QueueHandle_t * xQueue, char *pcQueueName ) * * @see * * The registry is provided as a means for kernel aware debuggers to locate * queues, semaphores and mutexes. Call addToRegistry() add a queue, * semaphore or mutex handle to the registry if you want the handle to be * available to a kernel aware debugger. If you are not using a kernel aware * debugger then this function can be ignored. * * configQUEUE_REGISTRY_SIZE defines the maximum number of handles the * registry can hold. configQUEUE_REGISTRY_SIZE must be greater than 0 within * FreeRTOSConfig.h for the registry to be available. Its value does not * effect the number of queues, semaphores and mutexes that can be created - * just the number that the registry can hold. * * If addToRegistry() is called more than once for the same queue, the * registry will store the name parameter from the most recent call to * addToRegistry(). * * @param name The name to be associated with the handle. This is the name * that the kernel aware debugger will display. The queue registry only * stores a pointer to the string - so the string must be persistent (global * or preferably in ROM/Flash), not on the stack. */ inline void addToRegistry(const char* name) const { vQueueAddToRegistry(handle, name); } /** * Queue.hpp * * @brief Function that calls void vQueueUnregisterQueue( QueueHandle_t * xQueue ) * * @see * * The registry is provided as a means for kernel aware debuggers to locate * queues, semaphores and mutexes. Call addToRegistry() add a queue, * semaphore or mutex handle to the registry if you want the handle to be * available to a kernel aware debugger, and unregister() to remove the queue, * semaphore or mutex from the register. If you are not using a kernel aware * debugger then this function can be ignored. */ inline void unregister() const { vQueueUnregisterQueue(handle); } /** * Queue.hpp * * @brief Function that calls const char *pcQueueGetName( QueueHandle_t * xQueue ) * * @see * * The queue registry is provided as a means for kernel aware debuggers to * locate queues, semaphores and mutexes. Call getName() to look up and return * the name of a queue in the queue registry from the queue's handle. * * @return If the queue referenced by the queue is in the queue registry, then * the text name of the queue is returned, otherwise NULL is returned. */ inline const char* getName() const { return pcQueueGetName(handle); } /** * Queue.hpp * * @brief Function that calls BaseType_t xQueueIsQueueFullFromISR( const * QueueHandle_t xQueue ) * * @see * * Queries a queue to determine if the queue is empty. This function should * only be used in an ISR. * * @return true if the queue is full. * @return false if the queue is not full. */ inline bool isFullFromISR() const { return (xQueueIsQueueFullFromISR(handle) == pdTRUE); } /** * Queue.hpp * * @brief Function that calls BaseType_t xQueueIsQueueEmptyFromISR( const * QueueHandle_t xQueue ) * * @see * * Queries a queue to determine if the queue is empty. This function should * only be used in an ISR. * * @retval true if the queue is empty. * @retval false if the queue is not empty. */ inline bool isEmptyFromISR() const { return (xQueueIsQueueEmptyFromISR(handle) == pdTRUE); } private: /** * Queue.hpp * * @brief Construct a new QueueBase object. * * @note Default constructor is deliberately private as this class is not * intended to be instantiated or derived from by the user. Use * FreeRTOS::Queue or FreeRTOS::StaticQueue. */ QueueBase() = default; /** * Queue.hpp * * @brief Destroy the QueueBase object by calling void vQueueDelete( * QueueHandle_t xQueue ) * * @see * * Delete a queue - freeing all the memory allocated for storing of items * placed on the queue. */ ~QueueBase() { vQueueDelete(this->handle); } QueueBase(QueueBase&&) noexcept = default; QueueBase& operator=(QueueBase&&) noexcept = default; /** * @brief Handle used to refer to the queue when using the FreeRTOS interface. */ QueueHandle_t handle = NULL; }; #if (configSUPPORT_DYNAMIC_ALLOCATION == 1) /** * @class Queue Queue.hpp * * @brief Class that encapsulates the functionality of a FreeRTOS queue. * * Each queue requires RAM that is used to hold the queue state, and to hold the * items that are contained in the queue (the queue storage area). If a queue is * created using this class then the required RAM is automatically allocated * from the FreeRTOS heap. * * @tparam T Type to be stored in the queue. */ template class Queue : public QueueBase { public: /** * Queue.hpp * * @brief Construct a new Queue object by calling QueueHandle_t * xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize ) * * @see * * @warning The user should call isValid() on this object to verify that the * queue was created successfully in case the memory required to create the * queue could not be allocated. * * @param length The maximum number of items the queue can hold at any one * time. * * Example Usage * @include Queue/queue.cpp */ explicit Queue(const UBaseType_t length) { this->handle = xQueueCreate(length, sizeof(T)); } ~Queue() = default; Queue(const Queue&) = delete; Queue& operator=(const Queue&) = delete; Queue(Queue&&) noexcept = default; Queue& operator=(Queue&&) noexcept = default; }; #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ #if (configSUPPORT_STATIC_ALLOCATION == 1) /** * @class StaticQueue Queue.hpp * * @brief Class that encapsulates the functionality of a FreeRTOS queue. * * If a queue 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 T Type to be stored in the queue. * @tparam N The maximum number of items the queue can hold at any one time. */ template class StaticQueue : public QueueBase { public: /** * Queue.hpp * * @brief Construct a new StaticQueue object by calling * QueueHandle_t xQueueCreateStatic( UBaseType_t uxQueueLength, * UBaseType_t uxItemSize, uint8_t *pucQueueStorageBuffer, StaticQueue_t * *pxQueueBuffer ) * * @see * * @warning This class contains the storage buffer for the queue, 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 Queue/staticQueue.cpp */ StaticQueue() { this->handle = xQueueCreateStatic(N, sizeof(T), storage, &staticQueue); } ~StaticQueue() = default; StaticQueue(const StaticQueue&) = delete; StaticQueue& operator=(const StaticQueue&) = delete; StaticQueue(StaticQueue&&) noexcept = default; StaticQueue& operator=(StaticQueue&&) noexcept = default; private: StaticQueue_t staticQueue; uint8_t storage[N * sizeof(T)]; }; #endif /* configSUPPORT_STATIC_ALLOCATION */ } // namespace FreeRTOS #endif // FREERTOS_QUEUE_HPP