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( _EFM_DEVICE )
00024 #include "em_rtc.h"
00025 #elif defined( _EFR_DEVICE )
00026 #include "em_rtcc.h"
00027 #endif
00028 
00029 #include "rtcdriver.h"
00030 #if defined( EMDRV_RTCDRV_SLEEPDRV_INTEGRATION )
00031 #include "sleep.h"
00032 #endif
00033 
00035 
00036 #if     defined( EMDRV_RTCDRV_SLEEPDRV_INTEGRATION ) \
00037     && !defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG     ) \
00038     &&  defined( _EFM_DEVICE )
00039 // Do not allow EM3/EM4 energy modes when the RTC is running.
00040 #define EMODE_DYNAMIC
00041 #endif
00042 
00043 #if    defined( EMDRV_RTCDRV_SLEEPDRV_INTEGRATION ) \
00044     && defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG     ) \
00045     &&  defined( _EFM_DEVICE )
00046 // Always deny EM3/EM4 energy modes when wallclock is enabled.
00047 #define EMODE_NEVER_ALLOW_EM3EM4
00048 #endif
00049 
00050 //
00051 // Various #define's to enable use of both RTC and RTCC.
00052 //
00053 #if defined( _EFM_DEVICE )
00054 // To get the math correct we must have the MSB of the underlying 24bit
00055 // counter in the MSB position of a uint32_t datatype.
00056 #define TIMEDIFF( a, b )              ((( (a)<<8) - ((b)<<8) ) >> 8 )
00057 #define RTC_COUNTERGET()              RTC_CounterGet()
00058 #define RTC_ALL_INTS                  _RTC_IF_MASK
00059 #define RTC_OF_INT                    RTC_IF_OF
00060 #define RTC_COMP_INT                  RTC_IF_COMP0
00061 #define RTC_COUNTER_MASK              (_RTC_CNT_MASK)
00062 #define RTC_MAX_VALUE                 (_RTC_CNT_MASK)
00063 #define RTC_INTDISABLE( x )           RTC_IntDisable( x )
00064 #define RTC_INTENABLE( x )            RTC_IntEnable(  x )
00065 #define RTC_INTCLEAR( x )             RTC_IntClear(   x )
00066 #define RTC_INTGET()                  RTC_IntGet()
00067 #define RTC_COUNTERRESET()            RTC_CounterReset()
00068 #define RTC_COMPARESET( x )           RTC_CompareSet( 0, (x) & _RTC_COMP0_MASK )
00069 #define RTC_COMPAREGET()              RTC_CompareGet( 0 )
00070 #define NVIC_CLEARPENDINGIRQ()        NVIC_ClearPendingIRQ( RTC_IRQn )
00071 #define NVIC_DISABLEIRQ()             NVIC_DisableIRQ( RTC_IRQn )
00072 #define NVIC_ENABLEIRQ()              NVIC_EnableIRQ( RTC_IRQn )
00073 
00074 #elif defined( _EFR_DEVICE )
00075 #define TIMEDIFF( a, b )              ((a) - (b))
00076 #define RTC_COUNTERGET()              RTCC_CounterGet()
00077 #define RTC_ALL_INTS                  _RTCC_IF_MASK
00078 #define RTC_OF_INT                    RTCC_IF_OF
00079 #define RTC_COMP_INT                  RTCC_IF_CC1
00080 #define RTC_COUNTER_MASK              (_RTCC_CNT_MASK)
00081 #define RTC_MAX_VALUE                 (_RTCC_CNT_MASK)
00082 #define RTC_INTDISABLE( x )           RTCC_IntDisable( x )
00083 #define RTC_INTENABLE( x )            RTCC_IntEnable(  x )
00084 #define RTC_INTCLEAR( x )             RTCC_IntClear(   x )
00085 #define RTC_INTGET()                  RTCC_IntGet()
00086 #define RTC_COUNTERRESET()            RTCC->CNT = _RTCC_CNT_RESETVALUE
00087 #define RTC_COMPARESET( x )           RTCC_CapComSet( 1, x )
00088 #define RTC_COMPAREGET()              RTCC_CapComGet( 1 )
00089 #define NVIC_CLEARPENDINGIRQ()        NVIC_ClearPendingIRQ( RTCC_IRQn )
00090 #define NVIC_DISABLEIRQ()             NVIC_DisableIRQ( RTCC_IRQn )
00091 #define NVIC_ENABLEIRQ()              NVIC_EnableIRQ( RTCC_IRQn )
00092 #endif
00093 
00094 // Maximum number of ticks per overflow period (not the maximum tick value)
00095 #define MAX_RTC_TICK_CNT              (RTC_MAX_VALUE+1UL)
00096 #define RTC_CLOSE_TO_MAX_VALUE        (RTC_MAX_VALUE-100UL)
00097 
00098 #if defined(_EFM32_GECKO_FAMILY)
00099 // Assume 32kHz RTC/RTCC clock, cmuClkDiv_2 prescaler, 16 ticks per millisecond
00100 #define RTC_DIVIDER                     ( cmuClkDiv_2 )
00101 #else
00102 // Assume 32kHz RTC/RTCC clock, cmuClkDiv_8 prescaler, 4 ticks per millisecond
00103 #define RTC_DIVIDER                     ( cmuClkDiv_8 )
00104 #endif
00105 #define RTC_CLOCK                       ( 32768U )
00106 #define MSEC_TO_TICKS_DIVIDER           ( 1000U * RTC_DIVIDER )
00107 #define MSEC_TO_TICKS_ROUNDING_FACTOR   ( MSEC_TO_TICKS_DIVIDER / 2 )
00108 #define MSEC_TO_TICKS( ms )             ( ( ( (uint64_t)ms * RTC_CLOCK )      \
00109                                             + MSEC_TO_TICKS_ROUNDING_FACTOR ) \
00110                                           / MSEC_TO_TICKS_DIVIDER )
00111 
00112 #define TICKS_TO_MSEC_ROUNDING_FACTOR   ( RTC_CLOCK / 2 )
00113 #define TICKS_TO_MSEC( ticks )          ( ( ( (uint64_t)ticks                 \
00114                                               * RTC_DIVIDER * 1000U )         \
00115                                             + TICKS_TO_MSEC_ROUNDING_FACTOR ) \
00116                                           / RTC_CLOCK )
00117 
00118 #define TICKS_TO_SEC_ROUNDING_FACTOR    ( RTC_CLOCK / 2 )
00119 #define TICKS_TO_SEC( ticks )           ( ( ( (uint64_t)ticks                 \
00120                                               * RTC_DIVIDER )                 \
00121                                             + TICKS_TO_SEC_ROUNDING_FACTOR )  \
00122                                           / RTC_CLOCK )
00123 #define TICK_TIME_USEC                  ( 1000000 * RTC_DIVIDER / RTC_CLOCK )
00124 
00125 typedef struct Timer
00126 {
00127   uint64_t            remaining;
00128   uint64_t            ticks;
00129   int                 periodicCompensationUsec;
00130   unsigned int        periodicDriftUsec;
00131   RTCDRV_Callback_t   callback;
00132   bool                running;
00133   bool                doCallback;
00134   bool                allocated;
00135   RTCDRV_TimerType_t  timerType;
00136   void                *user;
00137 } Timer_t;
00138 
00139 static Timer_t            timer[ EMDRV_RTCDRV_NUM_TIMERS ];
00140 static uint32_t           lastStart;
00141 static volatile uint32_t  startTimerNestingLevel;
00142 static bool               inTimerIRQ;
00143 static bool               rtcRunning;
00144 static bool               rtcdrvIsInitialized = false;
00145 #if defined( EMODE_DYNAMIC )
00146 static bool               sleepBlocked;
00147 #endif
00148 
00149 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00150 static uint32_t           wallClockTime;
00151 static uint32_t           wallClockInitTime;
00152 #endif
00153 
00154 #if defined( _EFM_DEVICE )
00155 static const RTC_Init_TypeDef initRTC =
00156 {
00157   true,  // Start counting when init completed.
00158   false, // Disable updating RTC during debug halt.
00159   false  // Count until max. to wrap around.
00160 };
00161 
00162 #elif defined( _EFR_DEVICE )
00163 static RTCC_Init_TypeDef initRTCC =
00164 {
00165   true,                 /* Start counting when init completed. */
00166   rtccCntModeNormal,    /* Use RTCC in normal mode and not in calender mode */
00167   false,                /* Disable updating RTC during debug halt. */
00168   false,                /* Count until max. to wrap around. */
00169   rtccCntPresc_8,       /* Set RTCC prescaler to 8 */
00170   rtccCntTickPresc,     /* Count according to prescaler configuration */
00171   false,                /* Disable storing RTCC counter value in RTCC_CCV2 upon backup mode entry. */
00172   false,                /* LFXO fail detection disabled */
00173   false                 /* Not applicable in this configuation */
00174 };
00175 
00176 static RTC_CCChConf_TypeDef initRTCCCompareChannel =
00177 {
00178   rtccCapComChModeCompare,    /* Use Compare mode*/
00179   rtccCompMatchOutActionNone, /* Do no action on compare*/
00180   rtccPRSCh0,                 /* PRS not used */
00181   rtccInEdgeNone,             /* Capture Input not used */
00182   rtccCompBaseCnt,            /* Compare with Base CNT register*/
00183   0
00184 };
00185 #endif
00186 
00187 static void checkAllTimers( uint32_t timeElapsed );
00188 static void delayTicks( uint32_t ticks );
00189 static void executeTimerCallbacks( void );
00190 static void rescheduleRtc( uint32_t rtcCnt );
00191 
00193 
00194 /***************************************************************************/
00208 Ecode_t RTCDRV_AllocateTimer( RTCDRV_TimerID_t *id )
00209 {
00210   int i      = 0;
00211   int retVal = 0;
00212 
00213   INT_Disable();
00214   // Iterate through the table of the timers until the first available.
00215   while ( ( timer[ i ].allocated ) && ( i < EMDRV_RTCDRV_NUM_TIMERS ) ) {
00216     i++;
00217   }
00218 
00219   // Check if we reached the end of the table.
00220   if ( i == EMDRV_RTCDRV_NUM_TIMERS ) {
00221     retVal = ECODE_EMDRV_RTCDRV_ALL_TIMERS_USED;
00222   } else {
00223     // Check if a NULL pointer was passed.
00224     if ( id != NULL ) {
00225       timer[ i ].allocated = true;
00226       *id = i;
00227       retVal = ECODE_EMDRV_RTCDRV_OK;
00228     } else {
00229       retVal = ECODE_EMDRV_RTCDRV_PARAM_ERROR;
00230     }
00231   }
00232   INT_Enable();
00233 
00234   return retVal;
00235 }
00236 
00237 /***************************************************************************/
00246 Ecode_t RTCDRV_Delay( uint32_t ms )
00247 {
00248   uint64_t totalTicks;
00249 
00250   totalTicks = MSEC_TO_TICKS( ms );
00251 
00252   while ( totalTicks > RTC_CLOSE_TO_MAX_VALUE ) {
00253     delayTicks( RTC_CLOSE_TO_MAX_VALUE );
00254     totalTicks -= RTC_CLOSE_TO_MAX_VALUE;
00255   }
00256   delayTicks( totalTicks );
00257 
00258   return ECODE_EMDRV_RTCDRV_OK;
00259 }
00260 
00261 /***************************************************************************/
00274 Ecode_t RTCDRV_FreeTimer( RTCDRV_TimerID_t id )
00275 {
00276   // Check if valid timer ID.
00277   if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
00278     return ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID;
00279   }
00280 
00281   INT_Disable();
00282 
00283   timer[ id ].running   = false;
00284   timer[ id ].allocated = false;
00285 
00286   INT_Enable();
00287 
00288   return ECODE_EMDRV_RTCDRV_OK;
00289 }
00290 
00291 /***************************************************************************/
00302 Ecode_t RTCDRV_Init( void )
00303 {
00304   if ( rtcdrvIsInitialized == true ) {
00305     return ECODE_EMDRV_RTCDRV_OK;
00306   }
00307   rtcdrvIsInitialized = true;
00308 
00309   // Ensure LE modules are clocked.
00310   CMU_ClockEnable( cmuClock_CORELE, true );
00311 
00312   // Enable LFACLK in CMU (will also enable oscillator if not enabled).
00313   CMU_ClockSelectSet( cmuClock_LFA, cmuSelect_LFXO );
00314 
00315 #if defined( _EFM_DEVICE )
00316   // Set clock divider.
00317   CMU_ClockDivSet( cmuClock_RTC, RTC_DIVIDER );
00318 
00319   // Enable RTC module clock.
00320   CMU_ClockEnable( cmuClock_RTC, true );
00321 
00322   // Initialize RTC.
00323   RTC_Init( &initRTC );
00324 
00325 #elif defined( _EFR_DEVICE )
00326   // Set clock divider.
00327   initRTCC.cntPresc = (RTCC_CntPresc_TypeDef)CMU_DivToLog2( RTC_DIVIDER );
00328 
00329   // Enable RTCC module clock.
00330   CMU_ClockEnable( cmuClock_RTCC, true );
00331 
00332   // Initialize RTCC.
00333   RTCC_Init( &initRTCC );
00334 
00335   // Set up Compare channel.
00336   RTCC_CapComChannelConfig( 1, &initRTCCCompareChannel );
00337 #endif
00338 
00339   // Disable RTC/RTCC interrupt generation.
00340   RTC_INTDISABLE( RTC_ALL_INTS );
00341   RTC_INTCLEAR( RTC_ALL_INTS );
00342 
00343   RTC_COUNTERRESET();
00344 
00345   // Clear and then enable RTC interrupts in NVIC.
00346   NVIC_CLEARPENDINGIRQ();
00347   NVIC_ENABLEIRQ();
00348 
00349 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00350   // Enable overflow interrupt for wallclock.
00351   RTC_INTENABLE( RTC_OF_INT );
00352 #endif
00353 
00354   // Reset RTCDRV internal data structures/variables.
00355   memset( timer, 0, sizeof( timer ) );
00356   inTimerIRQ             = false;
00357   rtcRunning             = false;
00358   startTimerNestingLevel = 0;
00359 #if defined( EMODE_DYNAMIC )
00360   sleepBlocked           = false;
00361 #endif
00362 
00363 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00364   wallClockTime     = 0;
00365   wallClockInitTime = 0;
00366 
00367 #if defined( EMODE_NEVER_ALLOW_EM3EM4 )
00368   // Always block EM3 and EM4 if wallclock is running.
00369   SLEEP_SleepBlockBegin( sleepEM3 );
00370 #endif
00371 
00372 #endif
00373 
00374   return ECODE_EMDRV_RTCDRV_OK;
00375 }
00376 
00377 /***************************************************************************/
00390 Ecode_t RTCDRV_DeInit( void )
00391 {
00392   // Disable and clear all interrupt sources.
00393   NVIC_DISABLEIRQ();
00394   RTC_INTDISABLE( RTC_ALL_INTS );
00395   RTC_INTCLEAR( RTC_ALL_INTS );
00396   NVIC_CLEARPENDINGIRQ();
00397 
00398   // Disable RTC module and its clock.
00399 #if defined( _EFM_DEVICE )
00400   RTC_Enable( false );
00401   CMU_ClockEnable( cmuClock_RTC, false );
00402 #elif defined( _EFR_DEVICE )
00403   RTCC_Enable( false );
00404   CMU_ClockEnable( cmuClock_RTCC, false );
00405 #endif
00406 
00407 #if defined( EMODE_NEVER_ALLOW_EM3EM4 )
00408   // End EM3 and EM4 blocking.
00409   SLEEP_SleepBlockEnd( sleepEM3 );
00410 #endif
00411 
00412 #if defined( EMODE_DYNAMIC )
00413   // End EM3 and EM4 blocking if a block start has been set.
00414   if ( sleepBlocked ) {
00415     SLEEP_SleepBlockEnd( sleepEM3 );
00416   }
00417 #endif
00418 
00419   // Mark the driver as uninitialized.
00420   rtcdrvIsInitialized = false;
00421 
00422   return ECODE_EMDRV_RTCDRV_OK;
00423 }
00424 
00425 /***************************************************************************/
00441 Ecode_t RTCDRV_IsRunning( RTCDRV_TimerID_t id, bool *isRunning )
00442 {
00443   // Check if valid timer ID.
00444   if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
00445     return ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID;
00446   }
00447 
00448   // Check pointer validity.
00449   if ( isRunning == NULL ) {
00450     return ECODE_EMDRV_RTCDRV_PARAM_ERROR;
00451   }
00452 
00453   INT_Disable();
00454   // Check if timer is reserved.
00455   if ( ! timer[ id ].allocated ) {
00456     INT_Enable();
00457     return ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED;
00458   }
00459   *isRunning = timer[ id ].running;
00460   INT_Enable();
00461 
00462   return ECODE_EMDRV_RTCDRV_OK;
00463 }
00464 
00465 /***************************************************************************/
00486 Ecode_t RTCDRV_StartTimer(  RTCDRV_TimerID_t id,
00487                             RTCDRV_TimerType_t type,
00488                             uint32_t timeout,
00489                             RTCDRV_Callback_t callback,
00490                             void *user )
00491 {
00492   uint32_t timeElapsed, cnt, compVal, loopCnt = 0;
00493   uint32_t timeToNextTimerCompletion;
00494 
00495   // Check if valid timer ID.
00496   if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
00497     return ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID;
00498   }
00499 
00500   INT_Disable();
00501   if ( ! timer[ id ].allocated ) {
00502     INT_Enable();
00503     return ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED;
00504   }
00505 
00506   if ( timeout == 0 ) {
00507     if ( callback != NULL ) {
00508       callback( id, user );
00509     }
00510     INT_Enable();
00511     return ECODE_EMDRV_RTCDRV_OK;
00512   }
00513 
00514   cnt = RTC_COUNTERGET();
00515 
00516   timer[ id ].callback  = callback;
00517   timer[ id ].ticks     = MSEC_TO_TICKS( timeout );
00518   if (rtcdrvTimerTypePeriodic == type) {
00519     // Calculate compensation value for periodic timers.
00520     timer[ id ].periodicCompensationUsec = 1000 * timeout -
00521       (timer[ id ].ticks * TICK_TIME_USEC);
00522     timer[ id ].periodicDriftUsec = TICK_TIME_USEC/2;
00523   }
00524   // Add one tick in order to compensate if RTC is close to an increment event.
00525   timer[ id ].remaining = timer[ id ].ticks + 1;
00526   timer[ id ].running   = true;
00527   timer[ id ].timerType = type;
00528   timer[ id ].user      = user;
00529 
00530   if ( inTimerIRQ == true ) {
00531     // Exit now, remaining processing will be done in IRQ handler.
00532     INT_Enable();
00533     return ECODE_EMDRV_RTCDRV_OK;
00534   }
00535 
00536   // StartTimer() may recurse, keep track of recursion level.
00537   if ( startTimerNestingLevel < UINT32_MAX ) {
00538     startTimerNestingLevel++;
00539   }
00540 
00541   if ( rtcRunning == false ) {
00542 
00543 #if defined( _EFM_DEVICE )
00544     lastStart = ( cnt ) & RTC_COUNTER_MASK;
00545 #elif defined( _EFR_DEVICE )
00546     lastStart = cnt;
00547 #endif
00548 
00549     RTC_INTCLEAR( RTC_COMP_INT );
00550 
00551     compVal = EFM32_MIN( timer[ id ].remaining, RTC_CLOSE_TO_MAX_VALUE );
00552     RTC_COMPARESET( cnt + compVal );
00553 
00554     // Start the timer system by enabling the compare interrupt.
00555     RTC_INTENABLE( RTC_COMP_INT );
00556 
00557 #if defined( EMODE_DYNAMIC )
00558     // When RTC is running, we can not allow EM3 or EM4.
00559     if ( sleepBlocked == false ) {
00560       sleepBlocked = true;
00561       SLEEP_SleepBlockBegin( sleepEM3 );
00562     }
00563 #endif
00564 
00565     rtcRunning = true;
00566 
00567   } else {
00568 
00569     // The timer system is running. We must stop, update timers with the time
00570     // elapsed so far, find the timer with the shortest timeout and then restart.
00571     // As StartTimer() may be called from the callbacks we only do this
00572     // processing at the first nesting level.
00573     if ( startTimerNestingLevel == 1  ) {
00574 
00575       timer[ id ].running = false;
00576       // This loop is repeated if CNT is incremented while processing.
00577       do {
00578 
00579         RTC_INTDISABLE( RTC_COMP_INT );
00580 
00581         timeElapsed = TIMEDIFF( cnt, lastStart );
00582 #if defined( _EFM_DEVICE )
00583         // Compensate for the fact that CNT is normally COMP0+1 after a
00584         // compare match event.
00585         if ( timeElapsed == RTC_MAX_VALUE ) {
00586           timeElapsed = 0;
00587         }
00588 #endif
00589 
00590         // Update all timers with elapsed time.
00591         checkAllTimers( timeElapsed );
00592 
00593         // Execute timer callbacks.
00594         executeTimerCallbacks();
00595 
00596         // Set timer to running only after checkAllTimers() is called once.
00597         if ( loopCnt == 0 ) {
00598           timer[ id ].running = true;
00599         }
00600         loopCnt++;
00601 
00602         // Restart RTC according to next timeout.
00603         rescheduleRtc( cnt );
00604 
00605         cnt = RTC_COUNTERGET();
00606         timeElapsed = TIMEDIFF( cnt, lastStart );
00607         timeToNextTimerCompletion = TIMEDIFF( RTC_COMPAREGET(), lastStart );
00608 
00609         /* If the counter has passed the COMP(ARE) register value since we
00610            checked the timers, then we should recheck the timers and reschedule
00611            again. */
00612       }
00613       while ( rtcRunning && (timeElapsed > timeToNextTimerCompletion));
00614     }
00615   }
00616 
00617   if ( startTimerNestingLevel > 0 ) {
00618     startTimerNestingLevel--;
00619   }
00620 
00621   INT_Enable();
00622   return ECODE_EMDRV_RTCDRV_OK;
00623 }
00624 
00625 /***************************************************************************/
00636 Ecode_t RTCDRV_StopTimer( RTCDRV_TimerID_t id )
00637 {
00638   // Check if valid timer ID.
00639   if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
00640     return ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID;
00641   }
00642 
00643   INT_Disable();
00644   if ( ! timer[ id ].allocated ) {
00645     INT_Enable();
00646     return ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED;
00647   }
00648 
00649   timer[ id ].running = false;
00650   INT_Enable();
00651 
00652   return ECODE_EMDRV_RTCDRV_OK;
00653 }
00654 
00655 /***************************************************************************/
00671 Ecode_t RTCDRV_TimeRemaining( RTCDRV_TimerID_t id, uint32_t *timeRemaining )
00672 {
00673   uint64_t tmp;
00674   uint32_t timeLeft, currentCnt, lastRtcStart;
00675 
00676   // Check if valid timer ID.
00677   if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
00678     return ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID;
00679   }
00680 
00681   // Check pointer validity.
00682   if ( timeRemaining == NULL ) {
00683     return ECODE_EMDRV_RTCDRV_PARAM_ERROR;
00684   }
00685 
00686   INT_Disable();
00687   // Check if timer is reserved.
00688   if ( ! timer[ id ].allocated ) {
00689     INT_Enable();
00690     return ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED;
00691   }
00692 
00693   // Check if timer is running.
00694   if ( ! timer[ id ].running ) {
00695     INT_Enable();
00696     return ECODE_EMDRV_RTCDRV_TIMER_NOT_RUNNING;
00697   }
00698 
00699   timeLeft     = timer[ id ].remaining;
00700   currentCnt   = RTC_COUNTERGET();
00701   lastRtcStart = lastStart;
00702   INT_Enable();
00703 
00704   // Get number of RTC clock ticks elapsed since last RTC reschedule.
00705   currentCnt = TIMEDIFF( currentCnt, lastRtcStart );
00706 
00707   if ( currentCnt > timeLeft ) {
00708     timeLeft = 0;
00709   } else {
00710     timeLeft -= currentCnt;
00711   }
00712 
00713   tmp = TICKS_TO_MSEC( timeLeft );
00714   *timeRemaining = tmp;
00715 
00716   return ECODE_EMDRV_RTCDRV_OK;
00717 }
00718 
00719 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00720 /***************************************************************************/
00727 uint32_t RTCDRV_GetWallClock( void )
00728 {
00729   uint64_t tmp;
00730   uint32_t ticks, wallClock, wallClockStartPoint;
00731 
00732   INT_Disable();
00733   ticks               = RTC_COUNTERGET();
00734   wallClock           = wallClockTime;
00735   wallClockStartPoint = wallClockInitTime;
00736   INT_Enable();
00737 
00738   tmp = ticks - wallClockStartPoint;
00739   return wallClock + (uint32_t)TICKS_TO_SEC( tmp );
00740 }
00741 #endif
00742 
00743 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00744 /***************************************************************************/
00753 Ecode_t RTCDRV_SetWallClock( uint32_t secs )
00754 {
00755   INT_Disable();
00756   wallClockTime     = secs;
00757   wallClockInitTime = RTC_COUNTERGET();
00758   INT_Enable();
00759 
00760   return ECODE_EMDRV_RTCDRV_OK;
00761 }
00762 #endif
00763 
00765 
00766 #if defined( _EFM_DEVICE )
00767 void RTC_IRQHandler(void)
00768 #elif defined( _EFR_DEVICE )
00769 void RTCC_IRQHandler(void)
00770 #endif
00771 {
00772   uint32_t flags, timeElapsed, cnt, timeToNextTimerCompletion;
00773 
00774   INT_Disable();
00775 
00776   // CNT will normally be COMP0+1 at this point,
00777   // unless IRQ latency exceeded one tick period.
00778 
00779   flags = RTC_INTGET();
00780 
00781   if ( flags & RTC_COMP_INT ) {
00782 
00783     // Stop timer system by disabling the compare IRQ.
00784     // Update all timers with the time elapsed, call callbacks if needed,
00785     // then find the timer with the shortest timeout (if any at all) and
00786     // reenable the compare IRQ if needed.
00787 
00788     inTimerIRQ = true;
00789 
00790     cnt = RTC_COUNTERGET();
00791 
00792     // This loop is repeated if CNT is incremented while processing.
00793     do {
00794 
00795       RTC_INTDISABLE( RTC_COMP_INT );
00796 
00797       timeElapsed = TIMEDIFF( cnt, lastStart );
00798 
00799       // Update all timers with elapsed time.
00800       checkAllTimers( timeElapsed );
00801 
00802       // Execute timer callbacks.
00803       executeTimerCallbacks();
00804 
00805       // Restart RTC according to next timeout.
00806       rescheduleRtc( cnt );
00807 
00808       cnt = RTC_COUNTERGET();
00809       timeElapsed = TIMEDIFF( cnt, lastStart );
00810       timeToNextTimerCompletion = TIMEDIFF( RTC_COMPAREGET(), lastStart );
00811       /* If the counter has passed the COMP(ARE) register value since we
00812          checked the timers, then we should recheck the timers and reschedule
00813          again. */
00814     }
00815     while ( rtcRunning && (timeElapsed > timeToNextTimerCompletion));
00816     inTimerIRQ = false;
00817   }
00818 
00819 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
00820   if ( flags & RTC_OF_INT ) {
00821     uint64_t ticks;
00822 
00823     RTC_INTCLEAR( RTC_OF_INT );
00824 
00825     ticks             = MAX_RTC_TICK_CNT - wallClockInitTime;
00826     wallClockInitTime = 0;
00827     wallClockTime    += TICKS_TO_SEC( ticks );
00828   }
00829 #endif
00830 
00831   INT_Enable();
00832 }
00833 
00834 static void checkAllTimers( uint32_t timeElapsed )
00835 {
00836   int i;
00837 #if defined( EMODE_DYNAMIC )
00838   int numOfTimersRunning = 0;
00839 #endif
00840 
00841   // Iterate through the timer table.
00842   // Update time remaining, check for timeout and rescheduling of periodic
00843   // timers, check for callbacks.
00844 
00845   for ( i = 0; i < EMDRV_RTCDRV_NUM_TIMERS; i++ ) {
00846     timer[ i ].doCallback = false;
00847     if ( timer[ i ].running == true ) {
00848 #if defined( EMODE_DYNAMIC )
00849       numOfTimersRunning++;
00850 #endif
00851       if ( timer[ i ].remaining > timeElapsed ) {
00852         timer[ i ].remaining -= timeElapsed;
00853       } else {
00854         if ( timer[ i ].timerType == rtcdrvTimerTypeOneshot ) {
00855           timer[ i ].running = false;
00856 #if defined( EMODE_DYNAMIC )
00857           numOfTimersRunning--;
00858 #endif
00859         } else {
00860           // Compensate overdue periodic timers to avoid accumlating errors.
00861           timer[ i ].remaining = timer[ i ].ticks - timeElapsed +
00862                                  timer[ i ].remaining;
00863           if ( timer[ i ].periodicCompensationUsec > 0 ) {
00864             timer[ i ].periodicDriftUsec += timer[i].periodicCompensationUsec;
00865             if (timer[ i ].periodicDriftUsec >= TICK_TIME_USEC) {
00866               // Add a tick if the timer drift is longer than the time of
00867               // one tick.
00868               timer[ i ].remaining += 1;
00869               timer[ i ].periodicDriftUsec -= TICK_TIME_USEC;
00870             }
00871           }
00872           else {
00873             timer[ i ].periodicDriftUsec -= timer[i].periodicCompensationUsec;
00874             if (timer[ i ].periodicDriftUsec >= TICK_TIME_USEC) {
00875               // Subtract one tick if the timer drift is longer than the time
00876               // of one tick.
00877               timer[ i ].remaining -= 1;
00878               timer[ i ].periodicDriftUsec -= TICK_TIME_USEC;
00879             }
00880           }
00881         }
00882         if ( timer[ i ].callback != NULL ) {
00883           timer[ i ].doCallback = true;
00884         }
00885       }
00886     }
00887   }
00888 
00889 #if defined( EMODE_DYNAMIC )
00890   // If no timers are running, we can remove block on EM3 and EM4 sleep modes.
00891   if ( ( numOfTimersRunning == 0 ) && ( sleepBlocked == true ) ) {
00892     sleepBlocked = false;
00893     SLEEP_SleepBlockEnd( sleepEM3 );
00894   }
00895 #endif
00896 }
00897 
00898 static void delayTicks( uint32_t ticks )
00899 {
00900   uint32_t startTime;
00901   volatile uint32_t now;
00902 
00903   if ( ticks ) {
00904     startTime = RTC_COUNTERGET();
00905     do {
00906       now = RTC_COUNTERGET();
00907     } while ( TIMEDIFF( now, startTime ) < ticks );
00908   }
00909 }
00910 
00911 static void executeTimerCallbacks( void )
00912 {
00913   int i;
00914 
00915   for ( i = 0; i < EMDRV_RTCDRV_NUM_TIMERS; i++ ) {
00916     if ( timer[ i ].doCallback ) {
00917       timer[ i ].callback( i, timer[ i ].user );
00918     }
00919   }
00920 }
00921 
00922 static void rescheduleRtc( uint32_t rtcCnt )
00923 {
00924   int i;
00925   uint64_t min = UINT64_MAX;
00926 
00927   // Find the timer with shortest timeout.
00928   for ( i = 0; i < EMDRV_RTCDRV_NUM_TIMERS; i++ ) {
00929     if (    ( timer[ i ].running   == true )
00930          && ( timer[ i ].remaining <  min  ) ) {
00931       min = timer[ i ].remaining;
00932     }
00933   }
00934 
00935   rtcRunning = false;
00936   if ( min != UINT64_MAX ) {
00937     min = EFM32_MIN( min, RTC_CLOSE_TO_MAX_VALUE );
00938 #if defined( _EFM_DEVICE )
00939     if ( inTimerIRQ == false ) {
00940       lastStart = ( rtcCnt ) & RTC_COUNTER_MASK;
00941     } else
00942 #endif
00943     {
00944       lastStart = rtcCnt;
00945     }
00946     RTC_INTCLEAR( RTC_COMP_INT );
00947 
00948     RTC_COMPARESET( rtcCnt + min );
00949 
00950 #if defined( EMODE_DYNAMIC )
00951     // When RTC is running, we can not allow EM3 or EM4.
00952     if ( sleepBlocked == false ) {
00953       sleepBlocked = true;
00954       SLEEP_SleepBlockBegin( sleepEM3 );
00955     }
00956 #endif
00957 
00958     rtcRunning = true;
00959 
00960     // Reenable compare IRQ.
00961     RTC_INTENABLE( RTC_COMP_INT );
00962   }
00963 }
00965 
00966 /******** THE REST OF THE FILE IS DOCUMENTATION ONLY !**********************/