/* * 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_EVENTGROUPS_HPP #define FREERTOS_EVENTGROUPS_HPP #include #include "FreeRTOS.h" #include "event_groups.h" namespace FreeRTOS { /** * @class EventGroupBase EventGroupBase.hpp * * @brief Base class that provides the standard event group interface to * FreeRTOS::EventGroup and FreeRTOS::StaticEventGroup. * * @note This class is not intended to be instantiated by the user. Use * FreeRTOS::EventGroup or FreeRTOS::StaticEventGroup. */ class EventGroupBase { public: friend class EventGroup; friend class StaticEventGroup; EventGroupBase(const EventGroupBase&) = delete; EventGroupBase& operator=(const EventGroupBase&) = 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; // NOLINTNEXTLINE using EventBits = std::bitset<((configUSE_16_BIT_TICKS == 1) ? 8 : 24)>; /** * EventGroups.hpp * * @brief Function that checks if the underlying event group handle is not * NULL. This should be used to ensure an event group has been created * correctly. * * @retval true the handle is not NULL. * @retval false the handle is NULL. */ inline bool isValid() const { return (handle != NULL); } /** * EventGroups.hpp * * @brief Function that calls EventBits_t xEventGroupWaitBits( const * EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const * BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t * xTicksToWait ) * * @see * * Read bits within an RTOS event group, optionally entering the Blocked state * (with a timeout) to wait for a bit or group of bits to become set. * * @warning This function cannot be called from an interrupt. * * @param bitsToWaitFor A bitwise value that indicates the bit or bits to test * inside the event group. bitsToWaitFor must not be set to 0. * @param clearOnExit If clearOnExit is set to true then any bits set in the * value passed as the bitsToWaitFor parameter will be cleared in the event * group before wait() returns if wait() returns for any reason other than a * timeout. The timeout value is set by the ticksToWait parameter. If * clearOnExit is set to false then the bits set in the event group are not * altered when the call to wait() returns. * @param waitForAllBits waitForAllBits is used to create either a logical AND * test (where all bits must be set) or a logical OR test (where one or more * bits must be set) as follows: If waitForAllBits is set to true then wait() * will return when either all the bits set in the value passed as the * bitsToWaitFor parameter are set in the event group or the specified block * time expires. If waitForAllBits is set to false then wait() will return * when any of the bits set in the value passed as the bitsToWaitFor parameter * are set in the event group or the specified block time expires. * @param ticksToWait The maximum amount of time (specified in 'ticks') to * wait for one/all (depending on the waitForAllBits value) of the bits * specified by bitsToWaitFor to become set. * @return EventBits The value of the event group at the time either the event * bits being waited for became set, or the block time expired. The current * value of the event bits in an event group will be different to the returned * value if a higher priority task or interrupt changed the value of an event * bit between the calling task leaving the Blocked state and exiting the * wait() function. Test the return value to know which bits were set. If * wait() returned because its timeout expired then not all the bits being * waited for will be set. If wait() returned because the bits it was waiting * for were set then the returned value is the event group value before any * bits were automatically cleared because the clearOnExit parameter was set * to true. * * Example Usage * @include EventGroups/wait.cpp */ inline EventBits wait(const EventBits& bitsToWaitFor = 0, const bool clearOnExit = false, const bool waitForAllBits = false, const TickType_t ticksToWait = portMAX_DELAY) const { return EventBits(xEventGroupWaitBits( handle, bitsToWaitFor.to_ulong(), (clearOnExit ? pdTRUE : pdFALSE), (waitForAllBits ? pdTRUE : pdFALSE), ticksToWait)); } /** * EventGroups.hpp * * @brief Function that calls EventBits_t xEventGroupSetBits( * EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) * * @see * * Set bits (flags) within an RTOS event group. This function cannot be called * from an interrupt. setFromISR() is a version that can be called from an * interrupt. * * Setting bits in an event group will automatically unblock tasks that are * blocked waiting for the bits. * * @param bitsToSet A bitwise value that indicates the bit or bits to set in * the event group. * @return EventBits The value of the event group at the time the call to * set() returns. There are two reasons why the returned value might have the * bits specified by the uxBitsToSet parameter cleared: * 1. If setting a bit results in a task that was waiting for the bit leaving * the blocked state then it is possible the bit will have been cleared * automatically (see the clearOnExit parameter of wait()). * 2. Any unblocked (or otherwise Ready state) task that has a priority above * that of the task that called set() will execute and may change the event * group value before the call to set() returns. * * Example Usage * @include EventGroups/set.cpp */ inline EventBits set(const EventBits& bitsToSet) const { return EventBits(xEventGroupSetBits(handle, bitsToSet.to_ulong())); } /** * EventGroups.hpp * * @brief Function that calls BaseType_t xEventGroupSetBitsFromISR( * EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t * *pxHigherPriorityTaskWoken ) * * @see * * Set bits (flags) within an RTOS event group. A version of set() that can be * called from an interrupt service routine (ISR). * * Setting bits in an event group will automatically unblock tasks that are * blocked waiting for the bits. * * Setting bits in an event group is not a deterministic operation because * there are an unknown number of tasks that may be waiting for the bit or * bits being set. FreeRTOS does not allow non-deterministic operations to be * performed in interrupts or from critical sections. Therefore * xEventGroupSetBitFromISR() sends a message to the RTOS daemon task to have * the set operation performed in the context of the daemon task - where a * scheduler lock is used in place of a critical section. * * @note As mentioned in the paragraph above, setting bits from an ISR will * defer the set operation to the RTOS daemon task (also known as the timer * service task). The RTOS daemon task is scheduled according to its priority, * just like any other RTOS task. Therefore, if it is essential the set * operation completes immediately (before a task created by the application * executes) then the priority of the RTOS daemon task must be higher than the * priority of any application task that uses the event group. The priority of * the RTOS daemon task is set by the configTIMER_TASK_PRIORITY definition in * FreeRTOSConfig.h. * * @param higherPriorityTaskWoken As mentioned above, calling this function * will result in a message being sent to the RTOS daemon task. If the * priority of the daemon task is higher than the priority of the currently * running task (the task the interrupt interrupted) then * higherPriorityTaskWoken will be set to true by setFromISR(), indicating * that a context switch should be requested before the interrupt exits. For * that reason higherPriorityTaskWoken must be initialised to false. See the * example code below. * @param bitsToSet A bitwise value that indicates the bit or bits to set in * the event group. * @retval true If the message was sent to the RTOS daemon task. * @retval false Otherwise or if the timer service queue was full * * Example Usage * @include EventGroups/setFromISR.cpp */ inline bool setFromISR(bool& higherPriorityTaskWoken, const EventBits& bitsToSet) const { BaseType_t taskWoken = pdFALSE; bool result = (xEventGroupSetBitsFromISR(handle, bitsToSet.to_ulong(), &taskWoken) == pdPASS); if (taskWoken == pdTRUE) { higherPriorityTaskWoken = true; } return result; } /** * EventGroups.hpp * * @brief Function that calls BaseType_t xEventGroupSetBitsFromISR( * EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t * *pxHigherPriorityTaskWoken ) * * @see * * @overload */ inline bool setFromISR(const EventBits& bitsToSet) const { return (xEventGroupSetBitsFromISR(handle, bitsToSet.to_ulong(), NULL) == pdPASS); } /** * EventGroups.hpp * * @brief Function that calls EventBits_t xEventGroupClearBits( * EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) * * @see * * Clear bits (flags) within an RTOS event group. This function cannot be * called from an interrupt. See clearFromISR() for a version that can be * called from an interrupt. * * @param bitsToClear A bitwise value that indicates the bit or bits to clear * in the event group. * @return EventBits The value of the event group before the specified bits * were cleared. * * Example Usage * @include EventGroups/clear.cpp */ inline EventBits clear(const EventBits& bitsToClear) const { return EventBits(xEventGroupClearBits(handle, bitsToClear.to_ulong())); } /** * EventGroups.hpp * * @brief Function that calls BaseType_t xEventGroupClearBitsFromISR( * EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) * * @see * * A version of clear() that can be called from an interrupt. The clear * operation is deferred to the RTOS daemon task which is also known as the * timer service task. The priority of the daemon task is set by the * configTIMER_TASK_PRIORITY setting in FreeRTOSConfig.h. * * @param bitsToClear A bitwise value that indicates the bit or bits to clear * in the event group. * @return true If the operation was successfully deferred to the RTOS daemon * task. * @return false If the timer command queue is full. * * Example Usage * @include EventGroups/clearFromISR.cpp */ inline bool clearFromISR(const EventBits& bitsToClear) const { return (xEventGroupClearBitsFromISR(handle, bitsToClear.to_ulong()) == pdPASS); } /** * EventGroups.hpp * * @brief Function that calls EventBits_t xEventGroupGetBits( * EventGroupHandle_t xEventGroup ) * * @see * * Returns the current value of the event bits (event flags) in an RTOS event * group. This function cannot be used from an interrupt. See getFromISR() for * a version that can be used in an interrupt. * * @return EventBits The value of the event bits in the event group at the * time get() was called. */ inline EventBits get() const { return EventBits(xEventGroupGetBits(handle)); } /** * EventGroups.hpp * * @brief Function that calls EventBits_t xEventGroupGetBitsFromISR( * EventGroupHandle_t xEventGroup ) * * @see * * A version of get() that can be called from an interrupt. * * @return EventBits The value of the event bits in the event group at the * time getFromISR() was called. */ inline EventBits getFromISR() const { return EventBits(xEventGroupGetBitsFromISR(handle)); } /** * EventGroups.hpp * * @brief Function that calls EventBits_t xEventGroupSync( * EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const * EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) * * @see * * Atomically set bits (flags) within an RTOS event group, then wait for a * combination of bits to be set within the same event group. This * functionality is typically used to synchronize multiple tasks (often called * a task rendezvous), where each task has to wait for the other tasks to * reach a synchronization point before proceeding. * * This function cannot be used from an interrupt. * * The function will return before its block time expires if the bits * specified by the bitsToWait parameter are set, or become set within that * time. In this case all the bits specified by bitsToWait will be * automatically cleared before the function returns. * * @param bitsToSet The bit or bits to set in the event group before * determining if (and possibly waiting for), all the bits specified by the * bitsToWait parameter are set. * @param bitsToWaitFor A bitwise value that indicates the bit or bits to test * inside the event group. * @param ticksToWait The maximum amount of time (specified in 'ticks') to * wait for all the bits specified by the uxBitsToWaitFor parameter value to * become set. * @return EventBits * * Example Usage * @include EventGroups/sync.cpp */ inline EventBits sync(const EventBits& bitsToSet = 0, const EventBits& bitsToWaitFor = 0, const TickType_t ticksToWait = portMAX_DELAY) const { return EventBits(xEventGroupSync(handle, bitsToSet.to_ulong(), bitsToWaitFor.to_ulong(), ticksToWait)); } private: /** * EventGroups.hpp * * @brief Construct a new EventGroupBase object. * * @note Default constructor is deliberately private as this class is not * intended to be instantiated or derived from by the user. Use * FreeRTOS::EventGroup or FreeRTOS::StaticEventGroup. */ EventGroupBase() = default; /** * EventGroup.hpp * * @brief Destroy the EventGroupBase object by calling void * vEventGroupDelete( EventGroupHandle_t xEventGroup ) * * @see * * Delete an event group. * * Tasks that are blocked on the event group being deleted will be unblocked, * and report an event group value of 0. */ ~EventGroupBase() { vEventGroupDelete(this->handle); }; EventGroupBase(EventGroupBase&&) noexcept = default; EventGroupBase& operator=(EventGroupBase&&) noexcept = default; /** * @brief Handle used to refer to the event group when using the FreeRTOS * interface. */ EventGroupHandle_t handle = NULL; }; #if (configSUPPORT_DYNAMIC_ALLOCATION == 1) /** * @class EventGroup EventGroups.hpp * * @brief Class that encapsulates the functionality of a FreeRTOS event group. * * Each event group requires a [very] small amount of RAM that is used to hold * the event group's state. If an event group is created using this class then * the required RAM is automatically allocated from the FreeRTOS heap. If an * event group is created using FreeRTOS::StaticEventGroup then the RAM is * provided by the application writer, which requires an additional parameter, * but allows the RAM to be statically allocated at compile time. See the Static * Vs Dynamic allocation page for more information. */ class EventGroup : public EventGroupBase { public: /** * EventGroup.hpp * * @brief Construct a new EventGroup object by calling EventGroupHandle_t * xEventGroupCreate( void ) * * @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. * * Example Usage * @include EventGroups/eventGroup.cpp */ EventGroup() { this->handle = xEventGroupCreate(); } ~EventGroup() = default; EventGroup(const EventGroup&) = delete; EventGroup& operator=(const EventGroup&) = delete; EventGroup(EventGroup&&) noexcept = default; EventGroup& operator=(EventGroup&&) noexcept = default; }; #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ #if (configSUPPORT_STATIC_ALLOCATION == 1) /** * @class StaticEventGroup EventGroups.hpp * * @brief Class that encapsulates the functionality of a FreeRTOS event group. * * Each event group requires a [very] small amount of RAM that is used to hold * the event group's state. If an event group is created using * FreeRTOS::EventGroup then the required RAM is automatically allocated from * the FreeRTOS heap. If an event group is created using this class then the RAM * is provided by the application writer, which requires an additional * parameter, but allows the RAM to be statically allocated at compile time. See * the Static Vs Dynamic allocation page for more information. */ class StaticEventGroup : public EventGroupBase { public: /** * EventGroups.hpp * * @brief Construct a new StaticEventGroup object by calling * EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * *pxEventGroupBuffer ) * * @see * * @warning This class contains the storage buffer for the event group, 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 EventGroups/staticEventGroup.cpp */ StaticEventGroup() { this->handle = xEventGroupCreateStatic(&staticEventGroup); } ~StaticEventGroup() = default; StaticEventGroup(const StaticEventGroup&) = delete; StaticEventGroup& operator=(const StaticEventGroup&) = delete; StaticEventGroup(StaticEventGroup&&) noexcept = default; StaticEventGroup& operator=(StaticEventGroup&&) noexcept = default; private: StaticEventGroup_t staticEventGroup; }; #endif /* configSUPPORT_STATIC_ALLOCATION */ } // namespace FreeRTOS #endif // FREERTOS_EVENTGROUPS_HPP