void RTCC_IRQHandler(void) #endif { uint32_t flags, timeElapsed, cnt, timeToNextTimerCompletion; INT_Disable(); // CNT will normally be COMP0+1 at this point, // unless IRQ latency exceeded one tick period. flags = RTC_INTGET(); if ( flags & RTC_COMP_INT ) { // Stop timer system by disabling the compare IRQ. // Update all timers with the time elapsed, call callbacks if needed, // then find the timer with the shortest timeout (if any at all) and // reenable the compare IRQ if needed. inTimerIRQ = true; cnt = RTC_COUNTERGET(); // This loop is repeated if CNT is incremented while processing. do { RTC_INTDISABLE( RTC_COMP_INT ); timeElapsed = TIMEDIFF( cnt, lastStart ); // Update all timers with elapsed time. checkAllTimers( timeElapsed ); // Execute timer callbacks. executeTimerCallbacks(); // Restart RTC according to next timeout. rescheduleRtc( cnt ); cnt = RTC_COUNTERGET(); timeElapsed = TIMEDIFF( cnt, lastStart ); timeToNextTimerCompletion = TIMEDIFF( RTC_COMPAREGET(), lastStart ); /* If the counter has passed the COMP(ARE) register value since we checked the timers, then we should recheck the timers and reschedule again. */ } while ( rtcRunning && (timeElapsed > timeToNextTimerCompletion)); inTimerIRQ = false; } #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG ) if ( flags & RTC_OF_INT ) { RTC_INTCLEAR( RTC_OF_INT ); wallClockOverflowCnt++; } #endif INT_Enable(); }
static void delayTicks( uint32_t ticks ) { uint32_t startTime; volatile uint32_t now; if ( ticks ) { startTime = RTC_COUNTERGET(); do { now = RTC_COUNTERGET(); } while ( TIMEDIFF( now, startTime ) < ticks ); } }
/***************************************************************************//** * @brief * Set wallclock time. * * @param[in] secs Value to set (seconds). * * @return * @ref ECODE_EMDRV_RTCDRV_OK ******************************************************************************/ Ecode_t RTCDRV_SetWallClock( uint32_t secs ) { INT_Disable(); wallClockTime = secs; wallClockInitTime = RTC_COUNTERGET(); INT_Enable(); return ECODE_EMDRV_RTCDRV_OK; }
/***************************************************************************//** * @brief * Get wallclock tick count as a 64 bit value. This will never overflow. * * @return * Wallclock tick counter. ******************************************************************************/ uint64_t RTCDRV_GetWallClockTicks64( void ) { uint64_t overflows, ticks; /* Need to re-read data in case overflow cnt is incremented while we read. */ do { overflows = wallClockOverflowCnt; ticks = RTC_COUNTERGET(); } while ( overflows != wallClockOverflowCnt ); return ( overflows << RTC_COUNTER_BITS ) + ticks; }
/***************************************************************************//** * @brief * Get wallclock time. * * @return * Seconds elapsed since RTCDRV was initialized. ******************************************************************************/ uint32_t RTCDRV_GetWallClock( void ) { uint64_t tmp; uint32_t ticks, wallClock, wallClockStartPoint; INT_Disable(); ticks = RTC_COUNTERGET(); wallClock = wallClockTime; wallClockStartPoint = wallClockInitTime; INT_Enable(); tmp = ticks - wallClockStartPoint; return wallClock + (uint32_t)TICKS_TO_SEC( tmp ); }
/***************************************************************************//** * @brief * Get time left before a given timer expires. * * @param[in] id The id of the timer to query. * * @param[out] timeRemaining Time left expressed in milliseconds. * Only valid if return status is ECODE_EMDRV_RTCDRV_OK. * @return * @ref ECODE_EMDRV_RTCDRV_OK on success.@n * @ref ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID if id has an illegal value. @n * @ref ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED if the timer is not reserved.@n * @ref ECODE_EMDRV_RTCDRV_TIMER_NOT_RUNNING if the timer is not running.@n * @ref ECODE_EMDRV_RTCDRV_PARAM_ERROR if an invalid timeRemaining pointer * was supplied. ******************************************************************************/ Ecode_t RTCDRV_TimeRemaining( RTCDRV_TimerID_t id, uint32_t *timeRemaining ) { uint64_t tmp; uint32_t timeLeft, currentCnt, lastRtcStart; // Check if valid timer ID. if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) { return ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID; } // Check pointer validity. if ( timeRemaining == NULL ) { return ECODE_EMDRV_RTCDRV_PARAM_ERROR; } INT_Disable(); // Check if timer is reserved. if ( ! timer[ id ].allocated ) { INT_Enable(); return ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED; } // Check if timer is running. if ( ! timer[ id ].running ) { INT_Enable(); return ECODE_EMDRV_RTCDRV_TIMER_NOT_RUNNING; } timeLeft = timer[ id ].remaining; currentCnt = RTC_COUNTERGET(); lastRtcStart = lastStart; INT_Enable(); // Get number of RTC clock ticks elapsed since last RTC reschedule. currentCnt = TIMEDIFF( currentCnt, lastRtcStart ); if ( currentCnt > timeLeft ) { timeLeft = 0; } else { timeLeft -= currentCnt; } tmp = TICKS_TO_MSEC( timeLeft ); *timeRemaining = tmp; return ECODE_EMDRV_RTCDRV_OK; }
/***************************************************************************//** * @brief * Get wallclock tick count as a 32bit value. At 4 ticks per millisecond, * overflow occurs after approximately 12.5 days * * @return * Wallclock tick counter. ******************************************************************************/ uint32_t RTCDRV_GetWallClockTicks32( void ) { uint32_t overflows, ticks; /* Need to re-read data in case overflow cnt is incremented while we read. */ do { overflows = wallClockOverflowCnt; ticks = RTC_COUNTERGET(); } while ( overflows != wallClockOverflowCnt ); #if ( RTC_COUNTER_BITS < 32 ) return ( overflows << RTC_COUNTER_BITS ) + ticks; #else return ticks; #endif }
/***************************************************************************//** * @brief * Start a timer. * * @note * It is legal to start an already running timer. * * @param[in] id The id of the timer to start. * @param[in] type Timer type, oneshot or periodic. See @ref RTCDRV_TimerType_t. * @param[in] timeout Timeout expressed in milliseconds. If the timeout value * is 0, the callback function will be called immediately and * the timer will not be started. * @param[in] callback Function to call on timer expiry. See @ref * RTCDRV_Callback_t. NULL is a legal value. * @param[in] user Extra callback function parameter for user application. * * @return * @ref ECODE_EMDRV_RTCDRV_OK on success.@n * @ref ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID if id has an illegal value.@n * @ref ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED if the timer is not reserved. ******************************************************************************/ Ecode_t RTCDRV_StartTimer( RTCDRV_TimerID_t id, RTCDRV_TimerType_t type, uint32_t timeout, RTCDRV_Callback_t callback, void *user ) { uint32_t timeElapsed, cnt, compVal, loopCnt = 0; uint32_t timeToNextTimerCompletion; // Check if valid timer ID. if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) { return ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID; } INT_Disable(); if ( ! timer[ id ].allocated ) { INT_Enable(); return ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED; } if ( timeout == 0 ) { if ( callback != NULL ) { callback( id, user ); } INT_Enable(); return ECODE_EMDRV_RTCDRV_OK; } cnt = RTC_COUNTERGET(); timer[ id ].callback = callback; timer[ id ].ticks = MSEC_TO_TICKS( timeout ); if (rtcdrvTimerTypePeriodic == type) { // Calculate compensation value for periodic timers. timer[ id ].periodicCompensationUsec = 1000 * timeout - (timer[ id ].ticks * TICK_TIME_USEC); timer[ id ].periodicDriftUsec = TICK_TIME_USEC/2; } else { // Compensate for the fact that CNT is normally COMP0+1 after a // compare match event on some devices. timer[ id ].ticks -= RTC_ONESHOT_TICK_ADJUST; } // Add one tick in order to compensate if RTC is close to an increment event. timer[ id ].remaining = timer[ id ].ticks + 1; timer[ id ].running = true; timer[ id ].timerType = type; timer[ id ].user = user; if ( inTimerIRQ == true ) { // Exit now, remaining processing will be done in IRQ handler. INT_Enable(); return ECODE_EMDRV_RTCDRV_OK; } // StartTimer() may recurse, keep track of recursion level. if ( startTimerNestingLevel < UINT32_MAX ) { startTimerNestingLevel++; } if ( rtcRunning == false ) { #if defined( RTCDRV_USE_RTC ) lastStart = ( cnt ) & RTC_COUNTER_MASK; #elif defined( RTCDRV_USE_RTCC ) lastStart = cnt; #endif RTC_INTCLEAR( RTC_COMP_INT ); compVal = SL_MIN( timer[ id ].remaining, RTC_CLOSE_TO_MAX_VALUE ); RTC_COMPARESET( cnt + compVal ); // Start the timer system by enabling the compare interrupt. RTC_INTENABLE( RTC_COMP_INT ); #if defined( EMODE_DYNAMIC ) // When RTC is running, we can not allow EM3 or EM4. if ( sleepBlocked == false ) { sleepBlocked = true; SLEEP_SleepBlockBegin( sleepEM3 ); } #endif rtcRunning = true; } else { // The timer system is running. We must stop, update timers with the time // elapsed so far, find the timer with the shortest timeout and then restart. // As StartTimer() may be called from the callbacks we only do this // processing at the first nesting level. if ( startTimerNestingLevel == 1 ) { timer[ id ].running = false; // This loop is repeated if CNT is incremented while processing. do { RTC_INTDISABLE( RTC_COMP_INT ); timeElapsed = TIMEDIFF( cnt, lastStart ); #if defined( RTCDRV_USE_RTC ) // Compensate for the fact that CNT is normally COMP0+1 after a // compare match event. if ( timeElapsed == RTC_MAX_VALUE ) { timeElapsed = 0; } #endif // Update all timers with elapsed time. checkAllTimers( timeElapsed ); // Execute timer callbacks. executeTimerCallbacks(); // Set timer to running only after checkAllTimers() is called once. if ( loopCnt == 0 ) { timer[ id ].running = true; } loopCnt++; // Restart RTC according to next timeout. rescheduleRtc( cnt ); cnt = RTC_COUNTERGET(); timeElapsed = TIMEDIFF( cnt, lastStart ); timeToNextTimerCompletion = TIMEDIFF( RTC_COMPAREGET(), lastStart ); /* If the counter has passed the COMP(ARE) register value since we checked the timers, then we should recheck the timers and reschedule again. */ } while ( rtcRunning && (timeElapsed > timeToNextTimerCompletion)); } } if ( startTimerNestingLevel > 0 ) { startTimerNestingLevel--; } INT_Enable(); return ECODE_EMDRV_RTCDRV_OK; }