rtcdriver.c

Go to the documentation of this file.
00001 /***************************************************************************/
00016 #include <string.h>
00017 
00018 #include "em_device.h"
00019 #include "em_cmu.h"
00020 #include "em_common.h"
00021 #include "em_int.h"
00022 
00023 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
00024 #define RTCDRV_USE_RTCC
00025 #else
00026 #define RTCDRV_USE_RTC
00027 #endif
00028 
00029 #if defined( RTCDRV_USE_RTCC )
00030 #include "em_rtcc.h"
00031 #else
00032 #include "em_rtc.h"
00033 #endif
00034 
00035 #include "rtcdriver.h"
00036 #if defined( EMDRV_RTCDRV_SLEEPDRV_INTEGRATION )
00037 #include "sleep.h"
00038 #endif
00039 
00041 
00042 #if     defined( EMDRV_RTCDRV_SLEEPDRV_INTEGRATION ) \
00043     && !defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG     ) \
00044     &&  defined( RTCDRV_USE_RTC )
00045 // Do not allow EM3/EM4 energy modes when the RTC is running.
00046 #define EMODE_DYNAMIC
00047 #endif
00048 
00049 #if    defined( EMDRV_RTCDRV_SLEEPDRV_INTEGRATION ) \
00050     && defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG     ) \
00051     &&  defined( RTCDRV_USE_RTC )
00052 // Always deny EM3/EM4 energy modes when wallclock is enabled.
00053 #define EMODE_NEVER_ALLOW_EM3EM4
00054 #endif
00055 
00056 //
00057 // Various #define's to enable use of both RTC and RTCC.
00058 //
00059 #if defined( RTCDRV_USE_RTCC )
00060 #define TIMEDIFF( a, b )              ((a) - (b))
00061 #define RTC_COUNTERGET()              RTCC_CounterGet()
00062 #define RTC_COUNTER_BITS              32
00063 #define RTC_ALL_INTS                  _RTCC_IF_MASK
00064 #define RTC_OF_INT                    RTCC_IF_OF
00065 #define RTC_COMP_INT                  RTCC_IF_CC1
00066 #define RTC_COUNTER_MASK              (_RTCC_CNT_MASK)
00067 #define RTC_MAX_VALUE                 (_RTCC_CNT_MASK)
00068 #define RTC_INTDISABLE( x )           RTCC_IntDisable( x )
00069 #define RTC_INTENABLE( x )            RTCC_IntEnable(  x )
00070 #define RTC_INTCLEAR( x )             RTCC_IntClear(   x )
00071 #define RTC_INTGET()                  RTCC_IntGet()
00072 #define RTC_COUNTERRESET()            RTCC->CNT = _RTCC_CNT_RESETVALUE
00073 #define RTC_COMPARESET( x )           RTCC_ChannelCCVSet( 1, x )
00074 #define RTC_COMPAREGET()              RTCC_ChannelCCVGet( 1 )
00075 #define NVIC_CLEARPENDINGIRQ()        NVIC_ClearPendingIRQ( RTCC_IRQn )
00076 #define NVIC_DISABLEIRQ()             NVIC_DisableIRQ( RTCC_IRQn )
00077 #define NVIC_ENABLEIRQ()              NVIC_EnableIRQ( RTCC_IRQn )
00078 
00079 #else
00080 // To get the math correct we must have the MSB of the underlying 24bit
00081 // counter in the MSB position of a uint32_t datatype.
00082 #define TIMEDIFF( a, b )              ((( (a)<<8) - ((b)<<8) ) >> 8 )
00083 #define RTC_COUNTERGET()              RTC_CounterGet()
00084 #define RTC_COUNTER_BITS              24
00085 #define RTC_ALL_INTS                  _RTC_IF_MASK
00086 #define RTC_OF_INT                    RTC_IF_OF
00087 #define RTC_COMP_INT                  RTC_IF_COMP0
00088 #define RTC_COUNTER_MASK              (_RTC_CNT_MASK)
00089 #define RTC_MAX_VALUE                 (_RTC_CNT_MASK)
00090 #define RTC_INTDISABLE( x )           RTC_IntDisable( x )
00091 #define RTC_INTENABLE( x )            RTC_IntEnable(  x )
00092 #define RTC_INTCLEAR( x )             RTC_IntClear(   x )
00093 #define RTC_INTGET()                  RTC_IntGet()
00094 #define RTC_COUNTERRESET()            RTC_CounterReset()
00095 #define RTC_COMPARESET( x )           RTC_CompareSet( 0, (x) & _RTC_COMP0_MASK )
00096 #define RTC_COMPAREGET()              RTC_CompareGet( 0 )
00097 #define NVIC_CLEARPENDINGIRQ()        NVIC_ClearPendingIRQ( RTC_IRQn )
00098 #define NVIC_DISABLEIRQ()             NVIC_DisableIRQ( RTC_IRQn )
00099 #define NVIC_ENABLEIRQ()              NVIC_EnableIRQ( RTC_IRQn )
00100 #endif
00101 
00102 // Maximum number of ticks per overflow period (not the maximum tick value)
00103 #define MAX_RTC_TICK_CNT              (RTC_MAX_VALUE+1UL)
00104 #define RTC_CLOSE_TO_MAX_VALUE        (RTC_MAX_VALUE-100UL)
00105 
00106 #if defined(_EFM32_GECKO_FAMILY)
00107 // Assume 32kHz RTC/RTCC clock, cmuClkDiv_2 prescaler, 16 ticks per millisecond
00108 #define RTC_DIVIDER                     ( cmuClkDiv_2 )
00109 #else
00110 // Assume 32kHz RTC/RTCC clock, cmuClkDiv_8 prescaler, 4 ticks per millisecond
00111 #define RTC_DIVIDER                     ( cmuClkDiv_8 )
00112 #endif
00113 
00114 #define RTC_CLOCK                       ( 32768U )
00115 #define MSEC_TO_TICKS_DIVIDER           ( 1000U * RTC_DIVIDER )
00116 #define MSEC_TO_TICKS_ROUNDING_FACTOR   ( MSEC_TO_TICKS_DIVIDER / 2 )
00117 #define MSEC_TO_TICKS( ms )             ( ( ( (uint64_t)(ms) * RTC_CLOCK )    \
00118                                             + MSEC_TO_TICKS_ROUNDING_FACTOR ) \
00119                                           / MSEC_TO_TICKS_DIVIDER )
00120 
00121 #define TICKS_TO_MSEC_ROUNDING_FACTOR   ( RTC_CLOCK / 2 )
00122 #define TICKS_TO_MSEC( ticks )          ( ( ( (uint64_t)(ticks)               \
00123                                               * RTC_DIVIDER * 1000U )         \
00124                                             + TICKS_TO_MSEC_ROUNDING_FACTOR ) \
00125                                           / RTC_CLOCK )
00126 
00127 #define TICKS_TO_SEC_ROUNDING_FACTOR    ( RTC_CLOCK / 2 )
00128 #define TICKS_TO_SEC( ticks )           ( ( ( (uint64_t)(ticks)               \
00129                                               * RTC_DIVIDER )                 \
00130                                             + TICKS_TO_SEC_ROUNDING_FACTOR )  \
00131                                           / RTC_CLOCK )
00132 #define TICK_TIME_USEC                  ( 1000000 * RTC_DIVIDER / RTC_CLOCK )
00133 
00134 typedef struct Timer
00135 {
00136   uint64_t            remaining;
00137   uint64_t            ticks;
00138   int                 periodicCompensationUsec;
00139   unsigned int        periodicDriftUsec;
00140   RTCDRV_Callback_t   callback;
00141   bool                running;
00142   bool                doCallback;
00143   bool                allocated;
00144   RTCDRV_TimerType_t  timerType;
00145   void                *user;
00146 } Timer_t;
00147 
00148 static Timer_t            timer[ EMDRV_RTCDRV_NUM_TIMERS ];
00149 static uint32_t           lastStart;
00150 static volatile uint32_t  startTimerNestingLevel;
00151 static bool               inTimerIRQ;
00152 static bool               rtcRunning;
00153 static bool               rtcdrvIsInitialized = false;
00154 #if defined( EMODE_DYNAMIC )
00155 static bool               sleepBlocked;
00156 #endif
00157 
00158 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00159 static volatile uint32_t  wallClockOverflowCnt;
00160 static uint32_t           wallClockTimeBase;
00161 #endif
00162 
00163 #if defined( RTCDRV_USE_RTC )
00164 static const RTC_Init_TypeDef initRTC =
00165 {
00166   true,  // Start counting when init completed.
00167   false, // Disable updating RTC during debug halt.
00168   false  // Count until max. to wrap around.
00169 };
00170 
00171 #elif defined( RTCDRV_USE_RTCC )
00172 static RTCC_Init_TypeDef initRTCC =
00173 {
00174   true,                 /* Start counting when init completed. */
00175   false,                /* Disable updating RTC during debug halt. */
00176   false,                /* Prescaler counts until max. before wrap around. */
00177   false,                /* Counter counts until max. before wrap around. */
00178   rtccCntPresc_8,       /* Set RTCC prescaler to 8 */
00179   rtccCntTickPresc,     /* Count according to prescaler configuration */
00180   false,                /* Disable storing RTCC counter value in RTCC_CCV2 upon backup mode entry. */
00181   false,                /* LFXO fail detection disabled */
00182   rtccCntModeNormal,    /* Use RTCC in normal mode and not in calender mode */
00183   false                 /* No leap year correction. */
00184 };
00185 
00186 static RTCC_CCChConf_TypeDef initRTCCCompareChannel =
00187 {
00188   rtccCapComChModeCompare,    /* Use Compare mode */
00189   rtccCompMatchOutActionPulse,/* Don't care */
00190   rtccPRSCh0,                 /* PRS not used */
00191   rtccInEdgeNone,             /* Capture Input not used */
00192   rtccCompBaseCnt,            /* Compare with Base CNT register */
00193   0,                          /* Compare mask */
00194   rtccDayCompareModeMonth     /* Don't care */
00195 };
00196 #endif
00197 
00198 static void checkAllTimers( uint32_t timeElapsed );
00199 static void delayTicks( uint32_t ticks );
00200 static void executeTimerCallbacks( void );
00201 static void rescheduleRtc( uint32_t rtcCnt );
00202 
00204 
00205 /***************************************************************************/
00219 Ecode_t RTCDRV_AllocateTimer( RTCDRV_TimerID_t *id )
00220 {
00221   int i      = 0;
00222   int retVal = 0;
00223 
00224   INT_Disable();
00225   // Iterate through the table of the timers until the first available.
00226   while ( ( i < EMDRV_RTCDRV_NUM_TIMERS ) && ( timer[ i ].allocated ) ) {
00227     i++;
00228   }
00229 
00230   // Check if we reached the end of the table.
00231   if ( i == EMDRV_RTCDRV_NUM_TIMERS ) {
00232     retVal = ECODE_EMDRV_RTCDRV_ALL_TIMERS_USED;
00233   } else {
00234     // Check if a NULL pointer was passed.
00235     if ( id != NULL ) {
00236       timer[ i ].allocated = true;
00237       *id = i;
00238       retVal = ECODE_EMDRV_RTCDRV_OK;
00239     } else {
00240       retVal = ECODE_EMDRV_RTCDRV_PARAM_ERROR;
00241     }
00242   }
00243   INT_Enable();
00244 
00245   return retVal;
00246 }
00247 
00248 /***************************************************************************/
00257 Ecode_t RTCDRV_Delay( uint32_t ms )
00258 {
00259   uint64_t totalTicks;
00260 
00261   totalTicks = MSEC_TO_TICKS( ms );
00262 
00263   while ( totalTicks > RTC_CLOSE_TO_MAX_VALUE ) {
00264     delayTicks( RTC_CLOSE_TO_MAX_VALUE );
00265     totalTicks -= RTC_CLOSE_TO_MAX_VALUE;
00266   }
00267   delayTicks( totalTicks );
00268 
00269   return ECODE_EMDRV_RTCDRV_OK;
00270 }
00271 
00272 /***************************************************************************/
00285 Ecode_t RTCDRV_FreeTimer( RTCDRV_TimerID_t id )
00286 {
00287   // Check if valid timer ID.
00288   if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
00289     return ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID;
00290   }
00291 
00292   INT_Disable();
00293 
00294   timer[ id ].running   = false;
00295   timer[ id ].allocated = false;
00296 
00297   INT_Enable();
00298 
00299   return ECODE_EMDRV_RTCDRV_OK;
00300 }
00301 
00302 /***************************************************************************/
00313 Ecode_t RTCDRV_Init( void )
00314 {
00315   if ( rtcdrvIsInitialized == true ) {
00316     return ECODE_EMDRV_RTCDRV_OK;
00317   }
00318   rtcdrvIsInitialized = true;
00319 
00320   // Ensure LE modules are clocked.
00321   CMU_ClockEnable( cmuClock_CORELE, true );
00322 
00323 #if defined( CMU_LFECLKEN0_RTCC )
00324   // Enable LFECLK in CMU (will also enable oscillator if not enabled).
00325   CMU_ClockSelectSet( cmuClock_LFE, cmuSelect_LFXO );
00326 #else
00327   // Enable LFACLK in CMU (will also enable oscillator if not enabled).
00328   CMU_ClockSelectSet( cmuClock_LFA, cmuSelect_LFXO );
00329 #endif
00330 
00331 #if defined( RTCDRV_USE_RTC )
00332   // Set clock divider.
00333   CMU_ClockDivSet( cmuClock_RTC, RTC_DIVIDER );
00334 
00335   // Enable RTC module clock.
00336   CMU_ClockEnable( cmuClock_RTC, true );
00337 
00338   // Initialize RTC.
00339   RTC_Init( &initRTC );
00340 
00341 #elif defined( RTCDRV_USE_RTCC )
00342   // Set clock divider.
00343   initRTCC.presc = (RTCC_CntPresc_TypeDef)CMU_DivToLog2( RTC_DIVIDER );
00344 
00345   // Enable RTCC module clock.
00346   CMU_ClockEnable( cmuClock_RTCC, true );
00347 
00348   // Initialize RTCC.
00349   RTCC_Init( &initRTCC );
00350 
00351   // Set up Compare channel.
00352   RTCC_ChannelInit( 1, &initRTCCCompareChannel );
00353 #endif
00354 
00355   // Disable RTC/RTCC interrupt generation.
00356   RTC_INTDISABLE( RTC_ALL_INTS );
00357   RTC_INTCLEAR( RTC_ALL_INTS );
00358 
00359   RTC_COUNTERRESET();
00360 
00361   // Clear and then enable RTC interrupts in NVIC.
00362   NVIC_CLEARPENDINGIRQ();
00363   NVIC_ENABLEIRQ();
00364 
00365 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00366   // Enable overflow interrupt for wallclock.
00367   RTC_INTENABLE( RTC_OF_INT );
00368 #endif
00369 
00370   // Reset RTCDRV internal data structures/variables.
00371   memset( timer, 0, sizeof( timer ) );
00372   inTimerIRQ             = false;
00373   rtcRunning             = false;
00374   startTimerNestingLevel = 0;
00375 #if defined( EMODE_DYNAMIC )
00376   sleepBlocked           = false;
00377 #endif
00378 
00379 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00380   wallClockOverflowCnt = 0;
00381   wallClockTimeBase    = 0;
00382 
00383 #if defined( EMODE_NEVER_ALLOW_EM3EM4 )
00384   // Always block EM3 and EM4 if wallclock is running.
00385   SLEEP_SleepBlockBegin( sleepEM3 );
00386 #endif
00387 
00388 #endif
00389 
00390   return ECODE_EMDRV_RTCDRV_OK;
00391 }
00392 
00393 /***************************************************************************/
00406 Ecode_t RTCDRV_DeInit( void )
00407 {
00408   // Disable and clear all interrupt sources.
00409   NVIC_DISABLEIRQ();
00410   RTC_INTDISABLE( RTC_ALL_INTS );
00411   RTC_INTCLEAR( RTC_ALL_INTS );
00412   NVIC_CLEARPENDINGIRQ();
00413 
00414   // Disable RTC module and its clock.
00415 #if defined( RTCDRV_USE_RTC )
00416   RTC_Enable( false );
00417   CMU_ClockEnable( cmuClock_RTC, false );
00418 #elif defined( RTCDRV_USE_RTCC )
00419   RTCC_Enable( false );
00420   CMU_ClockEnable( cmuClock_RTCC, false );
00421 #endif
00422 
00423 #if defined( EMODE_NEVER_ALLOW_EM3EM4 )
00424   // End EM3 and EM4 blocking.
00425   SLEEP_SleepBlockEnd( sleepEM3 );
00426 #endif
00427 
00428 #if defined( EMODE_DYNAMIC )
00429   // End EM3 and EM4 blocking if a block start has been set.
00430   if ( sleepBlocked ) {
00431     SLEEP_SleepBlockEnd( sleepEM3 );
00432   }
00433 #endif
00434 
00435   // Mark the driver as uninitialized.
00436   rtcdrvIsInitialized = false;
00437 
00438   return ECODE_EMDRV_RTCDRV_OK;
00439 }
00440 
00441 /***************************************************************************/
00457 Ecode_t RTCDRV_IsRunning( RTCDRV_TimerID_t id, bool *isRunning )
00458 {
00459   // Check if valid timer ID.
00460   if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
00461     return ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID;
00462   }
00463 
00464   // Check pointer validity.
00465   if ( isRunning == NULL ) {
00466     return ECODE_EMDRV_RTCDRV_PARAM_ERROR;
00467   }
00468 
00469   INT_Disable();
00470   // Check if timer is reserved.
00471   if ( ! timer[ id ].allocated ) {
00472     INT_Enable();
00473     return ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED;
00474   }
00475   *isRunning = timer[ id ].running;
00476   INT_Enable();
00477 
00478   return ECODE_EMDRV_RTCDRV_OK;
00479 }
00480 
00481 /***************************************************************************/
00502 Ecode_t RTCDRV_StartTimer(  RTCDRV_TimerID_t id,
00503                             RTCDRV_TimerType_t type,
00504                             uint32_t timeout,
00505                             RTCDRV_Callback_t callback,
00506                             void *user )
00507 {
00508   uint32_t timeElapsed, cnt, compVal, loopCnt = 0;
00509   uint32_t timeToNextTimerCompletion;
00510 
00511   // Check if valid timer ID.
00512   if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
00513     return ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID;
00514   }
00515 
00516   INT_Disable();
00517   if ( ! timer[ id ].allocated ) {
00518     INT_Enable();
00519     return ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED;
00520   }
00521 
00522   if ( timeout == 0 ) {
00523     if ( callback != NULL ) {
00524       callback( id, user );
00525     }
00526     INT_Enable();
00527     return ECODE_EMDRV_RTCDRV_OK;
00528   }
00529 
00530   cnt = RTC_COUNTERGET();
00531 
00532   timer[ id ].callback  = callback;
00533   timer[ id ].ticks     = MSEC_TO_TICKS( timeout );
00534   if (rtcdrvTimerTypePeriodic == type) {
00535     // Calculate compensation value for periodic timers.
00536     timer[ id ].periodicCompensationUsec = 1000 * timeout -
00537       (timer[ id ].ticks * TICK_TIME_USEC);
00538     timer[ id ].periodicDriftUsec = TICK_TIME_USEC/2;
00539   }
00540   // Add one tick in order to compensate if RTC is close to an increment event.
00541   timer[ id ].remaining = timer[ id ].ticks + 1;
00542   timer[ id ].running   = true;
00543   timer[ id ].timerType = type;
00544   timer[ id ].user      = user;
00545 
00546   if ( inTimerIRQ == true ) {
00547     // Exit now, remaining processing will be done in IRQ handler.
00548     INT_Enable();
00549     return ECODE_EMDRV_RTCDRV_OK;
00550   }
00551 
00552   // StartTimer() may recurse, keep track of recursion level.
00553   if ( startTimerNestingLevel < UINT32_MAX ) {
00554     startTimerNestingLevel++;
00555   }
00556 
00557   if ( rtcRunning == false ) {
00558 
00559 #if defined( RTCDRV_USE_RTC )
00560     lastStart = ( cnt ) & RTC_COUNTER_MASK;
00561 #elif defined( RTCDRV_USE_RTCC )
00562     lastStart = cnt;
00563 #endif
00564 
00565     RTC_INTCLEAR( RTC_COMP_INT );
00566 
00567     compVal = EFM32_MIN( timer[ id ].remaining, RTC_CLOSE_TO_MAX_VALUE );
00568     RTC_COMPARESET( cnt + compVal );
00569 
00570     // Start the timer system by enabling the compare interrupt.
00571     RTC_INTENABLE( RTC_COMP_INT );
00572 
00573 #if defined( EMODE_DYNAMIC )
00574     // When RTC is running, we can not allow EM3 or EM4.
00575     if ( sleepBlocked == false ) {
00576       sleepBlocked = true;
00577       SLEEP_SleepBlockBegin( sleepEM3 );
00578     }
00579 #endif
00580 
00581     rtcRunning = true;
00582 
00583   } else {
00584 
00585     // The timer system is running. We must stop, update timers with the time
00586     // elapsed so far, find the timer with the shortest timeout and then restart.
00587     // As StartTimer() may be called from the callbacks we only do this
00588     // processing at the first nesting level.
00589     if ( startTimerNestingLevel == 1  ) {
00590 
00591       timer[ id ].running = false;
00592       // This loop is repeated if CNT is incremented while processing.
00593       do {
00594 
00595         RTC_INTDISABLE( RTC_COMP_INT );
00596 
00597         timeElapsed = TIMEDIFF( cnt, lastStart );
00598 #if defined( RTCDRV_USE_RTC )
00599         // Compensate for the fact that CNT is normally COMP0+1 after a
00600         // compare match event.
00601         if ( timeElapsed == RTC_MAX_VALUE ) {
00602           timeElapsed = 0;
00603         }
00604 #endif
00605 
00606         // Update all timers with elapsed time.
00607         checkAllTimers( timeElapsed );
00608 
00609         // Execute timer callbacks.
00610         executeTimerCallbacks();
00611 
00612         // Set timer to running only after checkAllTimers() is called once.
00613         if ( loopCnt == 0 ) {
00614           timer[ id ].running = true;
00615         }
00616         loopCnt++;
00617 
00618         // Restart RTC according to next timeout.
00619         rescheduleRtc( cnt );
00620 
00621         cnt = RTC_COUNTERGET();
00622         timeElapsed = TIMEDIFF( cnt, lastStart );
00623         timeToNextTimerCompletion = TIMEDIFF( RTC_COMPAREGET(), lastStart );
00624 
00625         /* If the counter has passed the COMP(ARE) register value since we
00626            checked the timers, then we should recheck the timers and reschedule
00627            again. */
00628       }
00629       while ( rtcRunning && (timeElapsed > timeToNextTimerCompletion));
00630     }
00631   }
00632 
00633   if ( startTimerNestingLevel > 0 ) {
00634     startTimerNestingLevel--;
00635   }
00636 
00637   INT_Enable();
00638   return ECODE_EMDRV_RTCDRV_OK;
00639 }
00640 
00641 /***************************************************************************/
00652 Ecode_t RTCDRV_StopTimer( RTCDRV_TimerID_t id )
00653 {
00654   // Check if valid timer ID.
00655   if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
00656     return ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID;
00657   }
00658 
00659   INT_Disable();
00660   if ( ! timer[ id ].allocated ) {
00661     INT_Enable();
00662     return ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED;
00663   }
00664 
00665   timer[ id ].running = false;
00666   INT_Enable();
00667 
00668   return ECODE_EMDRV_RTCDRV_OK;
00669 }
00670 
00671 /***************************************************************************/
00687 Ecode_t RTCDRV_TimeRemaining( RTCDRV_TimerID_t id, uint32_t *timeRemaining )
00688 {
00689   uint64_t tmp;
00690   uint32_t timeLeft, currentCnt, lastRtcStart;
00691 
00692   // Check if valid timer ID.
00693   if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
00694     return ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID;
00695   }
00696 
00697   // Check pointer validity.
00698   if ( timeRemaining == NULL ) {
00699     return ECODE_EMDRV_RTCDRV_PARAM_ERROR;
00700   }
00701 
00702   INT_Disable();
00703   // Check if timer is reserved.
00704   if ( ! timer[ id ].allocated ) {
00705     INT_Enable();
00706     return ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED;
00707   }
00708 
00709   // Check if timer is running.
00710   if ( ! timer[ id ].running ) {
00711     INT_Enable();
00712     return ECODE_EMDRV_RTCDRV_TIMER_NOT_RUNNING;
00713   }
00714 
00715   timeLeft     = timer[ id ].remaining;
00716   currentCnt   = RTC_COUNTERGET();
00717   lastRtcStart = lastStart;
00718   INT_Enable();
00719 
00720   // Get number of RTC clock ticks elapsed since last RTC reschedule.
00721   currentCnt = TIMEDIFF( currentCnt, lastRtcStart );
00722 
00723   if ( currentCnt > timeLeft ) {
00724     timeLeft = 0;
00725   } else {
00726     timeLeft -= currentCnt;
00727   }
00728 
00729   tmp = TICKS_TO_MSEC( timeLeft );
00730   *timeRemaining = tmp;
00731 
00732   return ECODE_EMDRV_RTCDRV_OK;
00733 }
00734 
00735 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00736 /***************************************************************************/
00743 uint32_t RTCDRV_GetWallClock( void )
00744 {
00745   return wallClockTimeBase
00746          + (uint32_t)TICKS_TO_SEC( RTCDRV_GetWallClockTicks32() );
00747 }
00748 #endif
00749 
00750 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00751 /***************************************************************************/
00759 uint32_t RTCDRV_GetWallClockTicks32( void )
00760 {
00761   uint32_t overflows, ticks;
00762 
00763   /* Need to re-read data in case overflow cnt is incremented while we read. */
00764   do
00765   {
00766     overflows = wallClockOverflowCnt;
00767     ticks     = RTC_COUNTERGET();
00768   } while ( overflows != wallClockOverflowCnt );
00769 
00770 #if ( RTC_COUNTER_BITS < 32 )
00771   return ( overflows << RTC_COUNTER_BITS ) + ticks;
00772 #else
00773   return ticks;
00774 #endif
00775 }
00776 #endif
00777 
00778 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00779 /***************************************************************************/
00786 uint64_t RTCDRV_GetWallClockTicks64( void )
00787 {
00788   uint64_t overflows, ticks;
00789 
00790   /* Need to re-read data in case overflow cnt is incremented while we read. */
00791   do
00792   {
00793     overflows = wallClockOverflowCnt;
00794     ticks     = RTC_COUNTERGET();
00795   } while ( overflows != wallClockOverflowCnt );
00796 
00797   return ( overflows << RTC_COUNTER_BITS ) + ticks;
00798 }
00799 #endif
00800 
00801 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00802 /***************************************************************************/
00811 Ecode_t RTCDRV_SetWallClock( uint32_t secs )
00812 {
00813   wallClockTimeBase = secs - TICKS_TO_SEC( RTCDRV_GetWallClockTicks32() );
00814   return ECODE_EMDRV_RTCDRV_OK;
00815 }
00816 #endif
00817 
00818 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00819 /***************************************************************************/
00828 uint64_t  RTCDRV_MsecsToTicks( uint32_t ms )
00829 {
00830   return MSEC_TO_TICKS( ms );
00831 }
00832 #endif
00833 
00834 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00835 /***************************************************************************/
00844 uint64_t  RTCDRV_SecsToTicks( uint32_t secs )
00845 {
00846   return MSEC_TO_TICKS( 1000 * secs );
00847 }
00848 #endif
00849 
00850 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00851 /***************************************************************************/
00860 uint32_t  RTCDRV_TicksToMsec( uint64_t ticks )
00861 {
00862   return TICKS_TO_MSEC( ticks );
00863 }
00864 #endif
00865 
00866 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00867 /***************************************************************************/
00876 uint32_t  RTCDRV_TicksToSec( uint64_t ticks )
00877 {
00878   return TICKS_TO_MSEC( ticks ) / 1000;
00879 }
00880 #endif
00881 
00883 
00884 #if defined( RTCDRV_USE_RTC )
00885 void RTC_IRQHandler(void)
00886 #elif defined( RTCDRV_USE_RTCC )
00887 void RTCC_IRQHandler(void)
00888 #endif
00889 {
00890   uint32_t flags, timeElapsed, cnt, timeToNextTimerCompletion;
00891 
00892   INT_Disable();
00893 
00894   // CNT will normally be COMP0+1 at this point,
00895   // unless IRQ latency exceeded one tick period.
00896 
00897   flags = RTC_INTGET();
00898 
00899   if ( flags & RTC_COMP_INT ) {
00900 
00901     // Stop timer system by disabling the compare IRQ.
00902     // Update all timers with the time elapsed, call callbacks if needed,
00903     // then find the timer with the shortest timeout (if any at all) and
00904     // reenable the compare IRQ if needed.
00905 
00906     inTimerIRQ = true;
00907 
00908     cnt = RTC_COUNTERGET();
00909 
00910     // This loop is repeated if CNT is incremented while processing.
00911     do {
00912 
00913       RTC_INTDISABLE( RTC_COMP_INT );
00914 
00915       timeElapsed = TIMEDIFF( cnt, lastStart );
00916 
00917       // Update all timers with elapsed time.
00918       checkAllTimers( timeElapsed );
00919 
00920       // Execute timer callbacks.
00921       executeTimerCallbacks();
00922 
00923       // Restart RTC according to next timeout.
00924       rescheduleRtc( cnt );
00925 
00926       cnt = RTC_COUNTERGET();
00927       timeElapsed = TIMEDIFF( cnt, lastStart );
00928       timeToNextTimerCompletion = TIMEDIFF( RTC_COMPAREGET(), lastStart );
00929       /* If the counter has passed the COMP(ARE) register value since we
00930          checked the timers, then we should recheck the timers and reschedule
00931          again. */
00932     }
00933     while ( rtcRunning && (timeElapsed > timeToNextTimerCompletion));
00934     inTimerIRQ = false;
00935   }
00936 
00937 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00938   if ( flags & RTC_OF_INT )
00939   {
00940     RTC_INTCLEAR( RTC_OF_INT );
00941     wallClockOverflowCnt++;
00942   }
00943 #endif
00944 
00945   INT_Enable();
00946 }
00947 
00948 static void checkAllTimers( uint32_t timeElapsed )
00949 {
00950   int i;
00951 #if defined( EMODE_DYNAMIC )
00952   int numOfTimersRunning = 0;
00953 #endif
00954 
00955   // Iterate through the timer table.
00956   // Update time remaining, check for timeout and rescheduling of periodic
00957   // timers, check for callbacks.
00958 
00959   for ( i = 0; i < EMDRV_RTCDRV_NUM_TIMERS; i++ ) {
00960     timer[ i ].doCallback = false;
00961     if ( timer[ i ].running == true ) {
00962 #if defined( EMODE_DYNAMIC )
00963       numOfTimersRunning++;
00964 #endif
00965       if ( timer[ i ].remaining > timeElapsed ) {
00966         timer[ i ].remaining -= timeElapsed;
00967       } else {
00968         if ( timer[ i ].timerType == rtcdrvTimerTypeOneshot ) {
00969           timer[ i ].running = false;
00970 #if defined( EMODE_DYNAMIC )
00971           numOfTimersRunning--;
00972 #endif
00973         } else {
00974           // Compensate overdue periodic timers to avoid accumlating errors.
00975           timer[ i ].remaining = timer[ i ].ticks - timeElapsed +
00976                                  timer[ i ].remaining;
00977           if ( timer[ i ].periodicCompensationUsec > 0 ) {
00978             timer[ i ].periodicDriftUsec += timer[i].periodicCompensationUsec;
00979             if (timer[ i ].periodicDriftUsec >= TICK_TIME_USEC) {
00980               // Add a tick if the timer drift is longer than the time of
00981               // one tick.
00982               timer[ i ].remaining += 1;
00983               timer[ i ].periodicDriftUsec -= TICK_TIME_USEC;
00984             }
00985           }
00986           else {
00987             timer[ i ].periodicDriftUsec -= timer[i].periodicCompensationUsec;
00988             if (timer[ i ].periodicDriftUsec >= TICK_TIME_USEC) {
00989               // Subtract one tick if the timer drift is longer than the time
00990               // of one tick.
00991               timer[ i ].remaining -= 1;
00992               timer[ i ].periodicDriftUsec -= TICK_TIME_USEC;
00993             }
00994           }
00995         }
00996         if ( timer[ i ].callback != NULL ) {
00997           timer[ i ].doCallback = true;
00998         }
00999       }
01000     }
01001   }
01002 
01003 #if defined( EMODE_DYNAMIC )
01004   // If no timers are running, we can remove block on EM3 and EM4 sleep modes.
01005   if ( ( numOfTimersRunning == 0 ) && ( sleepBlocked == true ) ) {
01006     sleepBlocked = false;
01007     SLEEP_SleepBlockEnd( sleepEM3 );
01008   }
01009 #endif
01010 }
01011 
01012 static void delayTicks( uint32_t ticks )
01013 {
01014   uint32_t startTime;
01015   volatile uint32_t now;
01016 
01017   if ( ticks ) {
01018     startTime = RTC_COUNTERGET();
01019     do {
01020       now = RTC_COUNTERGET();
01021     } while ( TIMEDIFF( now, startTime ) < ticks );
01022   }
01023 }
01024 
01025 static void executeTimerCallbacks( void )
01026 {
01027   int i;
01028 
01029   for ( i = 0; i < EMDRV_RTCDRV_NUM_TIMERS; i++ ) {
01030     if ( timer[ i ].doCallback ) {
01031       timer[ i ].callback( i, timer[ i ].user );
01032     }
01033   }
01034 }
01035 
01036 static void rescheduleRtc( uint32_t rtcCnt )
01037 {
01038   int i;
01039   uint64_t min = UINT64_MAX;
01040 
01041   // Find the timer with shortest timeout.
01042   for ( i = 0; i < EMDRV_RTCDRV_NUM_TIMERS; i++ ) {
01043     if (    ( timer[ i ].running   == true )
01044          && ( timer[ i ].remaining <  min  ) ) {
01045       min = timer[ i ].remaining;
01046     }
01047   }
01048 
01049   rtcRunning = false;
01050   if ( min != UINT64_MAX ) {
01051     min = EFM32_MIN( min, RTC_CLOSE_TO_MAX_VALUE );
01052 #if defined( RTCDRV_USE_RTC )
01053     if ( inTimerIRQ == false ) {
01054       lastStart = ( rtcCnt ) & RTC_COUNTER_MASK;
01055     } else
01056 #endif
01057     {
01058       lastStart = rtcCnt;
01059     }
01060     RTC_INTCLEAR( RTC_COMP_INT );
01061 
01062     RTC_COMPARESET( rtcCnt + min );
01063 
01064 #if defined( EMODE_DYNAMIC )
01065     // When RTC is running, we can not allow EM3 or EM4.
01066     if ( sleepBlocked == false ) {
01067       sleepBlocked = true;
01068       SLEEP_SleepBlockBegin( sleepEM3 );
01069     }
01070 #endif
01071 
01072     rtcRunning = true;
01073 
01074     // Reenable compare IRQ.
01075     RTC_INTENABLE( RTC_COMP_INT );
01076   }
01077 }
01079 
01080 /******** THE REST OF THE FILE IS DOCUMENTATION ONLY !**********************/