diff --git a/source/test/src/FreeRTOSCPP/EventGroups.hpp b/source/test/src/FreeRTOSCPP/EventGroups.hpp new file mode 100644 index 0000000..6aa7a1b --- /dev/null +++ b/source/test/src/FreeRTOSCPP/EventGroups.hpp @@ -0,0 +1,503 @@ +/* + * 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 diff --git a/source/test/src/FreeRTOSCPP/Kernel.hpp b/source/test/src/FreeRTOSCPP/Kernel.hpp new file mode 100644 index 0000000..0f28646 --- /dev/null +++ b/source/test/src/FreeRTOSCPP/Kernel.hpp @@ -0,0 +1,409 @@ +/* + * 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_KERNEL_HPP +#define FREERTOS_KERNEL_HPP + +#include "FreeRTOS.h" +#include "task.h" + +namespace FreeRTOS { + +/** + * @brief Kernel namespace that provides an interface to kernel functions. + * + */ +namespace Kernel { +enum class SchedulerState : BaseType_t { + Suspended = taskSCHEDULER_SUSPENDED, + NotStarted = taskSCHEDULER_NOT_STARTED, + Running = taskSCHEDULER_RUNNING, +}; + +/** + * @brief If versionNumber ends with + it represents the version in development + * after the numbered release. + */ +inline constexpr char versionNumber[] = tskKERNEL_VERSION_NUMBER; +inline constexpr BaseType_t versionMajor = tskKERNEL_VERSION_MAJOR; +inline constexpr BaseType_t versionMinor = tskKERNEL_VERSION_MINOR; +inline constexpr BaseType_t versionBuild = tskKERNEL_VERSION_BUILD; + +#if (INCLUDE_xTaskGetSchedulerState == 1) +/** + * Kernel.hpp + * + * @brief Function that calls xTaskGetSchedulerState() + * + * @see + * + * @retval SchedulerState Returns the scheduler state as Running, NotStarted, or + * Suspended. + */ +inline SchedulerState getSchedulerState() { + return static_cast(xTaskGetSchedulerState()); +} +#endif /* INCLUDE_xTaskGetSchedulerState */ + +/** + * Kernel.hpp + * + * @brief Function that calls uxTaskGetNumberOfTasks() + * + * @see + * + * @retval UBaseType_t The number of tasks that the real time kernel is + * currently managing. This includes all ready, blocked and suspended tasks. A + * task that has been deleted but not yet freed by the idle task will also be + * included in the count. + */ +inline UBaseType_t getNumberOfTasks() { return uxTaskGetNumberOfTasks(); } + +#if (INCLUDE_xTaskGetIdleTaskHandle == 1 && configGENERATE_RUN_TIME_STATS == 1) +/** + * Kernel.hpp + * + * @brief Function that calls xTaskGetIdleRunTimeCounter() + * + * @see + * + * @retval TickType_t The run-time counter for the Idle task. + * + * This function can be used to determine how much CPU time the idle task + * receives. See the Run Time Stats page for a full description of the + * run-time-stats feature. + * + * configGENERATE_RUN_TIME_STATS and INCLUDE_xTaskGetIdleTaskHandle must both be + * defined as 1 for this function to be available. The application must also + * then provide definitions for portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and + * portGET_RUN_TIME_COUNTER_VALUE to configure a peripheral timer/counter and + * return the timer's current count value respectively. It is recommended to + * make the timer at least 10 times the frequency of the tick count. + */ +inline TickType_t getIdleRunTimeCounter() { + return xTaskGetIdleRunTimeCounter(); +} +#endif /* INCLUDE_xTaskGetIdleTaskHandle && configGENERATE_RUN_TIME_STATS*/ + +/** + * Kernel.hpp + * + * @brief Function that calls xTaskGetTickCount() + * + * @see + * + * @retval TickType_t The count of ticks since + * FreeRTOS::Kernel::startScheduler() was called. + */ +inline TickType_t getTickCount() { return xTaskGetTickCount(); } + +/** + * Kernel.hpp + * + * @brief Function that calls xTaskGetTickCountFromISR() + * + * @see + * + * @retval TickType_t The count of ticks since + * FreeRTOS::Kernel::startScheduler() was called. + * + * This is a version of FreeRTOS::Kernel::getTickCount() that is safe to be + * called from an ISR - provided that TickType_t is the natural word size of the + * microcontroller being used or interrupt nesting is either not supported or + * not being used. + */ +inline TickType_t getTickCountFromISR() { return xTaskGetTickCountFromISR(); } + +/** + *Kernel.hpp + * + * @brief Function that calls taskYIELD() + * + * @see + * + * FreeRTOS::Kernel::yield() is used to request a context switch to another + *task. However, if there are no other tasks at a higher or equal priority to + *the task that calls FreeRTOS::Kernel::yield() then the RTOS scheduler will + *simply select the task that called FreeRTOS::Kernel::yield() to run again. + */ +inline void yield() { taskYIELD(); } + +/** + * Kernel.hpp + * + * @brief Function that calls taskENTER_CRITICAL() + * + * @see + * + * Function to mark the start of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * @note This may alter the stack (depending on the portable implementation) so + * must be used with care! + * + * Example Usage + * @include Kernel/enterExitCritical.cpp + */ +inline void enterCritical() { taskENTER_CRITICAL(); } + +/** + * Kernel.hpp + * + * @brief Function that calls taskENTER_CRITICAL_FROM_ISR() + * + * @see + * + * + * @retval uint32_t the interrupt mask state as it was before the macro was + * called. The value returned by FreeRTOS::Kernel::enterCriticalFromISR() must + * be used as the interruptStatus parameter in the matching call to + * FreeRTOS::Kernel::exitCriticalFromISR(). + * + * Function to mark the start of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * @note This may alter the stack (depending on the portable implementation) so + * must be used with care! + * + * Example Usage + * @include Kernel/enterExitCriticalFromISR.cpp + */ +inline uint32_t enterCriticalFromISR() { return taskENTER_CRITICAL_FROM_ISR(); } + +/** + * Kernel.hpp + * + * @brief Function that calls taskEXIT_CRITICAL() + * + * @see + * + * Function to mark the end of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * @note This may alter the stack (depending on the portable implementation) so + * must be used with care! + * + * Example Usage + * @include Kernel/enterExitCritical.cpp + */ +inline void exitCritical() { taskEXIT_CRITICAL(); } + +/** + * Kernel.hpp + * + * @brief Function that calls taskEXIT_CRITICAL_FROM_ISR() + * + * @see + * + * + * @param interruptStatus The value used as the interruptStatus parameter must + * be the value returned from the matching call to + * FreeRTOS::Kernel::enterCriticalFromISR(). + * + * Function to mark the end of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * @note This may alter the stack (depending on the portable implementation) so + * must be used with care! + * + * Example Usage + * @include Kernel/enterExitCriticalFromISR.cpp + */ +inline void exitCriticalFromISR(const uint32_t interruptStatus) { + taskEXIT_CRITICAL_FROM_ISR(interruptStatus); +} + +/** + * Kernel.hpp + * + * @brief Function that calls taskDISABLE_INTERRUPTS() + * + * @see + * + * Function to disable all maskable interrupts. + */ +inline void disableInterrupts() { taskDISABLE_INTERRUPTS(); } + +/** + * Kernel.hpp + * + * @brief Function that calls taskENABLE_INTERRUPTS() + * + * @see + * + * Function to enable microcontroller interrupts. + */ +inline void enableInterrupts() { taskENABLE_INTERRUPTS(); } + +/** + * Kernel.hpp + * + * @brief Function that calls vTaskStartScheduler() + * + * @see + * + * Starts the real time kernel tick processing. After calling the kernel has + * control over which tasks are executed and when. + * + * Example Usage + * @include Kernel/startScheduler.cpp + */ +inline void startScheduler() { vTaskStartScheduler(); } + +/** + * Kernel.hpp + * + * @brief Function that calls vTaskEndScheduler() + * + * @see + * + * @note At the time of writing only the x86 real mode port, which runs on a PC + * in place of DOS, implements this function. + * + * Stops the real time kernel tick. All created tasks will be automatically + * deleted and multitasking (either preemptive or cooperative) will stop. + * Execution then resumes from the point where + * FreeRTOS::Kernel::startScheduler() was called, as if + * FreeRTOS::Kernel::startScheduler() had just returned. + * + * See the demo application file main. c in the demo/PC directory for an example + * that uses FreeRTOS::Kernel::endScheduler(). + * + * FreeRTOS::Kernel::endScheduler() requires an exit function to be defined + * within the portable layer (see vPortEndScheduler () in port. c for the PC + * port). This performs hardware specific operations such as stopping the + * kernel tick. + * + * FreeRTOS::Kernel::endScheduler() will cause all of the resources allocated by + * the kernel to be freed - but will not free resources allocated by application + * tasks. + * + * Example Usage + * @include Kernel/endScheduler.cpp + */ +inline void endScheduler() { vTaskEndScheduler(); } + +/** + * Kernel.hpp + * + * @brief Function that calls vTaskSuspendAll() + * + * @see + * + * Suspends the scheduler without disabling interrupts. Context switches will + * not occur while the scheduler is suspended. + * + * After calling FreeRTOS::Kernel::suspendAll() the calling task will continue + * to execute without risk of being swapped out until a call to + * FreeRTOS::Kernel::resumeAll() has been made. + * + * API functions that have the potential to cause a context switch (for example, + * FreeRTOS::Task::delayUntil(), FreeRTOS::Queue::send(), etc.) must not be + * called while the scheduler is suspended. + * + * Example Usage + * @include Kernel/suspendAll.cpp + */ +inline void suspendAll() { vTaskSuspendAll(); } + +/** + * Kernel.hpp + * + * @brief Function that calls xTaskResumeAll() + * + * @see + * + * Resumes scheduler activity after it was suspended by a call to + * FreeRTOS::Kernel::suspendAll(). + * + * FreeRTOS::Kernel::resumeAll() only resumes the scheduler. It does not + * unsuspend tasks that were previously suspended by a call to + * FreeRTOS::Task::suspend(). + * + * @retval true If resuming the scheduler caused a context switch. + * @retval false Otherwise. + * + * Example Usage + * @include Kernel/resumeAll.cpp + */ +inline bool resumeAll() { return (xTaskResumeAll() == pdTRUE); } + +/** + * Kernel.hpp + * + * @brief Function that calls vTaskStepTick( const TickType_t xTicksToJump + * ) + * + * @see + * + * Only available when configUSE_TICKLESS_IDLE is set to 1. If tickless mode is + * being used, or a low power mode is implemented, then the tick interrupt will + * not execute during idle periods. When this is the case, the tick count value + * maintained by the scheduler needs to be kept up to date with the actual + * execution time by being skipped forward by a time equal to the idle period. + * + * @param ticksToJump The number of RTOS ticks that have passed since the tick + * interrupt was stopped. + */ +inline void stepTick(const TickType_t ticksToJump) { + vTaskStepTick(ticksToJump); +} + +/** + * Kernel.hpp + * + * @brief Function that calls xTaskCatchUpTicks( TickType_t xTicksToCatchUp + * ) + * + * @see + * + * This function corrects the tick count value after the application code has + * held interrupts disabled for an extended period resulting in tick interrupts + * having been missed. + * + * This function is similar to FreeRTOS::Kernel::stepTick(), however, unlike + * FreeRTOS::Kernel::stepTick(), FreeRTOS::Kernel::catchUpTicks() may move the + * tick count forward past a time at which a task should be removed from the + * blocked state. That means tasks may have to be removed from the blocked + * state as the tick count is moved. + * + * @param ticksToCatchUp The number of tick interrupts that have been missed due + * to interrupts being disabled. Its value is not computed automatically, so + * must be computed by the application writer. + * + * @retval true If moving the tick count forward resulted in a task leaving the + * blocked state and a context switch being performed. + * @retval false Otherwise. + */ +inline bool catchUpTicks(const TickType_t ticksToCatchUp) { + return (xTaskCatchUpTicks(ticksToCatchUp) == pdTRUE); +} +} // namespace Kernel + +} // namespace FreeRTOS + +#endif // FREERTOS_KERNEL_HPP diff --git a/source/test/src/FreeRTOSCPP/MessageBuffer.hpp b/source/test/src/FreeRTOSCPP/MessageBuffer.hpp new file mode 100644 index 0000000..0e9fdad --- /dev/null +++ b/source/test/src/FreeRTOSCPP/MessageBuffer.hpp @@ -0,0 +1,518 @@ +/* + * 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 \ No newline at end of file diff --git a/source/test/src/FreeRTOSCPP/Mutex.hpp b/source/test/src/FreeRTOSCPP/Mutex.hpp new file mode 100644 index 0000000..e045324 --- /dev/null +++ b/source/test/src/FreeRTOSCPP/Mutex.hpp @@ -0,0 +1,495 @@ +/* + * 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_MUTEX_HPP +#define FREERTOS_MUTEX_HPP + +#include "FreeRTOS.h" +#include "semphr.h" + +namespace FreeRTOS { + +/** + * @class MutexBase Mutex.hpp + * + * @brief Base class that provides the standard mutex interface to + * FreeRTOS::Mutex, FreeRTOS::StaticMutex, FreeRTOS::RecursiveMutex, and + * FreeRTOS::StaticRecursiveMutex. + * + * @note This class is not intended to be instantiated by the user. Use + * FreeRTOS::Mutex, FreeRTOS::StaticMutex, FreeRTOS::RecursiveMutex, and + * FreeRTOS::StaticRecursiveMutex. + */ +class MutexBase { + public: + friend class Mutex; + friend class StaticMutex; + friend class RecursiveMutexBase; + friend class RecursiveMutex; + friend class StaticRecursiveMutex; + + MutexBase(const MutexBase&) = delete; + MutexBase& operator=(const MutexBase&) = 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; + + /** + * Mutex.hpp + * + * @brief Function that checks if the underlying semaphore handle is not NULL. + * This should be used to ensure a semaphore has been created correctly. + * + * @retval true the handle is not NULL. + * @retval false the handle is NULL. + */ + inline bool isValid() const { return (handle != NULL); } + + /** + * Mutex.hpp + * + * @brief Function that calls xSemaphoreTake( SemaphoreHandle_t + * xSemaphore, TickType_t xTicksToWait ) + * + * @see + * + * @param ticksToWait The time in ticks to wait for the mutex to become + * available. The macro portTICK_PERIOD_MS can be used to convert this to a + * real time. A block time of zero can be used to poll the mutex. + * @retval true If the mutex was locked. + * @retval false If ticksToWait expired without the mutex becoming available. + * + * Example Usage + * @include Mutex/lock.cpp + */ + inline bool lock(const TickType_t ticksToWait = portMAX_DELAY) const { + return (xSemaphoreTake(handle, ticksToWait) == pdTRUE); + } + + /** + * Mutex.hpp + * + * @brief Function that calls xSemaphoreTakeFromISR ( SemaphoreHandle_t + * xSemaphore, signed BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * @param higherPriorityTaskWoken It is possible (although unlikely, and + * dependent on the semaphore type) that a mutex will have one or more tasks + * blocked on it waiting to give the mutex. Calling lockFromISR() will make a + * task that was blocked waiting to give the mutex leave the Blocked state. If + * calling the API function causes a task to leave the Blocked state, and the + * unblocked task has a priority equal to or higher than the currently + * executing task (the task that was interrupted), then, internally, the API + * function will set higherPriorityTaskWoken to true. + * @return true If the mutex was successfully locked. + * @return false If the mutex was not successfully locked because it was not + * available. + */ + inline bool lockFromISR(bool& higherPriorityTaskWoken) const { + BaseType_t taskWoken = pdFALSE; + bool result = (xSemaphoreTakeFromISR(handle, &taskWoken) == pdTRUE); + if (taskWoken == pdTRUE) { + higherPriorityTaskWoken = true; + } + return result; + } + + /** + * Mutex.hpp + * + * @brief Function that calls xSemaphoreTakeFromISR ( SemaphoreHandle_t + * xSemaphore, signed BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * @overload + */ + inline bool lockFromISR() const { + return (xSemaphoreTakeFromISR(handle, NULL) == pdTRUE); + } + + /** + * Mutex.hpp + * + * @brief Function that calls xSemaphoreGive( SemaphoreHandle_t xSemaphore + * ) + * + * @see + * + * @warning This must not be used from an ISR. + * + * @return true If the mutex was unlocked. + * @return false If an error occurred. Mutexes (semaphores) are implemented + * using queues. An error can occur if there is no space on the queue to post + * a message indicating that the mutex was not first locked correctly. + * + * Example Usage + * @include Mutex/unlock.cpp + */ + inline bool unlock() const { return (xSemaphoreGive(handle) == pdTRUE); } + + private: + MutexBase() = default; + + /** + * Mutex.hpp + * + * @brief Destroy the MutexBase object by calling void vSemaphoreDelete( + * SemaphoreHandle_t xSemaphore ) + * + * @see + * + * @note Do not delete a mutex that has tasks blocked on it (tasks that are in + * the Blocked state waiting for the mutex to become available). + */ + ~MutexBase() { vSemaphoreDelete(this->handle); } + + MutexBase(MutexBase&&) noexcept = default; + MutexBase& operator=(MutexBase&&) noexcept = default; + + /** + * @brief Handle used to refer to the semaphore when using the FreeRTOS + * interface. + */ + SemaphoreHandle_t handle = NULL; +}; + +/** + * @class RecursiveMutexBase Mutex.hpp + * + * @brief Base class that provides the recursive mutex interface to + * FreeRTOS::RecursiveMutex and FreeRTOS::StaticRecursiveMutex. This class + * exists to override the lock() and unlock() functions which require different + * underlying functions from what is used in FreeRTOS::MutexBase. + * + * @note This class is not intended to be instantiated by the user. Use + * FreeRTOS::RecursiveMutex or FreeRTOS::StaticRecursiveMutex. + */ +class RecursiveMutexBase : public MutexBase { + public: + friend class RecursiveMutex; + friend class StaticRecursiveMutex; + + RecursiveMutexBase(const RecursiveMutexBase&) = delete; + RecursiveMutexBase& operator=(const RecursiveMutexBase&) = delete; + + static void* operator new(size_t, void*); + static void* operator new[](size_t, void*); + static void* operator new(size_t) = delete; + static void* operator new[](size_t) = delete; + + /** + * Mutex.hpp + * + * @brief Function that calls xSemaphoreTakeRecursive( SemaphoreHandle_t + * xMutex, TickType_t xTicksToWait ) + * + * @see + * + * @param ticksToWait The time in ticks to wait for the mutex to become + * available. The macro portTICK_PERIOD_MS can be used to convert this to a + * real time. A block time of zero can be used to poll the mutex. If the task + * already owns the mutex then take() will return immediately no matter what + * the value of ticksToWait. + * @retval true If the mutex was locked. + * @retval false If ticksToWait expired without the mutex becoming available. + * + * Example Usage + * @include Mutex/recursiveLock.cpp + */ + inline bool lock(const TickType_t ticksToWait = portMAX_DELAY) const { + return (xSemaphoreTakeRecursive(handle, ticksToWait) == pdTRUE); + } + + /** + * Mutex.hpp + * + * @brief Function that calls xSemaphoreGiveRecursive( SemaphoreHandle_t + * xSemaphore ) + * + * @see + * + * A mutex used recursively can be locked repeatedly by the owner. The mutex + * doesn't become available again until the owner has called unlock() for each + * successful lock request. For example, if a task successfully locks the + * same mutex 5 times then the mutex will not be available to any other task + * until it has also unlocked the mutex back exactly five times. + * + * @return true If the mutex was unlocked. + * @return false Otherwise. + * + * Example Usage + * @include Mutex/recursiveLock.cpp + */ + inline bool unlock() const { + return (xSemaphoreGiveRecursive(handle) == pdTRUE); + } + + private: + RecursiveMutexBase() = default; + ~RecursiveMutexBase() = default; + + RecursiveMutexBase(RecursiveMutexBase&&) noexcept = default; + RecursiveMutexBase& operator=(RecursiveMutexBase&&) noexcept = default; +}; + +#if (configSUPPORT_DYNAMIC_ALLOCATION == 1) + +/** + * @class Mutex Mutex.hpp + * + * @brief Class that encapsulates the functionality of a FreeRTOS mutex. + * + * Each mutex require a small amount of RAM that is used to hold the mutex's + * state. If a mutex is created using FreeRTOS::Mutex then the required RAM is + * automatically allocated from the FreeRTOS heap. If a mutex is created using + * FreeRTOS::StaticMutex then the RAM is provided by the application writer and + * allows the RAM to be statically allocated at compile time. See the Static Vs + * Dynamic allocation page for more information. + * + * Mutexes and binary semaphores are very similar but have some subtle + * differences: Mutexes include a priority inheritance mechanism, binary + * semaphores do not. This makes binary semaphores the better choice for + * implementing synchronisation (between tasks or between tasks and an + * interrupt), and mutexes the better choice for implementing simple mutual + * exclusion. + * + * The priority of a task that locks a mutex will be temporarily raised if + * another task of higher priority attempts to obtain the same mutex. The task + * that owns the mutex 'inherits' the priority of the task attempting to lock + * the same mutex. This means the mutex must always be unlocked back otherwise + * the higher priority task will never be able to lock the mutex, and the lower + * priority task will never 'disinherit' the priority. + */ +class Mutex : public MutexBase { + public: + /** + * Mutex.hpp + * + * @brief Construct a new Mutex object by calling SemaphoreHandle_t + * xSemaphoreCreateMutex( void ) + * + * @see + * + * @warning The user should call isValid() on this object to verify that the + * mutex was created successfully in case the memory required to create the + * queue could not be allocated. + * + * Example Usage + * @include Mutex/mutex.cpp + */ + Mutex() { this->handle = xSemaphoreCreateMutex(); } + ~Mutex() = default; + + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; + + Mutex(Mutex&&) noexcept = default; + Mutex& operator=(Mutex&&) noexcept = default; +}; + +/** + * @class RecursiveMutex Mutex.hpp + * + * @brief Class that encapsulates the functionality of a FreeRTOS recursive + * mutex. + * + * Each recursive mutex require a small amount of RAM that is used to hold the + * recursive mutex's state. If a mutex is created using FreeRTOS::RecursiveMutex + * then the required RAM is automatically allocated from the FreeRTOS heap. If a + * recursive mutex is created using FreeRTOS::StaticRecursiveMutex 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. + * + * Contrary to non-recursive mutexes, a task can lock a recursive mutex multiple + * times, and the recursive mutex will only be returned after the holding task + * has unlocked the mutex the same number of times it locked the mutex. + * + * Like non-recursive mutexes, recursive mutexes implement a priority + * inheritance algorithm. The priority of a task that locks a mutex will be + * temporarily raised if another task of higher priority attempts to obtain the + * same mutex. The task that owns the mutex 'inherits' the priority of the task + * attempting to lock the same mutex. This means the mutex must always be + * unlocked otherwise the higher priority task will never be able to obtain the + * mutex, and the lower priority task will never 'disinherit' the priority. + */ +class RecursiveMutex : public RecursiveMutexBase { + public: + /** + * Mutex.hpp + * + * @brief Construct a new RecursiveMutex object by calling + * SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void ) + * + * @see + * + * @warning The user should call isValid() on this object to verify that the + * recursive mutex was created successfully in case the memory required to + * create the queue could not be allocated. + * + * Example Usage + * @include Mutex/recursiveMutex.cpp + */ + RecursiveMutex() { this->handle = xSemaphoreCreateRecursiveMutex(); } + ~RecursiveMutex() = default; + + RecursiveMutex(const RecursiveMutex&) = delete; + RecursiveMutex& operator=(const RecursiveMutex&) = delete; + + RecursiveMutex(RecursiveMutex&&) noexcept = default; + RecursiveMutex& operator=(RecursiveMutex&&) noexcept = default; +}; + +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + +#if (configSUPPORT_STATIC_ALLOCATION == 1) + +/** + * @class StaticMutex Mutex.hpp + * + * @brief Class that encapsulates the functionality of a FreeRTOS mutex. + * + * Each mutex require a small amount of RAM that is used to hold the mutex's + * state. If a mutex is created using FreeRTOS::Mutex then the required RAM is + * automatically allocated from the FreeRTOS heap. If a mutex is created using + * FreeRTOS::StaticMutex then the RAM is provided by the application writer and + * allows the RAM to be statically allocated at compile time. See the Static Vs + * Dynamic allocation page for more information. + * + * Mutexes and binary semaphores are very similar but have some subtle + * differences: Mutexes include a priority inheritance mechanism, binary + * semaphores do not. This makes binary semaphores the better choice for + * implementing synchronisation (between tasks or between tasks and an + * interrupt), and mutexes the better choice for implementing simple mutual + * exclusion. + * + * The priority of a task that locks a mutex will be temporarily raised if + * another task of higher priority attempts to obtain the same mutex. The task + * that owns the mutex 'inherits' the priority of the task attempting to lock + * the same mutex. This means the mutex must always be unlocked back otherwise + * the higher priority task will never be able to lock the mutex, and the lower + * priority task will never 'disinherit' the priority. + */ +class StaticMutex : public MutexBase { + public: + /** + * Mutex.hpp + * + * @brief Construct a new StaticMutex object by calling + * SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t + * *pxMutexBuffer ) + * + * @see + * + * @warning This class contains the storage buffer for the mutex, 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 Mutex/staticMutex.cpp + */ + StaticMutex() { this->handle = xSemaphoreCreateMutexStatic(&staticMutex); } + ~StaticMutex() = default; + + StaticMutex(const StaticMutex&) = delete; + StaticMutex& operator=(const StaticMutex&) = delete; + + StaticMutex(StaticMutex&&) noexcept = default; + StaticMutex& operator=(StaticMutex&&) noexcept = default; + + private: + StaticSemaphore_t staticMutex; +}; + +/** + * @class StaticRecursiveMutex Mutex.hpp + * + * @brief Class that encapsulates the functionality of a FreeRTOS recursive + * mutex. + * + * Each recursive mutex require a small amount of RAM that is used to hold the + * recursive mutex's state. If a mutex is created using FreeRTOS::RecursiveMutex + * then the required RAM is automatically allocated from the FreeRTOS heap. If a + * recursive mutex is created using FreeRTOS::StaticRecursiveMutex 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. + * + * Contrary to non-recursive mutexes, a task can lock a recursive mutex multiple + * times, and the recursive mutex will only be returned after the holding task + * has unlocked the mutex the same number of times it locked the mutex. + * + * Like non-recursive mutexes, recursive mutexes implement a priority + * inheritance algorithm. The priority of a task that locks a mutex will be + * temporarily raised if another task of higher priority attempts to obtain the + * same mutex. The task that owns the mutex 'inherits' the priority of the task + * attempting to lock the same mutex. This means the mutex must always be + * unlocked otherwise the higher priority task will never be able to obtain the + * mutex, and the lower priority task will never 'disinherit' the priority. + */ +class StaticRecursiveMutex : public RecursiveMutexBase { + public: + /** + * Mutex.hpp + * + * @brief Construct a new StaticRecursiveMutex object by calling + * SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( + * StaticSemaphore_t *pxMutexBuffer ) + * + * @see + * + * @warning This class contains the storage buffer for the recursive mutex, 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 Mutex/staticRecursiveMutex.cpp + */ + StaticRecursiveMutex() { + this->handle = xSemaphoreCreateRecursiveMutexStatic(&staticRecursiveMutex); + } + ~StaticRecursiveMutex() = default; + + StaticRecursiveMutex(const StaticRecursiveMutex&) = delete; + StaticRecursiveMutex& operator=(const StaticRecursiveMutex&) = delete; + + StaticRecursiveMutex(StaticRecursiveMutex&&) noexcept = default; + StaticRecursiveMutex& operator=(StaticRecursiveMutex&&) noexcept = default; + + private: + StaticSemaphore_t staticRecursiveMutex; +}; + +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +} // namespace FreeRTOS + +#endif // FREERTOS_MUTEX_HPP diff --git a/source/test/src/FreeRTOSCPP/Queue.hpp b/source/test/src/FreeRTOSCPP/Queue.hpp new file mode 100644 index 0000000..fe6cc31 --- /dev/null +++ b/source/test/src/FreeRTOSCPP/Queue.hpp @@ -0,0 +1,737 @@ +/* + * 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 diff --git a/source/test/src/FreeRTOSCPP/Semaphore.hpp b/source/test/src/FreeRTOSCPP/Semaphore.hpp new file mode 100644 index 0000000..63d7bfa --- /dev/null +++ b/source/test/src/FreeRTOSCPP/Semaphore.hpp @@ -0,0 +1,523 @@ +/* + * 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_SEMAPHORE_HPP +#define FREERTOS_SEMAPHORE_HPP + +#include "FreeRTOS.h" +#include "semphr.h" + +namespace FreeRTOS { + +/** + * @class SemaphoreBase Semaphore.hpp + * + * @brief Base class that provides the standard semaphore interface to + * FreeRTOS::BinarySemaphore, FreeRTOS::StaticBinarySemaphore, + * FreeRTOS::CountingSemaphore, and FreeRTOS::StaticCountingSemaphore. + * + * @note This class is not intended to be instantiated by the user. Use + * FreeRTOS::BinarySemaphore, FreeRTOS::StaticBinarySemaphore, + * FreeRTOS::CountingSemaphore, or FreeRTOS::StaticCountingSemaphore. + */ +class SemaphoreBase { + public: + friend class BinarySemaphore; + friend class StaticBinarySemaphore; + friend class CountingSemaphore; + friend class StaticCountingSemaphore; + + SemaphoreBase(const SemaphoreBase&) = delete; + SemaphoreBase& operator=(const SemaphoreBase&) = 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; + + /** + * Semaphore.hpp + * + * @brief Function that checks if the underlying semaphore handle is not NULL. + * This should be used to ensure a semaphore has been created correctly. + * + * @retval true the handle is not NULL. + * @retval false the handle is NULL. + */ + inline bool isValid() const { return (handle != NULL); } + + /** + * Semaphore.hpp + * + * @brief Function that calls UBaseType_t uxSemaphoreGetCount( + * SemaphoreHandle_t xSemaphore ) + * + * @see + * + * Returns the count of a semaphore. + * + * @return UBaseType_t If the semaphore is a counting semaphore then the + * semaphores current count value is returned. If the semaphore is a binary + * semaphore then 1 is returned if the semaphore is available, and 0 is + * returned if the semaphore is not available. + */ + inline UBaseType_t getCount() const { return uxSemaphoreGetCount(handle); } + + /** + * Semaphore.hpp + * + * @brief Function that calls xSemaphoreTake( SemaphoreHandle_t + * xSemaphore, TickType_t xTicksToWait ) + * + * @see + * + * Function to obtain a semaphore. + * + * This macro must not be called from an ISR. takeFromISR() can be used to + * take a semaphore from within an interrupt if required, although this would + * not be a normal operation. Semaphores use queues as their underlying + * mechanism, so functions are to some extent interoperable. + * + * @param ticksToWait The time in ticks to wait for the semaphore to become + * available. The macro portTICK_PERIOD_MS can be used to convert this to a + * real time. A block time of zero can be used to poll the semaphore. + * @retval true If the semaphore was obtained. + * @retval false If xTicksToWait expired without the semaphore becoming + * available. + * + * Example Usage + * @include Semaphore/take.cpp + */ + inline bool take(const TickType_t ticksToWait = portMAX_DELAY) const { + return (xSemaphoreTake(handle, ticksToWait) == pdTRUE); + } + + /** + * Semaphore.hpp + * + * @brief Function that calls xSemaphoreTakeFromISR( SemaphoreHandle_t + * xSemaphore, signed BaseType_t *pxHigherPriorityTaskWoken) + * + * @see + * + * A version of take() that can be called from an ISR. Unlike take(), + * takeFromISR() does not permit a block time to be specified. + * + * @param higherPriorityTaskWoken It is possible (although unlikely, and + * dependent on the semaphore type) that a semaphore will have one or more + * tasks blocked on it waiting to give the semaphore. Calling takeFromISR() + * will make a task that was blocked waiting to give the semaphore leave the + * Blocked state. If calling the API function causes a task to leave the + * Blocked state, and the unblocked task has a priority equal to or higher + * than the currently executing task (the task that was interrupted), then, + * internally, the API function will set higherPriorityTaskWoken to true. If + * takeFromISR() sets higherPriorityTaskWoken to true, then 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. + * The mechanism is identical to that used in the FreeRTOS::receiveFromISR() + * function, and readers are referred to the FreeRTOS::receiveFromISR() + * documentation for further explanation. + * @retval true If the semaphore was successfully taken. + * @retval false If the semaphore was not successfully taken because it was + * not available. + */ + inline bool takeFromISR(bool& higherPriorityTaskWoken) const { + BaseType_t taskWoken = pdFALSE; + bool result = (xSemaphoreTakeFromISR(handle, &taskWoken) == pdTRUE); + if (taskWoken == pdTRUE) { + higherPriorityTaskWoken = true; + } + return result; + } + + /** + * Semaphore.hpp + * + * @brief Function that calls xSemaphoreTakeFromISR( SemaphoreHandle_t + * xSemaphore, signed BaseType_t *pxHigherPriorityTaskWoken) + * + * @see + * + * @overload + */ + inline bool takeFromISR() const { + return (xSemaphoreTakeFromISR(handle, NULL) == pdTRUE); + } + + /** + * Semaphore.hpp + * + * @brief Function that calls xSemaphoreGive( SemaphoreHandle_t xSemaphore + * ) + * + * @see + * + * Function to release a semaphore. + * + * This must not be used from an ISR. See giveFromISR() for an alternative + * which can be used from an ISR. + * + * @retval true If the semaphore was released. + * @retval false If an error occurred. Semaphores are implemented using + * queues. An error can occur if there is no space on the queue to post a + * message indicating that the semaphore was not first obtained correctly. + * + * Example Usage + * @include Semaphore/give.cpp + */ + inline bool give() const { return (xSemaphoreGive(handle) == pdTRUE); } + + /** + * Semaphore.hpp + * + * @brief Function that calls xSemaphoreGiveFromISR( SemaphoreHandle_t + * xSemaphore, signed BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * Function to release a semaphore. + * + * This macro can be used from an ISR. + * + * @param higherPriorityTaskWoken giveFromISR() will set + * higherPriorityTaskWoken to true if giving the semaphore caused a task to + * unblock, and the unblocked task has a priority higher than the currently + * running task. If giveFromISR() sets this value to true then a context + * switch should be requested before the interrupt is exited. + * @retval true If the semaphore was successfully given. + * @retval false Otherwise. + * + * Example Usage + * @include Semaphore/giveFromISR.cpp + */ + inline bool giveFromISR(bool& higherPriorityTaskWoken) const { + BaseType_t taskWoken = pdFALSE; + bool result = (xSemaphoreGiveFromISR(handle, &taskWoken) == pdTRUE); + if (taskWoken == pdTRUE) { + higherPriorityTaskWoken = true; + } + return result; + } + + /** + * Semaphore.hpp + * + * @brief Function that calls xSemaphoreGiveFromISR( SemaphoreHandle_t + * xSemaphore, signed BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * @overload + */ + inline bool giveFromISR() const { + return (xSemaphoreGiveFromISR(handle, NULL) == pdTRUE); + } + + private: + SemaphoreBase() = default; + + /** + * Semaphore.hpp + * + * @brief Destroy the SemaphoreBase object by calling void + * vSemaphoreDelete( SemaphoreHandle_t xSemaphore ) + * + * @see + * + * @note Do not delete a semaphore that has tasks blocked on it (tasks that + * are in the Blocked state waiting for the semaphore to become available). + */ + ~SemaphoreBase() { vSemaphoreDelete(this->handle); } + + SemaphoreBase(SemaphoreBase&&) noexcept = default; + SemaphoreBase& operator=(SemaphoreBase&&) noexcept = default; + + /** + * @brief Handle used to refer to the semaphore when using the FreeRTOS + * interface. + */ + SemaphoreHandle_t handle = NULL; +}; + +#if (configSUPPORT_DYNAMIC_ALLOCATION == 1) + +/** + * @class BinarySemaphore Semaphore.hpp + * + * @brief Class that encapsulates the functionality of a FreeRTOS binary + * semaphore. + * + * Each binary semaphore require a small amount of RAM that is used to hold the + * semaphore's state. If a binary semaphore is created using + * FreeRTOS::BinarySemaphore then the required RAM is automatically allocated + * from the FreeRTOS heap. If a binary semaphore is created using + * FreeRTOS::StaticBinarySemaphore then the RAM is provided by the application + * writer as part of the class and allows the RAM to be statically allocated at + * compile time. See the Static Vs Dynamic allocation page for more information. + * + * The semaphore is created in the 'empty' state, meaning the semaphore must + * first be given using the give() API function before it can subsequently be + * taken (obtained) using the take() function. + * + * Binary semaphores and mutexes are very similar but have some subtle + * differences: Mutexes include a priority inheritance mechanism, binary + * semaphores do not. This makes binary semaphores the better choice for + * implementing synchronisation (between tasks or between tasks and an + * interrupt), and mutexes the better choice for implementing simple mutual + * exclusion. + * + * A binary semaphore need not be given back once obtained, so task + * synchronisation can be implemented by one task/interrupt continuously + * 'giving' the semaphore while another continuously 'takes' the semaphore. This + * is demonstrated by the sample code on the giveFromISR() documentation page. + * Note the same functionality can often be achieved in a more efficient way + * using a direct to task notification. + * + * The priority of a task that 'takes' a mutex can potentially be raised if + * another task of higher priority attempts to obtain the same mutex. The task + * that owns the mutex 'inherits' the priority of the task attempting to 'take' + * the same mutex. This means the mutex must always be 'given' back - otherwise + * the higher priority task will never be able to obtain the mutex, and the + * lower priority task will never 'disinherit' the priority. An example of a + * mutex being used to implement mutual exclusion is provided on the take() + * documentation page. + */ +class BinarySemaphore : public SemaphoreBase { + public: + /** + * Semaphore.hpp + * + * @brief Construct a new BinarySemaphore object by calling + * SemaphoreHandle_t xSemaphoreCreateBinary( void ) + * + * @see + * + * @warning The user should call isValid() on this object to verify that the + * binary semaphore was created successfully in case the memory required to + * create the queue could not be allocated. + * + * Example Usage + * @include Semaphore/binarySemaphore.cpp + */ + BinarySemaphore() { this->handle = xSemaphoreCreateBinary(); } + ~BinarySemaphore() = default; + + BinarySemaphore(const BinarySemaphore&) = delete; + BinarySemaphore& operator=(const BinarySemaphore&) = delete; + + BinarySemaphore(BinarySemaphore&&) noexcept = default; + BinarySemaphore& operator=(BinarySemaphore&&) noexcept = default; +}; + +/** + * @class CountingSemaphore Semaphore.hpp + * + * @brief Class that encapsulates the functionality of a FreeRTOS counting + * semaphore. + * + * Each counting semaphore require a small amount of RAM that is used to hold + * the semaphore's state. If a counting semaphore is created using + * FreeRTOS::CountingSemaphore then the required RAM is automatically allocated + * from the FreeRTOS heap. If a counting semaphore is created using + * FreeRTOS::StaticCountingSemaphore then the RAM is provided by the application + * writer as part of the class and allows the RAM to be statically allocated at + * compile time. See the Static Vs Dynamic allocation page for more information. + * + * Counting semaphores are typically used for two things: + * 1. Counting Events: + * In this usage scenario an event handler will 'give' a semaphore each time an + * event occurs (incrementing the semaphore count value), and a handler task + * will 'take' a semaphore each time it processes an event (decrementing the + * semaphore count value). The count value is therefore the difference between + * the number of events that have occurred and the number that have been + * processed. In this case it is desirable for the initial count value to be + * zero. Note the same functionality can often be achieved in a more efficient + * way using a direct to task notification. + * + * 2. Resource Management: + * In this usage scenario the count value indicates the number of resources + * available. To obtain control of a resource a task must first obtain a + * semaphore - decrementing the semaphore count value. When the count value + * reaches zero there are no free resources. When a task finishes with the + * resource it 'gives' the semaphore back - incrementing the semaphore count + * value. In this case it is desirable for the initial count value to be equal + * to the maximum count value, indicating that all resources are free. + */ +class CountingSemaphore : public SemaphoreBase { + public: + /** + * Semaphore.hpp + * + * @brief Construct a new CountingSemaphore by calling + * SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, + * UBaseType_t uxInitialCount) + * + * @warning The user should call isValid() on this object to verify that the + * binary semaphore was created successfully in case the memory required to + * create the queue could not be allocated. + * + * @param maxCount The maximum count value that can be reached. When the + * semaphore reaches this value it can no longer be 'given'. + * @param initialCount The count value assigned to the semaphore when + * it is created. + */ + explicit CountingSemaphore(const UBaseType_t maxCount, + const UBaseType_t initialCount = 0) { + this->handle = xSemaphoreCreateCounting(maxCount, initialCount); + } + ~CountingSemaphore() = default; + + CountingSemaphore(const CountingSemaphore&) = delete; + CountingSemaphore& operator=(const CountingSemaphore&) = delete; + + CountingSemaphore(CountingSemaphore&&) noexcept = default; + CountingSemaphore& operator=(CountingSemaphore&&) noexcept = default; +}; + +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + +#if (configSUPPORT_STATIC_ALLOCATION == 1) + +/** + * @class StaticBinarySemaphore Semaphore.hpp + * + * @brief Class that encapsulates the functionality of a FreeRTOS binary + * semaphore. + * + * Each binary semaphore require a small amount of RAM that is used to hold the + * semaphore's state. If a binary semaphore is created using + * FreeRTOS::BinarySemaphore then the required RAM is automatically allocated + * from the FreeRTOS heap. If a binary semaphore is created using + * FreeRTOS::StaticBinarySemaphore then the RAM is provided by the application + * writer as part of the class and allows the RAM to be statically allocated at + * compile time. See the Static Vs Dynamic allocation page for more information. + * + * The semaphore is created in the 'empty' state, meaning the semaphore must + * first be given using the give() API function before it can subsequently be + * taken (obtained) using the take() function. + * + * Binary semaphores and mutexes are very similar but have some subtle + * differences: Mutexes include a priority inheritance mechanism, binary + * semaphores do not. This makes binary semaphores the better choice for + * implementing synchronisation (between tasks or between tasks and an + * interrupt), and mutexes the better choice for implementing simple mutual + * exclusion. + * + * A binary semaphore need not be given back once obtained, so task + * synchronisation can be implemented by one task/interrupt continuously + * 'giving' the semaphore while another continuously 'takes' the semaphore. This + * is demonstrated by the sample code on the giveFromISR() documentation page. + * Note the same functionality can often be achieved in a more efficient way + * using a direct to task notification. + * + * The priority of a task that 'takes' a mutex can potentially be raised if + * another task of higher priority attempts to obtain the same mutex. The task + * that owns the mutex 'inherits' the priority of the task attempting to 'take' + * the same mutex. This means the mutex must always be 'given' back - otherwise + * the higher priority task will never be able to obtain the mutex, and the + * lower priority task will never 'disinherit' the priority. An example of a + * mutex being used to implement mutual exclusion is provided on the take() + * documentation page. + */ +class StaticBinarySemaphore : public SemaphoreBase { + public: + /** + * Semaphore.hpp + * + * @brief Construct a new StaticBinarySemaphore object by calling + * SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t + * *pxSemaphoreBuffer ) + * + * @see + * + * @warning This class contains the storage buffer for the binary semaphore, + * 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 Semaphore/staticBinarySemaphore.cpp + */ + StaticBinarySemaphore() { + this->handle = xSemaphoreCreateBinaryStatic(&staticBinarySemaphore); + } + ~StaticBinarySemaphore() = default; + + StaticBinarySemaphore(const StaticBinarySemaphore&) = delete; + StaticBinarySemaphore& operator=(const StaticBinarySemaphore&) = delete; + + StaticBinarySemaphore(StaticBinarySemaphore&&) noexcept = default; + StaticBinarySemaphore& operator=(StaticBinarySemaphore&&) noexcept = default; + + private: + StaticSemaphore_t staticBinarySemaphore; +}; + +class StaticCountingSemaphore : public SemaphoreBase { + public: + /** + * @brief Construct a new StaticCountingSemaphore object by calling + * SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t + * uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t + * *pxSemaphoreBuffer ) + * + * @see + * + * @warning This class contains the storage buffer for the counting semaphore, + * 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. + * + * @param maxCount The maximum count value that can be reached. When the + * semaphore reaches this value it can no longer be 'given'. + * @param initialCount The count value assigned to the semaphore when it is + * created. + * + * Example Usage + * @include Semaphore/staticCountingSemaphore.cpp + */ + explicit StaticCountingSemaphore(const UBaseType_t maxCount, + const UBaseType_t initialCount = 0) { + this->handle = xSemaphoreCreateCountingStatic(maxCount, initialCount, + &staticCountingSemaphore); + } + ~StaticCountingSemaphore() = default; + + StaticCountingSemaphore(const StaticCountingSemaphore&) = delete; + StaticCountingSemaphore& operator=(const StaticCountingSemaphore&) = delete; + + StaticCountingSemaphore(StaticCountingSemaphore&&) noexcept = default; + StaticCountingSemaphore& operator=(StaticCountingSemaphore&&) noexcept = + default; + + private: + StaticSemaphore_t staticCountingSemaphore; +}; + +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +} // namespace FreeRTOS + +#endif // FREERTOS_SEMAPHORE_HPP \ No newline at end of file diff --git a/source/test/src/FreeRTOSCPP/StreamBuffer.hpp b/source/test/src/FreeRTOSCPP/StreamBuffer.hpp new file mode 100644 index 0000000..c87a734 --- /dev/null +++ b/source/test/src/FreeRTOSCPP/StreamBuffer.hpp @@ -0,0 +1,566 @@ +/* + * 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 + * + * @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 + 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 size_t xStreamBufferSend( + * StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t + * xDataLengthBytes, TickType_t xTicksToWait ) + * + * @see + * + * 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. + * + * Example Usage + * @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 size_t xStreamBufferSendFromISR( + * StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t + * xDataLengthBytes, BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * 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. + * + * Example Usage + * @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 size_t xStreamBufferSendFromISR( + * StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t + * xDataLengthBytes, BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * @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 size_t xStreamBufferReceive( + * StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t + * xBufferLengthBytes, TickType_t xTicksToWait ) + * + * @see + * + * 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. + * + * Example Usage + * @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 size_t xStreamBufferReceiveFromISR( + * StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t + * xBufferLengthBytes, BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * 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. + * + * Example Usage + * @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 size_t xStreamBufferReceiveFromISR( + * StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t + * xBufferLengthBytes, BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * @overload + */ + inline size_t receiveFromISR(void* buffer, const size_t bufferLength) const { + return xStreamBufferReceiveFromISR(handle, buffer, bufferLength, NULL); + } + + /** + * StreamBuffer.hpp + * + * @brief Function that calls size_t xStreamBufferBytesAvailable( + * StreamBufferHandle_t xStreamBuffer ) + * + * @see + * + * 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 size_t xStreamBufferSpacesAvailable( + * StreamBufferHandle_t xStreamBuffer ) + * + * @see + * + * 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 BaseType_t xStreamBufferSetTriggerLevel( + * StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel ) + * + * @see + * + * 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 BaseType_t xStreamBufferReset( + * StreamBufferHandle_t xStreamBuffer ) + * + * @see + * + * 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 BaseType_t xStreamBufferIsEmpty( + * StreamBufferHandle_t xStreamBuffer ) + * + * @see + * + * 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 BaseType_t xStreamBufferIsFull( + * StreamBufferHandle_t xStreamBuffer ) + * + * @see + * + * 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 + * void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) + * + * @see + * + * 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 + * + * @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 + * StreamBufferHandle_t xStreamBufferCreate( size_t xBufferSizeBytes, + * size_t xTriggerLevelBytes ) + * + * @see + * + * @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. + * + * Example Usage + * @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 + * + * @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 +class StaticStreamBuffer : public StreamBufferBase { + public: + /** + * StreamBuffer.hpp + * + * @brief Construct a new StaticStreamBuffer object by calling + * StreamBufferHandle_t xStreamBufferCreateStatic( size_t + * xBufferSizeBytes, size_t xTriggerLevelBytes, uint8_t + * *pucStreamBufferStorageArea, StaticStreamBuffer_t *pxStaticStreamBuffer + * ) + * + * @see + * + * @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. + * + * Example Usage + * @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 \ No newline at end of file diff --git a/source/test/src/FreeRTOSCPP/Task.hpp b/source/test/src/FreeRTOSCPP/Task.hpp new file mode 100644 index 0000000..7da36ab --- /dev/null +++ b/source/test/src/FreeRTOSCPP/Task.hpp @@ -0,0 +1,1480 @@ +/* + * 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_TASK_HPP +#define FREERTOS_TASK_HPP + +#include +#include + +#include "FreeRTOS.h" +#include "task.h" +#include "../FreeRTOSCPP/Kernel.hpp" + +/** + * @brief C function that is used to interface this class with the FreeRTOS + * kernel. + * + * @note This function should not be called or referenced by the user. + * + * @param task Pointer to an instance of FreeRTOS::TaskBase. + */ +void callTaskFunction(void* task); + +namespace FreeRTOS { + +/** + * @class TaskBase Task.hpp + * + * @brief Base class that provides the standard task interface to FreeRTOS::Task + * and FreeRTOS::StaticTask. + * + * @note This class is not intended to be instantiated or derived from by the + * user. Use FreeRTOS::Task or FreeRTOS::StaticTask as a base class for a user + * implemented task. + */ +class TaskBase { + public: + friend class Task; + template + friend class StaticTask; + + TaskBase(const TaskBase&) = delete; + TaskBase& operator=(const TaskBase&) = 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; + + enum class State { + Running = eRunning, + Ready = eReady, + Blocked = eBlocked, + Suspended = eSuspended, + Deleted = eDeleted, + Invalid = eInvalid, + }; + + enum class NotifyAction { + NoAction = eNoAction, + SetBits = eSetBits, + Increment = eIncrement, + SetValueWithOverwrite = eSetValueWithOverwrite, + SetValueWithoutOverwrite = eSetValueWithoutOverwrite, + }; + + // NOLINTNEXTLINE + using NotificationBits = std::bitset<32>; + + /** + * Task.hpp + * + * @brief Function that acts as the entry point of the task instance. This + * function initializes the previous wake time of the task and calls the user + * implemented taskFunction(). + * + * @note This function is only public so that it can be accessed by the C + * interface function callTaskFunction() and should not be called or + * referenced by the user. + */ + virtual inline void taskEntry() final { + previousWakeTime = FreeRTOS::Kernel::getTickCount(); + taskFunction(); + }; + +#if (INCLUDE_uxTaskPriorityGet == 1) + /** + * Task.hpp + * + * @brief Function that calls UBaseType_t uxTaskPriorityGet( TaskHandle_t + * xTask ) + * + * @see + * + * INCLUDE_uxTaskPriorityGet must be defined as 1 for this function to be + * available. See the RTOS Configuration documentation for more information. + * + * Obtain the priority of any task. + * + * @return UBaseType_t The priority of the task. + * + * Example Usage + * @include Task/getPriority.cpp + */ + inline UBaseType_t getPriority() const { return uxTaskPriorityGet(handle); } +#endif /* INCLUDE_uxTaskPriorityGet */ + +#if (INCLUDE_vTaskPrioritySet == 1) + /** + * Task.hpp + * + * @brief Function that calls void vTaskPrioritySet( TaskHandle_t xTask, + * UBaseType_t uxNewPriority ) + * + * @see + * + * INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * Set the priority of the task. + * + * A context switch will occur before the function returns if the priority + * being set is higher than the currently executing task. + * + * @param newPriority The priority to which the task will be set. + * + * Example Usage + * @include Task/setPriority.cpp + */ + inline void setPriority(const UBaseType_t newPriority) const { + vTaskPrioritySet(handle, newPriority); + } +#endif /* INCLUDE_vTaskPrioritySet */ + +#if (INCLUDE_vTaskSuspend == 1) + /** + * Task.hpp + * + * @brief Function that calls void vTaskSuspend( TaskHandle_t + * xTaskToSuspend ) + * + * @see + * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be + * available. See the RTOS Configuration documentation for more information. + * + * Suspend a task. When suspended a task will never get any microcontroller + * processing time, no matter what its priority. + * + * Calls to suspend() are not accumulative - i.e. calling suspend() twice on + * the same task still only requires one call to resume() to ready the + * suspended task. + * + * Example Usage + * @include Task/suspend.cpp + */ + inline void suspend() const { vTaskSuspend(handle); } + + /** + * Task.hpp + * + * @brief Function that calls void vTaskResume( TaskHandle_t xTaskToResume + * ) + * + * @see + * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be + * available. See the RTOS Configuration documentation for more information. + * + * Resumes a suspended task. + * + * A task that has been suspended by one or more calls to suspend() will be + * made available for running again by a single call to resume(). + * + * Example Usage + * @include Task/resume.cpp + */ + inline void resume() const { vTaskResume(handle); } + +#if (INCLUDE_xTaskResumeFromISR == 1) + /** + * Task.hpp + * + * @brief Function that calls BaseType_t xTaskResumeFromISR( TaskHandle_t + * xTaskToResume ) + * + * @see + * + * INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * An implementation of resume() that can be called from within an ISR. + * + * A task that has been suspended by one or more calls to suspend() will be + * made available for running again by a single call to resumeFromISR(). + * + * resumeFromISR() should not be used to synchronize a task with an interrupt + * if there is a chance that the interrupt could arrive prior to the task + * being suspended - as this can lead to interrupts being missed. Use of a + * semaphore as a synchronisation mechanism would avoid this eventuality. + * + * @retval true If resuming the task should result in a context switch. This + * is used by the ISR to determine if a context switch may be required + * following the ISR. + * @retval false Otherwise. + * + * Example Usage + * @include Task/resumeFromISR.cpp + */ + inline bool resumeFromISR() const { + return (xTaskResumeFromISR(handle) == pdTRUE); + } +#endif /* INCLUDE_xTaskResumeFromISR */ +#endif /* INCLUDE_vTaskSuspend */ + +#if (INCLUDE_xTaskAbortDelay == 1) + /** + * Task.hpp + * + * @brief Function that calls BaseType_t xTaskAbortDelay( TaskHandle_t + * xTask ) + * + * @see + * + * INCLUDE_xTaskAbortDelay must be defined as 1 in FreeRTOSConfig.h for this + * function to be available. + * + * A task will enter the Blocked state when it is waiting for an event. The + * event it is waiting for can be a temporal event (waiting for a time), such + * as when delay() is called, or an event on an object, such as when + * FreeRTOS::Queue::receive() or notifyTake() is called. If the handle of a + * task that is in the Blocked state is used in a call to abortDelay() then + * the task will leave the Blocked state, and return from whichever function + * call placed the task into the Blocked state. + * + * There is no 'FromISR' version of this function as an interrupt would need + * to know which object a task was blocked on in order to know which actions + * to take. For example, if the task was blocked on a queue the interrupt + * handler would then need to know if the queue was locked. + * + * @retval true Otherwise. + * @retval false If the task was not in the Blocked state. + */ + inline bool abortDelay() const { return (xTaskAbortDelay(handle) == pdPASS); } +#endif /* INCLUDE_xTaskAbortDelay */ + +#if (INCLUDE_xTaskGetIdleTaskHandle == 1) + /** + * Task.hpp + * + * @brief Function that calls TaskHandle_t xTaskGetIdleTaskHandle( void + * ) + * + * @see + * + * getIdleTaskHandle() is only available if INCLUDE_xTaskGetIdleTaskHandle is + * set to 1 in FreeRTOSConfig.h. + * + * Simply returns the handle of the idle task. It is not valid to call + * getIdleTaskHandle() before the scheduler has been started. + * + * @return TaskHandle_t The task handle associated with the Idle task. The + * Idle task is created automatically when the RTOS scheduler is started. + */ + inline static TaskHandle_t getIdleHandle() { + return xTaskGetIdleTaskHandle(); + } +#endif /* INCLUDE_xTaskGetIdleTaskHandle */ + +#if (INCLUDE_uxTaskGetStackHighWaterMark == 1) + /** + * Task.hpp + * + * @brief Function that calls UBaseType_t uxTaskGetStackHighWaterMark( + * TaskHandle_t xTask ) + * + * @see + * + * INCLUDE_uxTaskGetStackHighWaterMark must be set to 1 in FreeRTOSConfig.h + * for this function to be available. + * + * Returns the high water mark of the stack. That is, the minimum free stack + * space there has been (in words, so on a 32 bit machine a value of 1 means 4 + * bytes) since the task started. The smaller the returned number the closer + * the task has come to overflowing its stack. + * + * getStackHighWaterMark() and getStackHighWaterMark2() are the same except + * for their return type. Using configSTACK_DEPTH_TYPE allows the user to + * determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. + * + * @return BaseType_t The smallest amount of free stack space there has been + * (in words, so actual spaces on the stack rather than bytes) since the task + * was created. + */ + inline UBaseType_t getStackHighWaterMark() const { + return uxTaskGetStackHighWaterMark(handle); + } + + /** + * Task.hpp + * + * @brief Function that calls UBaseType_t uxTaskGetStackHighWaterMark2( + * TaskHandle_t xTask ) + * + * @see + * + * Returns the high water mark of the stack. That is, the minimum free stack + * space there has been (in words, so on a 32 bit machine a value of 1 means 4 + * bytes) since the task started. The smaller the returned number the closer + * the task has come to overflowing its stack. + * + * getStackHighWaterMark() and getStackHighWaterMark2() are the same except + * for their return type. Using configSTACK_DEPTH_TYPE allows the user to + * determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. + * + * @return configSTACK_DEPTH_TYPE The smallest amount of free stack space + * there has been (in words, so actual spaces on the stack rather than bytes) + * since the task was created. + */ + inline configSTACK_DEPTH_TYPE getStackHighWaterMark2() const { + return uxTaskGetStackHighWaterMark2(handle); + } +#endif /* INCLUDE_uxTaskGetStackHighWaterMark */ + +#if (INCLUDE_eTaskGetState == 1) + /** + * Task.hpp + * + * @brief Function that calls UBaseType_t uxTaskGetStackHighWaterMark2( + * TaskHandle_t xTask ) + * + * @see + * + * @see getInfo() + * + * INCLUDE_eTaskGetState must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * Obtain the state of any task. States are encoded by the + * FreeRTOS::Task::State enumerated class type. + * + * @return State The state of the task at the time the function was called. + * Note the state of the task might change between the function being called, + * and the functions return value being tested by the calling task. + */ + inline State getState() const { + return static_cast(eTaskGetState(handle)); + } +#endif /* INCLUDE_eTaskGetState */ + + /** + * Task.hpp + * + * @brief Function that calls char *pcTaskGetName( TaskHandle_t + * xTaskToQuery ) + * + * @see + * + * Looks up the name of a task. + * + * @return const char* The text (human readable) name of the task. A pointer + * to the subject task's name, which is a standard NULL terminated C string. + */ + inline const char* getName() const { return pcTaskGetName(handle); } + +#if (INCLUDE_xTaskGetHandle == 1) + /** + * Task.hpp + * + * @brief Function that calls TaskHandle_t xTaskGetHandle( const char + * *pcNameToQuery ) + * + * @see + * + * @note This function takes a relatively long time to complete and should be + * used sparingly. + * + * @param name The text name (as a standard C NULL terminated string) of the + * task for which the handle will be returned. + * @return TaskHandle_t The handle of the task that has the human readable + * name. NULL is returned if no matching name is found. + * INCLUDE_xTaskGetHandle must be set to 1 in FreeRTOSConfig.h for getHandle() + * to be available. + */ + inline static TaskHandle_t getHandle(const char* name) { + return xTaskGetHandle(name); + } +#endif /* INCLUDE_xTaskGetHandle */ + +#if (configUSE_TASK_NOTIFICATIONS == 1) + /** + * Task.hpp + * + * @brief Function that calls BaseType_t xTaskNotifyGiveIndexed( + * TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify ) + * + * @see + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Each task has a private array of "notification values" (or + * 'notifications'), each of which is a 32-bit unsigned integer (uint32_t). + * The constant configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of + * indexes in the array, and (for backward compatibility) defaults to 1 if + * left undefined. Prior to FreeRTOS V10.4.0 there was only one notification + * value per task. + * + * Events can be sent to a task using an intermediary object. Examples of + * such objects are queues, semaphores, mutexes and event groups. Task + * notifications are a method of sending an event directly to a task without + * the need for such an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used + * as light weight and fast binary or counting semaphores. + * + * notifyGive() indicies is are intended for use when task notifications are + * used as light weight and faster binary or counting semaphore equivalents. + * Actual FreeRTOS semaphores are given using the FreeRTOS::Semaphore API, the + * equivalent action that instead uses a task notification is notifyGive(). + * + * When task notifications are being used as a binary or counting semaphore + * equivalent then the task being notified should wait for the notification + * using the notificationTake() API function rather than the + * notifyWaitIndexed() API function. + * + * @note Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not + * be unblocked by a notification sent to any other array index. + * + * @param index The index within the target task's array of notification + * values to which the notification is to be sent. index must be less than + * configTASK_NOTIFICATION_ARRAY_ENTRIES. + * + * Example Usage + * @include Task/notifyGive.cpp + */ + inline void notifyGive(const UBaseType_t index = 0) const { + xTaskNotifyGiveIndexed(handle, index); + } + + /** + * Task.hpp + * + * @brief Function that calls void vTaskNotifyGiveIndexedFromISR( + * TaskHandle_t xTaskHandle, UBaseType_t uxIndexToNotify, BaseType_t + * *pxHigherPriorityTaskWoken ) + * + * @see + * + * Version of notifyGive() that can be used from an interrupt service routine + * (ISR). See the documentation page for the notifyGive() API function for a + * description of their operation and the necessary configuration parameters. + * + * @param higherPriorityTaskWoken A reference that will be set to true if + * sending the notification caused a task to unblock, and the unblocked task + * has a priority higher than the currently running task. If + * higherPriorityTaskWoken is set to true, then a context switch should be + * requested before the interrupt is exited. + * @param index The index within the target task's array of notification + * values to which the notification is to be sent. index must be less than + * configTASK_NOTIFICATION_ARRAY_ENTRIES. + * + * Example Usage + * @include Task/notifyGiveFromISR.cpp + */ + inline void notifyGiveFromISR(bool& higherPriorityTaskWoken, + const UBaseType_t index = 0) const { + BaseType_t taskWoken = pdFALSE; + vTaskNotifyGiveIndexedFromISR(handle, index, &taskWoken); + if (taskWoken == pdTRUE) { + higherPriorityTaskWoken = true; + } + } + + /** + * Task.hpp + * + * @brief Function that calls void vTaskNotifyGiveIndexedFromISR( + * TaskHandle_t xTaskHandle, UBaseType_t uxIndexToNotify, BaseType_t + * *pxHigherPriorityTaskWoken ) + * + * @see + * + * @overload + */ + inline void notifyGiveFromISR(const UBaseType_t index = 0) const { + vTaskNotifyGiveIndexedFromISR(handle, index, NULL); + } + + /** + * Task.hpp + * + * @brief Function that calls BaseType_t xTaskNotifyIndexed( TaskHandle_t + * xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction + * eAction ) + * + * @see + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Each task has a private array of "notification values" (or + * 'notifications'), each of which is a 32-bit unsigned integer (uint32_t). + * The constant configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of + * indexes in the array, and (for backward compatibility) defaults to 1 if + * left undefined. Prior to FreeRTOS V10.4.0 there was only one notification + * value per task. + * + * Events can be sent to a task using an intermediary object. Examples of + * such objects are queues, semaphores, mutexes and event groups. Task + * notifications are a method of sending an event directly to a task without + * the need for such an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used + * as light weight and fast binary or counting semaphores. + * + * A task can use notifyWait() or notifyTake() to [optionally] block to wait + * for a notification to be pending. The task does not consume any CPU time + * while it is in the Blocked state. + * + * A notification sent to a task will remain pending until it is cleared by + * the task calling notifyWait() or notifyTake(). If the task was already in + * the Blocked state to wait for a notification when the notification arrives + * then the task will automatically be removed from the Blocked state + * (unblocked) and the notification cleared. + * + * @note Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not + * be unblocked by a notification sent to any other array index. + * + * @param action Specifies how the notification updates the task's + * notification value, if at all. Valid values for action are as follows: + *
NotifyAction Setting Action Performed + *
NoAction The target task receives the event, + * but its notification value is not updated. In this case value is not used. + *
SetBits The notification value of the target + * task will be bitwise ORed with value. For example, if value is set to 0x01, + * then bit 0 will get set within the target task's notification value. + * Likewise if value is 0x04 then bit 2 will get set in the target task's + * notification value. In this way the RTOS task notification mechanism can be + * used as a light weight alternative to an event group.
Increment + * The notification value of the target task will be incremented by one, + * making the call to notify() equivalent to a call to notifyGive(). In this + * case value is not used.
SetValueWithOverwrite The + * notification value of the target task is unconditionally set to value. In + * this way the RTOS task notification mechanism is being used as a light + * weight alternative to FreeRTOS::Queue::Overwrite(). + *
SetValueWithoutOverwrite If the target task does not already + * have a notification pending then its notification value will be set to + * value. If the target task already has a notification pending then its + * notification value is not updated as to do so would overwrite the previous + * value before it was used. In this case the call to notify() fails and false + * is returned. In this way the RTOS task notification mechanism is being + * used as a light weight alternative to FreeRTOS::Queue::send() on a queue of + * length 1. + * @param value Data that can be sent with the notification. How the data is + * used depends on the value of the action parameter. + * @param index The index within the target task's array of notification + * values to which the notification is to be sent. index must be less than + * configTASK_NOTIFICATION_ARRAY_ENTRIES. + * + * @return false If action is set to SetValueWithoutOverwrite and the task's + * notification value cannot be updated because the target task already had a + * notification pending. + * @return true Otherwise. + * + * Example Usage + * @include Task/notify.cpp + */ + inline bool notify(const NotifyAction action, + const NotificationBits value = 0, + const UBaseType_t index = 0) const { + return (xTaskNotifyIndexed(handle, index, value.to_ulong(), + static_cast(action)) == pdPASS); + } + + /** + * Task.hpp + * + * @brief Function that calls BaseType_t xTaskNotifyAndQueryIndexed( + * TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, + * eNotifyAction eAction, uint32_t *pulPreviousNotifyValue ) + * + * @see + * + * notifyAndQuery() performs the same operation as notify() with the addition + * that it also returns the target task's prior notification value (the + * notification value at the time the function is called rather than when the + * function returns). + * + * @param action An enumerated type that can take one of the values documented + * in the table below in order to perform the associated action. + *
NotifyAction Setting Action Performed + *
NoAction The target task receives the event, + * but its notification value is not updated. In this case value is not used. + *
SetBits The notification value of the target + * task will be bitwise ORed with value. For example, if value is set to 0x01, + * then bit 0 will get set within the target task's notification value. + * Likewise if value is 0x04 then bit 2 will get set in the target task's + * notification value. In this way the RTOS task notification mechanism can be + * used as a light weight alternative to an event group.
Increment + * The notification value of the target task will be incremented by one, + * making the call to notify() equivalent to a call to notifyGive(). In this + * case value is not used.
SetValueWithOverwrite The + * notification value of the target task is unconditionally set to value. In + * this way the RTOS task notification mechanism is being used as a light + * weight alternative to FreeRTOS::Queue::Overwrite(). + *
SetValueWithoutOverwrite If the target task does not already + * have a notification pending then its notification value will be set to + * value. If the target task already has a notification pending then its + * notification value is not updated as to do so would overwrite the previous + * value before it was used. In this case the call to notify() fails and false + * is returned. In this way the RTOS task notification mechanism is being + * used as a light weight alternative to FreeRTOS::Queue::send() on a queue of + * length 1. + * @param value Used to update the notification value of the task. See the + * description of the action parameter below. + * @param index The index within the target task's array of notification + * values to which the notification is to be sent. index must be less than + * configTASK_NOTIFICATION_ARRAY_ENTRIES. + * + * @return false If action is set to SetValueWithoutOverwrite and the task's + * notification value cannot be updated because the target task already had a + * notification pending. + * @return true Otherwise. + * + * Example Usage + * @include Task/notifyAndQuery.cpp + */ + inline std::pair notifyAndQuery( + const NotifyAction action, const NotificationBits value = 0, + const UBaseType_t index = 0) const { + uint32_t pulNotificationValue; + bool result = + (xTaskNotifyAndQueryIndexed(handle, index, value.to_ulong(), + static_cast(action), + &pulNotificationValue) == pdPASS); + + return std::make_pair(result, NotificationBits(pulNotificationValue)); + } + + /** + * Task.hpp + * + * @brief Function that calls BaseType_t + xTaskNotifyAndQueryIndexedFromISR( TaskHandle_t xTaskToNotify, + * UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, + uint32_t *pulPreviousNotifyValue, + * BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * notifyAndQueryFromISR() performs the same operation as notifyFromISR() with + the addition that it also returns the + * target task's prior notification value (the notification value at the time + the function is called rather than at + * the time the function returns). + + * @param higherPriorityTaskWoken A reference that will be set to true if + sending the notification caused a task to + * unblock, and the unblocked task has a priority higher than the currently + running task. If higherPriorityTaskWoken + * is set to true, then a context switch should be requested before the + interrupt is exited. + * @param action An enumerated type that can take one of the values documented + in the table below in order to perform + * the associated action. + *
NotifyAction Setting Action Performed + *
NoAction The target task receives the event, + but its notification value is not updated. + * In this case value is not used. + *
SetBits The notification value of the target + task will be bitwise ORed with value. For + * example, if value is set to 0x01, then bit 0 will get set within the target + task's notification value. Likewise if + * value is 0x04 then bit 2 will get set in the target task's notification + value. In this way the RTOS task + * notification mechanism can be used as a light weight alternative to an + event group. + *
Increment The notification value of the target + task will be incremented by one, making + * the call to notify() equivalent to a call to notifyGive(). In this case + value is not used. + *
SetValueWithOverwrite The notification value of the target + task is unconditionally set to value. In + * this way the RTOS task notification mechanism is being used as a light + weight alternative to + * FreeRTOS::Queue::Overwrite(). + *
SetValueWithoutOverwrite If the target task does not already + have a notification pending then its + * notification value will be set to value. If the target task already has a + notification pending then its + * notification value is not updated as to do so would overwrite the previous + value before it was used. In this case + * the call to notify() fails and false is returned. In this way the RTOS + task notification mechanism is being used + * as a light weight alternative to FreeRTOS::Queue::send() on a queue of + length 1. + * @param value Used to update the notification value of the task. See the + description of the action parameter below. + * @param index The index within the target task's array of notification + values to which the notification is to be + * sent. index must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. + * + * @retval false If action is set to SetValueWithoutOverwrite and the task's + notification value cannot be updated + * because the target task already had a notification pending. + * @retval true Otherwise. + * @return NotificationBits The task's notification value before any bits are + modified. + * + * Example Usage + * @include Task/notifyAndQueryFromISR.cpp + */ + inline std::pair notifyAndQueryFromISR( + bool& higherPriorityTaskWoken, const NotifyAction action, + const NotificationBits value = 0, const UBaseType_t index = 0) const { + BaseType_t taskWoken = pdFALSE; + uint32_t pulNotificationValue; + bool result = (xTaskNotifyAndQueryIndexedFromISR( + handle, index, value.to_ulong(), + static_cast(action), + &pulNotificationValue, &taskWoken) == pdPASS); + + if (taskWoken == pdTRUE) { + higherPriorityTaskWoken = true; + } + + return std::make_pair(result, NotificationBits(pulNotificationValue)); + } + + /** + * Task.hpp + * + * @brief Function that calls BaseType_t + xTaskNotifyAndQueryIndexedFromISR( TaskHandle_t xTaskToNotify, + * UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, + uint32_t *pulPreviousNotifyValue, + * BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * @overload + */ + inline std::pair notifyAndQueryFromISR( + const NotifyAction action, const NotificationBits value = 0, + const UBaseType_t index = 0) const { + uint32_t pulNotificationValue; + bool result = (xTaskNotifyAndQueryIndexedFromISR( + handle, index, value.to_ulong(), + static_cast(action), + &pulNotificationValue, NULL) == pdPASS); + + return std::make_pair(result, NotificationBits(pulNotificationValue)); + } + + /** + * Task.hpp + * + * @brief Function that calls BaseType_t xTaskNotifyIndexedFromISR( + * TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, + * eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * Version of notify() that can be used from an interrupt service routine + * (ISR). See the documentation page for the notify() API function for a + * description of their operation and the necessary configuration parameters, + * as well as backward compatibility information. + * + * @param higherPriorityTaskWoken A reference that will be set to true if + * sending the notification caused a task to unblock, and the unblocked task + * has a priority higher than the currently running task. If + * higherPriorityTaskWoken is set to true, then a context switch should be + * requested before the interrupt is exited. + * @param action An enumerated type that can take one of the values documented + * in the table below in order to perform the associated action. + *
NotifyAction Setting Action Performed + *
NoAction The target task receives the event, + * but its notification value is not updated. In this case value is not used. + *
SetBits The notification value of the target + * task will be bitwise ORed with value. For example, if value is set to 0x01, + * then bit 0 will get set within the target task's notification value. + * Likewise if value is 0x04 then bit 2 will get set in the target task's + * notification value. In this way the RTOS task notification mechanism can be + * used as a light weight alternative to an event group.
Increment + * The notification value of the target task will be incremented by one, + * making the call to notify() equivalent to a call to notifyGive(). In this + * case value is not used.
SetValueWithOverwrite The + * notification value of the target task is unconditionally set to value. In + * this way the RTOS task notification mechanism is being used as a light + * weight alternative to FreeRTOS::Queue::Overwrite(). + *
SetValueWithoutOverwrite If the target task does not already + * have a notification pending then its notification value will be set to + * value. If the target task already has a notification pending then its + * notification value is not updated as to do so would overwrite the previous + * value before it was used. In this case the call to notify() fails and false + * is returned. In this way the RTOS task notification mechanism is being + * used as a light weight alternative to FreeRTOS::Queue::send() on a queue of + * length 1. + * @param value Used to update the notification value of the task. See the + * description of the action parameter below. + * @param index The index within the target task's array of notification + * values to which the notification is to be sent. index must be less than + * configTASK_NOTIFICATION_ARRAY_ENTRIES. + * + * @retval false If action is set to SetValueWithoutOverwrite and the task's + * notification value cannot be updated because the target task already had a + * notification pending. + * @retval true Otherwise. + * + * Example Usage + * This example demonstrates how to use notifyFromISR() with the SetBits + * action. See the notify() API documentation page for examples showing how to + * use the NoAction, SetValueWithOverwrite and SetValueWithoutOverwrite + * actions. + * @include Task/notifyFromISR.cpp + */ + inline bool notifyFromISR(bool& higherPriorityTaskWoken, + const NotifyAction action, + const NotificationBits value = 0, + const UBaseType_t index = 0) const { + BaseType_t taskWoken = pdFALSE; + bool result = (xTaskNotifyIndexedFromISR(handle, index, value.to_ulong(), + static_cast(action), + &taskWoken) == pdPASS); + if (taskWoken == pdTRUE) { + higherPriorityTaskWoken = true; + } + return result; + } + + /** + * Task.hpp + * + * @brief Function that calls BaseType_t xTaskNotifyIndexedFromISR( + * TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, + * eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * @overload + */ + inline bool notifyFromISR(const NotifyAction action, + const NotificationBits value = 0, + const UBaseType_t index = 0) const { + return (xTaskNotifyIndexedFromISR(handle, index, value.to_ulong(), + static_cast(action), + NULL) == pdPASS); + } + + /** + * Task.hpp + * + * @brief Function that calls BaseType_t xTaskNotifyWaitIndexed( + * UBaseType_t uxIndexToWaitOn, uint32_t ulBitsToClearOnEntry, uint32_t + * ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t + * xTicksToWait ) + * + * @see + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Each task has an array of 'task notifications' (or just 'notifications'), + * each of which has a state and a 32-bit value. A direct to task notification + * is an event sent directly to a task that can unblock the receiving task, + * and optionally update one of the receiving task’s notification values in a + * number of different ways. For example, a notification may overwrite one of + * the receiving task’s notification values, or just set one or more bits in + * one of the receiving task’s notification values. + * + * notifyWait() waits, with an optional timeout, for the calling task to + * receive a notification. If the receiving RTOS task was already Blocked + * waiting for a notification when the notification it is waiting for arrives + * the receiving RTOS task will be removed from the Blocked state and the + * notification cleared. + * + * @note Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not + * be unblocked by a notification sent to any other array index. + * + * notifyGive() must not be called from an interrupt service routine. Use + * notifyGiveFromISR() instead. + * + * @param ticksToWait The maximum time to wait in the Blocked state for a + * notification to be received if a notification is not already pending when + * notifyWait() is called. + * + * The RTOS task does not consume any CPU time when it is in the Blocked + * state. + * + * The time is specified in RTOS tick periods. The pdMS_TO_TICKS() macro can + * be used to convert a time specified in milliseconds into a time specified + * in ticks. + * + * @param bitsToClearOnEntry Any bits set in bitsToClearOnEntry will be + * cleared in the calling RTOS task's notification value on entry to the + * notifyWait() function (before the task waits for a new notification) + * provided a notification is not already pending when notifyWait() is called. + * + * For example, if bitsToClearOnEntry is 0x01, then bit 0 of the task's + * notification value will be cleared on entry to the function. + * + * Setting bitsToClearOnEntry to 0xffffffff (ULONG_MAX) will clear all the + * bits in the task's notification value, effectively clearing the value to 0. + * + * @param bitsToClearOnExit Any bits set in bitsToClearOnExit will be cleared + * in the calling RTOS task's notification value before notifyWait() function + * exits if a notification was received. + * + * The bits are cleared after the RTOS task's notification value are returned. + * + * For example, if bitsToClearOnExit is 0x03, then bit 0 and bit 1 of the + * task's notification value will be cleared before the function exits. + * + * Setting bitsToClearOnExit to 0xffffffff (ULONG_MAX) will clear all the bits + * in the task's notification value, effectively clearing the value to 0. + * + * @param index The index within the calling task's array of notification + * values on which the calling task will wait for a notification to be + * received. index must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. + * + * @retval true If a notification was received, or a notification was already + * pending when notifyWait() was called. + * @retval false If the call to notifyWait() timed out before a notification + * was received. + * @return The RTOS task's notification value as it was before any bits were + * cleared due to the bitsToClearOnExit setting. + * + * Example Usage + * @include Task/notifyWait.cpp + */ + inline static std::pair notifyWait( + const TickType_t ticksToWait = portMAX_DELAY, + const NotificationBits bitsToClearOnEntry = 0, + const NotificationBits bitsToClearOnExit = 0, + const UBaseType_t index = 0) { + uint32_t pulNotificationValue; + bool result = + (xTaskNotifyWaitIndexed(index, bitsToClearOnEntry.to_ulong(), + bitsToClearOnExit.to_ulong(), + &pulNotificationValue, ticksToWait) == pdTRUE); + return std::make_pair(result, NotificationBits(pulNotificationValue)); + } + + /** + * Task.hpp + * + * @brief Function that calls BaseType_t xTaskNotifyStateClearIndexed( + * TaskHandle_t xTask, UBaseType_t uxIndexToClear ) + * + * @see + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Each RTOS task has an array of task notifications. Each task notification + * has a notification state that can be either ‘pending’ or ‘not pending’, and + * a 32-bit notification value. + * + * If a notification is sent to an index within the array of notifications + * then the notification at that index is said to be 'pending' until the task + * reads its notification value or explicitly clears the notification state to + * 'not pending' by calling notifyStateClear(). + * + * @param index The index within the target task's array of notification + * values to act upon. For example, setting index to 1 will clear the state of + * the notification at index 1 within the array. + * + * index must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. + * + * notifyStateClear() does not have this parameter and always acts on the + * notification at index 0. + * + * @retval true If the task had a notification pending, and the notification + * was cleared. + * @retval false If the task didn't have a notification pending. + * + * Example Usage + * @include Task/notifyStateClear.cpp + */ + inline bool notifyStateClear(const UBaseType_t index = 0) const { + return (xTaskNotifyStateClearIndexed(handle, index) == pdTRUE); + } + + /** + * Task.hpp + * + * @brief Function that calls uint32_t ulTaskNotifyValueClearIndexed( + * TaskHandle_t xTask, UBaseType_t uxIndexToClear, uint32_t ulBitsToClear + * ) + * + * @see + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Each RTOS task has an array of task notifications. Each task notification + * has a notification state that can be either ‘pending’ or ‘not pending’, and + * a 32-bit notification value. + * + * notifyValueClear() clears the bits specified by the bitsToClear bit mask in + * the notification value at array index index of the task. + * + * @param bitsToClear Bit mask of the bits to clear in the notification value + * of the task. Set a bit to 1 to clear the corresponding bits in the task's + * notification value. Set bitsToClear to 0xffffffff (UINT_MAX on 32-bit + * architectures) to clear the notification value to 0. Set bitsToClear to 0 + * to query the task's notification value without clearing any bits. + * @param index The index within the target task's array of notification + * values in which to clear the bits. index must be less than + * configTASK_NOTIFICATION_ARRAY_ENTRIES. + * @return NotificationBits The value of the target task's notification value + * before the bits specified by bitsToClear were cleared. + * + * Example Usage + * @include Task/notifyValueClear.cpp + */ + inline NotificationBits notifyValueClear( + const NotificationBits bitsToClear = 0, + const UBaseType_t index = 0) const { + return NotificationBits( + ulTaskNotifyValueClearIndexed(handle, index, bitsToClear.to_ulong())); + } +#endif /* configUSE_TASK_NOTIFICATIONS */ + + protected: + /** + * Task.hpp + * + * @brief Abstraction function that acts as the entry point of the task for + * the user. + */ + virtual void taskFunction() = 0; + +#if (INCLUDE_vTaskDelay == 1) + /** + * Task.hpp + * + * @brief Function that calls void vTaskDelay( const TickType_t + * xTicksToDelay ) + * + * @see + * + * INCLUDE_vTaskDelay must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Delay a task for a given number of ticks. The actual time that the task + * remains blocked depends on the tick rate. The constant portTICK_PERIOD_MS + * can be used to calculate real time from the tick rate - with the resolution + * of one tick period. + * + * delay() specifies a time at which the task wishes to unblock relative to + * the time at which delay() is called. For example, specifying a block + * period of 100 ticks will cause the task to unblock 100 ticks after delay() + * is called. delay() does not therefore provide a good method of controlling + * the frequency of a periodic task as the path taken through the code, as + * well as other task and interrupt activity, will affect the frequency at + * which delay() gets called and therefore the time at which the task next + * executes. See delayUntil() for an alternative API function designed to + * facilitate fixed frequency execution. It does this by specifying an + * absolute time (rather than a relative time) at which the calling task + * should unblock. + * + * @param ticksToDelay The amount of time, in tick periods, that the task + * should block. + * + * Example Usage + * @include Task/delay.cpp + */ + inline static void delay(const TickType_t ticksToDelay = 0) { + vTaskDelay(ticksToDelay); + } +#endif /* INCLUDE_vTaskDelay */ + +#if (INCLUDE_xTaskDelayUntil == 1) + /** + * Task.hpp + * + * @brief Function that calls BaseType_t xTaskDelayUntil( TickType_t + * *pxPreviousWakeTime, const TickType_t xTimeIncrement ) + * + * @see + * + * INCLUDE_xTaskDelayUntil must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * Delay a task until a specified time. This function can be used by periodic + * tasks to ensure a constant execution frequency. + * + * This function differs from delay() in one important aspect: delay() will + * cause a task to block for the specified number of ticks from the time delay + * () is called. It is therefore difficult to use delay() by itself to + * generate a fixed execution frequency as the time between a task starting to + * execute and that task calling delay() may not be fixed [the task may take a + * different path though the code between calls, or may get interrupted or + * preempted a different number of times each time it executes]. + * + * Whereas delay() specifies a wake time relative to the time at which the + * function is called, delayUntil() specifies the absolute (exact) time at + * which it wishes to unblock. + * + * The function pdMS_TO_TICKS() can be used to calculate the number of ticks + * from a time specified in milliseconds with a resolution of one tick period. + * + * @param timeIncrement The cycle time period. The task will be unblocked at + * time (previousWakeTime + timeIncrement). Calling delayUntil() with the same + * timeIncrement parameter value will cause the task to execute with a fixed + * interval period. + * @return true If the task way delayed. + * @return false Otherwise. A task will not be delayed if the next expected + * wake time is in the past. + * + * Example Usage + * @include Task/delayUntil.cpp + */ + inline bool delayUntil(const TickType_t timeIncrement = 0) { + return (xTaskDelayUntil(&previousWakeTime, timeIncrement) == pdTRUE); + } +#endif /* INCLUDE_xTaskDelayUntil */ + + /** + * Task.hpp + * + * @brief Function that calls uint32_t ulTaskNotifyTakeIndexed( + * UBaseType_t uxIndexToWaitOn, BaseType_t xClearCountOnExit, TickType_t + * xTicksToWait ) + * + * @see + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Each task has a private array of "notification values" (or + * 'notifications'), each of which is a 32-bit unsigned integer (uint32_t). + * The constant configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of + * indexes in the array, and (for backward compatibility) defaults to 1 if + * left undefined. Prior to FreeRTOS V10.4.0 there was only one notification + * value per task. + * + * Events can be sent to a task using an intermediary object. Examples of + * such objects are queues, semaphores, mutexes and event groups. Task + * notifications are a method of sending an event directly to a task without + * the need for such an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used + * as light weight and fast binary or counting semaphores. + * + * notifyTake() is intended for use when a task notification is used as a + * faster and lighter weight binary or counting semaphore alternative. Actual + * FreeRTOS semaphores are taken using the FreeRTOS::Semaphore::take() API + * function, the equivalent action that instead uses a task notification is + * notifyTake(). + * + * When task notifications are being used as a binary or counting semaphore + * equivalent then the task being notified should wait for the notification + * using the notificationTake() API function rather than the notifyWait() API + * function. + * + * notifyTake() can either clear the task's notification value at the array + * index specified by the indexToWaitOn parameter to zero on exit, in which + * case the notification value acts like a binary semaphore, or decrement the + * notification value on exit, in which case the notification value acts like + * a counting semaphore. + * + * A task can use notifyTake() to [optionally] block to wait for a + * notification. The task does not consume any CPU time while it is in the + * Blocked state. + * + * Where as notifyWait() will return when a notification is pending, + * notifyTake() will return when the task's notification value is not zero. + * + * @note Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not + * be unblocked by a notification sent to any other array index. + * + * @param ticksToWait The maximum time to wait in the Blocked state for a + * notification to be received if a notification is not already pending when + * notifyTake() is called. The RTOS task does not consume any CPU time when + * it is in the Blocked state. The time is specified in RTOS tick periods. + * The pdMS_TO_TICKS() macro can be used to convert a time specified in + * milliseconds into a time specified in ticks. + * + * @param clearCountOnExit If an RTOS task notification is received and + * clearCountOnExit is set to false then the RTOS task's notification value is + * decremented before notifyTake() exits. This is equivalent to the value of a + * counting semaphore being decremented by a successful call to + * FreeRTOS::Semaphore::Take(). If an RTOS task notification is received and + * clearCountOnExit is set to true then the RTOS task's notification value is + * reset to 0 before notifyTake() exits. This is equivalent to the value of a + * binary semaphore being left at zero (or empty, or 'not available') after a + * successful call to FreeRTOS::Semaphore::Take(). + * + * @param index The index within the calling task's array of notification + * values on which the calling task will wait for a notification to be + * non-zero. index must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. + * + * @return NotificationBits The value of the task's notification value before + * it is decremented or cleared (see the description of clearCountOnExit) + * + * Example Usage + * @include Task/notifyTake.cpp + */ + inline static NotificationBits notifyTake( + const TickType_t ticksToWait = portMAX_DELAY, + const bool clearCountOnExit = true, const UBaseType_t index = 0) { + return NotificationBits( + ulTaskNotifyTakeIndexed(index, clearCountOnExit, ticksToWait)); + } + + private: + /** + * @brief Construct a new TaskBase object. This default constructor is + * deliberately private as this class is not intended to be instantiated or + * derived from by the user. Use FreeRTOS::Task or FreeRTOS::StaticTask as a + * base class for creating a task. + */ + TaskBase() = default; + + TaskBase(TaskBase&&) noexcept = default; + TaskBase& operator=(TaskBase&&) noexcept = default; + + /** + * Task.hpp + * + * @brief Destroy the Task object. + * + * @see + * + * If INCLUDE_vTaskDelete is defined as 1 and the task handle is not NULL, + * then the destructor will call void vTaskDelete( TaskHandle_t xTask + * ) See the RTOS Configuration documentation for more information. + * + * Calling void vTaskDelete( TaskHandle_t xTask ) will remove a task + * from the RTOS kernels management. The task being deleted will be removed + * from all ready, blocked, suspended and event lists. + * + * @note The idle task is responsible for freeing the RTOS kernel allocated + * memory from tasks that have been deleted. It is therefore important that + * the idle task is not starved of microcontroller processing time if your + * application makes any calls to void vTaskDelete( TaskHandle_t xTask + * ) Memory allocated by the task code is not automatically freed, and + * should be freed before the task is deleted. + * + * Example Usage + * @include Task/task.cpp + */ + ~TaskBase() { +#if (INCLUDE_vTaskDelete == 1) + + if (handle != NULL) { + vTaskDelete(handle); + } + +#endif /* INCLUDE_vTaskDelete */ + } + + /** + * @brief Handle used to refer to the task when using the FreeRTOS interface. + */ + TaskHandle_t handle = NULL; + + /** + * @brief Variable that holds the time at which the task was last unblocked. + */ + TickType_t previousWakeTime = 0; +}; + +#if (configSUPPORT_DYNAMIC_ALLOCATION == 1) + +/** + * Task Task.hpp + * + * @brief Class that encapsulates the functionality of a FreeRTOS task. + * + * Each task requires RAM that is used to hold the task state, and used by the + * task as its stack. If a task is created using this class then the required + * RAM is automatically allocated from the FreeRTOS heap. If a task is created + * using FreeRTOS::StaticTask() then the RAM is included in the object, so it + * can be statically allocated at compile time. See the Static Vs Dynamic + * allocation page for more information. + * + * @note This class is not intended to be instantiated by the user. The user + * should create a class that derives from this class and implement + * taskFunction(). + */ +class Task : public TaskBase { + public: + Task(const Task&) = delete; + Task& operator=(const Task&) = delete; + + /** + * @brief Function that checks the return value of the call to xTaskCreate in + * the constructor. This function should be called to ensure the task was + * created successfully before starting the scheduler. + * + * @return true If the task was created successfully. + * @return false If the task was not created successfully due to insufficient + * memory. + */ + bool isValid() const { return taskCreatedSuccessfully; } + + protected: + /** + * Task.hpp + * + * @brief Construct a new Task object by calling BaseType_t xTaskCreate( + * TaskFunction_t pvTaskCode, const char * const pcName, + * configSTACK_DEPTH_TYPE usStackDepth, void *pvParameters, BaseType_t + * uxPriority, TaskHandle_t *pxCreatedTask ) + * + * @see + * + * @warning The user should call isValid() on this object to verify that the + * task was created successfully in case the memory required to create the + * task could not be allocated. + * + * @note When calling xTaskCreate the constructor passes the + * this pointer as the task function argument. This pointer is used + * so that the interface function callTaskFunction() can invoke taskEntry() + * for this instance of the class. + * + * @param priority The priority at which the created task will execute. + * Priorities are asserted to be less than configMAX_PRIORITIES. If + * configASSERT is undefined, priorities are silently capped at + * (configMAX_PRIORITIES - 1). + * @param stackDepth The number of words (not bytes!) to allocate for use as + * the task's stack. For example, if the stack is 16-bits wide and stackDepth + * is 100, then 200 bytes will be allocated for use as the task's stack. As + * another example, if the stack is 32-bits wide and stackDepth is 400 then + * 1600 bytes will be allocated for use as the task's stack. The stack depth + * multiplied by the stack width must not exceed the maximum value that can be + * contained in a variable of type size_t. + * @param name A descriptive name for the task. This is mainly used to + * facilitate debugging, but can also be used to obtain a task handle. The + * maximum length of a task's name is defined by configMAX_TASK_NAME_LEN in + * FreeRTOSConfig.h. + * + * Example Usage + * @include Task/task.cpp + */ + explicit Task( + const UBaseType_t priority = tskIDLE_PRIORITY, + const configSTACK_DEPTH_TYPE stackDepth = configMINIMAL_STACK_SIZE, + const char* name = "") { + taskCreatedSuccessfully = (xTaskCreate(callTaskFunction, name, stackDepth, + this, priority, &handle) == pdPASS); + } + + ~Task() = default; + + Task(Task&&) noexcept = default; + Task& operator=(Task&&) noexcept = default; + + private: + bool taskCreatedSuccessfully = false; +}; + +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + +#if (configSUPPORT_STATIC_ALLOCATION == 1) + +/** + * StaticTask Task.hpp + * + * @brief Class that encapsulates the functionality of a FreeRTOS task. + * + * Each task requires RAM that is used to hold the task state, and used by the + * task as its stack. If a task is created using this class then the RAM is + * included in the object. If a task is created using FreeRTOS::Task() then the + * required RAM is automatically allocated from the FreeRTOS heap. See the + * Static Vs Dynamic allocation page for more information. + * + * @note This class is not intended to be instantiated by the user. The user + * should create a class that derives from this class and implement + * taskFunction(). + * + * @warning This class contains the task's data structures (TCB) and the array + * used to store the task's stack, so any instace of this class or class derived + * from this class must be persistent (not declared on the stack of another + * function). + * + * @tparam N The number of indexes in the array of StackType_t used to + * store the stack for this task. + */ +template +class StaticTask : public TaskBase { + public: + StaticTask(const StaticTask&) = delete; + StaticTask& operator=(const StaticTask&) = delete; + + protected: + /** + * Task.hpp + * + * @brief Construct a new Task object by calling TaskHandle_t + * xTaskCreateStatic( TaskFunction_t pxTaskCode, const char * const pcName, + * const uint32_t ulStackDepth, void * const pvParameters, UBaseType_t + * uxPriority, StackType_t * const puxStackBuffer, StaticTask_t * const + * pxTaskBuffer ) + * + * @see + * + * @note When calling xTaskCreateStatic the constructor passes the + * this pointer as the task function argument. This pointer is used + * so that the interface function callTaskFunction() can invoke taskEntry() + * for this instance of the class. + * + * @param priority The priority at which the created task will execute. + * Priorities are asserted to be less than configMAX_PRIORITIES. If + * configASSERT is undefined, priorities are silently capped at + * (configMAX_PRIORITIES - 1). + * @param name A descriptive name for the task. This is mainly used to + * facilitate debugging, but can also be used to obtain a task handle. The + * maximum length of a task's name is defined by configMAX_TASK_NAME_LEN in + * FreeRTOSConfig.h. + * + * Example Usage + * @include Task/staticTask.cpp + */ + explicit StaticTask(const UBaseType_t priority = tskIDLE_PRIORITY, + const char* name = "") { + handle = xTaskCreateStatic(callTaskFunction, name, N, this, priority, stack, + &taskBuffer); + } + ~StaticTask() = default; + + StaticTask(StaticTask&&) noexcept = default; + StaticTask& operator=(StaticTask&&) noexcept = default; + + private: + StaticTask_t taskBuffer; + StackType_t stack[N]; +}; + +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +} // namespace FreeRTOS + +inline void callTaskFunction(void* task) { + static_cast(task)->taskEntry(); +} + +#endif // FREERTOS_TASK_HPP diff --git a/source/test/src/FreeRTOSCPP/Timer.hpp b/source/test/src/FreeRTOSCPP/Timer.hpp new file mode 100644 index 0000000..6bc67fe --- /dev/null +++ b/source/test/src/FreeRTOSCPP/Timer.hpp @@ -0,0 +1,928 @@ +/* + * 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_TIMER_HPP +#define FREERTOS_TIMER_HPP + +#include "FreeRTOS.h" +#include "timers.h" + +/** + * @brief C function that is used to interface this class with the FreeRTOS + * kernel. + * + * @note This function should not be called or referenced by the user. + * + * @param task Pointer to an instance of FreeRTOS::TimerBase. + */ +void callTimerFunction(TimerHandle_t timer); + +namespace FreeRTOS { + +/** + * @class TimerBase Timer.hpp + * + * @brief Base class that provides the standard task interface to + * FreeRTOS::Timer and FreeRTOS::StaticTimer. + * + * @note This class is not intended to be instantiated or derived from by the + * user. Use FreeRTOS::Timer or FreeRTOS::StaticTimer as a base class for a user + * implemented task. + */ +class TimerBase { + public: + friend class Timer; + friend class StaticTimer; + + TimerBase(const TimerBase&) = delete; + TimerBase& operator=(const TimerBase&) = 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; + + /** + * Timer.hpp + * + * @brief Function that acts as the entry point of the timer instance. + * + * @note This function is only public so that it can be accessed by the C + * interface function callTimerFunction() and should not be called or + * referenced by the user. + */ + virtual inline void timerEntry() final { timerFunction(); } + + /** + * Timer.hpp + * + * @brief Function that checks the value of the timer handle. This function + * should be called to ensure the timer was created successfully. + * + * @return true If the timer was created successfully. + * @return false If the timer was not created successfully due to insufficient + * memory. + */ + inline bool isValid() const { return (handle != NULL); } + + /** + * Timer.hpp + * + * @brief Function that calls BaseType_t xTimerIsTimerActive( + * TimerHandle_t xTimer ) + * + * @see + * + * Queries a software timer to see if it is active or dormant. + * + * A timer will be dormant if: + * -# It has been created but not started, or + * -# It is an expired one-shot timer that has not been restarted. + * + * @note Timers are created in the dormant state. The start(), reset(), + * startFromISR(), resetFromISR(), changePeriod() and changePeriodFromISR() + * API functions can all be used to transition a timer into the active state. + * + * @return false If the timer is dormant. + * @return true Otherwise. + * + * Example Usage + * @include Timer/isActive.cpp + */ + inline bool isActive() const { + return (xTimerIsTimerActive(handle) != pdFALSE); + } + + /** + * Timer.hpp + * + * @brief Function that calls BaseType_t xTimerStart( TimerHandle_t + * xTimer, TickType_t xBlockTime ) + * + * @see + * + * start() starts a timer. If the timer had already been started and was + * already in the active state, then start() has equivalent functionality to + * the reset() API function. + * + * Starting a timer ensures the timer is in the active state. If the timer is + * not stopped, deleted, or reset in the mean time, timerFunction() will get + * called 'n 'ticks after start() was called, where 'n' is the timers defined + * period. + * + * It is valid to call start() before the RTOS scheduler has been started, but + * when this is done the timer will not actually start until the RTOS + * scheduler is started, and the timers expiry time will be relative to when + * the RTOS scheduler is started, not relative to when start() was called. + * + * @param blockTime Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the start command to be + * successfully sent to the timer command queue, should the queue already be + * full when start() was called. blockTime is ignored if start() is called + * before the RTOS scheduler is started. + * @return true If the command was successfully sent to the timer command + * queue. When the command is actually processed will depend on the priority + * of the timer service/daemon task relative to other tasks in the system, + * although the timers expiry time is relative to when start() is actually + * called. The timer service/daemon task priority is set by the + * configTIMER_TASK_PRIORITY configuration constant. + * @return false If the start command could not be sent to the timer command + * queue even after blockTime ticks had passed. + * + * Example Usage + * @include Timer/timer.cpp + */ + inline bool start(const TickType_t blockTime = 0) const { + return (xTimerStart(handle, blockTime) == pdPASS); + } + + /** + * Timer.hpp + * + * @brief Function that calls BaseType_t xTimerStartFromISR( TimerHandle_t + * xTimer, BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * A version of start() that can be called from an interrupt service routine. + * + * @param higherPriorityTaskWoken The timer service/daemon task spends most of + * its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling startFromISR() writes a message to the timer command + * queue, so has the potential to transition the timer service/daemon task out + * of the Blocked state. If calling startFromISR() causes the timer + * service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then higherPriorityTaskWoken will get + * set to true internally within the startFromISR() function. If + * startFromISR() sets this value to true, then a context switch should be + * performed before the interrupt exits. + * @return true If the command was successfully sent to the timer command + * queue. When the command is actually processed will depend on the priority + * of the timer service/daemon task relative to other tasks in the system, + * although the timers expiry time is relative to when startFromISR() is + * actually called. The timer service/daemon task priority is set by the + * configTIMER_TASK_PRIORITY configuration constant. + * @return false If the start command could not be sent to the timer command + * queue. + * + * Example Usage + * @include Timer/startFromISR.cpp + */ + inline bool startFromISR(bool& higherPriorityTaskWoken) const { + BaseType_t taskWoken = pdFALSE; + bool result = (xTimerStartFromISR(handle, &taskWoken) == pdPASS); + if (taskWoken == pdTRUE) { + higherPriorityTaskWoken = true; + } + return result; + } + + /** + * Timer.hpp + * + * @brief Function that calls BaseType_t xTimerStartFromISR( TimerHandle_t + * xTimer, BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * @overload + */ + inline bool startFromISR() const { + return (xTimerStartFromISR(handle, NULL) == pdPASS); + } + + /** + * Timer.hpp + * + * @brief Function that calls BaseType_t xTimerStop( TimerHandle_t xTimer, + * TickType_t xBlockTime ) + * + * @see + * + * stop() stops a timer that was previously started using either of the + * start(), reset(), startFromISR(), resetFromISR(), changePeriod() and + * changePeriodFromISR() API functions. + * + * Stopping a timer ensures the timer is not in the active state. + * + * @param blockTime Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the stop command to be + * successfully sent to the timer command queue, should the queue already be + * full when stop() was called. blockTime is ignored if stop() is called + * before the RTOS scheduler is started. + * @return true If the command was successfully sent to the timer command + * queue. When the command is actually processed will depend on the priority + * of the timer service/daemon task relative to other tasks in the system. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * @return false If the stop command could not be sent to the timer command + * queue even after blockTime ticks had passed. + * + * Example Usage + * @include Timer/timer.cpp + */ + inline bool stop(const TickType_t blockTime = 0) const { + return (xTimerStop(handle, blockTime) == pdPASS); + } + + /** + * Timer.hpp + * + * @brief Function that calls BaseType_t xTimerStopFromISR( TimerHandle_t + * xTimer, BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * A version of stop() that can be called from an interrupt service routine. + * + * @param higherPriorityTaskWoken The timer service/daemon task spends most of + * its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling stopFromISR() writes a message to the timer command + * queue, so has the potential to transition the timer service/daemon task out + * of the Blocked state. If calling stopFromISR() causes the timer + * service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then higherPriorityTaskWoken will get + * set to true internally within the stopFromISR() function. If stopFromISR() + * sets this value to true, then a context switch should be performed before + * the interrupt exits. + * @return true If the command was successfully sent to the timer command + * queue. When the command is actually processed will depend on the priority + * of the timer service/daemon task relative to other tasks in the system. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * @return false If the start command could not be sent to the timer command + * queue. + * + * Example Usage + * @include Timer/stopFromISR.cpp + */ + inline bool stopFromISR(bool& higherPriorityTaskWoken) const { + BaseType_t taskWoken = pdFALSE; + bool result = (xTimerStopFromISR(handle, &taskWoken) == pdPASS); + if (taskWoken == pdTRUE) { + higherPriorityTaskWoken = true; + } + return result; + } + + /** + * Timer.hpp + * + * @brief Function that calls BaseType_t xTimerStopFromISR( TimerHandle_t + * xTimer, BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * @overload + */ + inline bool stopFromISR() const { + return (xTimerStopFromISR(handle, NULL) == pdPASS); + } + + /** + * Timer.hpp + * + * @brief Function that calls BaseType_t xTimerChangePeriod( TimerHandle_t + * xTimer, TickType_t xNewPeriod, TickType_t xBlockTime ) + * + * @see + * + * changePeriod() changes the period of a timer. + * + * changePeriod() can be called to change the period of an active or dormant + * state timer. Changing the period of a dormant timers will also start the + * timer. + * + * @param newPeriod The new period for timer. Timer periods are specified in + * tick periods, so the constant portTICK_PERIOD_MS can be used to convert a + * time that has been specified in milliseconds. For example, if the timer + * must expire after 100 ticks, then newPeriod should be set to 100. + * Alternatively, if the timer must expire after 500ms, then newPeriod can be + * set to ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less + * than or equal to 1000. + * @param blockTime Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the change period command to be + * successfully sent to the timer command queue, should the queue already be + * full when changePeriod() was called. blockTime is ignored if changePeriod() + * is called before the RTOS scheduler is started. + * @return true If the command was successfully sent to the timer command + * queue. When the command is actually processed will depend on the priority + * of the timer service/daemon task relative to other tasks in the system. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * @return false If the change period command could not be sent to the timer + * command queue even after blockTime ticks had passed. + * + * Example Usage + * @include Timer/changePeriod.cpp + */ + inline bool changePeriod(const TickType_t newPeriod, + const TickType_t blockTime = 0) const { + return (xTimerChangePeriod(handle, newPeriod, blockTime) == pdPASS); + } + + /** + * Timer.hpp + * + * @brief Function that calls BaseType_t xTimerChangePeriodFromISR( + * TimerHandle_t xTimer, TickType_t xNewPeriod, BaseType_t + * *pxHigherPriorityTaskWoken ) + * + * @see + * + * + * A version of changePeriod() that can be called from an interrupt service + * routine. + * + * @param newPeriod The new period for timer. Timer periods are specified in + * tick periods, so the constant portTICK_PERIOD_MS can be used to convert a + * time that has been specified in milliseconds. For example, if the timer + * must expire after 100 ticks, then newPeriod should be set to 100. + * Alternatively, if the timer must expire after 500ms, then newPeriod can be + * set to ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less + * than or equal to 1000. + * @param higherPriorityTaskWoken The timer service/daemon task spends most of + * its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling changePeriodFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/ daemon + * task out of the Blocked state. If calling changePeriodFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer + * service/daemon task has a priority equal to or greater than the currently + * executing task (the task that was interrupted), then + * higherPriorityTaskWoken will get set to true internally within the + * changePeriodFromISR() function. If changePeriodFromISR() sets this value to + * true, then a context switch should be performed before the interrupt exits. + * @return true If the command was successfully sent to the timer command + * queue. When the command is actually processed will depend on the priority + * of the timer service/daemon task relative to other tasks in the system. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * @return false If the change period command could not be sent to the timer + * command queue. + * + * Example Usage + * @include Timer/changePeriodFromISR.cpp + */ + inline bool changePeriodFromISR(bool& higherPriorityTaskWoken, + const TickType_t newPeriod) const { + BaseType_t taskWoken = pdFALSE; + bool result = + (xTimerChangePeriodFromISR(handle, newPeriod, &taskWoken) == pdPASS); + if (taskWoken == pdTRUE) { + higherPriorityTaskWoken = true; + } + return result; + } + + /** + * Timer.hpp + * + * @brief Function that calls BaseType_t xTimerChangePeriodFromISR( + * TimerHandle_t xTimer, TickType_t xNewPeriod, BaseType_t + * *pxHigherPriorityTaskWoken ) + * + * @see + * + * + * @overload + */ + inline bool changePeriodFromISR(const TickType_t newPeriod) const { + return (xTimerChangePeriodFromISR(handle, newPeriod, NULL) == pdPASS); + } + + /** + * Timer.hpp + * + * @brief Function that calls BaseType_t xTimerDelete( TimerHandle_t + * xTimer, TickType_t xBlockTime ) + * + * @see + * + * deleteTimer() deletes a timer from the FreeRTOS timer task. + * + * @note This function is also called in the destructor of the timer using the + * deleteBlockTime specified when the object was created. This function + * should be used when the user wants to delete the timer from the FreeRTOS + * timer task without destroying the timer object or with a different block + * time than what was specified in the constructor. + * + * @param blockTime Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the delete command to be + * successfully sent to the timer command queue, should the queue already be + * full when deleteTimer() was called. blockTime is ignored if deleteTimer() + * is called before the RTOS scheduler is started. + * @return true If the command was successfully sent to the timer command + * queue. When the command is actually processed will depend on the priority + * of the timer service/daemon task relative to other tasks in the system. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * @return false If the delete command could not be sent to the timer command + * queue even after blockTime ticks had passed. + * + * Example Usage + * @include Timer/changePeriod.cpp + */ + inline bool deleteTimer(const TickType_t blockTime = 0) { + if (xTimerDelete(handle, blockTime) == pdPASS) { + handle = NULL; + return true; + } + return false; + } + + /** + * Timer.hpp + * + * @brief Function that calls BaseType_t xTimerReset( TimerHandle_t + * xTimer, TickType_t xBlockTime ) + * + * @see + * + * reset() re-starts a timer. If the timer had already been started and was + * already in the active state, then reset() will cause the timer to + * re-evaluate its expiry time so that it is relative to when reset() was + * called. If the timer was in the dormant state then reset() has equivalent + * functionality to the start() API function. + * + * Resetting a timer ensures the timer is in the active state. If the timer + * is not stopped, deleted, or reset in the mean time, the callback function + * associated with the timer will get called 'n' ticks after reset() was + * called, where 'n' is the timers defined period. + * + * It is valid to call reset() before the RTOS scheduler has been started, but + * when this is done the timer will not actually start until the RTOS + * scheduler is started, and the timers expiry time will be relative to when + * the RTOS scheduler is started, not relative to when reset() was called. + * + * @param blockTime Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the reset command to be + * successfully sent to the timer command queue, should the queue already be + * full when reset() was called. blockTime is ignored if reset() is called + * before the RTOS scheduler is started. + * @return true If the command was successfully sent to the timer command + * queue. When the command is actually processed will depend on the priority + * of the timer service/daemon task relative to other tasks in the system, + * although the timers expiry time is relative to when reset() is actually + * called. The timer service/daemon task priority is set by the + * configTIMER_TASK_PRIORITY configuration constant. + * @return false If the reset command could not be sent to the timer command + * queue even after blockTime ticks had passed. + * + * Example Usage + * @include Timer/reset.cpp + */ + inline bool reset(const TickType_t blockTime = 0) const { + return (xTimerReset(handle, blockTime) == pdPASS); + } + + /** + * Timer.hpp + * + * @brief Function that calls BaseType_t xTimerResetFromISR( TimerHandle_t + * xTimer, BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * A version of reset() that can be called from an interrupt service routine. + * + * @param higherPriorityTaskWoken The timer service/daemon task spends most of + * its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling resetFromISR() writes a message to the timer command + * queue, so has the potential to transition the timer service/daemon task out + * of the Blocked state. If calling resetFromISR() causes the timer + * service/daemon task to leave the Blocked state, and the timer + * service/daemon task has a priority equal to or greater than the currently + * executing task (the task that was interrupted), then + * higherPriorityTaskWoken will get set to true internally within the + * resetFromISR() function. If resetFromISR() sets this value to true, then a + * context switch should be performed before the interrupt exits. + * @return true If the command was successfully sent to the timer command + * queue. When the command is actually processed will depend on the priority + * of the timer service/daemon task relative to other tasks in the system, + * although the timers expiry time is relative to when resetFromISR() is + * actually called. The timer service/daemon task priority is set by the + * configTIMER_TASK_PRIORITY configuration constant. + * @return false If the change period command could not be sent to the timer + * command queue. + * + * Example Usage + * @include Timer/resetFromISR.cpp + */ + inline bool resetFromISR(bool& higherPriorityTaskWoken) const { + BaseType_t taskWoken = pdFALSE; + bool result = (xTimerResetFromISR(handle, &taskWoken) == pdPASS); + if (taskWoken == pdTRUE) { + higherPriorityTaskWoken = true; + } + return result; + } + + /** + * Timer.hpp + * + * @brief Function that calls BaseType_t xTimerResetFromISR( TimerHandle_t + * xTimer, BaseType_t *pxHigherPriorityTaskWoken ) + * + * @see + * + * @overload + */ + inline bool resetFromISR() const { + return (xTimerResetFromISR(handle, NULL) == pdPASS); + } + + /** + * Timer.hpp + * + * @brief Function that calls void vTimerSetReloadMode( TimerHandle_t + * xTimer, const UBaseType_t uxAutoReload ) + * + * @see + * + * Updates the 'mode' of a software timer to be either an auto reload timer or + * a one-shot timer. + * + * An auto reload timer resets itself each time it expires, causing the timer + * to expire (and therefore execute its callback) periodically. + * + * A one shot timer does not automatically reset itself, so will only expire + * (and therefore execute its callback) once unless it is manually restarted. + * + * @param autoReload Set autoReload to true to set the timer into auto reload + * mode, or false to set the timer into one shot mode. + */ + inline void setReloadMode(const bool autoReload) const { + vTimerSetReloadMode(handle, (autoReload ? pdTRUE : pdFALSE)); + } + + /** + * Timer.hpp + * + * @brief Function that calls const char * pcTimerGetName( TimerHandle_t + * xTimer ) + * + * @see + * + * Returns the human readable text name of a software timer. + * + * Text names are assigned to timers in the constructor. + * + * @return const char* A pointer to the text name of the timer as a standard + * NULL terminated C string. + * + * Example Usage + * @include Timer/getName.cpp + */ + inline const char* getName() const { return pcTimerGetName(handle); } + + /** + * Timer.hpp + * + * @brief Function that calls TickType_t xTimerGetPeriod( TimerHandle_t + * xTimer ) + * + * @see + * + * Returns the period of a software timer. The period is specified in ticks. + * + * The period of a timer is initially set using the period parameter of the + * constructor. It can then be changed using the changePeriod() and + * changePeriodFromISR() API functions. + * + * @return TickType_t The period of the timer, in ticks. + * + * Example Usage + * @include Timer/getPeriod.cpp + */ + inline TickType_t getPeriod() const { return xTimerGetPeriod(handle); } + + /** + * Timer.hpp + * + * @brief Function that calls TickType_t xTimerGetExpiryTime( + * TimerHandle_t xTimer ) + * + * @see + * + * Returns the time at which the software timer will expire, which is the time + * at which the timer's callback function will execute. + * + * If the value returned by getExpiryTime() is less than the current time then + * the timer will expire after the tick count has overflowed and wrapped back + * to 0. Overflows are handled in the RTOS implementation itself, so a timer's + * callback function will execute at the correct time whether it is before or + * after the tick count overflows. + * + * @return TickType_t If the timer is active, then the time at which the timer + * will next expire is returned (which may be after the current tick count has + * overflowed, see the notes above). If the timer is not active then the + * return value is undefined. + * + * Example Usage + * @include Timer/getExpiryTime.cpp + */ + inline TickType_t getExpiryTime() const { + return xTimerGetExpiryTime(handle); + } + + /** + * Timer.hpp + * + * @brief Function that calls UBaseType_t uxTimerGetReloadMode( + * TimerHandle_t xTimer ) + * + * @see + * + * Queries the 'mode' of the software timer. + * + * The mode can be either an auto-reloaded timer, which automatically resets + * itself each time it expires, or a one-shot timer, which will expire only + * once unless it is manually restarted. + * + * @return true If the timer is an auto-reload timer. + * @return false Otherwise. + */ + inline bool getReloadMode() const { + return (uxTimerGetReloadMode(handle) == pdTRUE); + } + + /** + * Timer.hpp + * + * @brief Set the delete block time. This value is used when the destructor + * calls deleteTimer(). + * + * @param deleteBlockTime Delete block time to be set in ticks. + */ + inline void setDeleteBlockTime(const TickType_t deleteBlockTime = 0) { + this->deleteBlockTime = deleteBlockTime; + } + + /** + * Timer.hpp + * + * @brief Set the delete block time. This value is used when the destructor + * calls deleteTimer(). + * + * @return TickType_t Delete block time in ticks. + */ + inline TickType_t getDeleteBlockTime() const { return deleteBlockTime; } + + protected: + /** + * Timer.hpp + * + * @brief Abstraction function that acts as the entry point of the timer + * callback for the user. + */ + virtual void timerFunction() = 0; + + private: + /** + * Timer.hpp + * + * @brief Construct a new TimerBase object. This default constructor is + * deliberately private as this class is not intended to be instantiated or + * derived from by the user. Use FreeRTOS::Timer or FreeRTOS::StaticTimer as + * a base class for creating a task. + * + * @param deleteBlockTime Set the delete block time. This value is used when + * the destructor calls deleteTimer(). + */ + explicit TimerBase(const TickType_t deleteBlockTime = 0) + : deleteBlockTime(deleteBlockTime) {} + + /** + * Timer.hpp + * + * @brief Destroy the TimerBase object. + * + * @note This destructor will check that the timer is still valid and has not + * already been deleted by deleteTimer() before calling the function. If the + * timer is still valid the destructor will call deleteTimer() and block for + * up to the amount of time specified by deleteBlockTime. + */ + ~TimerBase() { + if (isValid()) { + deleteTimer(getDeleteBlockTime()); + } + } + + TimerBase(TimerBase&&) noexcept = default; + TimerBase& operator=(TimerBase&&) noexcept = default; + + TimerHandle_t handle = NULL; + TickType_t deleteBlockTime; +}; + +#if (configSUPPORT_DYNAMIC_ALLOCATION == 1) + +/** + * Timer Timer.hpp + * + * @brief Class that encapsulates the functionality of a FreeRTOS timer. + * + * Each software timer requires a small amount of RAM that is used to hold the + * timer's state. If a timer is created using this class then this RAM is + * automatically allocated from the FreeRTOS heap. If a software timer is + * created using FreeRTOS::StaticTimer() then the RAM is included in the object + * so it can be statically allocated at compile time. See the Static Vs Dynamic + * allocation page for more information. + * + * @note This class is not intended to be instantiated by the user. The user + * should create a class that derives from this class and implement + * timerFunction(). + */ +class Timer : public TimerBase { + public: + Timer(const Timer&) = delete; + Timer& operator=(const Timer&) = delete; + + protected: + /** + * Timer.hpp + * + * @brief Construct a new Timer object by calling TimerHandle_t + * xTimerCreate( const char * const pcTimerName, const TickType_t + * xTimerPeriod, const UBaseType_t uxAutoReload, void * const pvTimerID, + * TimerCallbackFunction_t pxCallbackFunction ) + * + * @see + * + * @warning The user should call isValid() on this object to verify that the + * timer was created successfully in case the memory required to create the + * timer could not be allocated. + * + * @note Timers are created in the dormant state. The start(), reset(), + * startFromISR(), resetFromISR(), changePeriod() and changePeriodFromISR() + * API functions can all be used to transition a timer into the active state. + * + * @note When calling xTimerCreate the constructor passes the + * this pointer as the pvTimerID argument. This pointer is used so + * that the interface function callTimerFunction() can invoke timerFunction() + * for this instance of the class. + * + * @param period The period of the timer. The period is specified in ticks, + * and the macro pdMS_TO_TICKS() can be used to convert a time specified in + * milliseconds to a time specified in ticks. For example, if the timer must + * expire after 100 ticks, then simply set period to 100. Alternatively, if + * the timer must expire after 500ms, then set period to pdMS_TO_TICKS( 500 ). + * pdMS_TO_TICKS() can only be used if configTICK_RATE_HZ is less than or + * equal to 1000. The timer period must be greater than 0. + * @param autoReload If autoReload is set to true, then the timer will expire + * repeatedly with a frequency set by the period parameter. If autoReload is + * set to false, then the timer will be a one-shot and enter the dormant state + * after it expires. + * @param name A human readable text name that is assigned to the timer. This + * is done purely to assist debugging. The RTOS kernel itself only ever + * references a timer by its handle, and never by its name. + * @param deleteBlockTime Specifies the time, in ticks, that the calling task + * should be held in the Blocked state to wait for the delete command to be + * successfully sent to the timer command queue, should the queue already be + * full when the destructor is called. deleteBlockTime is ignored if the + * destructor is called before the RTOS scheduler is started or if the timer + * has already been deleted by deleteTimer() before the destructor is called. + * This value can be updated by calling setDeleteBlockTime(). + * + * Example Usage + * @include Timer/timer.cpp + */ + explicit Timer(const TickType_t period, const bool autoReload = false, + const char* name = "", const TickType_t deleteBlockTime = 0) + : TimerBase(deleteBlockTime) { + this->handle = xTimerCreate(name, period, (autoReload ? pdTRUE : pdFALSE), + this, callTimerFunction); + } + ~Timer() = default; + + Timer(Timer&&) noexcept = default; + Timer& operator=(Timer&&) noexcept = default; +}; + +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + +#if (configSUPPORT_STATIC_ALLOCATION == 1) + +/** + * Timer Timer.hpp + * + * @brief Class that encapsulates the functionality of a FreeRTOS timer. + * + * Each software timer requires a small amount of RAM that is used to hold the + * timer's state. If a timer is created using FreeRTOS::Timer() then this RAM is + * automatically allocated from the FreeRTOS heap. If a software timer is + * created this class then the RAM is included in the object so it can be + * statically allocated at compile time. See the Static Vs Dynamic allocation + * page for more information. + * + * @note This class is not intended to be instantiated by the user. The user + * should create a class that derives from this class and implement + * timerFunction(). + * + * @warning This class contains the timer data structure, so any instance of + * this class or class derived from this class should be persistent (not + * declared on the stack of another function). + */ +class StaticTimer : public TimerBase { + public: + StaticTimer(const StaticTimer&) = delete; + StaticTimer& operator=(const StaticTimer&) = delete; + + protected: + /** + * Timer.hpp + * + * @brief Construct a new StaticTimer object by calling TimerHandle_t + * xTimerCreateStatic( const char * const pcTimerName, const TickType_t + * xTimerPeriod, const UBaseType_t uxAutoReload, void * const pvTimerID, + * TimerCallbackFunction_t pxCallbackFunction StaticTimer_t *pxTimerBuffer + * ) + * + * @see + * + * @note When calling xTimerCreateStatic the constructor passes the + * this pointer as the pvTimerID argument. This pointer is used so + * that the interface function callTimerFunction() can invoke timerFunction() + * for this instance of the class. + * + * @note Timers are created in the dormant state. The start(), reset(), + * startFromISR(), resetFromISR(), changePeriod() and changePeriodFromISR() + * API functions can all be used to transition a timer into the active state. + * + * @param period The period of the timer. The period is specified in ticks, + * and the macro pdMS_TO_TICKS() can be used to convert a time specified in + * milliseconds to a time specified in ticks. For example, if the timer must + * expire after 100 ticks, then simply set period to 100. Alternatively, if + * the timer must expire after 500ms, then set period to pdMS_TO_TICKS( 500 ). + * pdMS_TO_TICKS() can only be used if configTICK_RATE_HZ is less than or + * equal to 1000. The timer period must be greater than 0. + * @param autoReload If autoReload is set to true, then the timer will expire + * repeatedly with a frequency set by the period parameter. If autoReload is + * set to false, then the timer will be a one-shot and enter the dormant state + * after it expires. + * @param name A human readable text name that is assigned to the timer. This + * is done purely to assist debugging. The RTOS kernel itself only ever + * references a timer by its handle, and never by its name. + * @param deleteBlockTime Specifies the time, in ticks, that the calling task + * should be held in the Blocked state to wait for the delete command to be + * successfully sent to the timer command queue, should the queue already be + * full when the destructor is called. deleteBlockTime is ignored if the + * destructor is called before the RTOS scheduler is started or if the timer + * has already been deleted by deleteTimer() before the destructor is called. + * This value can be updated by calling setDeleteBlockTime(). + * + * Example Usage + * @include Timer/staticTimer.cpp + */ + explicit StaticTimer(const TickType_t period, const bool autoReload = false, + const char* name = "", + const TickType_t deleteBlockTime = 0) + : TimerBase(deleteBlockTime) { + this->handle = + xTimerCreateStatic(name, period, (autoReload ? pdTRUE : pdFALSE), this, + callTimerFunction, &staticTimer); + } + ~StaticTimer() = default; + + StaticTimer(StaticTimer&&) noexcept = default; + StaticTimer& operator=(StaticTimer&&) noexcept = default; + + private: + StaticTimer_t staticTimer; +}; + +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +} // namespace FreeRTOS + +inline void callTimerFunction(TimerHandle_t timer) { + static_cast(pvTimerGetTimerID(timer))->timerEntry(); +} + +#endif // FREERTOS_TIMER_HPP