]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_EFM32_Pearl_Gecko_Simplicity_Studio/Low_Power_Demo/low_power_tick_management_RTCC.c
Add Pearl Gecko demo.
[freertos] / FreeRTOS / Demo / CORTEX_EFM32_Pearl_Gecko_Simplicity_Studio / Low_Power_Demo / low_power_tick_management_RTCC.c
1 /*\r
2     FreeRTOS V9.0.0rc1 - Copyright (C) 2016 Real Time Engineers Ltd.\r
3     All rights reserved\r
4 \r
5     VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.\r
6 \r
7     This file is part of the FreeRTOS distribution.\r
8 \r
9     FreeRTOS is free software; you can redistribute it and/or modify it under\r
10     the terms of the GNU General Public License (version 2) as published by the\r
11     Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception.\r
12 \r
13     ***************************************************************************\r
14     >>!   NOTE: The modification to the GPL is included to allow you to     !<<\r
15     >>!   distribute a combined work that includes FreeRTOS without being   !<<\r
16     >>!   obliged to provide the source code for proprietary components     !<<\r
17     >>!   outside of the FreeRTOS kernel.                                   !<<\r
18     ***************************************************************************\r
19 \r
20     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY\r
21     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\r
22     FOR A PARTICULAR PURPOSE.  Full license text is available on the following\r
23     link: http://www.freertos.org/a00114.html\r
24 \r
25     ***************************************************************************\r
26      *                                                                       *\r
27      *    FreeRTOS provides completely free yet professionally developed,    *\r
28      *    robust, strictly quality controlled, supported, and cross          *\r
29      *    platform software that is more than just the market leader, it     *\r
30      *    is the industry's de facto standard.                               *\r
31      *                                                                       *\r
32      *    Help yourself get started quickly while simultaneously helping     *\r
33      *    to support the FreeRTOS project by purchasing a FreeRTOS           *\r
34      *    tutorial book, reference manual, or both:                          *\r
35      *    http://www.FreeRTOS.org/Documentation                              *\r
36      *                                                                       *\r
37     ***************************************************************************\r
38 \r
39     http://www.FreeRTOS.org/FAQHelp.html - Having a problem?  Start by reading\r
40     the FAQ page "My application does not run, what could be wrong?".  Have you\r
41     defined configASSERT()?\r
42 \r
43     http://www.FreeRTOS.org/support - In return for receiving this top quality\r
44     embedded software for free we request you assist our global community by\r
45     participating in the support forum.\r
46 \r
47     http://www.FreeRTOS.org/training - Investing in training allows your team to\r
48     be as productive as possible as early as possible.  Now you can receive\r
49     FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers\r
50     Ltd, and the world's leading authority on the world's leading RTOS.\r
51 \r
52     http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,\r
53     including FreeRTOS+Trace - an indispensable productivity tool, a DOS\r
54     compatible FAT file system, and our tiny thread aware UDP/IP stack.\r
55 \r
56     http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.\r
57     Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.\r
58 \r
59     http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High\r
60     Integrity Systems ltd. to sell under the OpenRTOS brand.  Low cost OpenRTOS\r
61     licenses offer ticketed support, indemnification and commercial middleware.\r
62 \r
63     http://www.SafeRTOS.com - High Integrity Systems also provide a safety\r
64     engineered and independently SIL3 certified version for use in safety and\r
65     mission critical applications that require provable dependability.\r
66 \r
67     1 tab == 4 spaces!\r
68 */\r
69 \r
70 /* Standard inlcludes. */\r
71 #include <limits.h>\r
72 \r
73 /* FreeRTOS includes. */\r
74 #include "FreeRTOS.h"\r
75 #include "task.h"\r
76 \r
77 /* SiLabs library includes. */\r
78 #include "em_cmu.h"\r
79 #include "em_rtcc.h"\r
80 #include "em_rmu.h"\r
81 #include "em_int.h"\r
82 #include "sleep.h"\r
83 \r
84 /* SEE THE COMMENTS ABOVE THE DEFINITION OF configCREATE_LOW_POWER_DEMO IN\r
85 FreeRTOSConfig.h\r
86 This file contains functions that will override the default implementations\r
87 in the RTOS port layer.  Therefore only build this file if the low power demo\r
88 is being built. */\r
89 #if( configCREATE_LOW_POWER_DEMO == 1 )\r
90 \r
91 /* The RTCC channel used to generate the tick interrupt. */\r
92 #define lpRTCC_CHANNEL          ( 1 )\r
93 \r
94 /* 32768 clock divided by 1.  Don't use a prescale if errata RTCC_E201\r
95 applies. */\r
96 #define mainTIMER_FREQUENCY_HZ  ( 32768UL )\r
97 \r
98 /*\r
99  * The low power demo does not use the SysTick, so override the\r
100  * vPortSetupTickInterrupt() function with an implementation that configures\r
101  * a low power clock source.  NOTE:  This function name must not be changed as\r
102  * it is called from the RTOS portable layer.\r
103  */\r
104 void vPortSetupTimerInterrupt( void );\r
105 \r
106 /*\r
107  * Override the default definition of vPortSuppressTicksAndSleep() that is\r
108  * weakly defined in the FreeRTOS Cortex-M port layer with a version that\r
109  * manages the RTC clock, as the tick is generated from the low power RTC\r
110  * and not the SysTick as would normally be the case on a Cortex-M.\r
111  */\r
112 void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime );\r
113 \r
114 /*-----------------------------------------------------------*/\r
115 \r
116 /* Calculate how many clock increments make up a single tick period. */\r
117 static const uint32_t ulReloadValueForOneTick = ( mainTIMER_FREQUENCY_HZ / configTICK_RATE_HZ );\r
118 \r
119 /* Will hold the maximum number of ticks that can be suppressed. */\r
120 static uint32_t xMaximumPossibleSuppressedTicks = 0;\r
121 \r
122 /* Flag set from the tick interrupt to allow the sleep processing to know if\r
123 sleep mode was exited because of a timer interrupt or a different interrupt. */\r
124 static volatile uint32_t ulTickFlag = pdFALSE;\r
125 \r
126 /* As the clock is only 32KHz, it is likely a value of 1 will be enough. */\r
127 static const uint32_t ulStoppedTimerCompensation = 0UL;\r
128 \r
129 /* RTCC configuration structures. */\r
130 static const RTCC_Init_TypeDef xRTCInitStruct =\r
131 {\r
132         false,                /* Don't start counting when init complete. */\r
133         false,                /* Disable counter during debug halt. */\r
134         false,                /* Don't care. */\r
135         true,                 /* Enable counter wrap on ch. 1 CCV value. */\r
136         rtccCntPresc_1,       /* NOTE:  Do not use a pre-scale if errata RTCC_E201 applies. */\r
137         rtccCntTickPresc,     /* Count using the clock input directly. */\r
138 #if defined(_RTCC_CTRL_BUMODETSEN_MASK)\r
139         false,                /* Disable storing RTCC counter value in RTCC_CCV2 upon backup mode entry. */\r
140 #endif\r
141         false,                /* Oscillator fail detection disabled. */\r
142         rtccCntModeNormal,    /* Use RTCC in normal mode (increment by 1 on each tick) and not in calendar mode. */\r
143         false                 /* Don't care. */\r
144 };\r
145 \r
146 static const RTCC_CCChConf_TypeDef xRTCCChannel1InitStruct =\r
147 {\r
148         rtccCapComChModeCompare,    /* Use Compare mode. */\r
149         rtccCompMatchOutActionPulse,/* Don't care. */\r
150         rtccPRSCh0,                 /* PRS not used. */\r
151         rtccInEdgeNone,             /* Capture Input not used. */\r
152         rtccCompBaseCnt,            /* Compare with Base CNT register. */\r
153         0,                          /* Compare mask. */\r
154         rtccDayCompareModeMonth     /* Don't care. */\r
155 };\r
156 \r
157 /*-----------------------------------------------------------*/\r
158 \r
159 void vPortSetupTimerInterrupt( void )\r
160 {\r
161         /* Configure the RTCC to generate the RTOS tick interrupt. */\r
162 \r
163         /* The maximum number of ticks that can be suppressed depends on the clock\r
164         frequency. */\r
165         xMaximumPossibleSuppressedTicks = ULONG_MAX / ulReloadValueForOneTick;\r
166 \r
167         /* Ensure LE modules are accessible. */\r
168         CMU_ClockEnable( cmuClock_CORELE, true );\r
169 \r
170         /* Use LFXO. */\r
171         CMU_ClockSelectSet( cmuClock_LFE, cmuSelect_LFXO );\r
172 \r
173         /* Enable clock to the RTC module. */\r
174         CMU_ClockEnable( cmuClock_RTCC, true );\r
175 \r
176         /* Use channel 1 to generate the RTOS tick interrupt. */\r
177         RTCC_ChannelCCVSet( lpRTCC_CHANNEL, ulReloadValueForOneTick );\r
178 \r
179         RTCC_Init( &xRTCInitStruct );\r
180         RTCC_ChannelInit( lpRTCC_CHANNEL, &xRTCCChannel1InitStruct );\r
181         RTCC_EM4WakeupEnable( true );\r
182 \r
183         /* Disable RTCC interrupt. */\r
184         RTCC_IntDisable( _RTCC_IF_MASK );\r
185         RTCC_IntClear( _RTCC_IF_MASK );\r
186         RTCC->CNT = _RTCC_CNT_RESETVALUE;\r
187 \r
188         /* The tick interrupt must be set to the lowest priority possible. */\r
189         NVIC_SetPriority( RTCC_IRQn, configLIBRARY_LOWEST_INTERRUPT_PRIORITY );\r
190         NVIC_ClearPendingIRQ( RTCC_IRQn );\r
191         NVIC_EnableIRQ( RTCC_IRQn );\r
192         RTCC_IntEnable( RTCC_IEN_CC1 );\r
193         RTCC_Enable( true );\r
194 }\r
195 /*-----------------------------------------------------------*/\r
196 \r
197 void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )\r
198 {\r
199 uint32_t ulReloadValue, ulCompleteTickPeriods, ulCountBeforeSleep, ulCountAfterSleep;\r
200 eSleepModeStatus eSleepAction;\r
201 TickType_t xModifiableIdleTime;\r
202 \r
203         /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */\r
204 \r
205         /* Make sure the RTC reload value does not overflow the counter. */\r
206         if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )\r
207         {\r
208                 xExpectedIdleTime = xMaximumPossibleSuppressedTicks;\r
209         }\r
210 \r
211         /* Calculate the reload value required to wait xExpectedIdleTime tick\r
212         periods. */\r
213         ulReloadValue = ulReloadValueForOneTick * xExpectedIdleTime;\r
214         if( ulReloadValue > ulStoppedTimerCompensation )\r
215         {\r
216                 /* Compensate for the fact that the RTC is going to be stopped\r
217                 momentarily. */\r
218                 ulReloadValue -= ulStoppedTimerCompensation;\r
219         }\r
220 \r
221         /* Stop the RTC momentarily.  The time the RTC is stopped for is accounted\r
222         for as best it can be, but using the tickless mode will inevitably result\r
223         in some tiny drift of the time maintained by the kernel with respect to\r
224         calendar time.  The count is latched before stopping the timer as stopping\r
225         the timer appears to clear the count. */\r
226         ulCountBeforeSleep = RTCC_CounterGet();\r
227         RTCC_Enable( false );\r
228 \r
229         /* If this function is re-entered before one complete tick period then the\r
230         reload value might be set to take into account a partial time slice, but\r
231         just reading the count assumes it is counting up to a full ticks worth - so\r
232         add in the difference if any. */\r
233         ulCountBeforeSleep += ( ulReloadValueForOneTick - RTCC_ChannelCCVGet( lpRTCC_CHANNEL ) );\r
234 \r
235         /* Enter a critical section but don't use the taskENTER_CRITICAL() method as\r
236         that will mask interrupts that should exit sleep mode. */\r
237         INT_Disable();\r
238         __asm volatile( "dsb" );\r
239         __asm volatile( "isb" );\r
240 \r
241         /* The tick flag is set to false before sleeping.  If it is true when sleep\r
242         mode is exited then sleep mode was probably exited because the tick was\r
243         suppressed for the entire xExpectedIdleTime period. */\r
244         ulTickFlag = pdFALSE;\r
245 \r
246         /* If a context switch is pending then abandon the low power entry as the\r
247         context switch might have been pended by an external interrupt that     requires\r
248         processing. */\r
249         eSleepAction = eTaskConfirmSleepModeStatus();\r
250         if( eSleepAction == eAbortSleep )\r
251         {\r
252                 /* Restart tick and count up to whatever was left of the current time\r
253                 slice. */\r
254                 RTCC_ChannelCCVSet( lpRTCC_CHANNEL, ( ulReloadValueForOneTick - ulCountBeforeSleep ) + ulStoppedTimerCompensation );\r
255                 RTCC_Enable( true );\r
256 \r
257                 /* Re-enable interrupts - see comments above the cpsid instruction()\r
258                 above. */\r
259                 INT_Enable();\r
260         }\r
261         else\r
262         {\r
263                 /* Adjust the reload value to take into account that the current time\r
264                 slice is already partially complete. */\r
265                 ulReloadValue -= ulCountBeforeSleep;\r
266                 RTCC_ChannelCCVSet( lpRTCC_CHANNEL, ulReloadValue );\r
267 \r
268                 /* Restart the RTC. */\r
269                 RTCC_Enable( true );\r
270 \r
271                 /* Allow the application to define some pre-sleep processing. */\r
272                 xModifiableIdleTime = xExpectedIdleTime;\r
273                 configPRE_SLEEP_PROCESSING( xModifiableIdleTime );\r
274 \r
275                 /* xExpectedIdleTime being set to 0 by configPRE_SLEEP_PROCESSING()\r
276                 means the application defined code has already executed the WAIT\r
277                 instruction. */\r
278                 if( xModifiableIdleTime > 0 )\r
279                 {\r
280                         __asm volatile( "dsb" );\r
281                         SLEEP_Sleep();\r
282                         __asm volatile( "isb" );\r
283                 }\r
284 \r
285                 /* Allow the application to define some post sleep processing. */\r
286                 configPOST_SLEEP_PROCESSING( xModifiableIdleTime );\r
287 \r
288                 /* Stop RTC.  Again, the time the SysTick is stopped for is accounted\r
289                 for as best it can be, but using the tickless mode will inevitably\r
290                 result in some tiny drift of the time maintained by the kernel with\r
291                 respect to calendar time.  The count value is latched before stopping\r
292                 the timer as stopping the timer appears to clear the count. */\r
293                 ulCountAfterSleep = RTCC_CounterGet();\r
294                 RTCC_Enable( false );\r
295 \r
296                 /* Re-enable interrupts - see comments above the cpsid instruction()\r
297                 above. */\r
298                 INT_Enable();\r
299                 __asm volatile( "dsb" );\r
300                 __asm volatile( "isb" );\r
301 \r
302                 if( ulTickFlag != pdFALSE )\r
303                 {\r
304                         /* The tick interrupt has already executed, although because this\r
305                         function is called with the scheduler suspended the actual tick\r
306                         processing will not occur until after this function has exited.\r
307                         Reset the reload value with whatever remains of this tick period. */\r
308                         ulReloadValue = ulReloadValueForOneTick - ulCountAfterSleep;\r
309                         RTCC_ChannelCCVSet( lpRTCC_CHANNEL, ulReloadValue );\r
310 \r
311                         /* The tick interrupt handler will already have pended the tick\r
312                         processing in the kernel.  As the pending tick will be processed as\r
313                         soon as this function exits, the tick value     maintained by the tick\r
314                         is stepped forward by one less than the time spent sleeping.  The\r
315                         actual stepping of the tick appears later in this function. */\r
316                         ulCompleteTickPeriods = xExpectedIdleTime - 1UL;\r
317                 }\r
318                 else\r
319                 {\r
320                         /* Something other than the tick interrupt ended the sleep.  How\r
321                         many complete tick periods passed while the processor was\r
322                         sleeping?  Add back in the adjustment that was made to the reload\r
323                         value to account for the fact that a time slice was part way through\r
324                         when this function was called. */\r
325                         ulCountAfterSleep += ulCountBeforeSleep;\r
326                         ulCompleteTickPeriods = ulCountAfterSleep / ulReloadValueForOneTick;\r
327 \r
328                         /* The reload value is set to whatever fraction of a single tick\r
329                         period remains. */\r
330                         ulCountAfterSleep -= ( ulCompleteTickPeriods * ulReloadValueForOneTick );\r
331                         ulReloadValue = ulReloadValueForOneTick - ulCountAfterSleep;\r
332 \r
333                         if( ulReloadValue == 0 )\r
334                         {\r
335                                 /* There is no fraction remaining. */\r
336                                 ulReloadValue = ulReloadValueForOneTick;\r
337                                 ulCompleteTickPeriods++;\r
338                         }\r
339 \r
340                         RTCC_ChannelCCVSet( lpRTCC_CHANNEL, ulReloadValue );\r
341                 }\r
342 \r
343                 /* Restart the RTC so it runs up to the alarm value.  The alarm value\r
344                 will get set to the value required to generate exactly one tick period\r
345                 the next time the RTC interrupt executes. */\r
346                 RTCC_Enable( true );\r
347 \r
348                 /* Wind the tick forward by the number of tick periods that the CPU\r
349                 remained in a low power state. */\r
350                 vTaskStepTick( ulCompleteTickPeriods );\r
351         }\r
352 }\r
353 /*-----------------------------------------------------------*/\r
354 \r
355 void RTCC_IRQHandler( void )\r
356 {\r
357         ulTickFlag = pdTRUE;\r
358 \r
359         if( RTCC_ChannelCCVGet( lpRTCC_CHANNEL ) != ulReloadValueForOneTick )\r
360         {\r
361                 /* Set RTC interrupt to one RTOS tick period. */\r
362                 RTCC_Enable( false );\r
363                 RTCC_ChannelCCVSet( lpRTCC_CHANNEL, ulReloadValueForOneTick );\r
364                 RTCC_Enable( true );\r
365         }\r
366 \r
367         RTCC_IntClear( _RTCC_IF_MASK );\r
368 \r
369         /* Critical section which protect incrementing the tick*/\r
370         portDISABLE_INTERRUPTS();\r
371         {\r
372                 if( xTaskIncrementTick() != pdFALSE )\r
373                 {\r
374                         /* Pend a context switch. */\r
375                         portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;\r
376                 }\r
377         }\r
378         portENABLE_INTERRUPTS();\r
379 }\r
380 /*-----------------------------------------------------------*/\r
381 \r
382 #endif /* ( configCREATE_LOW_POWER_DEMO == 1 ) */\r