EZR32 Leopard Gecko Software Documentation  ezr32lg-doc-4.2.1
rtcdriver.c
Go to the documentation of this file.
1 /***************************************************************************/
16 #include <string.h>
17 
18 #include "em_device.h"
19 #include "em_cmu.h"
20 #include "em_common.h"
21 #include "em_int.h"
22 
23 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
24 #define RTCDRV_USE_RTCC
25 #else
26 #define RTCDRV_USE_RTC
27 #endif
28 
29 #if defined( RTCDRV_USE_RTCC )
30 #include "em_rtcc.h"
31 #else
32 #include "em_rtc.h"
33 #endif
34 
35 #include "rtcdriver.h"
36 #if defined( EMDRV_RTCDRV_SLEEPDRV_INTEGRATION )
37 #include "sleep.h"
38 #endif
39 
41 
42 #if defined( EMDRV_RTCDRV_SLEEPDRV_INTEGRATION ) \
43  && !defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG ) \
44  && defined( RTCDRV_USE_RTC )
45 // Do not allow EM3/EM4 energy modes when the RTC is running.
46 #define EMODE_DYNAMIC
47 #endif
48 
49 #if defined( EMDRV_RTCDRV_SLEEPDRV_INTEGRATION ) \
50  && defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG ) \
51  && defined( RTCDRV_USE_RTC )
52 // Always deny EM3/EM4 energy modes when wallclock is enabled.
53 #define EMODE_NEVER_ALLOW_EM3EM4
54 #endif
55 
56 //
57 // Various #define's to enable use of both RTC and RTCC.
58 //
59 #if defined( RTCDRV_USE_RTCC )
60 #define TIMEDIFF( a, b ) ((a) - (b))
61 #define RTC_COUNTERGET() RTCC_CounterGet()
62 #define RTC_COUNTER_BITS 32
63 #define RTC_ALL_INTS _RTCC_IF_MASK
64 #define RTC_OF_INT RTCC_IF_OF
65 #define RTC_COMP_INT RTCC_IF_CC1
66 #define RTC_COUNTER_MASK (_RTCC_CNT_MASK)
67 #define RTC_MAX_VALUE (_RTCC_CNT_MASK)
68 #define RTC_INTDISABLE( x ) RTCC_IntDisable( x )
69 #define RTC_INTENABLE( x ) RTCC_IntEnable( x )
70 #define RTC_INTCLEAR( x ) RTCC_IntClear( x )
71 #define RTC_INTGET() RTCC_IntGet()
72 #define RTC_COUNTERRESET() RTCC->CNT = _RTCC_CNT_RESETVALUE
73 #define RTC_COMPARESET( x ) RTCC_ChannelCCVSet( 1, x )
74 #define RTC_COMPAREGET() RTCC_ChannelCCVGet( 1 )
75 #define NVIC_CLEARPENDINGIRQ() NVIC_ClearPendingIRQ( RTCC_IRQn )
76 #define NVIC_DISABLEIRQ() NVIC_DisableIRQ( RTCC_IRQn )
77 #define NVIC_ENABLEIRQ() NVIC_EnableIRQ( RTCC_IRQn )
78 
79 #else
80 // To get the math correct we must have the MSB of the underlying 24bit
81 // counter in the MSB position of a uint32_t datatype.
82 #define TIMEDIFF( a, b ) ((( (a)<<8) - ((b)<<8) ) >> 8 )
83 #define RTC_COUNTERGET() RTC_CounterGet()
84 #define RTC_COUNTER_BITS 24
85 #define RTC_ALL_INTS _RTC_IF_MASK
86 #define RTC_OF_INT RTC_IF_OF
87 #define RTC_COMP_INT RTC_IF_COMP0
88 #define RTC_COUNTER_MASK (_RTC_CNT_MASK)
89 #define RTC_MAX_VALUE (_RTC_CNT_MASK)
90 #define RTC_INTDISABLE( x ) RTC_IntDisable( x )
91 #define RTC_INTENABLE( x ) RTC_IntEnable( x )
92 #define RTC_INTCLEAR( x ) RTC_IntClear( x )
93 #define RTC_INTGET() RTC_IntGet()
94 #define RTC_COUNTERRESET() RTC_CounterReset()
95 #define RTC_COMPARESET( x ) RTC_CompareSet( 0, (x) & _RTC_COMP0_MASK )
96 #define RTC_COMPAREGET() RTC_CompareGet( 0 )
97 #define NVIC_CLEARPENDINGIRQ() NVIC_ClearPendingIRQ( RTC_IRQn )
98 #define NVIC_DISABLEIRQ() NVIC_DisableIRQ( RTC_IRQn )
99 #define NVIC_ENABLEIRQ() NVIC_EnableIRQ( RTC_IRQn )
100 #endif
101 
102 // Maximum number of ticks per overflow period (not the maximum tick value)
103 #define MAX_RTC_TICK_CNT (RTC_MAX_VALUE+1UL)
104 #define RTC_CLOSE_TO_MAX_VALUE (RTC_MAX_VALUE-100UL)
105 
106 #if defined(_EFM32_GECKO_FAMILY)
107 // Assume 32kHz RTC/RTCC clock, cmuClkDiv_2 prescaler, 16 ticks per millisecond
108 #define RTC_DIVIDER ( cmuClkDiv_2 )
109 #else
110 // Assume 32kHz RTC/RTCC clock, cmuClkDiv_8 prescaler, 4 ticks per millisecond
111 #define RTC_DIVIDER ( cmuClkDiv_8 )
112 #endif
113 
114 #define RTC_CLOCK ( 32768U )
115 #define MSEC_TO_TICKS_DIVIDER ( 1000U * RTC_DIVIDER )
116 #define MSEC_TO_TICKS_ROUNDING_FACTOR ( MSEC_TO_TICKS_DIVIDER / 2 )
117 #define MSEC_TO_TICKS( ms ) ( ( ( (uint64_t)(ms) * RTC_CLOCK ) \
118  + MSEC_TO_TICKS_ROUNDING_FACTOR ) \
119  / MSEC_TO_TICKS_DIVIDER )
120 
121 #define TICKS_TO_MSEC_ROUNDING_FACTOR ( RTC_CLOCK / 2 )
122 #define TICKS_TO_MSEC( ticks ) ( ( ( (uint64_t)(ticks) \
123  * RTC_DIVIDER * 1000U ) \
124  + TICKS_TO_MSEC_ROUNDING_FACTOR ) \
125  / RTC_CLOCK )
126 
127 #define TICKS_TO_SEC_ROUNDING_FACTOR ( RTC_CLOCK / 2 )
128 #define TICKS_TO_SEC( ticks ) ( ( ( (uint64_t)(ticks) \
129  * RTC_DIVIDER ) \
130  + TICKS_TO_SEC_ROUNDING_FACTOR ) \
131  / RTC_CLOCK )
132 #define TICK_TIME_USEC ( 1000000 * RTC_DIVIDER / RTC_CLOCK )
133 
134 typedef struct Timer
135 {
136  uint64_t remaining;
137  uint64_t ticks;
138  int periodicCompensationUsec;
139  unsigned int periodicDriftUsec;
140  RTCDRV_Callback_t callback;
141  bool running;
142  bool doCallback;
143  bool allocated;
144  RTCDRV_TimerType_t timerType;
145  void *user;
146 } Timer_t;
147 
148 static Timer_t timer[ EMDRV_RTCDRV_NUM_TIMERS ];
149 static uint32_t lastStart;
150 static volatile uint32_t startTimerNestingLevel;
151 static bool inTimerIRQ;
152 static bool rtcRunning;
153 static bool rtcdrvIsInitialized = false;
154 #if defined( EMODE_DYNAMIC )
155 static bool sleepBlocked;
156 #endif
157 
158 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
159 static volatile uint32_t wallClockOverflowCnt;
160 static uint32_t wallClockTimeBase;
161 #endif
162 
163 #if defined( RTCDRV_USE_RTC )
164 static const RTC_Init_TypeDef initRTC =
165 {
166  true, // Start counting when init completed.
167  false, // Disable updating RTC during debug halt.
168  false // Count until max. to wrap around.
169 };
170 
171 #elif defined( RTCDRV_USE_RTCC )
172 static RTCC_Init_TypeDef initRTCC =
173 {
174  true, /* Start counting when init completed. */
175  false, /* Disable updating RTC during debug halt. */
176  false, /* Prescaler counts until max. before wrap around. */
177  false, /* Counter counts until max. before wrap around. */
178  rtccCntPresc_8, /* Set RTCC prescaler to 8 */
179  rtccCntTickPresc, /* Count according to prescaler configuration */
180 #if defined(_RTCC_CTRL_BUMODETSEN_MASK)
181  false, /* Disable storing RTCC counter value in RTCC_CCV2 upon backup mode entry. */
182 #endif
183  false, /* LFXO fail detection disabled */
184  rtccCntModeNormal, /* Use RTCC in normal mode and not in calender mode */
185  false /* No leap year correction. */
186 };
187 
188 static RTCC_CCChConf_TypeDef initRTCCCompareChannel =
189 {
190  rtccCapComChModeCompare, /* Use Compare mode */
191  rtccCompMatchOutActionPulse,/* Don't care */
192  rtccPRSCh0, /* PRS not used */
193  rtccInEdgeNone, /* Capture Input not used */
194  rtccCompBaseCnt, /* Compare with Base CNT register */
195  0, /* Compare mask */
196  rtccDayCompareModeMonth /* Don't care */
197 };
198 #endif
199 
200 // default to LFXO unless specifically directed to use LFRCO
201 #if defined(RTCDRV_USE_LFRCO)
202  #define RTCDRV_OSC cmuSelect_LFRCO
203 #else
204  #define RTCDRV_OSC cmuSelect_LFXO
205 #endif
206 
207 static void checkAllTimers( uint32_t timeElapsed );
208 static void delayTicks( uint32_t ticks );
209 static void executeTimerCallbacks( void );
210 static void rescheduleRtc( uint32_t rtcCnt );
211 
213 
214 /***************************************************************************/
229 {
230  int i = 0;
231  int retVal = 0;
232 
233  INT_Disable();
234  // Iterate through the table of the timers until the first available.
235  while ( ( i < EMDRV_RTCDRV_NUM_TIMERS ) && ( timer[ i ].allocated ) ) {
236  i++;
237  }
238 
239  // Check if we reached the end of the table.
240  if ( i == EMDRV_RTCDRV_NUM_TIMERS ) {
242  } else {
243  // Check if a NULL pointer was passed.
244  if ( id != NULL ) {
245  timer[ i ].allocated = true;
246  *id = i;
247  retVal = ECODE_EMDRV_RTCDRV_OK;
248  } else {
250  }
251  }
252  INT_Enable();
253 
254  return retVal;
255 }
256 
257 /***************************************************************************/
266 Ecode_t RTCDRV_Delay( uint32_t ms )
267 {
268  uint64_t totalTicks;
269 
270  totalTicks = MSEC_TO_TICKS( ms );
271 
272  while ( totalTicks > RTC_CLOSE_TO_MAX_VALUE ) {
273  delayTicks( RTC_CLOSE_TO_MAX_VALUE );
274  totalTicks -= RTC_CLOSE_TO_MAX_VALUE;
275  }
276  delayTicks( totalTicks );
277 
278  return ECODE_EMDRV_RTCDRV_OK;
279 }
280 
281 /***************************************************************************/
295 {
296  // Check if valid timer ID.
297  if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
299  }
300 
301  INT_Disable();
302 
303  timer[ id ].running = false;
304  timer[ id ].allocated = false;
305 
306  INT_Enable();
307 
308  return ECODE_EMDRV_RTCDRV_OK;
309 }
310 
311 /***************************************************************************/
323 {
324  if ( rtcdrvIsInitialized == true ) {
325  return ECODE_EMDRV_RTCDRV_OK;
326  }
327  rtcdrvIsInitialized = true;
328 
329  // Ensure LE modules are clocked.
331 
332 #if defined( CMU_LFECLKEN0_RTCC )
333  // Enable LFECLK in CMU (will also enable oscillator if not enabled).
334  CMU_ClockSelectSet( cmuClock_LFE, RTCDRV_OSC );
335 #else
336  // Enable LFACLK in CMU (will also enable oscillator if not enabled).
337  CMU_ClockSelectSet( cmuClock_LFA, RTCDRV_OSC );
338 #endif
339 
340 #if defined( RTCDRV_USE_RTC )
341  // Set clock divider.
342  CMU_ClockDivSet( cmuClock_RTC, RTC_DIVIDER );
343 
344  // Enable RTC module clock.
345  CMU_ClockEnable( cmuClock_RTC, true );
346 
347  // Initialize RTC.
348  RTC_Init( &initRTC );
349 
350 #elif defined( RTCDRV_USE_RTCC )
351  // Set clock divider.
352  initRTCC.presc = (RTCC_CntPresc_TypeDef)CMU_DivToLog2( RTC_DIVIDER );
353 
354  // Enable RTCC module clock.
355  CMU_ClockEnable( cmuClock_RTCC, true );
356 
357  // Initialize RTCC.
358  RTCC_Init( &initRTCC );
359 
360  // Set up Compare channel.
361  RTCC_ChannelInit( 1, &initRTCCCompareChannel );
362 #endif
363 
364  // Disable RTC/RTCC interrupt generation.
365  RTC_INTDISABLE( RTC_ALL_INTS );
366  RTC_INTCLEAR( RTC_ALL_INTS );
367 
368  RTC_COUNTERRESET();
369 
370  // Clear and then enable RTC interrupts in NVIC.
371  NVIC_CLEARPENDINGIRQ();
372  NVIC_ENABLEIRQ();
373 
374 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
375  // Enable overflow interrupt for wallclock.
376  RTC_INTENABLE( RTC_OF_INT );
377 #endif
378 
379  // Reset RTCDRV internal data structures/variables.
380  memset( timer, 0, sizeof( timer ) );
381  inTimerIRQ = false;
382  rtcRunning = false;
383  startTimerNestingLevel = 0;
384 #if defined( EMODE_DYNAMIC )
385  sleepBlocked = false;
386 #endif
387 
388 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
389  wallClockOverflowCnt = 0;
390  wallClockTimeBase = 0;
391 
392 #if defined( EMODE_NEVER_ALLOW_EM3EM4 )
393  // Always block EM3 and EM4 if wallclock is running.
395 #endif
396 
397 #endif
398 
399  return ECODE_EMDRV_RTCDRV_OK;
400 }
401 
402 /***************************************************************************/
416 {
417  // Disable and clear all interrupt sources.
418  NVIC_DISABLEIRQ();
419  RTC_INTDISABLE( RTC_ALL_INTS );
420  RTC_INTCLEAR( RTC_ALL_INTS );
421  NVIC_CLEARPENDINGIRQ();
422 
423  // Disable RTC module and its clock.
424 #if defined( RTCDRV_USE_RTC )
425  RTC_Enable( false );
426  CMU_ClockEnable( cmuClock_RTC, false );
427 #elif defined( RTCDRV_USE_RTCC )
428  RTCC_Enable( false );
429  CMU_ClockEnable( cmuClock_RTCC, false );
430 #endif
431 
432 #if defined( EMODE_NEVER_ALLOW_EM3EM4 )
433  // End EM3 and EM4 blocking.
435 #endif
436 
437 #if defined( EMODE_DYNAMIC )
438  // End EM3 and EM4 blocking if a block start has been set.
439  if ( sleepBlocked ) {
441  }
442 #endif
443 
444  // Mark the driver as uninitialized.
445  rtcdrvIsInitialized = false;
446 
447  return ECODE_EMDRV_RTCDRV_OK;
448 }
449 
450 /***************************************************************************/
467 {
468  // Check if valid timer ID.
469  if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
471  }
472 
473  // Check pointer validity.
474  if ( isRunning == NULL ) {
476  }
477 
478  INT_Disable();
479  // Check if timer is reserved.
480  if ( ! timer[ id ].allocated ) {
481  INT_Enable();
483  }
484  *isRunning = timer[ id ].running;
485  INT_Enable();
486 
487  return ECODE_EMDRV_RTCDRV_OK;
488 }
489 
490 /***************************************************************************/
512  RTCDRV_TimerType_t type,
513  uint32_t timeout,
514  RTCDRV_Callback_t callback,
515  void *user )
516 {
517  uint32_t timeElapsed, cnt, compVal, loopCnt = 0;
518  uint32_t timeToNextTimerCompletion;
519 
520  // Check if valid timer ID.
521  if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
523  }
524 
525  INT_Disable();
526  if ( ! timer[ id ].allocated ) {
527  INT_Enable();
529  }
530 
531  if ( timeout == 0 ) {
532  if ( callback != NULL ) {
533  callback( id, user );
534  }
535  INT_Enable();
536  return ECODE_EMDRV_RTCDRV_OK;
537  }
538 
539  cnt = RTC_COUNTERGET();
540 
541  timer[ id ].callback = callback;
542  timer[ id ].ticks = MSEC_TO_TICKS( timeout );
543  if (rtcdrvTimerTypePeriodic == type) {
544  // Calculate compensation value for periodic timers.
545  timer[ id ].periodicCompensationUsec = 1000 * timeout -
546  (timer[ id ].ticks * TICK_TIME_USEC);
547  timer[ id ].periodicDriftUsec = TICK_TIME_USEC/2;
548  }
549  // Add one tick in order to compensate if RTC is close to an increment event.
550  timer[ id ].remaining = timer[ id ].ticks + 1;
551  timer[ id ].running = true;
552  timer[ id ].timerType = type;
553  timer[ id ].user = user;
554 
555  if ( inTimerIRQ == true ) {
556  // Exit now, remaining processing will be done in IRQ handler.
557  INT_Enable();
558  return ECODE_EMDRV_RTCDRV_OK;
559  }
560 
561  // StartTimer() may recurse, keep track of recursion level.
562  if ( startTimerNestingLevel < UINT32_MAX ) {
563  startTimerNestingLevel++;
564  }
565 
566  if ( rtcRunning == false ) {
567 
568 #if defined( RTCDRV_USE_RTC )
569  lastStart = ( cnt ) & RTC_COUNTER_MASK;
570 #elif defined( RTCDRV_USE_RTCC )
571  lastStart = cnt;
572 #endif
573 
574  RTC_INTCLEAR( RTC_COMP_INT );
575 
576  compVal = EFM32_MIN( timer[ id ].remaining, RTC_CLOSE_TO_MAX_VALUE );
577  RTC_COMPARESET( cnt + compVal );
578 
579  // Start the timer system by enabling the compare interrupt.
580  RTC_INTENABLE( RTC_COMP_INT );
581 
582 #if defined( EMODE_DYNAMIC )
583  // When RTC is running, we can not allow EM3 or EM4.
584  if ( sleepBlocked == false ) {
585  sleepBlocked = true;
587  }
588 #endif
589 
590  rtcRunning = true;
591 
592  } else {
593 
594  // The timer system is running. We must stop, update timers with the time
595  // elapsed so far, find the timer with the shortest timeout and then restart.
596  // As StartTimer() may be called from the callbacks we only do this
597  // processing at the first nesting level.
598  if ( startTimerNestingLevel == 1 ) {
599 
600  timer[ id ].running = false;
601  // This loop is repeated if CNT is incremented while processing.
602  do {
603 
604  RTC_INTDISABLE( RTC_COMP_INT );
605 
606  timeElapsed = TIMEDIFF( cnt, lastStart );
607 #if defined( RTCDRV_USE_RTC )
608  // Compensate for the fact that CNT is normally COMP0+1 after a
609  // compare match event.
610  if ( timeElapsed == RTC_MAX_VALUE ) {
611  timeElapsed = 0;
612  }
613 #endif
614 
615  // Update all timers with elapsed time.
616  checkAllTimers( timeElapsed );
617 
618  // Execute timer callbacks.
619  executeTimerCallbacks();
620 
621  // Set timer to running only after checkAllTimers() is called once.
622  if ( loopCnt == 0 ) {
623  timer[ id ].running = true;
624  }
625  loopCnt++;
626 
627  // Restart RTC according to next timeout.
628  rescheduleRtc( cnt );
629 
630  cnt = RTC_COUNTERGET();
631  timeElapsed = TIMEDIFF( cnt, lastStart );
632  timeToNextTimerCompletion = TIMEDIFF( RTC_COMPAREGET(), lastStart );
633 
634  /* If the counter has passed the COMP(ARE) register value since we
635  checked the timers, then we should recheck the timers and reschedule
636  again. */
637  }
638  while ( rtcRunning && (timeElapsed > timeToNextTimerCompletion));
639  }
640  }
641 
642  if ( startTimerNestingLevel > 0 ) {
643  startTimerNestingLevel--;
644  }
645 
646  INT_Enable();
647  return ECODE_EMDRV_RTCDRV_OK;
648 }
649 
650 /***************************************************************************/
662 {
663  // Check if valid timer ID.
664  if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
666  }
667 
668  INT_Disable();
669  if ( ! timer[ id ].allocated ) {
670  INT_Enable();
672  }
673 
674  timer[ id ].running = false;
675  INT_Enable();
676 
677  return ECODE_EMDRV_RTCDRV_OK;
678 }
679 
680 /***************************************************************************/
696 Ecode_t RTCDRV_TimeRemaining( RTCDRV_TimerID_t id, uint32_t *timeRemaining )
697 {
698  uint64_t tmp;
699  uint32_t timeLeft, currentCnt, lastRtcStart;
700 
701  // Check if valid timer ID.
702  if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) {
704  }
705 
706  // Check pointer validity.
707  if ( timeRemaining == NULL ) {
709  }
710 
711  INT_Disable();
712  // Check if timer is reserved.
713  if ( ! timer[ id ].allocated ) {
714  INT_Enable();
716  }
717 
718  // Check if timer is running.
719  if ( ! timer[ id ].running ) {
720  INT_Enable();
722  }
723 
724  timeLeft = timer[ id ].remaining;
725  currentCnt = RTC_COUNTERGET();
726  lastRtcStart = lastStart;
727  INT_Enable();
728 
729  // Get number of RTC clock ticks elapsed since last RTC reschedule.
730  currentCnt = TIMEDIFF( currentCnt, lastRtcStart );
731 
732  if ( currentCnt > timeLeft ) {
733  timeLeft = 0;
734  } else {
735  timeLeft -= currentCnt;
736  }
737 
738  tmp = TICKS_TO_MSEC( timeLeft );
739  *timeRemaining = tmp;
740 
741  return ECODE_EMDRV_RTCDRV_OK;
742 }
743 
744 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
745 /***************************************************************************/
752 uint32_t RTCDRV_GetWallClock( void )
753 {
754  return wallClockTimeBase
755  + (uint32_t)TICKS_TO_SEC( RTCDRV_GetWallClockTicks32() );
756 }
757 #endif
758 
759 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
760 /***************************************************************************/
769 {
770  uint32_t overflows, ticks;
771 
772  /* Need to re-read data in case overflow cnt is incremented while we read. */
773  do
774  {
775  overflows = wallClockOverflowCnt;
776  ticks = RTC_COUNTERGET();
777  } while ( overflows != wallClockOverflowCnt );
778 
779 #if ( RTC_COUNTER_BITS < 32 )
780  return ( overflows << RTC_COUNTER_BITS ) + ticks;
781 #else
782  return ticks;
783 #endif
784 }
785 #endif
786 
787 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
788 /***************************************************************************/
796 {
797  uint64_t overflows, ticks;
798 
799  /* Need to re-read data in case overflow cnt is incremented while we read. */
800  do
801  {
802  overflows = wallClockOverflowCnt;
803  ticks = RTC_COUNTERGET();
804  } while ( overflows != wallClockOverflowCnt );
805 
806  return ( overflows << RTC_COUNTER_BITS ) + ticks;
807 }
808 #endif
809 
810 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
811 /***************************************************************************/
820 Ecode_t RTCDRV_SetWallClock( uint32_t secs )
821 {
822  wallClockTimeBase = secs - TICKS_TO_SEC( RTCDRV_GetWallClockTicks32() );
823  return ECODE_EMDRV_RTCDRV_OK;
824 }
825 #endif
826 
827 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
828 /***************************************************************************/
837 uint64_t RTCDRV_MsecsToTicks( uint32_t ms )
838 {
839  return MSEC_TO_TICKS( ms );
840 }
841 #endif
842 
843 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
844 /***************************************************************************/
853 uint64_t RTCDRV_SecsToTicks( uint32_t secs )
854 {
855  return MSEC_TO_TICKS( 1000 * secs );
856 }
857 #endif
858 
859 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
860 /***************************************************************************/
869 uint32_t RTCDRV_TicksToMsec( uint64_t ticks )
870 {
871  return TICKS_TO_MSEC( ticks );
872 }
873 #endif
874 
875 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
876 /***************************************************************************/
885 uint32_t RTCDRV_TicksToSec( uint64_t ticks )
886 {
887  return TICKS_TO_MSEC( ticks ) / 1000;
888 }
889 #endif
890 
892 
893 #if defined( RTCDRV_USE_RTC )
894 void RTC_IRQHandler(void)
895 #elif defined( RTCDRV_USE_RTCC )
896 void RTCC_IRQHandler(void)
897 #endif
898 {
899  uint32_t flags, timeElapsed, cnt, timeToNextTimerCompletion;
900 
901  INT_Disable();
902 
903  // CNT will normally be COMP0+1 at this point,
904  // unless IRQ latency exceeded one tick period.
905 
906  flags = RTC_INTGET();
907 
908  if ( flags & RTC_COMP_INT ) {
909 
910  // Stop timer system by disabling the compare IRQ.
911  // Update all timers with the time elapsed, call callbacks if needed,
912  // then find the timer with the shortest timeout (if any at all) and
913  // reenable the compare IRQ if needed.
914 
915  inTimerIRQ = true;
916 
917  cnt = RTC_COUNTERGET();
918 
919  // This loop is repeated if CNT is incremented while processing.
920  do {
921 
922  RTC_INTDISABLE( RTC_COMP_INT );
923 
924  timeElapsed = TIMEDIFF( cnt, lastStart );
925 
926  // Update all timers with elapsed time.
927  checkAllTimers( timeElapsed );
928 
929  // Execute timer callbacks.
930  executeTimerCallbacks();
931 
932  // Restart RTC according to next timeout.
933  rescheduleRtc( cnt );
934 
935  cnt = RTC_COUNTERGET();
936  timeElapsed = TIMEDIFF( cnt, lastStart );
937  timeToNextTimerCompletion = TIMEDIFF( RTC_COMPAREGET(), lastStart );
938  /* If the counter has passed the COMP(ARE) register value since we
939  checked the timers, then we should recheck the timers and reschedule
940  again. */
941  }
942  while ( rtcRunning && (timeElapsed > timeToNextTimerCompletion));
943  inTimerIRQ = false;
944  }
945 
946 #if defined( EMDRV_RTCDRV_WALLCLOCK_CONFIG )
947  if ( flags & RTC_OF_INT )
948  {
949  RTC_INTCLEAR( RTC_OF_INT );
950  wallClockOverflowCnt++;
951  }
952 #endif
953 
954  INT_Enable();
955 }
956 
957 static void checkAllTimers( uint32_t timeElapsed )
958 {
959  int i;
960 #if defined( EMODE_DYNAMIC )
961  int numOfTimersRunning = 0;
962 #endif
963 
964  // Iterate through the timer table.
965  // Update time remaining, check for timeout and rescheduling of periodic
966  // timers, check for callbacks.
967 
968  for ( i = 0; i < EMDRV_RTCDRV_NUM_TIMERS; i++ ) {
969  timer[ i ].doCallback = false;
970  if ( timer[ i ].running == true ) {
971 #if defined( EMODE_DYNAMIC )
972  numOfTimersRunning++;
973 #endif
974  if ( timer[ i ].remaining > timeElapsed ) {
975  timer[ i ].remaining -= timeElapsed;
976  } else {
977  if ( timer[ i ].timerType == rtcdrvTimerTypeOneshot ) {
978  timer[ i ].running = false;
979 #if defined( EMODE_DYNAMIC )
980  numOfTimersRunning--;
981 #endif
982  } else {
983  // Compensate overdue periodic timers to avoid accumlating errors.
984  timer[ i ].remaining = timer[ i ].ticks - timeElapsed +
985  timer[ i ].remaining;
986  if ( timer[ i ].periodicCompensationUsec > 0 ) {
987  timer[ i ].periodicDriftUsec += timer[i].periodicCompensationUsec;
988  if (timer[ i ].periodicDriftUsec >= TICK_TIME_USEC) {
989  // Add a tick if the timer drift is longer than the time of
990  // one tick.
991  timer[ i ].remaining += 1;
992  timer[ i ].periodicDriftUsec -= TICK_TIME_USEC;
993  }
994  }
995  else {
996  timer[ i ].periodicDriftUsec -= timer[i].periodicCompensationUsec;
997  if (timer[ i ].periodicDriftUsec >= TICK_TIME_USEC) {
998  // Subtract one tick if the timer drift is longer than the time
999  // of one tick.
1000  timer[ i ].remaining -= 1;
1001  timer[ i ].periodicDriftUsec -= TICK_TIME_USEC;
1002  }
1003  }
1004  }
1005  if ( timer[ i ].callback != NULL ) {
1006  timer[ i ].doCallback = true;
1007  }
1008  }
1009  }
1010  }
1011 
1012 #if defined( EMODE_DYNAMIC )
1013  // If no timers are running, we can remove block on EM3 and EM4 sleep modes.
1014  if ( ( numOfTimersRunning == 0 ) && ( sleepBlocked == true ) ) {
1015  sleepBlocked = false;
1017  }
1018 #endif
1019 }
1020 
1021 static void delayTicks( uint32_t ticks )
1022 {
1023  uint32_t startTime;
1024  volatile uint32_t now;
1025 
1026  if ( ticks ) {
1027  startTime = RTC_COUNTERGET();
1028  do {
1029  now = RTC_COUNTERGET();
1030  } while ( TIMEDIFF( now, startTime ) < ticks );
1031  }
1032 }
1033 
1034 static void executeTimerCallbacks( void )
1035 {
1036  int i;
1037 
1038  for ( i = 0; i < EMDRV_RTCDRV_NUM_TIMERS; i++ ) {
1039  if ( timer[ i ].doCallback ) {
1040  timer[ i ].callback( i, timer[ i ].user );
1041  }
1042  }
1043 }
1044 
1045 static void rescheduleRtc( uint32_t rtcCnt )
1046 {
1047  int i;
1048  uint64_t min = UINT64_MAX;
1049 
1050  // Find the timer with shortest timeout.
1051  for ( i = 0; i < EMDRV_RTCDRV_NUM_TIMERS; i++ ) {
1052  if ( ( timer[ i ].running == true )
1053  && ( timer[ i ].remaining < min ) ) {
1054  min = timer[ i ].remaining;
1055  }
1056  }
1057 
1058  rtcRunning = false;
1059  if ( min != UINT64_MAX ) {
1060  min = EFM32_MIN( min, RTC_CLOSE_TO_MAX_VALUE );
1061 #if defined( RTCDRV_USE_RTC )
1062  if ( inTimerIRQ == false ) {
1063  lastStart = ( rtcCnt ) & RTC_COUNTER_MASK;
1064  } else
1065 #endif
1066  {
1067  lastStart = rtcCnt;
1068  }
1069  RTC_INTCLEAR( RTC_COMP_INT );
1070 
1071  RTC_COMPARESET( rtcCnt + min );
1072 
1073 #if defined( EMODE_DYNAMIC )
1074  // When RTC is running, we can not allow EM3 or EM4.
1075  if ( sleepBlocked == false ) {
1076  sleepBlocked = true;
1078  }
1079 #endif
1080 
1081  rtcRunning = true;
1082 
1083  // Reenable compare IRQ.
1084  RTC_INTENABLE( RTC_COMP_INT );
1085  }
1086 }
1088 
1089 /******** THE REST OF THE FILE IS DOCUMENTATION ONLY !**********************/
Clock management unit (CMU) API.
void CMU_ClockSelectSet(CMU_Clock_TypeDef clock, CMU_Select_TypeDef ref)
Select reference clock/oscillator used for a clock branch.
Definition: em_cmu.c:2406
uint64_t RTCDRV_SecsToTicks(uint32_t secs)
Convert from seconds to RTC/RTCC ticks.
Definition: rtcdriver.c:853
#define EFM32_MIN(a, b)
Definition: em_common.h:79
void SLEEP_SleepBlockEnd(SLEEP_EnergyMode_t eMode)
End sleep block in the requested energy mode.
Definition: sleep.c:299
uint32_t RTCDRV_TimerID_t
Timer ID.
Definition: rtcdriver.h:49
Periodic timer.
Definition: rtcdriver.h:70
#define ECODE_EMDRV_RTCDRV_OK
Success return value.
Definition: rtcdriver.h:41
Oneshot timer.
Definition: rtcdriver.h:69
#define ECODE_EMDRV_RTCDRV_TIMER_NOT_RUNNING
Timer is not running.
Definition: rtcdriver.h:46
__STATIC_INLINE uint32_t INT_Enable(void)
Enable interrupts.
Definition: em_int.h:94
Ecode_t RTCDRV_AllocateTimer(RTCDRV_TimerID_t *id)
Allocate timer.
Definition: rtcdriver.c:228
Ecode_t RTCDRV_SetWallClock(uint32_t secs)
Set wallclock time.
Definition: rtcdriver.c:820
CMSIS Cortex-M Peripheral Access Layer for Silicon Laboratories microcontroller devices.
void SLEEP_SleepBlockBegin(SLEEP_EnergyMode_t eMode)
Begin sleep block in the requested energy mode.
Definition: sleep.c:255
Ecode_t RTCDRV_TimeRemaining(RTCDRV_TimerID_t id, uint32_t *timeRemaining)
Get time left before a given timer expires.
Definition: rtcdriver.c:696
#define ECODE_EMDRV_RTCDRV_PARAM_ERROR
Illegal input parameter.
Definition: rtcdriver.h:45
Emlib general purpose utilities.
RTCDRV timer API definition.
RTCDRV_TimerType_t
Timer type enumerator.
Definition: rtcdriver.h:68
#define ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID
Illegal timer id.
Definition: rtcdriver.h:43
Interrupt enable/disable unit API.
Ecode_t RTCDRV_StopTimer(RTCDRV_TimerID_t id)
Stop a given timer.
Definition: rtcdriver.c:661
Ecode_t RTCDRV_Init(void)
Initialize RTCDRV driver.
Definition: rtcdriver.c:322
#define ECODE_EMDRV_RTCDRV_ALL_TIMERS_USED
No timers available.
Definition: rtcdriver.h:42
uint32_t RTCDRV_GetWallClockTicks32(void)
Get wallclock tick count as a 32bit value. At 4 ticks per millisecond, overflow occurs after approxim...
Definition: rtcdriver.c:768
uint32_t RTCDRV_GetWallClock(void)
Get wallclock time.
Definition: rtcdriver.c:752
uint32_t RTCDRV_TicksToMsec(uint64_t ticks)
Convert from RTC/RTCC ticks to milliseconds.
Definition: rtcdriver.c:869
uint64_t RTCDRV_GetWallClockTicks64(void)
Get wallclock tick count as a 64 bit value. This will never overflow.
Definition: rtcdriver.c:795
uint64_t RTCDRV_MsecsToTicks(uint32_t ms)
Convert from milliseconds to RTC/RTCC ticks.
Definition: rtcdriver.c:837
Real Time Counter (RTCC) peripheral API.
__STATIC_INLINE uint32_t CMU_DivToLog2(CMU_ClkDiv_TypeDef div)
Convert dividend to logarithmic value. Only works for even numbers equal to 2^n.
Definition: em_cmu.h:1196
Ecode_t RTCDRV_Delay(uint32_t ms)
Millisecond delay function.
Definition: rtcdriver.c:266
Ecode_t RTCDRV_FreeTimer(RTCDRV_TimerID_t id)
Free timer.
Definition: rtcdriver.c:294
void RTC_Init(const RTC_Init_TypeDef *init)
Initialize RTC.
Definition: em_rtc.c:302
void CMU_ClockEnable(CMU_Clock_TypeDef clock, bool enable)
Enable/disable a clock.
Definition: em_cmu.c:1369
#define ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED
Timer is not allocated.
Definition: rtcdriver.h:44
Ecode_t RTCDRV_DeInit(void)
Deinitialize RTCDRV driver.
Definition: rtcdriver.c:415
Real Time Counter (RTC) peripheral API.
void(* RTCDRV_Callback_t)(RTCDRV_TimerID_t id, void *user)
Typedef for the user supplied callback function which is called when a timer elapse.
Definition: rtcdriver.h:65
void RTC_Enable(bool enable)
Enable/disable RTC.
Definition: em_rtc.c:214
Ecode_t RTCDRV_StartTimer(RTCDRV_TimerID_t id, RTCDRV_TimerType_t type, uint32_t timeout, RTCDRV_Callback_t callback, void *user)
Start a timer.
Definition: rtcdriver.c:511
uint32_t Ecode_t
Typedef for API function errorcode return values.
Definition: ecode.h:31
__STATIC_INLINE uint32_t INT_Disable(void)
Disable interrupts.
Definition: em_int.h:71
Ecode_t RTCDRV_IsRunning(RTCDRV_TimerID_t id, bool *isRunning)
Check if a given timer is running.
Definition: rtcdriver.c:466
uint32_t RTCDRV_TicksToSec(uint64_t ticks)
Convert from RTC/RTCC ticks to seconds.
Definition: rtcdriver.c:885
void CMU_ClockDivSet(CMU_Clock_TypeDef clock, CMU_ClkDiv_TypeDef div)
Set clock divisor/prescaler.
Definition: em_cmu.c:1141
Energy Modes management driver.