Dynamic Tick BSP

Dynamic Tick API

Dynamic Tick API Summary
Function NameOperation
OS_DynTickGet()Returns the number of OS_TICKS which have elapsed since OS_DynTickSet() was last called. Should not be called from the application.
OS_DynTickSet()Specifies the number of OS_TICKS which should elapse before the next tick interrupt. Should not be called from the application.

OSTimeDynTick()

Tells the OS how many ticks have elapsed since OS_DynTickSet() was last called. Must be called from the Tick ISR.

Because hardware timers are programmed in the BSP, µC/OS-III only provides the function declarations for OS_DynTickGet() and OS_DynTickSet(); the BSP developer is required to implement them correctly to support Dynamic Tick. OSTimeDynTick() is defined by the OS and called from the Tick ISR, similarly to OSTimeTick(). However, unlike OSTimeTick() it passes an argument to the OS: the number of ticks that have elapsed.

Template

bsp_os.c

Dynamic Tick BSP - Part 1
#include  <cpu.h>
#include  <os.h>

typedef  <int_type>                 TIMER_REG_T;                                                            (1)
#define  TIMER_MAX_INT              ()                                                                      (2)
#define  TIMER_COUNT_HZ             ()                                                                      (3)


#define  TIMER_TO_OSTICK(count)     (((CPU_INT64U)(count)  * OS_CFG_TICK_RATE_HZ) /      TIMER_COUNT_HZ)    (4)
#define  OSTICK_TO_TIMER(ostick)    (((CPU_INT64U)(ostick) * TIMER_COUNT_HZ)      / OS_CFG_TICK_RATE_HZ)

#define  TIMER_COUNT_MAX            (TIMER_MAX_INT - (TIMER_MAX_INT % OSTICK_TO_TIMER(1u)))                 (5)

static  OS_TICK  TickDelta = 0u;                                                                            (6)

(1) This type should be an integer large enough to hold the timer count.

(2) This must be set to the largest integer that the timer can hold in its count register.

(3) Set this to the timer's frequency, in Hz.

(4) The casts to 64-bit help to ensure that the products are not truncated.

(5) We artificially cap the maximum timer count to ensure that a partial tick cannot occur when we count up to the maximum value.

(6) TickDelta is used to store the number of ticks we expect to wait before notifying the OS through an interrupt.

OS_DynTickGet()

Dynamic Tick BSP - OS_DynTickGet()
OS_TICK  OS_DynTickGet (void)                                   (1)
{
    TIMER_REG_T  tmrcnt;
    OS_TICK      ticks;


    tmrcnt = TIMER_COUNT_GET();

    if (IS_TIMER_OVERFLOWED()) {                                (2)
        return (TickDelta);                                     (3)
    }

    ticks = TIMER_TO_OSTICK(tmrcnt);                            (4)

    return (ticks);
}

(1) This function must return a value between 0 and TickDelta. If it returns anything larger, the OS may behave improperly.

(2) If a counter overflow occurred, it means we've exceeded the amount of time we needed to wait. The timer count we read may not be valid.

(3) Regardless of how much extra time may have passed, the maximum number we can return is TickDelta. We ignore any extra time which has passed, since a certain amount of drift is unavoidable.

(4) If we did not overflow, our timer count is valid. Convert it to OS_TICK units before returning.

OS_DynTickSet()

Dynamic Tick BSP - OS_DynTickSet()
OS_TICK  OS_DynTickSet (OS_TICK  ticks)                          (1)
{                                                                
    TIMER_REG_T  tmrcnt;                                         
                                                                 
                                                                 
    tmrcnt = OSTICK_TO_TIMER(ticks);                             (2)
                                                                 
    if ((tmrcnt >= TIMER_COUNT_MAX) ||                           (3)
        (tmrcnt ==              0u)) {                           (4)
        tmrcnt = TIMER_COUNT_MAX;                                (5)
    }                                                            
                                                                 
    TickDelta = TIMER_TO_OSTICK(tmrcnt);                         (6)
                                                                 
                                                                 (7)
    STOP_TIMER();                                                (8)
    CLEAR_INTERRUPT();                                           (9)
    START_TIMER(tmrcnt);                                        (10)

    return (TickDelta);                                         (11)
}

(1) This function is called with interrupts disabled. It may be called by the OS before the previous count is complete.

(2) The OS has requested a delay in OS_TICK units. Convert this into the equivalent delay for our hardware timer.

(3) The requested delay may exceed our timer's capacity.

(4) The OS may request an indefinite delay when there are no tasks currently using the timing services.

(5) In case of (3) or (4), we delay for as many ticks as our timer will allow.

(6) TickDelta remembers the number of ticks we last set for the timer delay. TickDelta is not always the same as ticks. Should case (5) occur, the TickDelta will be less than ticks. That is why it is important to perform the conversions as shown in the code listing.

(7) We need to restart the timer for the new delay.

(8) The timer may still be running from a previous delay. We must halt it before going further.

(9) Always clear the interrupt flag here, in case the interrupt has fired but not been serviced.

(10) Restart the timer using the new delay value.

(11) Return TickDelta to the OS.

Dynamic Tick ISR

Dynamic Tick BSP - ISR Handler
OS_TICK  OS_DynTickSet (OS_TICK  ticks)                          (1)
{                                                                
    TIMER_REG_T  tmrcnt;                                         
                                                                 
                                                                 
    tmrcnt = OSTICK_TO_TIMER(ticks);                             (2)
                                                                 
    if ((tmrcnt >= TIMER_COUNT_MAX) ||                           (3)
        (tmrcnt ==              0u)) {                           (4)
        tmrcnt = TIMER_COUNT_MAX;                                (5)
    }                                                            
                                                                 
    TickDelta = TIMER_TO_OSTICK(tmrcnt);                         (6)
                                                                 
                                                                 (7)
    STOP_TIMER();                                                (8)
    CLEAR_INTERRUPT();                                           (9)
    START_TIMER(tmrcnt);                                        (10)

    return (TickDelta);                                         (11)
}

(1) Always report what was assigned to TickDelta.

  • Note: We do not clear the interrupt flag here, because the OS calls OS_DynTickSet() internally from OSTimeDynTick().

Related pages