IoT-MBBR (masterthesis)
Masterthesis upgrading a Moving Bed BioReactor with extra sensors and cloud connectivity.
delay.c
Go to the documentation of this file.
1 /***************************************************************************//**
2  * @file delay.c
3  * @brief Delay functionality.
4  * @version 3.4
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  * @li v3.3: Added include for the boolean type in the header file, changed `dbwarnInt`
33  * to `dbinfoInt`, added the ability to enable/disable the sleep-announcing.
34  * @li v3.4: Added logic to initialize delay/sleep when calling the methods using `0` as the delay time.
35  *
36  * ******************************************************************************
37  *
38  * @todo
39  * **Future improvements:**@n
40  * - Enable/disable clock functionality? (see comments using `//`)
41  *
42  * ******************************************************************************
43  *
44  * @section License
45  *
46  * **Copyright (C) 2020 - 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 "debug_dbprint.h" /* Enable or disable printing to UART */
81 //#include "util.h" /* Utility functionality (error) */
82 
83 
84 /* Local definitions (for RTC compare interrupts) */
85 #define ULFRCOFREQ 1000
86 #define ULFRCOFREQ_MS 1.000
87 #define LFXOFREQ 32768
88 #define LFXOFREQ_MS 32.768
89 
90 
91 /* Local variables */
92 /* -> Volatile because it's modified by an interrupt service routine (@RAM)
93  * -> Static so it's always kept in memory (@data segment, space provided during compile time) */
94 static volatile bool RTC_sleep_wakeup = false;
95 
96 #if SYSTICKDELAY == 1 /* SysTick delay selected */
97 static volatile uint32_t msTicks;
98 #endif /* SysTick/RTC selection */
99 
100 
101 bool sleeping = false;
102 bool RTC_initialized = false;
103 
104 #if SYSTICKDELAY == 1 /* SysTick delay selected */
105 bool SysTick_initialized = false;
106 #endif /* SysTick/RTC selection */
107 
108 
109 /* Local prototype */
110 static void initRTC (void);
111 
112 
113 /**************************************************************************//**
114  * @brief
115  * Wait for a certain amount of milliseconds in EM2/3.
116  *
117  * @details
118  * This method can be called with the argument `0` to force initialization.@n
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  /* Only execute if we're not initializing */
145  if (msDelay > 0)
146  {
147  /* Enable SysTick interrupt and counter by setting their bits. */
148  SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
149  }
150  }
151 
152  /* Only execute if we're not initializing */
153  if (msDelay > 0)
154  {
155  /* Wait a certain amount of ticks */
156  uint32_t curTicks = msTicks;
157  while ((msTicks - curTicks) < msDelay);
158 
159  /* Disable SysTick interrupt and counter (needs to be done before entering EM2/3) by clearing their bits. */
160  SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk & ~SysTick_CTRL_ENABLE_Msk;
161  }
162 
163 #else /* EM2/3 RTC delay selected */
164 
165 
166  /* Initialize RTC if not already the case */
167  if (!RTC_initialized) initRTC();
168  else
169  {
170  /* Only execute if we're not initializing */
171  if (msDelay > 0)
172  {
173  /* Enable necessary oscillator and clocks */
174 
175 #if ULFRCO == 1 /* ULFRCO selected */
176 
177  /* No specific code here */
178 
179 #else /* LFXO selected */
180 
181  /* Enable the low-frequency crystal oscillator for the RTC */
182  //CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
183 
184 #endif /* ULFRCO/LFXO selection */
185 
186  /* Enable the clock to the interface of the low energy modules
187  * cmuClock_CORELE = cmuClock_HFLE (deprecated) */
188  //CMU_ClockEnable(cmuClock_HFLE, true);
189 
190  /* Turn on the RTC clock */
191  CMU_ClockEnable(cmuClock_RTC, true);
192  }
193  }
194 
195  /* Only execute if we're not initializing */
196  if (msDelay > 0)
197  {
198 
199  /* Set RTC compare value for RTC compare register 0 depending on ULFRCO/LFXO selection */
200 
201 #if ULFRCO == 1 /* ULFRCO selected */
202 
203  if ((ULFRCOFREQ_MS * msDelay) <= 0x00ffffff) RTC_CompareSet(0, (ULFRCOFREQ_MS * msDelay));
204  else
205  {
206 
207 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
208  dbcrit("Delay too long, can't fit in the field!");
209 #endif /* DEBUG_DBPRINT */
210 
211  /* Turn off the RTC clock */
212  CMU_ClockEnable(cmuClock_RTC, false);
213 
214 // error(14);
215 
216  /* Exit function */
217  return;
218  }
219 
220 #else /* LFXO selected */
221 
222  if ((LFXOFREQ_MS * msDelay) <= 0x00ffffff) RTC_CompareSet(0, (LFXOFREQ_MS * msDelay));
223  else
224  {
225 
226 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
227  dbcrit("Delay too long, can't fit in the field!");
228 #endif /* DEBUG_DBPRINT */
229 
230  /* Turn off the RTC clock */
231  CMU_ClockEnable(cmuClock_RTC, false);
232 
233 // error(15);
234 
235  /* Exit function */
236  return;
237  }
238 
239 #endif /* ULFRCO/LFXO selection */
240 
241 
242  /* Start the RTC */
243  RTC_Enable(true);
244 
245  /* Enter EM2/3 depending on ULFRCO/LFXO selection */
246 
247 #if ULFRCO == 1 /* ULFRCO selected */
248  /* In EM3, high and low frequency clocks are disabled. No oscillator (except the ULFRCO) is running.
249  * Furthermore, all unwanted oscillators are disabled in EM3. This means that nothing needs to be
250  * manually disabled before the statement EMU_EnterEM3(true); */
251  EMU_EnterEM3(true); /* "true" - Save and restore oscillators, clocks and voltage scaling */
252 #else /* LFXO selected */
253  EMU_EnterEM2(true); /* "true" - Save and restore oscillators, clocks and voltage scaling */
254 #endif /* ULFRCO/LFXO selection */
255 
256 
257  /* Disable used oscillator and clocks after wake-up */
258 
259 #if ULFRCO == 1 /* ULFRCO selected */
260 
261  /* No specific code here */
262 
263 #else /* LFXO selected */
264 
265  /* Disable the low-frequency crystal oscillator for the RTC */
266  //CMU_OscillatorEnable(cmuOsc_LFXO, false, true);
267 
268 #endif /* ULFRCO/LFXO selection */
269 
270  /* Disable the clock to the interface of the low energy modules
271  * cmuClock_CORELE = cmuClock_HFLE (deprecated) */
272  //CMU_ClockEnable(cmuClock_HFLE, false);
273 
274  /* Turn off the RTC clock */
275  CMU_ClockEnable(cmuClock_RTC, false);
276  }
277 
278 #endif /* SysTick/RTC selection */
279 
280 }
281 
282 
283 /**************************************************************************//**
284  * @brief
285  * Sleep for a certain amount of seconds in EM2/3.
286  *
287  * @details
288  * This method also initializes the RTC if necessary.
289  *
290  * @param[in] sSleep
291  * The sleep time in **seconds**.
292  *****************************************************************************/
293 void sleep (uint32_t sSleep)
294 {
295  /* Initialize RTC if not already the case */
296  if (!RTC_initialized) initRTC();
297  else
298  {
299  /* Only execute if we're not initializing */
300  if (sSleep > 0)
301  {
302 
303  /* Enable necessary oscillator and clocks */
304 
305 #if ULFRCO == 1 /* ULFRCO selected */
306 
307  /* No specific code here */
308 
309 #else /* LFXO selected */
310 
311  /* Enable the low-frequency crystal oscillator for the RTC */
312  //CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
313 
314 #endif /* ULFRCO/LFXO selection */
315 
316  /* Enable the clock to the interface of the low energy modules
317  * cmuClock_CORELE = cmuClock_HFLE (deprecated) */
318  //CMU_ClockEnable(cmuClock_HFLE, true);
319 
320  /* Turn on the RTC clock */
321  CMU_ClockEnable(cmuClock_RTC, true);
322  }
323  }
324 
325  /* Only execute if we're not initializing */
326  if (sSleep > 0)
327  {
328 
329 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
330 #if ANNOUNCE_SLEEPING == 1 /* Announce sleeping enabled */
331 #if ULFRCO == 1 /* ULFRCO selected */
332  dbinfoInt("Sleeping in EM3 for ", sSleep, " s\n\r\n\r");
333 #else /* LFXO selected */
334  dbinfoInt("Sleeping in EM2 for ", sSleep, " s\n\r\n\r");
335 #endif /* ULFRCO/LFXO selection */
336 #endif /* Announce sleeping */
337 #endif /* DEBUG_DBPRINT */
338 
339  /* Set RTC compare value for RTC compare register 0 depending on ULFRCO/LFXO selection */
340 
341 #if ULFRCO == 1 /* ULFRCO selected */
342 
343  if ((ULFRCOFREQ * sSleep) <= 0x00ffffff) RTC_CompareSet(0, (ULFRCOFREQ * sSleep));
344  else
345  {
346 
347 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
348  dbcrit("Delay too long, can't fit in the field!");
349 #endif /* DEBUG_DBPRINT */
350 
351  /* Turn off the RTC clock */
352  CMU_ClockEnable(cmuClock_RTC, false);
353 
354 // error(16);
355 
356  /* Exit function */
357  return;
358  }
359 
360 #else /* LFXO selected */
361 
362  if ((LFXOFREQ * sSleep) <= 0x00ffffff) RTC_CompareSet(0, (LFXOFREQ * sSleep));
363  else
364  {
365 
366 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
367  dbcrit("Delay too long, can't fit in the field!");
368 #endif /* DEBUG_DBPRINT */
369 
370  /* Turn off the RTC clock */
371  CMU_ClockEnable(cmuClock_RTC, false);
372 
373 // error(17);
374 
375  /* Exit function */
376  return;
377  }
378 
379 #endif /* ULFRCO/LFXO selection */
380 
381  /* Indicate that we're using the sleep method */
382  sleeping = true;
383 
384 
385  /* Start the RTC */
386  RTC_Enable(true);
387 
388  /* Enter EM2/3 depending on ULFRCO/LFXO selection */
389 
390 #if ULFRCO == 1 /* ULFRCO selected */
391  /* In EM3, high and low frequency clocks are disabled. No oscillator (except the ULFRCO) is running.
392  * Furthermore, all unwanted oscillators are disabled in EM3. This means that nothing needs to be
393  * manually disabled before the statement EMU_EnterEM3(true); */
394  EMU_EnterEM3(true); /* "true" - Save and restore oscillators, clocks and voltage scaling */
395 #else /* LFXO selected */
396  EMU_EnterEM2(true); /* "true" - Save and restore oscillators, clocks and voltage scaling */
397 #endif /* ULFRCO/LFXO selection */
398 
399  /* Indicate that we're no longer sleeping */
400  sleeping = false;
401 
402  /* Disable used oscillator and clocks after wake-up */
403 
404 #if ULFRCO == 1 /* ULFRCO selected */
405 
406  /* No specific code here */
407 
408 #else /* LFXO selected */
409 
410  /* Disable the low-frequency crystal oscillator for the RTC */
411  //CMU_OscillatorEnable(cmuOsc_LFXO, false, true);
412 
413 #endif /* ULFRCO/LFXO selection */
414 
415  /* Disable the clock to the interface of the low energy modules
416  * cmuClock_CORELE = cmuClock_HFLE (deprecated) */
417  //CMU_ClockEnable(cmuClock_HFLE, false);
418 
419  /* Turn off the RTC clock */
420  CMU_ClockEnable(cmuClock_RTC, false);
421  }
422 }
423 
424 
425 /**************************************************************************//**
426  * @brief
427  * Method to check if the wakeup was caused by the RTC.
428  *
429  * @return
430  * The value of `RTC_sleep_wakeup`.
431  *****************************************************************************/
432 bool RTC_checkWakeup (void)
433 {
434  return (RTC_sleep_wakeup);
435 }
436 
437 
438 /**************************************************************************//**
439  * @brief
440  * Method to clear `RTC_sleep_wakeup`.
441  *****************************************************************************/
442 void RTC_clearWakeup (void)
443 {
444  RTC_sleep_wakeup = false;
445 }
446 
447 
448 /**************************************************************************//**
449  * @brief
450  * Method to get the time spend sleeping (in seconds) in the case
451  * of GPIO wake-up.
452  *
453  * @return
454  * The time spend sleeping in **seconds**.
455  *****************************************************************************/
457 {
458  uint32_t sSleep = RTC_CounterGet();
459 
460  /* Disable the counter */
461  RTC_Enable(false);
462 
463 #if ULFRCO == 1 /* ULFRCO selected */
464  sSleep /= ULFRCOFREQ;
465 #else /* LFXO selected */
466  sSleep /= LFXOFREQ;
467 #endif /* ULFRCO/LFXO selection */
468 
469  return (sSleep);
470 }
471 
472 
473 /**************************************************************************//**
474  * @brief
475  * RTC initialization.
476  *
477  * @note
478  * This is a static method because it's only internally used in this file
479  * and called by other methods if necessary.
480  *****************************************************************************/
481 static void initRTC (void)
482 {
483 
484 #if ULFRCO == 1 /* ULFRCO selected */
485 
486  /* Enable the ultra low-frequency RC oscillator for the RTC */
487  //CMU_OscillatorEnable(cmuOsc_ULFRCO, true, true); /* The ULFRCO is always on */
488 
489  /* Enable the clock to the interface of the low energy modules
490  * cmuClock_CORELE = cmuClock_HFLE (deprecated) */
491  CMU_ClockEnable(cmuClock_HFLE, true);
492 
493  /* Route the ULFRCO clock to the RTC */
494  CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_ULFRCO);
495 
496 #else /* LFXO selected */
497 
498  /* Enable the low-frequency crystal oscillator for the RTC */
499  CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
500 
501  /* Enable the clock to the interface of the low energy modules
502  * cmuClock_CORELE = cmuClock_HFLE (deprecated) */
503  CMU_ClockEnable(cmuClock_HFLE, true);
504 
505  /* Route the LFXO clock to the RTC */
506  CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO);
507 
508 #endif /* ULFRCO/LFXO selection */
509 
510  /* Turn on the RTC clock */
511  CMU_ClockEnable(cmuClock_RTC, true);
512 
513  /* Allow channel 0 to cause an interrupt */
514  RTC_IntEnable(RTC_IEN_COMP0);
515  RTC_IntClear(RTC_IFC_COMP0); /* This statement was in the ULFRCO but not in the LFXO example. It's kept here just in case. */
516  NVIC_ClearPendingIRQ(RTC_IRQn);
517  NVIC_EnableIRQ(RTC_IRQn);
518 
519  /* Configure the RTC settings */
520  RTC_Init_TypeDef rtc = RTC_INIT_DEFAULT;
521  rtc.enable = false; /* Don't start counting when initialization is done */
522 
523  /* Initialize RTC with pre-defined settings */
524  RTC_Init(&rtc);
525 
526 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
527 #if ULFRCO == 1 /* ULFRCO selected */
528  dbinfo("RTC initialized with ULFRCO");
529 #else /* LFXO selected */
530  dbinfo("RTC initialized with LFXO");
531 #endif /* ULFRCO/LFXO selection */
532 #endif /* DEBUG_DBPRINT */
533 
534  RTC_initialized = true;
535 }
536 
537 
538 #if SYSTICKDELAY == 1 /* SysTick delay selected */
539 /**************************************************************************//**
540  * @brief
541  * Interrupt Service Routine for system tick counter.
542  *****************************************************************************/
543 void SysTick_Handler (void)
544 {
545  msTicks++; /* Increment counter necessary by SysTick delay functionality */
546 }
547 #endif /* SysTick/RTC selection */
548 
549 
550 /**************************************************************************//**
551  * @brief
552  * Interrupt Service Routine for the RTC.
553  *
554  * @note
555  * The *weak* definition for this method is located in `system_efm32hg.h`.
556  *****************************************************************************/
557 void RTC_IRQHandler (void)
558 {
559  /* Disable the counter */
560  RTC_Enable(false);
561 
562  /* Clear the interrupt source */
563  RTC_IntClear(RTC_IFC_COMP0);
564 
565  /* If the wakeup was caused by "sleeping" (not a delay), act accordingly */
566  if (sleeping) RTC_sleep_wakeup = true;
567 }
void sleep(uint32_t sSleep)
Sleep for a certain amount of seconds in EM2/3.
Definition: delay.c:293
#define ULFRCOFREQ
Definition: delay.c:85
void dbinfoInt(char *message1, int32_t value, char *message2)
Print an info value surrounded by two strings (char array) to USARTx.
Definition: dbprint.c:569
static void initRTC(void)
RTC initialization.
Definition: delay.c:481
bool RTC_initialized
Definition: delay.c:102
void dbinfo(char *message)
Print an info string (char array) to USARTx and go to the next line.
Definition: dbprint.c:505
void RTC_IRQHandler(void)
Interrupt Service Routine for the RTC.
Definition: delay.c:557
uint32_t RTC_getPassedSleeptime(void)
Method to get the time spend sleeping (in seconds) in the case of GPIO wake-up.
Definition: delay.c:456
void RTC_clearWakeup(void)
Method to clear RTC_sleep_wakeup.
Definition: delay.c:442
Delay functionality.
bool RTC_checkWakeup(void)
Method to check if the wakeup was caused by the RTC.
Definition: delay.c:432
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:88
#define ULFRCOFREQ_MS
Definition: delay.c:86
static volatile bool RTC_sleep_wakeup
Definition: delay.c:94
void dbcrit(char *message)
Print a critical error string (char array) in red to USARTx and go to the next line.
Definition: dbprint.c:541
#define LFXOFREQ
Definition: delay.c:87
bool sleeping
Definition: delay.c:101
unsigned int uint32_t
Typedef defining 32 bit unsigned int type. The developer should modify this to suit the platform bein...
Definition: vl53l1_types.h:78