...
The section Interrupts Under µC/OS-II discussed discussed the tick ISR (Interrupt Service Routine) as well as the function that it needs to call to notify µC/OS-II about the tick interrupt — OSTimeTick()
.
...
Table of Contents | ||
---|---|---|
|
...
Some of the time management services must be enabled by seting configuration constants in OS_CFG.H
. Specifically, table 5.1 shows which services are compiled based on the value of configuration constants found in OS_CFG.H
.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||
|
Delaying a Task, OSTimeDly()
...
Listing 5.1 shows the code for OSTimeDly()
. Your application calls this function by supplying the number of ticks to delay — a value between 1 and 65535. A value of 0 specifies no delay.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
void OSTimeDly (INT32U ticks)
{
INT8U y;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
if (OSIntNesting > 0u) {
return;
}
if (OSLockNesting > 0u) {
return;
}
if (ticks > 0) { (1)
OS_ENTER_CRITICAL();
y = OSTCBCur->OSTCBY; (2)
OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;
if (OSRdyTbl[y] == 0u) {
OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;
}
OSTCBCur->OSTCBDly = ticks; (3)
OS_EXIT_CRITICAL();
OSSched(); (4)
}
} |
Panel | ||
---|---|---|
| ||
(1) If you specify a value of 0, you are indicating that you don’t want to delay the task, and the function returns immediately to the caller. (2) A nonzero value causes (3) Next, the number of ticks are stored in the (4) Finally, since the task is no longer ready, the scheduler is called so that the next highest priority task that is ready to run gets executed. |
It is important to realize that the resolution of a delay is between zero and one tick. In other words, if you try to delay for only one tick, you could end up with an intermediate delay between 0 and 1 tick. This is assuming, however, that your processor is not heavily loaded. Figure 5.1 illustrates what happens.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Panel | ||
---|---|---|
| ||
(1) A tick interrupt occurs every 10ms. (2) Assuming that you are not servicing any other interrupts and that you have interrupts enabled, the tick ISR will be invoked. (3) You may have a few high-priority tasks (HPTs) waiting for time to expire, so they will execute next. (4) The low-priority task (LPT) shown in Figure 5.1 then gets a chance to execute and, upon completion, calls (5) & (6) When the next tick arrives, the tick ISR executes, but this time there are no HPTs to execute, and µC/OS-II executes the task that delayed itself for one tick. As you can see, the task actually delayed for less than one tick! On heavily loaded systems, the task may call |
Delaying a Task, OSTimeDlyHMSM()
...
However, this is somewhat awkward. I added the function OSTimeDlyHMSM()
so that you can specify time in hours (H), minutes (M), seconds (S), and milliseconds (m), which is more natural. Like OSTimeDly()
, calling this function causes a context switch and forces µC/OS-II to execute the next highest priority task that is ready to run. The task calling OSTimeDlyHMSM()
is made ready to run as soon as the time specified expires or if another task cancels the delay by calling OSTimeDlyResume()
[see section 5.02, Resuming a Delayed Task, OSTimeDlyResume()
]. Again, this task runs only when it again becomes the highest priority task. Listing 5.2 shows the code for OSTimeDlyHMSM()
. As you can see, your application calls this function by supplying the delay in hours, minutes, seconds, and milliseconds. In practice, you should avoid delaying a task for long periods of time because it’s always a good idea to get some feedback activity from a task (increment a counter, blink an LED, etc.). However, if you do need long delays, µC/OS-II can delay a task for 256 hours (close to 11 days).
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U milli)
{
INT32U ticks;
if (OSIntNesting > 0u) {
return (OS_ERR_TIME_DLY_ISR);
}
if (OSLockNesting > 0u) {
return (OS_ERR_SCHED_LOCKED);
}
#if OS_ARG_CHK_EN > 0u
if (hours == 0u) { (1)
if (minutes == 0u) {
if (seconds == 0u) {
if (ms == 0u) {
return (OS_ERR_TIME_ZERO_DLY);
}
}
}
}
if (minutes > 59u) { (2)
return (OS_ERR_TIME_INVALID_MINUTES);
}
if (seconds > 59u) {
return (OS_ERR_TIME_INVALID_SECONDS);
}
if (ms > 999u) {
return (OS_ERR_TIME_INVALID_MS);
}
#endif
ticks = (INT32U)hours * 3600L * OS_TICKS_PER_SEC (3)
+ (INT32U)minutes * 60L * OS_TICKS_PER_SEC
+ (INT32U)seconds * OS_TICKS_PER_SEC
+ OS_TICKS_PER_SEC * ((INT32U)milli
+ 500L / OS_TICKS_PER_SEC) / 1000L; (4)
OSTimeDly(ticks);
return (OS_ERR_NONE);
} |
Panel | ||
---|---|---|
| ||
(1) As with (2) (3) Because µC/OS-II only knows about ticks, the total number of ticks is computed from the specified time. (4) This portion of the equation determines the number of ticks given the specified milliseconds with rounding to the nearest tick. The value |
Resuming a Delayed Task, OSTimeDlyResume()
...
The code for OSTimeDlyResume()
is shown in Listing 5.3.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
INT8U OSTimeDlyResume (INT8U prio)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
OS_TCB *ptcb;
if (prio >= OS_LOWEST_PRIO) { (1)
return (OS_ERR_PRIO_INVALID);
}
OS_ENTER_CRITICAL();
ptcb = (OS_TCB *)OSTCBPrioTbl[prio];
if (ptcb == (OS_TCB *)0) { (2)
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_NOT_EXIST);
}
if (ptcb == OS_TCB_RESERVED) {
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_NOT_EXIST);
}
if (ptcb->OSTCBDly == 0u) {
OS_EXIT_CRITICAL();
return (OS_ERR_TIME_NOT_DLY);
}
ptcb->OSTCBDly = 0u; (4)
if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {
ptcb->OSTCBStat &= ~OS_STAT_PEND_ANY;
ptcb->OSTCBStatPend = OS_STAT_PEND_TO;
} else {
ptcb->OSTCBStatPend = OS_STAT_PEND_OK;
}
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { (5)
OSRdyGrp |= ptcb->OSTCBBitY; (6)
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
OS_EXIT_CRITICAL();
OS_Sched(); (7)
} else {
OS_EXIT_CRITICAL();
}
return (OS_ERR_NONE);
} |
Panel | ||
---|---|---|
| ||
(1) (2) Next, (3) If the task exists, (4) The delay is then canceled by forcing (5) A delayed task may also have been suspended; thus, the task is only made ready to run if the task was not suspended. (6) The task is placed in the ready list when the time expired. (7) At this point, |
Note that you could also have a task delay itself by waiting on a semaphore, mutex, event flag, mailbox, or queue with a timeout (see Chapters 7 through 11). You would resume such a task by simply posting to the semaphore, mutex, event flag, mailbox, or queue, respectively. The only problem with this scenario is that it requires you to allocate an event control block (see section 6.00), so your application would consume a little bit more RAM.
...
Whenever a clock tick occurs, µC/OS-II increments a 32-bit counter. This counter starts at zero when you initiate multitasking by calling OSStart()
and rolls over after 4,294,967,295 ticks. At a tick rate of 100Hz, this 32-bit counter rolls over every 497 days. You can obtain the current value of this counter by calling OSTimeGet()
. You can also change the value of the counter by calling OSTimeSet()
. The code for both functions is shown in Listing 5.4. Note that interrupts are disabled when accessing OSTime
. This is because incrementing and copying a 32-bit value on most 8-bit processors requires multiple instructions that must be treated indivisibly.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
INT32U OSTimeGet (void)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
INT32U ticks;
OS_ENTER_CRITICAL();
ticks = OSTime;
OS_EXIT_CRITICAL();
return (ticks);
}
void OSTimeSet (INT32U ticks)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
OS_ENTER_CRITICAL();
OSTime = ticks;
OS_EXIT_CRITICAL();
} |