Time Services

µC/OS-III provides a number of services to manage time as summarized in the table below, and the code is found in os_time.c.

Table - Time Services API summary
Function NameOperation
OSTimeDly()Delay execution of a task for “n” ticks
OSTimeDlyHMSM()Delay a task for a user specified time in HH:MM:SS.mmm
OSTimeDlyResume()Resume a delayed task
OSTimeGet()Obtain the current value of the tick counter
OSTimeSet()Set the tick counter to a new value
OSTimeTick()Signal the occurrence of one clock tick
OSTimeDynTick()Signal the occurrence of multiple ticks (Dynamic Tick Mode Only)


The application programmer should refer to µC-OS-III API Reference for a detailed description of these services.

OSTimeDly() – Delay a Task

A task calls this function to suspend execution until some time expires. The calling function will not execute until the specified time expires. This function allows three modes: relative, periodic and absolute.

The listing below shows how to use OSTimeDly() in relative mode.

Listing - OSTimeDly() - Relative
void  MyTask (void *p_arg)
{
    OS_ERR  err;
    :
    :
    while (DEF_ON) {
        :
        :
        OSTimeDly(2,                               (1) 
                  OS_OPT_TIME_DLY,                 (2) 
                  &err);                           (3) 
        /* Check "err" */                          (4) 
        :
        :
    }
}

(1) The first argument specifies the amount of time delay (in number of ticks) from when the function is called. For the example above, if the tick rate (OS_CFG_TICK_RATE_HZ in os_cfg_app.h) is set to 1000 Hz, the user is asking to suspend the current task for approximately 2 milliseconds. However, the value is not accurate since the count starts from the next tick which could occur almost immediately. This will be explained shortly.

(2) Specifying OS_OPT_TIME_DLY indicates that the user wants to use “relative” mode.

(3) As with most µC/OS-III services an error return value will be returned. The example should return OS_ERR_NONE because the arguments are all valid.

(4) You should always check the error code returned by µC/OS-III. If “err” does not contain OS_ERR_NONEOSTimeDly() did not perform the intended work. For example, another task could remove the time delay suspension by calling OSTimeDlyResume() and when MyTask() returns, it would not have returned because the time had expired.


As mentioned above, the delay is not accurate. Refer to the figure below  and its description below to understand why.

Figure - OSTimeDly() - Relative

(1)  We get a tick interrupt and µC/OS-III services the ISR.

(2) At the end of the ISR, all Higher Priority Tasks (HPTs) execute. The execution time of HPTs is unknown and can vary.

(3) Once all HPTs have executed, µC/OS-III runs the task that has called OSTimeDly() as shown in the listing above. For the sake of discussion, it is assumed that this task is a lower priority task (LPT).

(4) The task calls OSTimeDly() and specifies to delay for two ticks in “relative” mode. At this point, µC/OS-III places the current task in the tick list where it will wait for two ticks to expire. The delayed task consumes zero CPU time while waiting for the time to expire.

(5) The next tick occurs. If there are HPTs waiting for this particular tick, µC/OS-III will schedule them to run at the end of the ISR.

(6) The HPTs execute.

(7) The next tick interrupt occurs. This is the tick that the LPT was waiting for and will now be made ready-to-run by µC/OS-III.

(8) Since there are no HPTs to execute on this tick, µC/OS-III switches to the LPT.

(9) Given the execution time of the HPTs, the time delay is not exactly two ticks, as requested. In fact, it is virtually impossible to obtain a delay of exactly the desired number of ticks. You might ask for a delay of two ticks, but the very next tick could occur almost immediately after calling OSTimeDly()! Just imagine what might happen if all HPTs took longer to execute and pushed 3 and 4 further to the right. In this case, the delay would actually appear as one tick instead of two.


OSTimeDly() can also be called with the OS_OPT_TIME_PERIODIC option as shown in the listing below . This option allows delaying the task until the tick counter reaches a certain periodic match value and thus ensures that the spacing in time is always the same as it is not subject to CPU load variations.

