From e1eb15a20d1752283f634b3cb0fb19743e52befd Mon Sep 17 00:00:00 2001 From: Nicola Fontana Date: Tue, 17 Feb 2026 16:22:07 +0100 Subject: [PATCH] New API xTaskPeriodicDelay (#1349) New function to be used for periodic tasks to ensure a constant execution frequency. It is intended to supersede xTaskDelayUntil to overcome its shortcomings, that is: - avoid run away of pxPreviousWakeTime (#1339) - catch up any skipped period immediately (and update pxPreviousWakeTime accordingly), notify the caller of the number of periods skipped (by returning them) and wait until the next period (it could be less than xTimeIncrement if we are close to the next period) - notify the caller when not enough ticks have been elapsed (by returning 0) and handle the situation gracefully (by properly waiting until the next wake time) Signed-off-by: Nicola Fontana --- include/FreeRTOS.h | 8 +++++++ include/task.h | 37 +++++++++++++++++++++++++++++ tasks.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) diff --git a/include/FreeRTOS.h b/include/FreeRTOS.h index 63e2feb519..6f7f84d1b2 100644 --- a/include/FreeRTOS.h +++ b/include/FreeRTOS.h @@ -1766,6 +1766,14 @@ #define traceRETURN_vTaskDelete() #endif +#ifndef traceENTER_xTaskPeriodicDelay + #define traceENTER_xTaskPeriodicDelay( pxPreviousWakeTime, xTimeIncrement ) +#endif + +#ifndef traceRETURN_xTaskPeriodicDelay + #define traceRETURN_xTaskPeriodicDelay( xIncrements ) +#endif + #ifndef traceENTER_xTaskDelayUntil #define traceENTER_xTaskDelayUntil( pxPreviousWakeTime, xTimeIncrement ) #endif diff --git a/include/task.h b/include/task.h index 72babea35f..ebd20a4832 100644 --- a/include/task.h +++ b/include/task.h @@ -862,6 +862,43 @@ void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; */ void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION; +/** + * task. h + * @code{c} + * TickType_t xTaskPeriodicDelay( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement ); + * @endcode + * + * INCLUDE_xTaskDelayUntil must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Periodic task delay to ensure a constant execution frequency. + * + * This function is similar to xTaskDelayUntil () with a few important differences: + * - pxPreviousWakeTime contains the last past wake time, so it never runs away + * - if you suspend the task, when you resume it pxPreviousWakeTime will instantly + * catch up all skipped increments + * - it returns the number of increments added to pxPreviosWakeTime + * + * @param pxPreviousWakeTime Pointer to a variable that holds the time at which the + * task was last unblocked. The variable must be initialised with the current time + * prior to its first use. Following this the variable is automatically updated. + * + * @param xTimeIncrement The cycle time period. The task will be unblocked at + * time *pxPreviousWakeTime + xTimeIncrement. Passing the same xTimeIncrement + * parameter value will cause the task to execute with a fixed interface period. + * + * @return Number of times xTimeIncrement has been added to pxPreviousWakeTime. + * It is 0 on the first call or if not enough ticks have been elapsed since the + * last call, 1 in normal circumstances or more than 1 if some period has been + * skipped for some reason (e.g. when the caller task is suspended for more than + * xTimeIncrement ticks). + * + * \defgroup xTaskPeriodicDelay xTaskPeriodicDelay + * \ingroup TaskCtrl + */ +TickType_t xTaskPeriodicDelay( TickType_t * const pxPreviousWakeTime, + const TickType_t xTimeIncrement ) PRIVILEGED_FUNCTION; + /** * task. h * @code{c} diff --git a/tasks.c b/tasks.c index c596c475f8..4841bce2a7 100644 --- a/tasks.c +++ b/tasks.c @@ -2374,6 +2374,64 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, #if ( INCLUDE_xTaskDelayUntil == 1 ) + TickType_t xTaskPeriodicDelay( TickType_t * const pxPreviousWakeTime, + const TickType_t xTimeIncrement ) + { + BaseType_t xAlreadyYielded; + TickType_t xIncrements, xTicksIncrements, xTicksToWait; + + traceENTER_xTaskPeriodicDelay( pxPreviousWakeTime, xTimeIncrement ); + + configASSERT( pxPreviousWakeTime ); + configASSERT( xTimeIncrement > 0 ); + + vTaskSuspendAll(); + { + /* This plays well with overflows */ + const TickType_t xTicksElapsed = xTickCount - *pxPreviousWakeTime; + + configASSERT( uxSchedulerSuspended == 1U ); + + /* Number of increments to catch up: it could be 0 if + * not enough ticks have elapsed, 1 in the common case or + * more than 1 if the task has not been resumed in time */ + xIncrements = xTicksElapsed / xTimeIncrement; + xTicksIncrements = xIncrements * xTimeIncrement; + + /* Update to the last wake time */ + *pxPreviousWakeTime += xTicksIncrements; + + /* Ticks to the next wake time */ + xTicksToWait = xTimeIncrement - ( xTicksElapsed - xTicksIncrements ); + + if( xTicksToWait > 0 ) + { + prvAddCurrentTaskToDelayedList( xTicksToWait, pdFALSE ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + xAlreadyYielded = xTaskResumeAll(); + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + * have put ourselves to sleep. */ + if( xAlreadyYielded == pdFALSE ) + { + taskYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_xTaskPeriodicDelay( xIncrements ); + + return xIncrements; + } + + BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) {