Understanding the Timer Module in FreeRTOS

Introduction:In modern embedded systems, timers are one of the core components for task scheduling, event triggering, and resource management. However, hardware timer resources are limited, making it difficult to meet the diverse timing requirements in complex application scenarios.The software timer module in FreeRTOS is a key component in real-time systems. By utilizing virtualization technology, it transforms limited hardware resources into flexible and programmable software timers, achieving high-precision and multifunctional timing management. This module not only supports basic functions such as one-time triggering and automatic reloading but also allows for dynamic adjustment of periods and ensures real-time performance and safety through a layered design (hardware interrupt layer, kernel scheduling layer, application task layer).

The timer is not just a simple timer; it addresses the core issues of resource management and task scheduling real-time performance from a system architecture perspective. This article will delve into the core mechanisms of the FreeRTOS timer module, including timer creation, start, expiration handling, and task scheduling logic, and explore how it achieves efficient and reliable timing functions through queue communication and linked list management. By analyzing key functions such as <span>xTimerCreate</span>, command handling (<span>xTimerGenericCommand</span>), expiration callback (<span>prvProcessExpiredTimer</span>), etc., and examining the interaction processes of automatic reloading and Tick overflow handling, we will reveal the design and implementation details of software timers in real-time systems, providing theoretical basis and practical guidance for developers to optimize timing tasks.

Table of Contents

1. The Role of the Timer Module

  1. Transforming limited hardware timer resources into unlimited programmable software timers

  2. Implementing more timer functions based on requirements

  3. Safely controlling communication between software and hardware (key point)

  • Hardware interrupt layer

  • Kernel scheduling layer

  • Application task layer

