...
Tasks can call OSTmrStateGet()
to find out the state of a timer. Also, at any time during the countdown process, the application code can call OSTmrRemainGet()
to find out how much time remains before the timer reaches zero (0). The value returned is expressed in “timer ticks.” If timers are decremented at a rate of 10 Hz then a count of 50 corresponds to 5 seconds. If the timer is in the stop state, the time remaining will correspond to either the initial delay (one shot or periodic with initial delay), or the period if the timer is configured for periodic without initial delay.
One-Shot Timers
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Panel | ||
---|---|---|
| ||
(1) The “Unused” state is a timer that has not been created or has been “deleted.” In other words, µC/OS-III does not know about this timer. (2) When creating a timer or calling (3) A timer is placed in running state when calling (4) The "Timeout" state is where we process timers that have expired. In particular, timer callbacks are executed in this state. (5) The “Completed” state is reached once a one-shot timer has been processed. However, if the timer's own callback stops, restarts or deletes it, this state will be bypassed. |
Periodic Timers
...
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Panel | ||
---|---|---|
| ||
(1) The “Unused” state is a timer that has not been created or has been “deleted.” In other words, µC/OS-III does not know about this timer. (2) When creating a timer or calling (3) A timer is placed in running state when calling (4) The "Timeout" state is where we process timers that have expired. In particular, timer callbacks are executed in this state. A periodic timer is returned to the running state once it is processed, unless the timer's own callback stops, restarts, or deletes it. |
OS_TMR
A timer is a kernel object as defined by the OS_TMR
data type (see os.h
) as shown in the listing below:
The services provided by µC/OS-III to manage timers are implemented in the file os_tmr.c
. Timer services are enabled at compile time by setting the configuration constant OS_CFG_TMR_EN
to DEF_ENABLED
in os_cfg.h
.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
typedef struct os_tmr OS_TMR; (1)
struct os_tmr {
OS_OBJ_TYPE Type; (2)
CPU_CHAR *NamePtr; (3)
OS_TMR_CALLBACK_PTR CallbackPtr; (4)
void *CallbackPtrArg; (5)
OS_TMR *NextPtr; (6)
OS_TMR *PrevPtr;
OS_TICK Remain; (7)
OS_TICK Dly; (8)
OS_TICK Period; (9)
OS_OPT Opt; (10)
OS_STATE State; (11)
}; |
Panel | ||
---|---|---|
| ||
(1) In µC/OS-III, all structures are given a data type. In fact, all data types start with “ (2) The structure starts with a “ (3) Each kernel object can be given a name for easier recognition by debuggers or µC/Probe. This member is simply a pointer to an ASCII string which is assumed to be NUL terminated. (4) The (5) If there is a non- (6) (7) The (8) The (9) The (10) The (11) The |
Even if the internals of the OS_TMR
data type are understood, the application code should never access any of the fields in this data structure directly. Instead, you should always use the Application Programming Interfaces (APIs) provided with µC/OS-III.
...
As of V3.07, OS_TmrTask()
no longer runs periodically. It now supports dynamic timer management, which is to say it only runs when it needs to process a change to the timer list. During normal operation, the task determines which timer will timeout next and performs a delay for that length of time. This approach helps reduce power consumption and is better suited to low-power applications, where a periodic wake-up is often undesirable. .
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Panel | ||
---|---|---|
| ||
(1) The timer task begins its core loop by checking if any timers are available in the timer list. (2) If no timers are available, the timer task waits indefinitely for a timer to be added. This helps to conserve power while the task has nothing to do. Once a timer is added, a signal will wake it up and we can check the timer list again. (3) If a timer is available in the list, we want to delay the timer task until it expires. Since timers are stored in a delta list, we only need to check the first timer in the list to know how long we should delay. However, if a timer API call requires us to load a new delay value, we must wake up early to do so. (4) Regardless of which condition caused us to wake-up in Step 3, we must account for the time which has passed while we were asleep. (5) If any timers have expired, we must process them. Callbacks are executed, timer states are advanced, and completed one-shot timers are removed from the timer list. |
The figure below shows timing diagram associated with the timer management task.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Panel | ||
---|---|---|
| ||
(1) The tick ISR occurs and assumes interrupts are enabled and executes. (2) The tick ISR signals the tick task that it is time for it to update timers. (3) The tick ISR terminates, however there might be higher priority tasks that need to execute (assuming the timer task has a lower priority). Therefore, µC/OS-III runs the higher priority task(s). (4) When all higher priority tasks have executed, µC/OS-III switches to the timer task and determines that there are three timers that expired. (5) The callback for the first timer is executed. (6) The callback for the second expired timer is executed. (7) The callback for the third expired timer is executed. |
There are a few interesting things to notice:
...
µC/OS-III applications may require many timers. The timer manager implements a simple linear delta list where each timer is linked in a doubly linked list as shown in the figure below.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Panel | ||
---|---|---|
| ||
(1) (2) (3) |
Timers are inserted in the timer list by calling OSTmrStart()
and, a timer must be created before it can be used. Newly created timers are always inserted at the beginning of the list as shown in the figure following the code listing below and the code listing itself.Timer locations in the list are determined by the order in which they expire, as shown below.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
OS_TMR MyTmr1;
OS_TMR MyTmr2;
void MyTmrCallbackFnct1 (void *p_arg)
{
/* Do something when timer #1 expires */
}
void MyTmrCallbackFnct2 (void *p_arg)
{
/* Do something when timer #2 expires */
}
void MyTask (void *p_arg)
{
OS_ERR err;
while (DEF_ON) {
:
OSTmrCreate((OS_TMR *)&MyTmr1,
(OS_CHAR *)"My Timer #1",
(OS_TICK )1,
(OS_TICK )0,
(OS_OPT )OS_OPT_TMR_ONE_SHOT,
(OS_TMR_CALLBACK_PTR)MyTmrCallbackFnct1,
(void *)0,
(OS_ERR *)&err);
/* Check 'err" */
OSTmrStart ((OS_TMR *)&MyTmr1,
(OS_ERR *)&err);
/* Check "err" */
// Continues in the next code listing! |
Since this is the first timer inserted in the timer list, the .NextPtr
and .PrevPtr
both point to NULL
.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
The code below shows creating and starting another timer. This is performed “before” the timer task is signaled.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
// Continuation of code from previous code listing.
:
:
OSTmrCreate((OS_TMR *)&MyTmr2,
(OS_CHAR *)"My Timer #2",
(OS_TICK )10,
(OS_TICK )0,
(OS_OPT )OS_OPT_TMR_ONE_SHOT,
(OS_TMR_CALLBACK_PTR)MyTmrCallbackFnct2,
(void *)0,
(OS_ERR *)&err);
/* Check 'err" */
OSTmrStart ((OS_TMR *)&MyTmr,
(OS_ERR *)&err);
/* Check 'err" */
}
} |
The “second timer” is inserted at the head tail of the list as shown in the figure below.
When the timer task executes (see OS_TmrTask()
in os_tmr.c
), it starts by incrementing OSTmrTickCtr
and goes through the list (linearly) and decrements each of the .Remain
field. When the .Remain
field reaches zero, the timer manager executes the callback function associated with the timer and, if the timer is set to periodic, reloads the .Remain
field with the time remaining to expiration. If the timer is configured as a one-shot timer then the timer is removed from the list upon expiration.
Timer management occurs at the task level. The list is protected using an internal mutual exclusion semaphore (mutex) when OS_CFG_MUTEX_EN
is set to DEF_ENABLED
in os_cfg.h
or, by locking the scheduler if mutexes are not enabled. It’s highly recommend that you use (and thus enable) mutexes because locking the scheduler impacts task responsiveness of other, higher priority tasks in your application., because it expires after the first. The delta property is maintained with respect to .Remain
: the remaining time of the a timer is the sum of its .Remain
value and that of all of the previous timers.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||