µC/OS-III determines the “match value” of OSTickCtr to determine when the task will need to wake up based on the desired period. This is shown in the figure below. µC/OS-III checks to ensure that if the match is computed such that it represents a value that has already gone by then, the delay will be zero.

Figure - OSTimeDly() - Periodic


Listing - OSTimeDly() - Periodic
{
    OS_ERR   err;
    :
    :
    while (DEF_ON) {
        OSTimeDly(4,                               (1) 
                  OS_OPT_TIME_PERIODIC,            (2) 
                  &err);
        /* Check "err" */                          (3) 
        :
        :
    }
}

(1) The first argument specifies the period for the task to execute, specifically every four ticks. Of course, if the task is a low-priority task, µC/OS-III only schedules and runs the task based on its priority relative to what else needs to be executed.

(2) Specifying OS_OPT_TIME_PERIODIC indicates that the task is to be ready-to-run when the tick counter reaches the desired period from the previous call.

(3) You should always check the error code returned by µC/OS-III.

Relative and Periodic modes might not look different, but they are. In Relative mode, it is possible to miss one of the ticks when the system is heavily loaded, missing a tick or more on occasion. In Periodic mode, the task may still execute later, but it will always be synchronized to the desired number of ticks. In fact, Periodic mode is the preferred mode to use to implement a time-of-day clock.

In V3.07, the behavior of Periodic mode was altered slightly. Rather than schedule the next delay based on when the previous delay completed, we always assume the previous delay completed at a multiple of the period specified. In other words, both versions synchronize the delays to an imagined function which has the specified period, but prior versions have the function beginning at the time when the first periodic delay was requested whereas V3.07 has it beginning at time 0. Future versions may provide support for both behaviors.

Finally, you can use the absolute mode to perform a specific action at a fixed time after power up. For example, turn off a light 10 seconds after the product powers up. In this case, you would specify OS_OPT_TIME_MATCH while “dly” actually corresponds to the desired value of OSTickCtr you want to reach. However, you should use the OS_OPT_TIME_MATCH with care because somewhere else in your code you can change the value of OSTickCtr by calling OSTimeSet() as described in Time Services.

To summarize, the task will wake up when OSTickCtr reaches the following value:

Value of “opt”Task wakes up when
OS_OPT_TIME_DLYOSTickCtr + dly
OS_OPT_TIME_PERIODICOSTCBCurPtr->TickCtrPrev + dly
OS_OPT_TIME_MATCHdly

OSTimeDlyHMSM() – Delay a Task for a Specified Time

If enabled, a task may call this function to suspend execution until some time expires by specifying the length of time in a more user-friendly way. Specifically, you can specify the delay in hours, minutes, seconds, and milliseconds (thus the HMSM). This function only works in “Relative” mode.

The listing below indicates how to use OSTimeDlyHMSM().

Listing - OSTimeDlyHMSM()
void  MyTask (void *p_arg)
{
    OS_ERR  err;
    :
    :
    while (DEF_ON) {
        :
        :
        OSTimeDlyHMSM(0,                             (1) 
                      0,
                      1,
                      0,
                      OS_OPT_TIME_HMSM_STRICT,       (2) 
                      &err);                         (3) 
        /* Check "err" */
        :
        :
    }
}

(1) The first four arguments specify the amount of time delay (in hours, minutes, seconds, and milliseconds) from this point in time. In the above example, the task should delay for 1 second. The resolution greatly depends on the tick rate. For example, if the tick rate (OS_CFG_TICK_RATE_HZ in os_cfg_app.h) is set to 1000 Hz there is technically a resolution of 1 millisecond. If the tick rate is 100 Hz then the delay of the current task is in increments of 10 milliseconds. Again, given the relative nature of this call, the actual delay may not be accurate.

(2) Specifying OS_OPT_TIME_HMSM_STRICT verifies that the user strictly passes valid values for hours, minutes, seconds and milliseconds. Valid hours are 0 to 99, valid minutes are 0 to 59, valid seconds are 0 to 59, and valid milliseconds are 0 to 999.

