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 <ntd@entidi.it>
This commit is contained in:
Nicola Fontana 2026-02-17 16:22:07 +01:00
parent 6cd736cfeb
commit e1eb15a20d
3 changed files with 103 additions and 0 deletions

View file

@ -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

View file

@ -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}

58
tasks.c
View file

@ -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 )
{