Embedded System Design 2 - Project
delay.c
Go to the documentation of this file.
1 /***************************************************************************//**
2  * @file delay.c
3  * @brief Delay functionality.
4  * @version 3.2
5  * @author Brecht Van Eeckhoudt
6  *
7  * ******************************************************************************
8  *
9  * @section Versions
10  *
11  * @li v1.0: Moved delay functionality from `util.c` to this file.
12  * @li v1.1: Changed global variables to be static (~hidden) and added dbprint `\ n \ r ` fixes.
13  * @li v1.2: Removed EM1 delay method (see note in delayRTCC_EM2).
14  * @li v1.3: Cleaned up includes, added option to select SysTick/EM2 delay functionality
15  * and moved all of the logic in one `delay` method. Renamed sleep method.
16  * @li v1.4: Changed names of static variables, made initRTCcomp static.
17  * @li v1.5: Updated documentation.
18  * @li v1.6: Changed RTCcomp names to RTC.
19  * @li v1.7: Moved IRQ handler of RTC to this file.
20  * @li v1.8: Added ULFRCO logic.
21  * @li v1.9: Updated code with new DEFINE checks.
22  * @li v2.0: Added functionality to enable/disable the clocks only when necessary.
23  * @li v2.1: Added functionality to check if a wakeup was caused by the RTC.
24  * @li v2.2: Removed some clock disabling statements.
25  * @li v2.3: Changed error numbering.
26  * @li v2.4: Moved definitions from header to source file.
27  * @li v2.5: Added functionality to the time spend sleeping.
28  * @li v3.0: Disabled peripheral clock before entering an `error` function, added
29  * functionality to exit methods after `error` call and updated version number.
30  * @li v3.1: Removed `static` before some local variables (not necessary).
31  * @li v3.2: Moved `msTicks` variable and systick handler in `#if` check.
32  *
33  * ******************************************************************************
34  *
35  * @todo
36  * **Future improvements:**@n
37  * - Split definition to use the `ULFRCO` for the *delay* and *sleep* method separately.
38  * - This isn't that easy because of the common INIT method.
39  * - Don't forget to update `documentation.h` if this functionality is changed.
40  * - Enable/disable clock functionality? (see comments using `//`)
41  *
42  * ******************************************************************************
43  *
44  * @section License
45  *
46  * **Copyright (C) 2019 - Brecht Van Eeckhoudt**
47  *
48  * This program is free software: you can redistribute it and/or modify
49  * it under the terms of the **GNU General Public License** as published by
50  * the Free Software Foundation, either **version 3** of the License, or
51  * (at your option) any later version.
52  *
53  * This program is distributed in the hope that it will be useful,
54  * but WITHOUT ANY WARRANTY; without even the implied warranty of
55  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
56  * GNU General Public License for more details.
57  *
58  * *A copy of the GNU General Public License can be found in the `LICENSE`
59  * file along with this source code.*
60  *
61  * @n
62  *
63  * Some methods use code obtained from examples from [Silicon Labs' GitHub](https://github.com/SiliconLabs/peripheral_examples).
64  * These sections are licensed under the Silabs License Agreement. See the file
65  * "Silabs_License_Agreement.txt" for details. Before using this software for
66  * any purpose, you must agree to the terms of that agreement.
67  *
68  ******************************************************************************/
69 
70 
71 #include <stdint.h> /* (u)intXX_t */
72 #include <stdbool.h> /* "bool", "true", "false" */
73 #include "em_device.h" /* Include necessary MCU-specific header file */
74 #include "em_cmu.h" /* Clock management unit */
75 #include "em_emu.h" /* Energy Management Unit */
76 #include "em_gpio.h" /* General Purpose IO */
77 #include "em_rtc.h" /* Real Time Counter (RTC) */
78 
79 #include "delay.h" /* Corresponding header file */
80 #include "pin_mapping.h" /* PORT and PIN definitions */
81 #include "debug_dbprint.h" /* Enable or disable printing to UART */
82 #include "util.h" /* Utility functionality */
83 
84 
85 /* Local definitions (for RTC compare interrupts) */
86 #define ULFRCOFREQ 1000
87 #define ULFRCOFREQ_MS 1.000
88 #define LFXOFREQ 32768
89 #define LFXOFREQ_MS 32.768
90 
91 
92 /* Local variables */
93 /* -> Volatile because it's modified by an interrupt service routine (@RAM)
94  * -> Static so it's always kept in memory (@data segment, space provided during compile time) */
95 static volatile bool RTC_sleep_wakeup = false;
96 
97 #if SYSTICKDELAY == 1 /* SysTick delay selected */
98 static volatile uint32_t msTicks;
99 #endif /* SysTick/RTC selection */
100 
101 
102 bool sleeping = false;
103 bool RTC_initialized = false;
104 
105 #if SYSTICKDELAY == 1 /* SysTick delay selected */
106 bool SysTick_initialized = false;
107 #endif /* SysTick/RTC selection */
108 
109 
110 /* Local prototype */
111 static void initRTC (void);
112 
113 
114 /**************************************************************************//**
115  * @brief
116  * Wait for a certain amount of milliseconds in EM2/3.
117  *
118  * @details
119  * This method also initializes SysTick/RTC if necessary.
120  *
121  * @param[in] msDelay
122  * The delay time in **milliseconds**.
123  *****************************************************************************/
124 void delay (uint32_t msDelay)
125 {
126 
127 #if SYSTICKDELAY == 1 /* SysTick delay selected */
128 
129  /* Initialize SysTick if not already the case */
130  if (!SysTick_initialized)
131  {
132  /* Initialize and start SysTick
133  * Number of ticks between interrupt = cmuClock_CORE/1000 */
134  if (SysTick_Config(CMU_ClockFreqGet(cmuClock_CORE) / 1000)) while (1);
135 
136 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
137  dbinfo("SysTick initialized");
138 #endif /* DEBUG_DBPRINT */
139 
140  SysTick_initialized = true;
141  }
142  else
143  {
144  /* Enable SysTick interrupt and counter by setting their bits. */
145  SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
146  }
147 
148  /* Wait a certain amount of ticks */
149  uint32_t curTicks = msTicks;
150  while ((msTicks - curTicks) < msDelay);
151 
152  /* Disable SysTick interrupt and counter (needs to be done before entering EM2/3) by clearing their bits. */
153  SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk & ~SysTick_CTRL_ENABLE_Msk;
154 
155 #else /* EM2/3 RTC delay selected */
156 
157 
158  /* Initialize RTC if not already the case */
159  if (!RTC_initialized) initRTC();
160  else
161  {
162  /* Enable necessary oscillator and clocks */
163 
164 #if ULFRCO == 1 /* ULFRCO selected */
165 
166  /* No specific code here */
167 
168 #else /* LFXO selected */
169 
170  /* Enable the low-frequency crystal oscillator for the RTC */
171  //CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
172 
173 #endif /* ULFRCO/LFXO selection */
174 
175  /* Enable the clock to the interface of the low energy modules
176  * cmuClock_CORELE = cmuClock_HFLE (deprecated) */
177  //CMU_ClockEnable(cmuClock_HFLE, true);
178 
179  /* Turn on the RTC clock */
180  CMU_ClockEnable(cmuClock_RTC, true);
181  }
182 
183  /* Set RTC compare value for RTC compare register 0 depending on ULFRCO/LFXO selection */
184 
185 #if ULFRCO == 1 /* ULFRCO selected */
186 
187  if ((ULFRCOFREQ_MS * msDelay) <= 0x00ffffff) RTC_CompareSet(0, (ULFRCOFREQ_MS * msDelay));
188  else
189  {
190 
191 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
192  dbcrit("Delay too long, can't fit in the field!");
193 #endif /* DEBUG_DBPRINT */
194 
195  /* Turn off the RTC clock */
196  CMU_ClockEnable(cmuClock_RTC, false);
197 
198  error(14);
199 
200  /* Exit function */
201  return;
202  }
203 
204 #else /* LFXO selected */
205 
206  if ((LFXOFREQ_MS * msDelay) <= 0x00ffffff) RTC_CompareSet(0, (LFXOFREQ_MS * msDelay));
207  else
208  {
209 
210 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
211  dbcrit("Delay too long, can't fit in the field!");
212 #endif /* DEBUG_DBPRINT */
213 
214  /* Turn off the RTC clock */
215  CMU_ClockEnable(cmuClock_RTC, false);
216 
217  error(15);
218 
219  /* Exit function */
220  return;
221  }
222 
223 #endif /* ULFRCO/LFXO selection */
224 
225 
226  /* Start the RTC */
227  RTC_Enable(true);
228 
229  /* Enter EM2/3 depending on ULFRCO/LFXO selection */
230 
231 #if ULFRCO == 1 /* ULFRCO selected */
232  /* In EM3, high and low frequency clocks are disabled. No oscillator (except the ULFRCO) is running.
233  * Furthermore, all unwanted oscillators are disabled in EM3. This means that nothing needs to be
234  * manually disabled before the statement EMU_EnterEM3(true); */
235  EMU_EnterEM3(true); /* "true" - Save and restore oscillators, clocks and voltage scaling */
236 #else /* LFXO selected */
237  EMU_EnterEM2(true); /* "true" - Save and restore oscillators, clocks and voltage scaling */
238 #endif /* ULFRCO/LFXO selection */
239 
240 
241  /* Disable used oscillator and clocks after wake-up */
242 
243 #if ULFRCO == 1 /* ULFRCO selected */
244 
245  /* No specific code here */
246 
247 #else /* LFXO selected */
248 
249  /* Disable the low-frequency crystal oscillator for the RTC */
250  //CMU_OscillatorEnable(cmuOsc_LFXO, false, true);
251 
252 #endif /* ULFRCO/LFXO selection */
253 
254  /* Disable the clock to the interface of the low energy modules
255  * cmuClock_CORELE = cmuClock_HFLE (deprecated) */
256  //CMU_ClockEnable(cmuClock_HFLE, false);
257 
258  /* Turn off the RTC clock */
259  CMU_ClockEnable(cmuClock_RTC, false);
260 
261 #endif /* SysTick/RTC selection */
262 
263 }
264 
265 
266 /**************************************************************************//**
267  * @brief
268  * Sleep for a certain amount of seconds in EM2/3.
269  *
270  * @details
271  * This method also initializes the RTC if necessary.
272  *
273  * @param[in] sSleep
274  * The sleep time in **seconds**.
275  *****************************************************************************/
276 void sleep (uint32_t sSleep)
277 {
278  /* Initialize RTC if not already the case */
279  if (!RTC_initialized) initRTC();
280  else
281  {
282  /* Enable necessary oscillator and clocks */
283 
284 #if ULFRCO == 1 /* ULFRCO selected */
285 
286  /* No specific code here */
287 
288 #else /* LFXO selected */
289 
290  /* Enable the low-frequency crystal oscillator for the RTC */
291  //CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
292 
293 #endif /* ULFRCO/LFXO selection */
294 
295  /* Enable the clock to the interface of the low energy modules
296  * cmuClock_CORELE = cmuClock_HFLE (deprecated) */
297  //CMU_ClockEnable(cmuClock_HFLE, true);
298 
299  /* Turn on the RTC clock */
300  CMU_ClockEnable(cmuClock_RTC, true);
301  }
302 
303 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
304 #if ULFRCO == 1 /* ULFRCO selected */
305  dbwarnInt("Sleeping in EM3 for ", sSleep, " s\n\r");
306 #else /* LFXO selected */
307  dbwarnInt("Sleeping in EM2 for ", sSleep, " s\n\r");
308 #endif /* ULFRCO/LFXO selection */
309 #endif /* DEBUG_DBPRINT */
310 
311  /* Set RTC compare value for RTC compare register 0 depending on ULFRCO/LFXO selection */
312 
313 #if ULFRCO == 1 /* ULFRCO selected */
314 
315  if ((ULFRCOFREQ * sSleep) <= 0x00ffffff) RTC_CompareSet(0, (ULFRCOFREQ * sSleep));
316  else
317  {
318 
319 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
320  dbcrit("Delay too long, can't fit in the field!");
321 #endif /* DEBUG_DBPRINT */
322 
323  /* Turn off the RTC clock */
324  CMU_ClockEnable(cmuClock_RTC, false);
325 
326  error(16);
327 
328  /* Exit function */
329  return;
330  }
331 
332 #else /* LFXO selected */
333 
334  if ((LFXOFREQ * sSleep) <= 0x00ffffff) RTC_CompareSet(0, (LFXOFREQ * sSleep));
335  else
336  {
337 
338 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
339  dbcrit("Delay too long, can't fit in the field!");
340 #endif /* DEBUG_DBPRINT */
341 
342  /* Turn off the RTC clock */
343  CMU_ClockEnable(cmuClock_RTC, false);
344 
345  error(17);
346 
347  /* Exit function */
348  return;
349  }
350 
351 #endif /* ULFRCO/LFXO selection */
352 
353  /* Indicate that we're using the sleep method */
354  sleeping = true;
355 
356 
357  /* Start the RTC */
358  RTC_Enable(true);
359 
360  /* Enter EM2/3 depending on ULFRCO/LFXO selection */
361 
362 #if ULFRCO == 1 /* ULFRCO selected */
363  /* In EM3, high and low frequency clocks are disabled. No oscillator (except the ULFRCO) is running.
364  * Furthermore, all unwanted oscillators are disabled in EM3. This means that nothing needs to be
365  * manually disabled before the statement EMU_EnterEM3(true); */
366  EMU_EnterEM3(true); /* "true" - Save and restore oscillators, clocks and voltage scaling */
367 #else /* LFXO selected */
368  EMU_EnterEM2(true); /* "true" - Save and restore oscillators, clocks and voltage scaling */
369 #endif /* ULFRCO/LFXO selection */
370 
371 
372  /* Indicate that we're no longer sleeping */
373  sleeping = false;
374 
375  /* Disable used oscillator and clocks after wake-up */
376 
377 #if ULFRCO == 1 /* ULFRCO selected */
378 
379  /* No specific code here */
380 
381 #else /* LFXO selected */
382 
383  /* Disable the low-frequency crystal oscillator for the RTC */
384  //CMU_OscillatorEnable(cmuOsc_LFXO, false, true);
385 
386 #endif /* ULFRCO/LFXO selection */
387 
388  /* Disable the clock to the interface of the low energy modules
389  * cmuClock_CORELE = cmuClock_HFLE (deprecated) */
390  //CMU_ClockEnable(cmuClock_HFLE, false);
391 
392  /* Turn off the RTC clock */
393  CMU_ClockEnable(cmuClock_RTC, false);
394 }
395 
396 
397 /**************************************************************************//**
398  * @brief
399  * Method to check if the wakeup was caused by the RTC.
400  *
401  * @return
402  * The value of `RTC_sleep_wakeup`.
403  *****************************************************************************/
404 bool RTC_checkWakeup (void)
405 {
406  return (RTC_sleep_wakeup);
407 }
408 
409 
410 /**************************************************************************//**
411  * @brief
412  * Method to clear `RTC_sleep_wakeup`.
413  *****************************************************************************/
414 void RTC_clearWakeup (void)
415 {
416  RTC_sleep_wakeup = false;
417 }
418 
419 
420 /**************************************************************************//**
421  * @brief
422  * Method to get the time spend sleeping (in seconds) in the case
423  * of GPIO wake-up.
424  *
425  * @return
426  * The time spend sleeping in **seconds**.
427  *****************************************************************************/
428 uint32_t RTC_getPassedSleeptime (void)
429 {
430  uint32_t sSleep = RTC_CounterGet();
431 
432  /* Disable the counter */
433  RTC_Enable(false);
434 
435 #if ULFRCO == 1 /* ULFRCO selected */
436  sSleep /= ULFRCOFREQ;
437 #else /* LFXO selected */
438  sSleep /= LFXOFREQ;
439 #endif /* ULFRCO/LFXO selection */
440 
441  return (sSleep);
442 }
443 
444 
445 /**************************************************************************//**
446  * @brief
447  * RTC initialization.
448  *
449  * @note
450  * This is a static method because it's only internally used in this file
451  * and called by other methods if necessary.
452  *****************************************************************************/
453 static void initRTC (void)
454 {
455 
456 #if ULFRCO == 1 /* ULFRCO selected */
457 
458  /* Enable the ultra low-frequency RC oscillator for the RTC */
459  //CMU_OscillatorEnable(cmuOsc_ULFRCO, true, true); /* The ULFRCO is always on */
460 
461  /* Enable the clock to the interface of the low energy modules
462  * cmuClock_CORELE = cmuClock_HFLE (deprecated) */
463  CMU_ClockEnable(cmuClock_HFLE, true);
464 
465  /* Route the ULFRCO clock to the RTC */
466  CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_ULFRCO);
467 
468 #else /* LFXO selected */
469 
470  /* Enable the low-frequency crystal oscillator for the RTC */
471  CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
472 
473  /* Enable the clock to the interface of the low energy modules
474  * cmuClock_CORELE = cmuClock_HFLE (deprecated) */
475  CMU_ClockEnable(cmuClock_HFLE, true);
476 
477  /* Route the LFXO clock to the RTC */
478  CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO);
479 
480 #endif /* ULFRCO/LFXO selection */
481 
482  /* Turn on the RTC clock */
483  CMU_ClockEnable(cmuClock_RTC, true);
484 
485  /* Allow channel 0 to cause an interrupt */
486  RTC_IntEnable(RTC_IEN_COMP0);
487  RTC_IntClear(RTC_IFC_COMP0); /* This statement was in the ULFRCO but not in the LFXO example. It's kept here just in case. */
488  NVIC_ClearPendingIRQ(RTC_IRQn);
489  NVIC_EnableIRQ(RTC_IRQn);
490 
491  /* Configure the RTC settings */
492  RTC_Init_TypeDef rtc = RTC_INIT_DEFAULT;
493  rtc.enable = false; /* Don't start counting when initialization is done */
494 
495  /* Initialize RTC with pre-defined settings */
496  RTC_Init(&rtc);
497 
498 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
499 #if ULFRCO == 1 /* ULFRCO selected */
500  dbinfo("RTC initialized with ULFRCO\n\r");
501 #else /* LFXO selected */
502  dbinfo("RTC initialized with LFXO\n\r");
503 #endif /* ULFRCO/LFXO selection */
504 #endif /* DEBUG_DBPRINT */
505 
506  RTC_initialized = true;
507 }
508 
509 
510 #if SYSTICKDELAY == 1 /* SysTick delay selected */
511 /**************************************************************************//**
512  * @brief
513  * Interrupt Service Routine for system tick counter.
514  *****************************************************************************/
515 void SysTick_Handler (void)
516 {
517  msTicks++; /* Increment counter necessary by SysTick delay functionality */
518 }
519 #endif /* SysTick/RTC selection */
520 
521 
522 /**************************************************************************//**
523  * @brief
524  * Interrupt Service Routine for the RTC.
525  *
526  * @note
527  * The *weak* definition for this method is located in `system_efm32hg.h`.
528  *****************************************************************************/
529 void RTC_IRQHandler (void)
530 {
531  /* Disable the counter */
532  RTC_Enable(false);
533 
534  /* Clear the interrupt source */
535  RTC_IntClear(RTC_IFC_COMP0);
536 
537  /* If the wakeup was caused by "sleeping" (not a delay), act accordingly */
538  if (sleeping) RTC_sleep_wakeup = true;
539 }
void sleep(uint32_t sSleep)
Sleep for a certain amount of seconds in EM2/3.
Definition: delay.c:276
#define ULFRCOFREQ
Definition: delay.c:86
static void initRTC(void)
RTC initialization.
Definition: delay.c:453
void dbwarnInt(char *message1, int32_t value, char *message2)
Print a warning value surrounded by two strings (char array) to USARTx.
Definition: dbprint.c:598
bool RTC_initialized
Definition: delay.c:103
void dbinfo(char *message)
Print an info string (char array) to USARTx and go to the next line.
Definition: dbprint.c:503
void RTC_IRQHandler(void)
Interrupt Service Routine for the RTC.
Definition: delay.c:529
uint32_t RTC_getPassedSleeptime(void)
Method to get the time spend sleeping (in seconds) in the case of GPIO wake-up.
Definition: delay.c:428
void RTC_clearWakeup(void)
Method to clear RTC_sleep_wakeup.
Definition: delay.c:414
void error(uint8_t number)
Error method.
Definition: util.c:131
Utility functionality.
The pin definitions for the regular and custom Happy Gecko board.
Delay functionality.
bool RTC_checkWakeup(void)
Method to check if the wakeup was caused by the RTC.
Definition: delay.c:404
void delay(uint32_t msDelay)
Wait for a certain amount of milliseconds in EM2/3.
Definition: delay.c:124
Enable or disable printing to UART with dbprint.
#define LFXOFREQ_MS
Definition: delay.c:89
#define ULFRCOFREQ_MS
Definition: delay.c:87
static volatile bool RTC_sleep_wakeup
Definition: delay.c:95
void dbcrit(char *message)
Print a critical error string (char array) in red to USARTx and go to the next line.
Definition: dbprint.c:539
#define LFXOFREQ
Definition: delay.c:88
bool sleeping
Definition: delay.c:102