Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

Periodic Tick

Nearly every RTOS requires a periodic time source called a Clock Tick or System Tick to keep track of time delays and timeouts. µC/OS-III’s clock tick handling is encapsulated in the file os_tick.c.

OS_TickTask() is a task created by µC/OS-III and its priority is configurable by the user through µC/OS-III’s configuration file os_cfg_app.h (see OS_CFG_TICK_TASK_PRIO). Typically OS_TickTask() is set to a relatively high priority but should be slightly lower in priority than your most important tasks.

OS_TickTask() is used by µC/OS-III to keep track of tasks waiting for time to expire or, for tasks that are pending on kernel objects with a timeout. OS_TickTask() is a periodic task and it waits for signals from the tick ISR (described in Interrupt Management) as shown in the figure below.

µC/OS-III may need to place literally hundreds of tasks (if an application has that many tasks) in the tick list. The tick list is implemented such that it takes as little CPU time as possible to determine if time has expired for those tasks placed in the tick list and, possibly makes those tasks ready-to-run. In fact, the tick management has been completely redesigned as of V3.04.00 and the tick list is actually split into two separate ‘delta-lists’ as shown in the figure below.

Tasks are automatically inserted in the proper tick list when the application programmer calls a OSTimeDly???() function, or when an OS???Pend() call is made with a non-zero timeout value. If the users application does not need time delays or timeouts, the user can remove the Tick Task from µC/OS-III, see the µC-OS-III Configuration Manual for more information.

Example

Using an example to illustrate the process of inserting a task in the delayed tick list. Here, we assume that the OSTickListDly list is completely empty as shown in the figure below. A task is placed in the tick list when OSTimeDly() is called and let’s assume OSTimeDly() is called as follows:

              :
              OSTimeDly(10, OS_OPT_TIME_DLY, &err);
              :

Referring to the µC-OS-III API Reference Manual that this action indicates that µC/OS-III has to delay the current task for 10 ticks. Since this is the first task inserted in the tick list, the .TickNextPtr and .TickPrevPtr of the task’s OS_TCB both point to NULL.

OSTimeDly() takes care of a few other details. Specifically, the task is removed from µC/OS-III’s ready list (described in The Ready List) since the task is no longer eligible to run (because it is waiting for time to expire). Also, the scheduler is called because µC/OS-III will need to run the next most important ready-to-run task.

If the next task to run also happens to call OSTimeDly() “before” the next tick arrives and calls OSTimeDly() as follows:

              :    
              OSTimeDly(7, OS_OPT_TIME_DLY, &err);
              :

µC/OS-III will place this new task at the head of the list and replace the "remaining" counts for the first task to 3. In other words, the timeout for the first task is the sum of the two values (7+3). When the tick task runs, it only needs to decrement the .TickRemain of the OS_TCB which is at the head of the list, and worry about the next OS_TCB in the list three ticks later.

When the tick task executes (see OS_TickTask() in os_tick.c), it starts by incrementing OSTickCtr and, if the delayed tasks list is not empty, decrements only the .TickRemain of the OS_TCB at the head of the list. When .TickRemain reaches zero, the OS_TCB is removed from the list and placed in the ready-list. Then, the .TickRemain field of the next OS_TCB is examined and if the .TickRemain field for that task is also zero, that task is also placed in the ready list. µC/OS-III continues like this until the .TickRemain of an OS_TCB has a non-zero value or, it reaches the end of the list.

The .NbrUpdated field of the tick list contains the number of OS_TCBs removed from the list whenever a tick occurs. Of course, if .TickRemain is non-zero, .NbrUpdated would also be zero because on that specific tick, no task would be made ready-to-run.

The list of tasks that are waiting for timeouts works exactly the same as the delayed task list. The reason there are two lists is to reduce the amount of time in a critical section.

Dynamic Tick

µC/OS-III offers a mode where the Tick task is not run periodically but behaves as it does.  In other words, as far as your application is concerned, it will not know the difference. The benefit of this mode come apparent when you are concerned about power consumption such as with battery operated devices.  Dynamic Tick mode has the advantage of not waking up the Tick task unless it has to.

Without Dynamic Tick, if a task needs to sleep for one second, as done by calling OSTimeDlyHMSM(), and  OS_CFG_TICK_RATE_HZ  is set to 1000, the Tick task would run 999 times for nothing before finally waking up the delayed task on it's 1000th run. This can impose a large overhead in terms of power usage. The standard method used for the Tick task is to wake up OS_CFG_TICK_RATE_HZ times per second by the Tick ISR. Then, the tick task increments OSTickCtr by one and updates the timeout and delay tick lists, reading all tasks that have an expired delay or timeout. 

It would be far more efficient to actually run the Tick task once the 1 second delay has elapsed. In order to support this new mode, we had to make some changes to the way the Tick task and ISR interact with each other. In the Dynamic ticking mode, the Tick task is awakened when the smallest needed delay of any tasks of either tick lists has elapsed.  This minimum delay, called the Tick Step, is calculated every time a task needs to be delayed, every time a task pends on a kernel object with a timeout and every time the Tick task is run. This Tick Step is applied to the Dynamic tick timer by calling the BSP_OS_TickNextSet() function. This guarantees that the Tick ISR will be called when at least Tick Step ticks have elapsed. The Tick ISR will then notify the Tick task, using OSTimeDynTick(), that Tick Step ticks have indeed elapsed. Now, instead of increment OSTickCtr by one and updating the tick lists, the Tick task will add the Tick Step to OSTickCtr and update the tick lists accordingly. After updating these lists, the Tick task will determine the new Tick Step based on the updated tick lists and apply this step to the Dynamic tick timer. This ensures that by setting a variable delay in the Dynamic tick timer, the Tick task will be awakened by the Tick ISR only when necessary. Note that to use the Dynamic Tick feature, the µC/OS-III port developer has to implement the Dynamic Tick API described in µC/OS-III Port and OS_CFG_DYN_TICK_EN needs to be set to DEF_ENABLED in os_cfg.h.

You should note that there are practical limitations in using the Dynamic Tick mode.  For one thing, if you use a 16-bit timer that increments at a rate of 1 MHz then the maximum reload time for the timer would be 65,535 milliseconds and thus, you could not rely on such a timer to provide a 1 second delay.  In this case, you probably would want the maximum Tick Step to be in increments of 65 milliseconds.  However, I'd probably set the maximum increment to 50 making this a clean multiple of 1 second.

 

  • No labels