Dynamic Tick BSP
Dynamic Tick API
Function Name | Operation |
---|---|
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
#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()
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()
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
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().