EFM32 Gecko Software Documentation  efm32g-doc-5.1.2
udelay.c
Go to the documentation of this file.
1 /**************************************************************************/
17 #include "em_device.h"
18 #include "em_cmu.h"
19 #include "em_core.h"
20 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
21 #include "em_rtcc.h"
22 #else
23 #include "em_rtc.h"
24 #endif
25 
26 #include "udelay.h"
27 
28 /**************************************************************************/
52 /* this should be approx 2 Bo*oMips to start (note initial shift), and will
53  * still work even if initially too large, it will just take slightly longer */
54 volatile unsigned long loops_per_jiffy = (1<<12);
55 
56 /* This is the number of bits of precision for the loops_per_jiffy. Each
57  * bit takes on average 1.5/HZ seconds. This (like the original) is a little
58  * better than 1% */
59 #define LPS_PREC 8
60 
61 static void calibrate_delay(void);
62 __STATIC_INLINE uint32_t clock(void);
63 static void _delay( uint32_t delay);
64 
67 /***************************************************************************/
71 void UDELAY_Calibrate(void)
72 {
73  CMU_Select_TypeDef lfaClkSel;
74  CMU_ClkDiv_TypeDef rtcClkDiv;
75  bool rtcRestore = false;
76  bool leClkTurnoff = false;
77  bool rtcClkTurnoff = false;
78  bool lfaClkSrcRestore = false;
79  bool lfaClkTurnoff = false;
80 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
81  RTCC_Init_TypeDef init = RTCC_INIT_DEFAULT;
82  uint32_t rtcCtrl=0, rtcIen=0;
83 #else
85  uint32_t rtcCtrl=0, rtcComp0=0, rtcComp1=0, rtcIen=0;
86 #endif
88 
89  /* Ensure LE modules are accessible */
90 #if defined (_CMU_HFBUSCLKEN0_MASK)
91  if ( !( CMU->HFBUSCLKEN0 & CMU_HFBUSCLKEN0_LE) )
92 #else
93  if ( !( CMU->HFCORECLKEN0 & CMU_HFCORECLKEN0_LE) )
94 #endif
95  {
96  CMU_ClockEnable(cmuClock_CORELE, true);
97  leClkTurnoff = true;
98  }
99 
100 #if defined (CMU_LFECLKEN0_RTCC)
101  lfaClkSel = CMU_ClockSelectGet(cmuClock_LFE);
102 #else
103  lfaClkSel = CMU_ClockSelectGet(cmuClock_LFA);
104 #endif
105 
106  #if defined( UDELAY_LFXO )
107  if ( !(CMU->STATUS & CMU_STATUS_LFXOENS) )
108  {
109  lfaClkTurnoff = true;
110  CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
111  }
112 
113  if ( lfaClkSel != cmuSelect_LFXO )
114  {
115  lfaClkSrcRestore = true;
116 #if defined (CMU_LFECLKEN0_RTCC)
117  CMU_ClockSelectSet(cmuClock_LFE, cmuSelect_LFXO);
118 #else
120 #endif
121  }
122 
123  #else
124  if ( lfaClkSel != cmuSelect_LFRCO )
125  {
126  lfaClkSrcRestore = true;
127  }
128  if ( !(CMU->STATUS & CMU_STATUS_LFRCOENS) )
129  {
130  lfaClkTurnoff = true;
131  }
132  /* Enable LFACLK in CMU (will also enable oscillator if not enabled) */
133 #if defined (CMU_LFECLKEN0_RTCC)
134  CMU_ClockSelectSet(cmuClock_LFE, cmuSelect_LFRCO);
135 #else
137 #endif
138  #endif
139 
140  /* Set up a reasonable prescaler. */
141 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
142  rtcClkDiv = CMU_ClockDivGet(cmuClock_RTCC);
143  if ( !(CMU->LFECLKEN0 & CMU_LFECLKEN0_RTCC) )
144  {
145  /* Enable clock to RTCC module */
146  CMU_ClockEnable(cmuClock_RTCC, true);
147  rtcClkTurnoff = true;
148  }
149 #else
150  rtcClkDiv = CMU_ClockDivGet(cmuClock_RTC);
152  if ( !(CMU->LFACLKEN0 & CMU_LFACLKEN0_RTC) )
153  {
154  /* Enable clock to RTC module */
156  rtcClkTurnoff = true;
157  }
158 #endif
159 
161 
162 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
163  if ( RTCC->CTRL & RTCC_CTRL_ENABLE )
164  {
165  /* Stash away current RTC settings. */
166  rtcCtrl = RTCC->CTRL;
167  rtcIen = RTCC->IEN;
168 
169  RTCC->CTRL = _RTCC_CTRL_RESETVALUE;
170  RTCC->IEN = 0;
171  RTCC->IFC = _RTCC_IEN_MASK;
172 
173  NVIC_ClearPendingIRQ( RTCC_IRQn );
174 
175  rtcRestore = true;
176  }
177  init.precntWrapOnCCV0 = false; /* Count to max before wrapping */
178  init.cntWrapOnCCV1 = false; /* Count to max before wrapping */
179  init.presc = rtccCntPresc_256; /* Setup prescaler */
180 
181  RTCC_Init(&init); /* Start RTC counter. */
182 
183 #else
184  if ( RTC->CTRL & RTC_CTRL_EN )
185  {
186  /* Stash away current RTC settings. */
187  rtcCtrl = RTC->CTRL;
188  rtcComp0 = RTC->COMP0;
189  rtcComp1 = RTC->COMP1;
190  rtcIen = RTC->IEN;
191 
192  RTC->CTRL = _RTC_CTRL_RESETVALUE;
193  RTC->IEN = 0;
194  RTC->IFC = _RTC_IEN_MASK;
195 
196  NVIC_ClearPendingIRQ( RTC_IRQn );
197 
198  rtcRestore = true;
199  }
200  init.comp0Top = false; /* Count to max before wrapping */
201 
202  RTC_Init(&init); /* Start RTC counter. */
203 
204 #endif
205 
206  calibrate_delay(); /* Calibrate the micro second delay loop. */
207 
209 
210  /* Restore all RTC related settings to how they were previously set. */
211  if ( rtcRestore )
212  {
213 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
214  CMU_ClockDivSet(cmuClock_RTCC, rtcClkDiv);
215  RTCC->CTRL = rtcCtrl;
216  RTCC->IEN = rtcIen;
217 #else
218  CMU_ClockDivSet(cmuClock_RTC, rtcClkDiv);
219  RTC_FreezeEnable(true);
220  #if defined(_EFM32_GECKO_FAMILY)
222  #endif
223  RTC->COMP0 = rtcComp0;
224  RTC->COMP1 = rtcComp1;
225  RTC->CTRL = rtcCtrl;
226  RTC->IEN = rtcIen;
227  RTC_FreezeEnable(false);
228 #endif
229  }
230  else
231  {
232 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
233  RTCC_Enable(false);
234 #else
235  RTC_Enable(false);
236 #endif
237  }
238 
239  if ( rtcClkTurnoff )
240  {
241 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
242  CMU_ClockEnable(cmuClock_RTCC, false);
243 #else
245 #endif
246  }
247 
248  if ( lfaClkSrcRestore )
249  {
250 #if defined (CMU_LFECLKEN0_RTCC)
251  CMU_ClockSelectSet(cmuClock_LFE, lfaClkSel);
252 #else
253  CMU_ClockSelectSet(cmuClock_LFA, lfaClkSel);
254 #endif
255  }
256 
257  if ( lfaClkTurnoff )
258  {
259  #if defined( UDELAY_LFXO )
260  CMU_OscillatorEnable(cmuOsc_LFXO, false, false);
261  #else
262  CMU_OscillatorEnable(cmuOsc_LFRCO, false, false);
263  #endif
264  }
265 
266  if ( leClkTurnoff )
267  {
268  CMU_ClockEnable(cmuClock_CORELE, false);
269  }
270 }
271 
272 #if defined(__GNUC__) /* GCC */
273 /***************************************************************************/
284 void UDELAY_Delay( uint32_t usecs )
285 {
286  __ASM volatile (
287 #if ( __CORTEX_M == 0x00 )
288 " .syntax unified \n"
289 " .arch armv6-m \n"
290 #endif
291 " cmp %0, #0 \n" /* Return if 0 delay. */
292 " beq.n 2f \n"
293 " subs %0, #1 \n" /* Correct for off by one error. */
294 " movs r2, #0x88 \n"
295 " lsls r2, r2, #8 \n"
296 " adds r2, #0x00 \n"
297 " muls %0, r2 \n"
298 " \n"
299 " ldr r2, [%1] \n"
300 " movs r0, %0, lsr #11 \n"
301 " movs r2, r2, lsr #11 \n"
302 " \n"
303 " muls r0, r2 \n"
304 " movs r0, r0, lsr #6 \n"
305 " \n"
306 " beq.n 2f \n"
307 " \n"
308 "1: subs r0, #1 \n"
309 " bhi 1b \n"
310 #if ( __CORTEX_M == 0x00 )
311 "2: \n"
312 " .syntax divided \n" : : "r" (usecs), "r" (&loops_per_jiffy) : "r0", "r2", "cc" );
313 #else
314 "2: \n" : : "r" (usecs), "r" (&loops_per_jiffy) : "r0", "r2", "cc" );
315 #endif
316 }
317 #endif /* defined(__GNUC__) */
318 
321 static void calibrate_delay(void)
322 {
323  /* From linux 2.4 source. */
324  unsigned long loopbit;
325  unsigned long ticks;
326  int lps_precision = LPS_PREC;
327 
328  loops_per_jiffy = (1<<12);
329 
330  while (loops_per_jiffy <<= 1) {
331  /* wait for "start of" clock tick */
332  ticks = clock();
333  while (ticks == clock())
334  /* nothing */;
335  /* Go .. */
336  ticks = clock();
337  _delay(loops_per_jiffy);
338  ticks = clock() - ticks;
339  if (ticks)
340  break;
341  }
342 
343  /* Do a binary approximation to get loops_per_jiffy set to equal one clock
344  (up to lps_precision bits) */
345 
346  loops_per_jiffy >>= 1;
347  loopbit = loops_per_jiffy;
348  while ( lps_precision-- && (loopbit >>= 1) ) {
349  loops_per_jiffy |= loopbit;
350  ticks = clock();
351  while (ticks == clock());
352  ticks = clock();
353  _delay(loops_per_jiffy);
354  if (clock() != ticks) /* longer than 1 tick */
355  loops_per_jiffy &= ~loopbit;
356  }
357 }
358 
359 __STATIC_INLINE uint32_t clock(void)
360 {
361 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
362  return RTCC_CounterGet();
363 #else
364  return RTC_CounterGet();
365 #endif
366 }
367 
368 #if defined(__ICCARM__) /* IAR */
369 static void _delay( uint32_t delay)
370 {
371  __ASM volatile (
372 "_delay_1: \n"
373 " subs r0, #1 \n"
374 " bhi.n _delay_1 \n" );
375 }
376 
377 void UDELAY_Delay( uint32_t usecs )
378 {
379  __ASM volatile (
380 " cmp %0, #0 \n" /* Return if 0 delay. */
381 " beq.n udelay_2 \n"
382 " subs %0, #1 \n" /* Correct for off by one error. */
383 " movs r2, #0x88 \n"
384 " lsls r2, r2, #8 \n"
385 " adds r2, #0x00 \n"
386 " muls %0, r2 \n"
387 " \n"
388 " ldr r2, [%1] \n"
389 " movs r0, %0, lsr #11 \n"
390 " movs r2, r2, lsr #11 \n"
391 " \n"
392 " muls r0, r2 \n"
393 " movs r0, r0, lsr #6 \n"
394 " \n"
395 " beq.n udelay_2 \n"
396 " \n"
397 "udelay_1: \n"
398 " subs r0, #1 \n"
399 " bhi.n udelay_1 \n"
400 "udelay_2: \n" : : "r" (usecs), "r" (&loops_per_jiffy) : "r0", "r2", "cc");
401 }
402 #endif /* defined(__ICCARM__) */
403 
404 #if defined(__GNUC__) /* GCC */
405 static void _delay( uint32_t delay )
406 {
407  __ASM volatile (
408 #if ( __CORTEX_M == 0x00 )
409 " .syntax unified \n"
410 " .arch armv6-m \n"
411 #endif
412 "1: subs %0, #1 \n"
413 #if ( __CORTEX_M == 0x00 )
414 " bhi.n 1b \n"
415 " .syntax divided \n" : : "r" (delay) );
416 #else
417 " bhi.n 1b \n" : : "r" (delay) );
418 #endif
419 }
420 #endif /* defined(__GNUC__) */
421 
422 #if defined(__CC_ARM) /* Keil */
423 static __ASM void _delay( uint32_t delay)
424 {
425 _delay_1
426  subs r0, #1
427  bhi _delay_1
428  bx lr
429 }
430 
431 __ASM void UDELAY_Delay( uint32_t usecs __attribute__ ((unused)) )
432 {
433  IMPORT loops_per_jiffy
434 
435  cmp r0, #0 /* Return if 0 delay. */
436  beq.n udelay_2
437  subs r0, #1 /* Correct for off by one error. */
438  movs r2, #0x88
439  lsls r2, r2, #8
440  adds r2, #0x00
441  muls r0, r2, r0
442 
443  ldr r2, =loops_per_jiffy
444  ldr r2, [r2]
445  movs r0, r0, lsr #11
446  movs r2, r2, lsr #11
447 
448  muls r0, r2, r0
449  movs r0, r0, lsr #6
450  beq udelay_2
451 udelay_1
452  subs r0, #1
453  bhi udelay_1
454 udelay_2
455  bx lr
456 }
457 #endif /* defined(__CC_ARM) */
458 
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:2521
#define CORE_DECLARE_IRQ_STATE
Definition: em_core.h:85
#define RTC
__STATIC_INLINE uint32_t RTC_CounterGet(void)
Get RTC counter value.
Definition: em_rtc.h:90
CMU_Select_TypeDef
Definition: em_cmu.h:962
#define CMU_STATUS_LFRCOENS
Definition: efm32g_cmu.h:460
void UDELAY_Calibrate(void)
Calibrates the microsecond delay loop.
Definition: udelay.c:71
#define RTC_SYNCBUSY_CTRL
Definition: efm32g_rtc.h:200
CMSIS Cortex-M Peripheral Access Layer for Silicon Laboratories microcontroller devices.
#define RTC_CTRL_EN
Definition: efm32g_rtc.h:64
uint32_t CMU_ClkDiv_TypeDef
Definition: em_cmu.h:167
#define RTC_SYNCBUSY_COMP0
Definition: efm32g_rtc.h:205
#define CORE_ENTER_ATOMIC()
Definition: em_core.h:138
#define CMU_HFCORECLKEN0_LE
Definition: efm32g_cmu.h:655
#define CMU_STATUS_LFXOENS
Definition: efm32g_cmu.h:470
Microsecond delay routine.
Real Time Counter (RTCC) peripheral API.
bool comp0Top
Definition: em_rtc.h:64
void RTC_Init(const RTC_Init_TypeDef *init)
Initialize RTC.
Definition: em_rtc.c:305
#define RTC_SYNCBUSY_COMP1
Definition: efm32g_rtc.h:210
Core interrupt handling API.
void CMU_ClockEnable(CMU_Clock_TypeDef clock, bool enable)
Enable/disable a clock.
Definition: em_cmu.c:1453
#define CMU_LFACLKEN0_RTC
Definition: efm32g_cmu.h:785
#define CORE_EXIT_ATOMIC()
Definition: em_core.h:142
Real Time Counter (RTC) peripheral API.
void CMU_OscillatorEnable(CMU_Osc_TypeDef osc, bool enable, bool wait)
Enable/disable oscillator.
Definition: em_cmu.c:3594
void RTC_Enable(bool enable)
Enable/disable RTC.
Definition: em_rtc.c:217
#define CMU
void RTC_FreezeEnable(bool enable)
RTC register synchronization freeze control.
Definition: em_rtc.c:261
#define _RTC_CTRL_RESETVALUE
Definition: efm32g_rtc.h:62
#define cmuClkDiv_256
Definition: em_cmu.h:157
#define RTC_INIT_DEFAULT
Definition: em_rtc.h:68
#define _RTC_IEN_MASK
Definition: efm32g_rtc.h:167
void CMU_ClockDivSet(CMU_Clock_TypeDef clock, CMU_ClkDiv_TypeDef div)
Set clock divisor/prescaler.
Definition: em_cmu.c:1244
void UDELAY_Delay(uint32_t usecs)
Microsecond active wait delay routine.
Definition: udelay.c:284
CMU_ClkDiv_TypeDef CMU_ClockDivGet(CMU_Clock_TypeDef clock)
Get clock divisor/prescaler.
Definition: em_cmu.c:1112
CMU_Select_TypeDef CMU_ClockSelectGet(CMU_Clock_TypeDef clock)
Get currently selected reference clock used for a clock branch.
Definition: em_cmu.c:2146