#pragma once #include "Thread.h" #include "freertoscompat/Timers.h" #include #include namespace tt { /** * Wrapper class for xTimer functions. * @warning Cannot be called from an ISR context except for ::setPendingCallback() */ class Timer final { public: enum class Type { Once = 0, // Timer triggers once after time has passed Periodic = 1 // Timer triggers repeatedly after time has passed }; typedef std::function Callback; typedef void (*PendingCallback)(void* context, uint32_t arg); private: struct TimerHandleDeleter { void operator()(TimerHandle_t handleToDelete) const { xTimerDelete(handleToDelete, kernel::MAX_TICKS); } }; Callback callback; std::unique_ptr, TimerHandleDeleter> handle; static TimerHandle_t createTimer(Type type, TickType_t ticks, void* timerId, TimerCallbackFunction_t callback) { assert(timerId != nullptr); assert(callback != nullptr); BaseType_t auto_reload = (type == Type::Once) ? pdFALSE : pdTRUE; return xTimerCreate(nullptr, ticks, auto_reload, timerId, callback); } static void onCallback(TimerHandle_t hTimer) { auto* timer = static_cast(pvTimerGetTimerID(hTimer)); if (timer != nullptr) { timer->callback(); } } public: /** * @param[in] type The timer type * @param[in] callback The callback function */ Timer(Type type, TickType_t ticks, Callback callback) : callback(callback), handle(createTimer(type, ticks, this, onCallback)) { assert(xPortInIsrContext() == pdFALSE); assert(handle != nullptr); } ~Timer() { assert(xPortInIsrContext() == pdFALSE); } /** * Start the timer * @return success result */ bool start() const { assert(xPortInIsrContext() == pdFALSE); return xTimerStart(handle.get(), kernel::MAX_TICKS) == pdPASS; } /** Stop the timer * @warning If the timer was just triggered, the callback might still be going on after stop() was called * @return success result */ bool stop() const { assert(xPortInIsrContext() == pdFALSE); return xTimerStop(handle.get(), kernel::MAX_TICKS) == pdPASS; } /** * Set a new interval and reset the timer * @param[in] interval The new timer interval * @return success result */ bool reset(TickType_t interval) const { assert(xPortInIsrContext() == pdFALSE); return xTimerChangePeriod(handle.get(), interval, kernel::MAX_TICKS) == pdPASS && xTimerReset(handle.get(), kernel::MAX_TICKS) == pdPASS; } /** * Reset the timer * @return success result */ bool reset() const { assert(xPortInIsrContext() == pdFALSE); return xTimerReset(handle.get(), kernel::MAX_TICKS) == pdPASS; } /** @return true when the timer is running */ bool isRunning() const { assert(xPortInIsrContext() == pdFALSE); return xTimerIsTimerActive(handle.get()) == pdTRUE; } /** @return the expiry time in ticks */ TickType_t getExpiryTime() const { assert(xPortInIsrContext() == pdFALSE); return xTimerGetExpiryTime(handle.get()); } /** * Calls xTimerPendFunctionCall internally. * @param[in] callback the function to call * @param[in] callbackContext the first function argument * @param[in] callbackArg the second function argument * @param[in] timeout the function timeout (must set to 0 in ISR mode) * @return true on success */ bool setPendingCallback(PendingCallback newCallback, void* callbackContext, uint32_t callbackArg, TickType_t timeout) const { if (xPortInIsrContext() == pdTRUE) { assert(timeout == 0); return xTimerPendFunctionCallFromISR(newCallback, callbackContext, callbackArg, nullptr) == pdPASS; } else { return xTimerPendFunctionCall(newCallback, callbackContext, callbackArg, timeout) == pdPASS; } } /** Set callback priority * @param[in] priority The priority */ void setCallbackPriority(Thread::Priority priority) const { assert(xPortInIsrContext() == pdFALSE); TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle(); assert(task_handle); // Don't call this method before timer task start vTaskPrioritySet(task_handle, static_cast(priority)); } }; } // namespace