Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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
Table - Table 5.1 Time Management configuration constants in OS_CFG.H
Table - Table 5.1 Time Management configuration constants in OS_CFG.H

Panel
borderWidth0
titleTable - Table 5.1 Time Management configuration constants in OS_CFG.H


µC/OS-II Time Management ServiceEnabled when set to 1 in OS_CFG.H
OSTimeDly()
OSTimeDlyHMSM()OS_TIME_DLY_HMSM_EN
OSTimeDlyResume()OS_TIME_DLY_RESUME_EN
OSTimeGet()OS_TIME_GET_SET_EN
OSTimeSet()OS_TIME_GET_SET_EN



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
Listing - Listing 5.1 OSTimeDly()
Listing - Listing 5.1 OSTimeDly()

Code Block
languagecpp
titleListing - Listing 5.1 OSTimeDly()
linenumberstrue
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
bgColor#f0f0f0

(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 OSTimeDly() to remove the current task from the ready list.

(3) Next, the number of ticks are stored in the OS_TCB of the current task, where it is decremented on every clock tick by OSTimeTick(). You should note that the calling task is not placed in any wait list. Simply having a non-zero value in .OSTCBDly is sufficient for OSTimeTick() to know that the task has been delayed.

(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
Figure - Figure 5.1 Delay resolution
Figure - Figure 5.1 Delay resolution

Panel
borderWidth0
titleFigure - Figure 5.1 Delay resolution

Image Added


Panel
bgColor#f0f0f0

(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 OSTimeDly(1) at the moment shown. µC/OS-II puts the task to sleep until the next tick.

(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 OSTimeDly(1) a few tens of microseconds before the tick occurs and thus the delay results in almost no delay because the task is immediately rescheduled. If your application must delay for at least one tick, you must call OSTimeDly(2) , specifying a delay of two ticks!


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
Listing - Listing 5.2
Listing - Listing 5.2

Code Block
languagecpp
titleListing - Listing 5.2
linenumberstrue
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
bgColor#f0f0f0

(1) As with OSTimeDly(), OSTimeDlyHMSM() exits if you specify no delay.

(2) OSTimeDlyHMSM() then checks that you have specified valid values for its arguments.

(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 500/OS_TICKS_PER_SECOND basically corresponds to 0.5 ticks converted to milliseconds. For example, if the tick rate (OS_TICKS_PER_SEC) is set to 100Hz (10ms), a delay of 4ms would result in no delay! A delay of 5ms would result in a delay of 10ms, and so on.


Resuming a Delayed Task, OSTimeDlyResume()

...

The code for OSTimeDlyResume() is shown in Listing 5.3.

Anchor
Listing - Listing 5.3 Resuming a delayed task
Listing - Listing 5.3 Resuming a delayed task

Code Block
languagecpp
titleListing - Listing 5.3 Resuming a delayed task
linenumberstrue
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
bgColor#f0f0f0

(1) OSTimeDlyResume() begins by making sure the task has a valid priority.

(2) Next, OSTimeDlyResume() verifies that the task to resume does in fact exist.

(3) If the task exists, OSTimeDlyResume() checks to see if the task is waiting for time to expire. Whenever the OS_TCB field .OSTCBDly contains a nonzero value, the task is waiting for time to expire because the task called either OSTimeDly(), OSTimeDlyHMSM(), or any of the PEND functions described in subsequent chapters.

(4) The delay is then canceled by forcing .OSTCBDly to 0.

(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, OSTimeDlyResume() calls the scheduler to see if the resumed task has a higher priority than the current task. This would result in a context switch.


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
Listing - Listing 5.4 Obtaining and setting the system time
Listing - Listing 5.4 Obtaining and setting the system time

Code Block
languagecpp
titleListing - Listing 5.4 Obtaining and setting the system time
linenumberstrue
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();
}