If specifying OS_OPT_TIME_HMSM_NON_STRICT, the function will accept nearly any value for hours (between 0 to 999), minutes (from 0 to 9999), seconds (any value, up to 65,535), and milliseconds (any value, up to 4,294,967,295). OSTimeDlyHMSM(203, 101, 69, 10000) may be accepted. Whether or not this makes sense is a different story.

The reason hours is limited to 999 is that time delays typically use 32-bit values to keep track of ticks. If the tick rate is set at 1000 Hz then, it is possible to only track 4,294,967 seconds, which corresponds to 1,193 hours, and therefore 999 is a reasonable limit.

(3) As with most µC/OS-III services the user will receive an error return value. The example should return OS_ERR_NONE since the arguments in the listing are all valid. Refer to µC-OS-III API Reference for a list of possible error codes.


Even though µC/OS-III allows for very long delays for tasks, it is actually not recommended to delay tasks for a long time. The reason is that there is no indication that the task is actually “alive” unless it is possible to monitor the amount of time remaining for the delay. It is better to have the task wake up approximately every minute or so, and have it “tell you” that it is still ok.

OSTimeDly() is often used to create periodic tasks (tasks that execute periodically). For example, it is possible to have a task that scans a keyboard every 50 milliseconds and another task that reads analog inputs every 10 milliseconds, etc.

OSTimeDlyResume() – Resume a Delayed Task

A task can resume another task that called OSTimeDly() or OSTimeDlyHMSM() by calling OSTimeDlyResume(). The listing below shows how to use OSTimeDlyResume(). The task that delayed itself will not know that it was resumed, but will think that the delay expired. Because of this, use this function with great care.

Listing - OSTimeDlyResume()
OS_TCB  MyTaskTCB;
 
 
void  MyTask (void *p_arg)
{
    OS_ERR  err;
    :
    :
    while (DEF_ON) {
        :
        :
        OSTimeDly(10,                            
                  OS_OPT_TIME_DLY,      
                  &err);                        
        /* Check "err" */
        :
        :
    }
}
 
 
void  MyOtherTask (void *p_arg)
{
    OS_ERR  err;
    :
    :
    while (DEF_ON) {
        :
        :
        OSTimeDlyResume(&MyTaskTCB,
                        &err);     
        /* Check "err" */
        :
        :
    }
}


OSTimeGet() – Get Value of the Tick Counter

µC/OS-III increments a tick counter every time a tick interrupt occurs. This counter allows the application to make coarse time measurements and have some notion of time (after power up).

OSTimeGet() allows the user to take a snapshot of the tick counter. You can use this value to delay a task for a specific number of ticks and repeat this periodically without losing track of time.

OSTimeSet() allows the user to change the current value of the tick counter. Although µC/OS-III allows for this, it is recommended to use this function with great care especially if you use ‘match’ time delays.

OSTimeSet() and OSTimeGet()

µC/OS-III increments a tick counter every time a tick interrupt occurs. This counter allows the application to make coarse time measurements and have some notion of time (after power up).

OSTimeGet() allows the user to take a snapshot of the tick counter. You can use this value to delay a task for a specific number of ticks and repeat this periodically without losing track of time.

OSTimeSet() allows the user to change the current value of the tick counter. Although µC/OS-III allows for this, it is recommended to use this function with great care especially if you use ‘match’ time delays.

OSTimeTick() – Signal a Clock Tick

The tick Interrupt Service Routine (ISR) must call this function every time a tick interrupt occurs. μC/OS-III uses this function to update time delays and timeouts used by other system calls. OSTimeTick() is considered an internal function to μC/OS-III.

OSTimeDynTick() – Signal multiple Ticks (Dynamic Tick Mode Only)

The dynamic tick Interrupt Service Routine (ISR) must call this function with the number of ticks that timer was last configured to delay. μC/OS-III uses this function to update time delays and timeouts used by other system calls. OSTimeDynTick() is considered an internal function to μC/OS-III.