2. Specific Implementation of Timer Functions

  1. xTimerCreate() Timer Creation

  • Dynamic and static creation

  • Timer_t structure analysis

  • prvInitialiseNewTimer Initialize Timer

    • Member initialization logic

    • Linked list item (xTimerListItem) initialization

  • Timer service task xTimerCreateTimerTask()

    • Task creation and priority setting

    • Infrastructure initialization (queue and linked list)

  • xTimerGenericCommand() Timer Service Task Control Task

    • Command message structure (DaemonTaskMessage_t)

    • Command sending between task and interrupt context

    • User API parameter parsing (xCommandID, xOptionalValue, etc.)

  • vTimerSetReloadMode() Timer Reload Mode Setting

    • Auto-reload (Auto-Reload) and one-shot mode (One-Shot)

  • prvProcessExpiredTimer Expired Timer Handling

    • Linked list removal and callback execution

    • Re-insertion logic for auto-reload timers

    1. The Role of the Timer Module1. Transforming limited hardware timer resources into unlimited programmable software timersHardware timers on a board are limited, but the demand for hardware timers in software is ubiquitous. Therefore, the timer module is needed to transform this from the software level.2. Implementing more timer functions based on requirementsHardware timers can only provide interrupts in tick units. The software module can provide various triggering methods and adjust multiple trigger lengths under the condition that the minimum unit equals the tick period, achieving various functions. It also provides APIs for various tasks to use easily.3. Safely controlling communication between software and hardware (key point)The FreeRTOS timer module operates at three levels of the operating system to help isolate these three levels, achieving real-time and safety.Hardware interrupt layer:Only processes the SysTick hardware interrupt, updates the global Tick counter, exists as a clock, and does not perform any business judgment.Kernel scheduling layer:Detects Tick count in <span><span>xTaskIncrementTick()</span></span>, triggers task switching. It also handles delayed tasks through the <span>taskSWITCH_DELAYED_LISTS</span> mechanism, thus achieving the transition from hardware interrupt to user layer.Application task layer:Handles specific timers through <span><span>prvTimerTask</span></span> guardian task, safely executing user callbacks in the task context to achieve specific business logic.

    For example, suppose a 10ms cycle timer’s lifecycle:

    1. Hardware layerSysTick triggers an interrupt every 1ms, and the counter <span><span>xTickCount</span></span> increments from 0 to 10
    2. Kernel layerIn <span><span>xTaskIncrementTick()</span></span><span><span>, it finds </span></span><code><span><span>xTickCount%10==0</span></span>, waking up the timer task
    3. Application layer
    • <span><span>prvTimerTask</span></span> transitions from blocked to ready state
    • The scheduler executes task switching
    • The guardian task calls <span><span>prvProcessExpiredTimer()</span></span> to execute user callbacks

    To illustrate the functions of the timer at three levels and some implementation functions:Understanding the Timer Module in FreeRTOSThese functions can be broadly divided into direct application layer APIs that developers call and core functions implemented in the driver layer:Application layer:

    Function Name Function Description Thread Safe Can be Called in ISR
    <span>xTimerCreate()</span> / <span>xTimerCreateStatic()</span> Create timer (dynamic/static memory) ✅ Yes (via queue) ❌ No
    <span>xTimerStart()</span> / <span>xTimerStop()</span> Start/Stop timer ✅ Yes (queue communication) ✅ Yes (with <span>FromISR</span> suffix)
    <span>xTimerReset()</span> Reset timer counter ✅ Yes ✅ Yes (with <span>FromISR</span> suffix)
    <span>xTimerChangePeriod()</span> Dynamically modify timer period ✅ Yes ✅ Yes (with <span>FromISR</span> suffix)
    <span>xTimerPendFunctionCall()</span> Delay function execution in guardian task context ✅ Yes ✅ Yes (with <span>FromISR</span> suffix)
    <span>pvTimerGetTimerID()</span> / <span>vTimerSetTimerID()</span> Get/Set timer ID (user-defined data) 🔄 Needs critical section protection ❌ No
    <span>xTimerGetPeriod()</span> / <span>xTimerGetExpiryTime()</span> Query timer period and next expiry time 🔄 Needs critical section protection ❌ No

    Driver layer: (can only be called by guardian tasks, so it is safe)

    <span>prvTimerTask()</span> Guardian task main loop Handles command queue + timer check dual loop Runs continuously
    <span>prvProcessReceivedCommands()</span> Command processor Parses and executes commands such as START/STOP/DELETE Each time a message is received in the queue
    <span>prvProcessExpiredTimer()</span> Expiration processor Executes user callback + handles auto-reload logic Each time a timer expires
    <span>prvInsertTimerInActiveList()</span> Time management Inserts timer into the correct list (handles tick overflow) Each time started/reset
    <span>prvSwitchTimerLists()</span> Overflow processor Switches <span>pxCurrentTimerList</span> and <span>pxOverflowTimerList</span> on tick overflow Each time tick overflows
    <span>prvSampleTimeNow()</span> Time sampler Gets current tick value and checks for overflow Each time time is checked
    <span>prvCheckForValidListAndQueue()</span> Infrastructure initialization Lazy initialization of doubly linked list and command queue (triggered when the timer is first created) Only during initialization phase

    Next, this article will highlight several important functions for explanation.2. Specific Implementation of Timer Functions1. xTimerCreate() Timer Creation

            TimerHandle_t xTimerCreate( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */                                    const TickType_t xTimerPeriodInTicks,                                    const UBaseType_t uxAutoReload,                                    void * const pvTimerID,                                    TimerCallbackFunction_t pxCallbackFunction )        {            Timer_t * pxNewTimer;            pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of Timer_t is always a pointer to the timer's name. */            if( pxNewTimer != NULL )            {                /* Status is thus far zero as the timer is not created statically                 * and has not been started.  The auto-reload bit may get set in                 * prvInitialiseNewTimer. */                pxNewTimer->ucStatus = 0x00;                prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer );            }            return pxNewTimer;        }

    This function creates a timer and returns its handle. It mallocs memory for a timer structure and initializes the timer status to 0, indicating it has not yet started, and then hands this newly malloced structure to the <span>prvInitialiseNewTimer()</span> initialization function to complete the actual initialization.

    Timer structure tmrs()

        typedef struct tmrTimerControl                  /* The old naming convention is used to prevent breaking kernel aware debuggers. */    {        const char * pcTimerName;                   /*&lt;&lt; Text name.  This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */        ListItem_t xTimerListItem;                  /*&lt;&lt; Standard linked list item as used by all kernel features for event management. */        TickType_t xTimerPeriodInTicks;             /*&lt;&lt; How quickly and often the timer expires. */        void * pvTimerID;                           /*&lt;&lt; An ID to identify the timer.  This allows the timer to be identified when the same callback is used for multiple timers. */        TimerCallbackFunction_t pxCallbackFunction; /*&lt;&lt; The function that will be called when the timer expires. */        #if ( configUSE_TRACE_FACILITY == 1 )            UBaseType_t uxTimerNumber;              /*&lt;&lt; An ID assigned by trace tools such as FreeRTOS+Trace */        #endif        uint8_t ucStatus;                           /*&lt;&lt; Holds bits to say if the timer was statically allocated or not, and if it is active or not. */    } xTIMER;

    This is a control block for a specific software timer, which is also the software timer itself. Below, I list the members of this structure and their functions.

    Member Variable Data Type Function Remarks
    <span>pcTimerName</span> <span>const char*</span> Text name of the timer, used only for debugging and logging Does not affect kernel functionality, helps identify the timer during debugging (e.g., “LED_Timer”).
    <span>xTimerListItem</span> <span>ListItem_t</span> Linked list node used to insert the timer into the management list of the timer service task. Contains <span>xItemValue</span> (expiration timestamp), implements timer sorting and triggering management through the linked list.
    <span>xTimerPeriodInTicks</span> <span>TickType_t</span> Length of the timer period, in system clock ticks. For example, <span>pdMS_TO_TICKS(1000)</span><span> represents a 1-second period.</span>
    <span>pvTimerID</span> <span>void*</span> Timer identifier used to distinguish multiple timers sharing the same callback function. Can store any type of pointer or integer value (e.g., device ID), accessed via <span>pvTimerGetTimerID()</span>.
    <span>pxCallbackFunction</span> <span>TimerCallbackFunction_t</span> Pointer to the callback function called when the timer expires. Function prototype: <span>void (*)(TimerHandle_t xTimer)</span><span>, executed in the context of the timer service task.</span>
    <span>uxTimerNumber</span> <span>UBaseType_t</span> Unique ID assigned by trace tools (e.g., FreeRTOS+Trace) for debugging and performance analysis. Only effective when <span>configUSE_TRACE_FACILITY == 1</span>.
    <span>ucStatus</span> <span>uint8_t</span> Status flag, records how the timer was allocated and its active state. Bit 0: Static allocation flag (1=static allocation); Bit 1: Active state flag (1=running).

    Initializing a timer mainly involves initializing its members.2. prvInitialiseNewTimer Initialize Timer

        static void prvInitialiseNewTimer( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */                                       const TickType_t xTimerPeriodInTicks,                                       const UBaseType_t uxAutoReload,                                       void * const pvTimerID,                                       TimerCallbackFunction_t pxCallbackFunction,                                       Timer_t * pxNewTimer )    {        /* 0 is not a valid value for xTimerPeriodInTicks. */        configASSERT( ( xTimerPeriodInTicks > 0 ) );        if( pxNewTimer != NULL )        {            /* Ensure the infrastructure used by the timer service task has been             * created/initialised. */            prvCheckForValidListAndQueue();            /* Initialise the timer structure members using the function             * parameters. */            pxNewTimer->pcTimerName = pcTimerName;            pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks;            pxNewTimer->pvTimerID = pvTimerID;            pxNewTimer->pxCallbackFunction = pxCallbackFunction;            vListInitialiseItem( &( pxNewTimer->xTimerListItem ) );            if( uxAutoReload != pdFALSE )            {                pxNewTimer->ucStatus |= tmrSTATUS_IS_AUTORELOAD;            }            traceTIMER_CREATE( pxNewTimer );        }    }/*----

    The purpose of this function is to initialize the timer members. It first checks the parameters and handle, then initializes the timer members.

    pxNewTimer->pcTimerName = pcTimerName;pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks;pxNewTimer->pvTimerID = pvTimerID;pxNewTimer->pxCallbackFunction = pxCallbackFunction;

    Assigns the function parameters to the corresponding members of the timer structure, including the timer name, period, ID, and callback function.

    Each tmr contains a linked list item vListInitialiseItem(), this function also initializes this linked list item.

    vListInitialiseItem( &( pxNewTimer->xTimerListItem ) );

    Sets <span>pxItem->pvOwner</span> to point to the containing timer structure, allowing reverse reference from the linked list item to the owning timer. Then sets <span>pxItem->pxNext</span> and <span>pxItem->pxPrevious</span> to point to itself, indicating that this linked list item is currently not in any linked list (independent state). Sets <span>pxItem->xItemValue</span> to 0, this value will later be set to the timer’s expiration time for sorting by time.

    After initialization, the linked list item state is as follows:

    pxNewTimer->xTimerListItem:{    xItemValue = 0,                  // Initially 0, will be set to expiration time later    pxNext = &pxNewTimer->xTimerListItem,  // Points to itself    pxPrevious = &pxNewTimer->xTimerListItem, // Points to itself    pvOwner = pxNewTimer,            // Points to the owning timer structure    pxContainer = NULL               // Not added to any linked list}

    3. Timer Service Task xTimerCreateTimerTask()

     BaseType_t xTimerCreateTimerTask( void )    {        BaseType_t xReturn = pdFAIL;        /* This function is called when the scheduler is started if         * configUSE_TIMERS is set to 1.  Check that the infrastructure used by the         * timer service task has been created/initialised.  If timers have already         * been created then the initialisation will already have been performed. */        prvCheckForValidListAndQueue();        if( xTimerQueue != NULL )        {            #if ( configSUPPORT_STATIC_ALLOCATION == 1 )                {                    StaticTask_t * pxTimerTaskTCBBuffer = NULL;                    StackType_t * pxTimerTaskStackBuffer = NULL;                    uint32_t ulTimerTaskStackSize;                    vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &ulTimerTaskStackSize );                    xTimerTaskHandle = xTaskCreateStatic( prvTimerTask,                                                          configTIMER_SERVICE_TASK_NAME,                                                          ulTimerTaskStackSize,                                                          NULL,                                                          ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,                                                          pxTimerTaskStackBuffer,                                                          pxTimerTaskTCBBuffer );                    if( xTimerTaskHandle != NULL )                    {                        xReturn = pdPASS;                    }                }            #else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */                {                    xReturn = xTaskCreate( prvTimerTask,                                           configTIMER_SERVICE_TASK_NAME,                                           configTIMER_TASK_STACK_DEPTH,                                           NULL,                                           ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,                                           &xTimerTaskHandle );                }            #endif /* configSUPPORT_STATIC_ALLOCATION */        }        else        {            mtCOVERAGE_TEST_MARKER();        }        configASSERT( xReturn );        return xReturn;    }

    <span><span>FreeRTOS has a timer service task</span></span><code><span>TimerTask to control tasks</span>, started when the scheduler is launched, used to:

    • Handle commands to start, stop, reset timers, etc.
    • Check if timers have expired
    • Execute callback functions when timers expire

    <span>xTimerCreateTimerTask</span> is the core function in FreeRTOS for creating the timer service task, responsible for initializing the infrastructure required for the timer service and creating the timer service task. It specifically performs the following steps:

    (1)Call <span><span>prvCheckForValidListAndQueue()</span></span> to ensure the infrastructure required for the timer service (linked list and queue) has been created/initialized

    prvCheckForValidListAndQueue();

    (2) If static creation, obtain static memory through the user-provided <span><span>vApplicationGetTimerTaskMemory</span></span> callback function

    vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &ulTimerTaskStackSize );

    Then use xTaskCreateStatic to create the function, setting the priority to <span>portPRIVILEGE_BIT</span> (to ensure timely response)

    (3) If dynamically created, directly call <span>xTaskCreate</span> and return the handle

    xReturn = xTaskCreate(     prvTimerTask,  // Task function     configTIMER_SERVICE_TASK_NAME,  // Task name     configTIMER_TASK_STACK_DEPTH,  // Stack depthNULL,  // Parameter     ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,  // Priority     &xTimerTaskHandle  // Task handle pointer);

    Most of these parameters are set in advance in the configuration items, summarizing in a table:

    Parameter Source of Assignment Can User Modify
    <span>prvTimerTask</span> FreeRTOS kernel internal implementation ❌ Cannot modify
    Task Name <span>FreeRTOSConfig.h</span> ✅ Customizable name
    Stack Depth <span>FreeRTOSConfig.h</span> ✅ Needs adjustment based on callback function
    Task Parameter Fixed to <span>NULL</span> ❌ Usually not modified
    Task Priority <span>FreeRTOSConfig.h</span> + hardware macros ✅ Needs reasonable priority setting
    Task Handle Pointer FreeRTOS kernel global variable ❌ Cannot directly assign

    (4) Assert check the handle and return.

    4. xTimerGenericCommand() Timer Service Task Control Task

     BaseType_t xTimerGenericCommand( TimerHandle_t xTimer,                                     const BaseType_t xCommandID,                                     const TickType_t xOptionalValue,                                     BaseType_t * const pxHigherPriorityTaskWoken,                                     const TickType_t xTicksToWait )    {        BaseType_t xReturn = pdFAIL;        DaemonTaskMessage_t xMessage;        configASSERT( xTimer );        /* Send a message to the timer service task to perform a particular action         * on a particular timer definition. */        if( xTimerQueue != NULL )        {            /* Send a command to the timer service task to start the xTimer timer. */            xMessage.xMessageID = xCommandID;            xMessage.u.xTimerParameters.xMessageValue = xOptionalValue;            xMessage.u.xTimerParameters.pxTimer = xTimer;            if( xCommandID < tmrFIRST_FROM_ISR_COMMAND )            {                if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )                {                    xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait );                }                else                {                    xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY );                }            }            else            {                xReturn = xQueueSendToBackFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken );            }            traceTIMER_COMMAND_SEND( xTimer, xCommandID, xOptionalValue, xReturn );        }        else        {            mtCOVERAGE_TEST_MARKER();        }        return xReturn;    }

    <span><span>xTimerGenericCommand</span></span> is a core internal function of the FreeRTOS timer module, used to encapsulate commands into a unified message format and send commands to the timer service task

    (such as starting, stopping, resetting timers, etc.). Users do not directly use it, but the APIs they use, such as <span><span>xTimerStart</span></span>, <span><span>xTimerStop</span></span>, etc., call this function to control the <span><span>TimerTask</span></span> to control the timer.

    The prerequisite is:xTimerQueue != NULL The timer system has been initialized.

    (1) Construct the command structure (command message)

    xMessage.xMessageID = xCommandID;xMessage.u.xTimerParameters.xMessageValue = xOptionalValue;xMessage.u.xTimerParameters.pxTimer = xTimer;

    <span><span>DaemonTaskMessage_t</span></span> includes:

    <span><span>xMessageID</span></span>: Command type (such as start, stop).

    <span><span>xMessageValue</span></span>: Optional parameter (such as new period value).

    <span><span>pxTimer</span></span>: Pointer to the target timer.

    (2) Send the command to the timer queue

    Handled in two cases:

    Case 1: Normal task context (non-ISR)

    if( xCommandID < tmrFIRST_FROM_ISR_COMMAND ){//Condition: xCommandID value range 0 to tmrFIRST_FROM_ISR_COMMAND-1if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ) {//Scheduler running   xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait ); }else {   xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY ); }}

    Scheduler running: Use <span><span>xQueueSendToBack</span></span> to send (waiting for <span><span>xTicksToWait</span></span>).

    Scheduler not running: Send immediately (<span><span>tmrNO_DELAY</span></span>), avoiding blocking.

    Case 2: Interrupt context (ISR)

    else{xReturn = xQueueSendToBackFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken );}

    Use <span><span>xQueueSendToBackFromISR</span></span> to send, safe for interrupts.

    <span><span>pxHigherPriorityTaskWoken</span></span> is used to mark whether a context switch is needed.

    (3) Return Value<span><span>pdPASS</span></span> (success) or <span><span>pdFAIL</span></span> (failure).

    As mentioned earlier, xTimerGenericCommand() is the essence of most user APIs, which simply call this function with different parameters:

    API Function

    xTimer (handle)

    xCommandID (command)

    xOptionalValue (optional value)

    pxHigherPriorityTaskWoken (ISR mark)

    xTicksToWait (blocking time)

    <span>xTimerStart</span>

    <span>tmrCOMMAND_START</span>

    <span>xTaskGetTickCount()</span><span>(start time)</span>

    <span>NULL</span>

    <span>xTimerStop</span>

    <span>tmrCOMMAND_STOP</span>

    <span>0</span>

    <span>NULL</span>

    <span>xTimerReset</span>

    <span>tmrCOMMAND_RESET</span>

    <span>xTaskGetTickCount()</span><span>(reset time)</span>

    <span>NULL</span>

    <span>xTimerDelete</span>

    <span>tmrCOMMAND_DELETE</span>

    <span>0</span>

    <span>NULL</span>

    <span>xTimerChangePeriod</span>

    <span>tmrCOMMAND_CHANGE_PERIOD</span>

    <span>xNewPeriod</span>(new period)

    <span>NULL</span>

    <span>xTimerStartFromISR</span>

    <span>tmrCOMMAND_START_FROM_ISR</span>

    <span>xTaskGetTickCountFromISR()</span>

    ✔ (points to <span>BaseType_t</span> variable)

    <span>0</span>(ISR does not block)

    <span>xTimerStopFromISR</span>

    <span>tmrCOMMAND_STOP_FROM_ISR</span>

    <span>0</span>

    ✔ (points to <span>BaseType_t</span> variable)

    <span>0</span>(ISR does not block)

    <span>xTimerResetFromISR</span>

    <span>tmrCOMMAND_RESET_FROM_ISR</span>

    <span>xTaskGetTickCount()</span>

    ✔ (points to <span>BaseType_t</span> variable)

    <span>0</span>(ISR does not block)

    We analyze the working mechanism of these APIs through parameters:(1) xTimer: The handle of the target timer, created by <span><span>xTimerCreate()</span></span>. The created timer will contain linked list nodes, period functions, and many identifiers, being the final object used, encapsulated in the message sent out.(2) xCommandID: The name of the timer task, corresponding to a value, which the system uses to determine whether it is task scheduling or interrupt scheduling. Below are the values corresponding to the names:

    #define tmrCOMMAND_START            0  // Start#define tmrCOMMAND_STOP             1  // Stop#define tmrCOMMAND_CHANGE_PERIOD    2  // Change period#define tmrCOMMAND_DELETE           3  // Delete#define tmrCOMMAND_START_FROM_ISR   4  // Start from ISR

    (3) xOptionalValue:Some commands will have additional parameters, which will be passed in the API, in <span>xTimerGenericCommand</span>, <span>xOptionalValue</span> will be encapsulated into the message structure and sent to the timer service task. After the task parses the command, it will decide how to use <span><span>xOptionalValue</span></span> based on <span>xCommandID</span>. Each task’s parameter requirements are fixed:

    API Function

    Corresponding <span>xCommandID</span>

    <span>xOptionalValue</span> Usage

    Example Value

    <span>xTimerStart</span>

    <span>tmrCOMMAND_START</span>

    Records the system tick value at the time the timer starts (used to calculate expiration time)

    <span>xTaskGetTickCount()</span>

    <span>xTimerReset</span>

    <span>tmrCOMMAND_RESET</span>

    Records the system tick value at the time the timer resets (recalculates expiration time)

    <span>xTaskGetTickCount()</span>

    <span>xTimerStartFromISR</span>

    <span>tmrCOMMAND_START_FROM_ISR</span>

    Records the system tick value at the time of starting in ISR (obtained via <span>xTaskGetTickCountFromISR()</span><span>)</span>

    <span>xTaskGetTickCountFromISR()</span>

    <span>xTimerResetFromISR</span>

    <span>tmrCOMMAND_RESET_FROM_ISR</span>

    Records the system tick value at the time of resetting in ISR (obtained via <span>xTaskGetTickCountFromISR()</span><span>)</span>

    <span>xTaskGetTickCountFromISR()</span>

    <span>xTimerChangePeriod</span>

    <span>tmrCOMMAND_CHANGE_PERIOD</span>

    Passes the new timer period (in ticks) for dynamically adjusting the timer interval

    <span>500 / portTICK_PERIOD_MS</span>

    <span>xTimerStop</span>

    <span>tmrCOMMAND_STOP</span>

    Not used (fixed value <span>0</span>)

    <span>0</span>

    <span>xTimerDelete</span>

    <span>tmrCOMMAND_DELETE</span>

    Not used (fixed value <span>0</span>)

    <span>0</span>

    <span>xTimerStopFromISR</span>

    <span>tmrCOMMAND_STOP_FROM_ISR</span>

    Not used (fixed value <span>0</span>)

    <span>0</span>(ISR does not block)

    (4) pxHigherPriorityTaskWoken blocking handling (generally only interrupt handling has this variable): Points to a flag that marks whether the timer service task (Timer Daemon Task) has transitioned from a blocked state to a ready state due to the current operation (such as <span><span>xTimerStartFromISR</span></span>), and its priority is higher than the interrupted task. If so, a task switch needs to be manually triggered.

    The FreeRTOS timer service task is usually in a blocked state, waiting for messages in the command queue. When an ISR (which prohibits blocking) sends a command (such as starting/stopping a timer) to the queue, it may wake up this task.

    If the awakened task’s priority is higher than the currently interrupted task, and the ISR exits directly, the high-priority task cannot execute immediately (because the ISR returns to the interrupted task by default after exiting).

    The specific judgment method is:

    void vKeyPressISR() {    BaseType_t xHigherPriorityTaskWoken = pdFALSE;    // Start timer in ISR    if (xTimerStartFromISR(xTimer, &xHigherPriorityTaskWoken) != pdPASS) {        // Handle error    }    // Check if task switch is needed    if (xHigherPriorityTaskWoken == pdTRUE) {        portYIELD_FROM_ISR();  // Trigger task switch    }}

    (5) xTicksToWait (blocking time): The time that allows the timer task to block, set by the user through the API, interrupts must be 0, others can be decided according to their needs.<span> </span>

    xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait );

    As shown, used in the sending function, indicating the allowable delay time.5. vTimerSetReloadMode() Timer Reload Mode Setting

        void vTimerSetReloadMode( TimerHandle_t xTimer,                              const UBaseType_t uxAutoReload )    {        Timer_t * pxTimer = xTimer;        configASSERT( xTimer );        taskENTER_CRITICAL();        {            if( uxAutoReload != pdFALSE )            {                pxTimer->ucStatus |= tmrSTATUS_IS_AUTORELOAD;            }            else            {                pxTimer->ucStatus &= ~tmrSTATUS_IS_AUTORELOAD;            }        }        taskEXIT_CRITICAL();    }

    There are two modes for timers: reload/one-shot

    Mode

    Behavior Characteristics

    Typical Application Scenarios

    Auto-Reload Mode(Auto-Reload)

    Timer automatically reloads the period value after expiration, triggering periodically

    Heartbeat detection, periodic data collection, LED blinking

    One-Shot Mode(One-Shot)

    Timer automatically stops after expiration, triggering only once

    Delay operations, timeout control, one-time events

    This function can control whether to reload by passing pdTRUE/pdFALSE (by setting <span>pxTimer->ucStatus |= tmrSTATUS_IS_AUTORELOAD/</span>pxTimer->ucStatus &= ~tmrSTATUS_IS_AUTORELOAD to control).

    6. prvProcessExpiredTimer Expired Timer Handling

        static void prvProcessExpiredTimer( const TickType_t xNextExpireTime,                                        const TickType_t xTimeNow )    {        BaseType_t xResult;        Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */        /* Remove the timer from the list of active timers.  A check has already         * been performed to ensure the list is not empty. */        ( void ) uxListRemove( &( pxTimer->xTimerListItem ) );        traceTIMER_EXPIRED( pxTimer );        /* If the timer is an auto-reload timer then calculate the next         * expiry time and re-insert the timer in the list of active timers. */        if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 )        {            /* The timer is inserted into a list using a time relative to anything             * other than the current time.  It will therefore be inserted into the             * correct list relative to the time this task thinks it is now. */            if( prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xNextExpireTime ) != pdFALSE )            {                /* The timer expired before it was added to the active timer                 * list.  Reload it now.  */                xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY );                configASSERT( xResult );                ( void ) xResult;            }            else            {                mtCOVERAGE_TEST_MARKER();            }        }        else        {            pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE;            mtCOVERAGE_TEST_MARKER();        }        /* Call the timer callback. */        pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );

    The functions mentioned above mainly create timers (tasks) and load them into the queue. Those who have read my previous article on Queues know that these timers’ linked list items will be called according to priority, which is time. This function’s role is to handle the expired timers, including executing callback functions and handling auto-reload (Auto-Reload) logic.

    PrvTimerTask() checks if the timer at the head of the linked list has expired; if it has, it will call this prvProcessExpiredTimer() function to handle the expired timer. The handling is divided into several steps:

    (1) Get the currently expired timer

    Timer_t * const pxTimer = (Timer_t *) listGET_OWNER_OF_HEAD_ENTRY(pxCurrentTimerList);

    Get the expired timer from <span>pxCurrentTimerList</span><span> (the current active timer linked list, sorted in ascending order) from the head.</span>

    (2) Remove the timer from the linked list

    (void) uxListRemove(&(pxTimer->xTimerListItem));traceTIMER_EXPIRED(pxTimer);
    1. After the timer expires, it must first be removed from the linked list to avoid repeated triggering.

    2. <span>uxListRemove</span>: Removes the timer from the active linked list (since it has expired).

    3. <span>traceTIMER_EXPIRED</span>: If tracing is enabled (<span>configUSE_TRACE_FACILITY</span>), logs the timer expiration event.

    (3) Handle auto-reload (Auto-Reload) timers

    if((pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD) != 0)

    As mentioned earlier, the reload timer flag: tmrSTATUS_IS_AUTORELOAD

    • Check if the timer is an auto-reload timer (whether the <span><span>ucStatus</span></span> flag of <span><span>tmrSTATUS_IS_AUTORELOAD</span></span> is set).

    • Auto-reload timer: After expiration, it will automatically restart (similar to the periodic mode of hardware timers).

    (4) If reloading, reinsert

    if (prvInsertTimerInActiveList(    pxTimer,     (xNextExpireTime + pxTimer->xTimerPeriodInTicks), // Next expiration time = current expiration time + period    xTimeNow,     xNextExpireTime) != pdFALSE)

    Calculate the next expiration time:<span>xNextExpireTime + pxTimer->xTimerPeriodInTicks</span>.

    Call <span>prvInsertTimerInActiveList</span> to attempt to reinsert the timer into the active linked list.

    (5) If it expired before insertion, restart the timer now.

    (6) Handle one-shot timers

    else{     pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE;  // Clear the "active" flag     mtCOVERAGE_TEST_MARKER();  }

    If it is a one-shot timer (not auto-reload), clear its <span><span>tmrSTATUS_IS_ACTIVE</span></span> flag, indicating the timer has stopped.

    (7) Execute the callback function pointed to by the timer

    pxTimer->pxCallbackFunction((TimerHandle_t)pxTimer);

    Call the timer’s callback function, executing user-defined logic.

    The parameter of the callback function is the timer handle <span><span>(TimerHandle_t)pxTimer</span></span>.

    The callback function is executed in the context of the timer service task, therefore blocking operations (such as <span><span>vTaskDelay</span></span>) cannot be performed.

    It should be kept as short as possible to avoid affecting the scheduling of other timers.

    This function is quite complex; a diagram summarizes the workflow:Understanding the Timer Module in FreeRTOS3. Summary of the ArticleThis article combines the working principles of the FreeRTOS timer module at three levels (user-system-hardware), explaining the entire process from xTimerCreate() to prvProcessExpiredTimer() from creation to expiration. The timer module abstracts the hardware-dependent timing functions into a task-driven software model, achieving flexible scheduling through message queues and linked list management, while strictly isolating critical paths (such as interrupt contexts) from non-critical paths (user callbacks) to meet various task requirements.The core workflow diagram of this article:Understanding the Timer Module in FreeRTOS

    Process Description

    1. User calls API (such as <span><span>xTimerStart/Stop</span></span>) → sends commands to the queue through <span><span>xTimerGenericCommand</span></span>.

    2. Timer Service Task (<span><span>prvTimerTask</span></span>):

    • Reads commands from the queue → <span><span>prvProcessReceivedCommands</span></span>.

    • Checks expired timers → <span><span>prvProcessExpiredTimer</span></span> → executes callback.

  • Callback Execution: Runs in the context of the service task, cannot block.

  • Core Interactions

    • User Layer: Only calls APIs, does not directly operate timers.

    • Kernel Layer: Isolates hardware from business logic through queues and linked lists.

    • Key Functions: <span><span>prvTimerTask</span></span> is the hub, coordinating command processing and timing triggers.

    Leave a Comment