]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_EFM32_Gecko_Starter_Kit_Simplicity_Studio/Source/Low_Power_Demo/low_power_tick_management_RTC.c
Kernel source code:
[freertos] / FreeRTOS / Demo / CORTEX_EFM32_Gecko_Starter_Kit_Simplicity_Studio / Source / Low_Power_Demo / low_power_tick_management_RTC.c
1 /*\r
2     FreeRTOS V8.2.3 - Copyright (C) 2015 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 /* FreeRTOS includes. */\r
71 #include "FreeRTOS.h"\r
72 #include "task.h"\r
73 \r
74 /* SiLabs library includes. */\r
75 #include "em_cmu.h"\r
76 #include "em_rtc.h"\r
77 #include "em_burtc.h"\r
78 #include "em_rmu.h"\r
79 #include "em_int.h"\r
80 #include "sleep.h"\r
81 \r
82 #define lpINCLUDE_TEST_TIMER    1\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 == 2 )\r
90 \r
91 #define mainTIMER_FREQUENCY_HZ  ( 4096UL ) /* 32768 clock divided by 8. */\r
92 \r
93 /*\r
94  * The low power demo does not use the SysTick, so override the\r
95  * vPortSetupTickInterrupt() function with an implementation that configures\r
96  * a low power clock source.  NOTE:  This function name must not be changed as\r
97  * it is called from the RTOS portable layer.\r
98  */\r
99 void vPortSetupTimerInterrupt( void );\r
100 \r
101 /*\r
102  * Override the default definition of vPortSuppressTicksAndSleep() that is\r
103  * weakly defined in the FreeRTOS Cortex-M port layer with a version that\r
104  * manages the RTC clock, as the tick is generated from the low power RTC\r
105  * and not the SysTick as would normally be the case on a Cortex-M.\r
106  */\r
107 void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime );\r
108 \r
109 /* If lpINCLUDE_TEST_TIMER is defined then the BURTC is used to generate\r
110 interrupts that will wake the processor prior to the expected idle time\r
111 completing.  The timer interval can be altered to test different\r
112 scenarios. */\r
113 #if( lpINCLUDE_TEST_TIMER == 1 )\r
114         static void prvSetupTestTimer( void );\r
115 #endif\r
116 \r
117 /*-----------------------------------------------------------*/\r
118 \r
119 /* Calculate how many clock increments make up a single tick period. */\r
120 static const uint32_t ulReloadValueForOneTick = ( mainTIMER_FREQUENCY_HZ / configTICK_RATE_HZ );\r
121 \r
122 /* Will hold the maximum number of ticks that can be suppressed. */\r
123 static uint32_t xMaximumPossibleSuppressedTicks = 0;\r
124 \r
125 /* Flag set from the tick interrupt to allow the sleep processing to know if\r
126 sleep mode was exited because of a timer interrupt or a different interrupt. */\r
127 static volatile uint32_t ulTickFlag = pdFALSE;\r
128 \r
129 /* As the clock is only 32KHz, it is likely a value of 1 will be enough. */\r
130 static const uint32_t ulStoppedTimerCompensation = 0UL;\r
131 \r
132 /*-----------------------------------------------------------*/\r
133 \r
134 void vPortSetupTimerInterrupt( void )\r
135 {\r
136 RTC_Init_TypeDef xRTCInitStruct;\r
137 const uint32_t ulMAX24BitValue = 0xffffffUL;\r
138 \r
139         xMaximumPossibleSuppressedTicks = ulMAX24BitValue / ulReloadValueForOneTick;\r
140 \r
141         /* Configure the RTC to generate the RTOS tick interrupt. */\r
142 \r
143         /* LXFO setup.  For rev D use 70% boost */\r
144         CMU->CTRL = ( CMU->CTRL & ~_CMU_CTRL_LFXOBOOST_MASK ) | CMU_CTRL_LFXOBOOST_70PCENT;\r
145         #if defined( EMU_AUXCTRL_REDLFXOBOOST )\r
146                 EMU->AUXCTRL = (EMU->AUXCTRL & ~_EMU_AUXCTRL_REDLFXOBOOST_MASK) | EMU_AUXCTRL_REDLFXOBOOST;\r
147         #endif\r
148 \r
149         /* Ensure LE modules are accessible. */\r
150         CMU_ClockEnable( cmuClock_CORELE, true );\r
151 \r
152         /* Use LFXO. */\r
153         CMU_ClockSelectSet( cmuClock_LFA, cmuSelect_LFXO );\r
154 \r
155         /* Use 8x divider to reduce energy. */\r
156         CMU_ClockDivSet( cmuClock_RTC, cmuClkDiv_8 );\r
157 \r
158         /* Enable clock to the RTC module. */\r
159         CMU_ClockEnable( cmuClock_RTC, true );\r
160         xRTCInitStruct.enable = false;\r
161         xRTCInitStruct.debugRun = false;\r
162         xRTCInitStruct.comp0Top = true;\r
163         RTC_Init( &xRTCInitStruct );\r
164 \r
165         /* Disable RTC0 interrupt. */\r
166         RTC_IntDisable( RTC_IFC_COMP0 );\r
167 \r
168         /* The tick interrupt must be set to the lowest priority possible. */\r
169         NVIC_SetPriority( RTC_IRQn, configLIBRARY_LOWEST_INTERRUPT_PRIORITY );\r
170         NVIC_ClearPendingIRQ( RTC_IRQn );\r
171         NVIC_EnableIRQ( RTC_IRQn );\r
172         RTC_CompareSet( 0, ulReloadValueForOneTick );\r
173         RTC_IntClear( RTC_IFC_COMP0 );\r
174         RTC_IntEnable( RTC_IF_COMP0 );\r
175         RTC_Enable( true );\r
176 \r
177         /* If lpINCLUDE_TEST_TIMER is defined then the BURTC is used to generate\r
178         interrupts that will wake the processor prior to the expected idle time\r
179         completing.  The timer interval can be altered to test different\r
180         scenarios. */\r
181         #if( lpINCLUDE_TEST_TIMER == 1 )\r
182                 prvSetupTestTimer();\r
183         #endif\r
184 }\r
185 /*-----------------------------------------------------------*/\r
186 \r
187 void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )\r
188 {\r
189 uint32_t ulReloadValue, ulCompleteTickPeriods, ulCountBeforeSleep, ulCountAfterSleep;\r
190 eSleepModeStatus eSleepAction;\r
191 TickType_t xModifiableIdleTime;\r
192 \r
193         /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */\r
194 \r
195         /* Make sure the RTC reload value does not overflow the counter. */\r
196         if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )\r
197         {\r
198                 xExpectedIdleTime = xMaximumPossibleSuppressedTicks;\r
199         }\r
200 \r
201         /* Calculate the reload value required to wait xExpectedIdleTime tick\r
202         periods. */\r
203         ulReloadValue = ulReloadValueForOneTick * xExpectedIdleTime;\r
204         if( ulReloadValue > ulStoppedTimerCompensation )\r
205         {\r
206                 /* Compensate for the fact that the RTC is going to be stopped\r
207                 momentarily. */\r
208                 ulReloadValue -= ulStoppedTimerCompensation;\r
209         }\r
210 \r
211         /* Stop the RTC momentarily.  The time the RTC is stopped for is accounted\r
212         for as best it can be, but using the tickless mode will inevitably result\r
213         in some tiny drift of the time maintained by the kernel with respect to\r
214         calendar time.  The count is latched before stopping the timer as stopping\r
215         the timer appears to clear the count. */\r
216         ulCountBeforeSleep = RTC_CounterGet();\r
217         RTC_Enable( false );\r
218 \r
219         /* If this function is re-entered before one complete tick period then the\r
220         reload value might be set to take into account a partial time slice, but\r
221         just reading the count assumes it is counting up to a full ticks worth - so\r
222         add in the difference if any. */\r
223         ulCountBeforeSleep += ( ulReloadValueForOneTick - RTC_CompareGet( 0 ) );\r
224 \r
225         /* Enter a critical section but don't use the taskENTER_CRITICAL() method as\r
226         that will mask interrupts that should exit sleep mode. */\r
227         INT_Disable();\r
228         __asm volatile( "dsb" );\r
229         __asm volatile( "isb" );\r
230 \r
231         /* The tick flag is set to false before sleeping.  If it is true when sleep\r
232         mode is exited then sleep mode was probably exited because the tick was\r
233         suppressed for the entire xExpectedIdleTime period. */\r
234         ulTickFlag = pdFALSE;\r
235 \r
236         /* If a context switch is pending then abandon the low power entry as the\r
237         context switch might have been pended by an external interrupt that     requires\r
238         processing. */\r
239         eSleepAction = eTaskConfirmSleepModeStatus();\r
240         if( eSleepAction == eAbortSleep )\r
241         {\r
242                 /* Restart tick and count up to whatever was left of the current time\r
243                 slice. */\r
244                 RTC_CompareSet( 0, ( ulReloadValueForOneTick - ulCountBeforeSleep ) + ulStoppedTimerCompensation );\r
245                 RTC_Enable( true );\r
246 \r
247                 /* Re-enable interrupts - see comments above the cpsid instruction()\r
248                 above. */\r
249                 INT_Enable();\r
250         }\r
251         else\r
252         {\r
253                 /* Adjust the reload value to take into account that the current time\r
254                 slice is already partially complete. */\r
255                 ulReloadValue -= ulCountBeforeSleep;\r
256                 RTC_CompareSet( 0, ulReloadValue );\r
257 \r
258                 /* Restart the RTC. */\r
259                 RTC_Enable( true );\r
260 \r
261                 /* Allow the application to define some pre-sleep processing. */\r
262                 xModifiableIdleTime = xExpectedIdleTime;\r
263                 configPRE_SLEEP_PROCESSING( xModifiableIdleTime );\r
264 \r
265                 /* xExpectedIdleTime being set to 0 by configPRE_SLEEP_PROCESSING()\r
266                 means the application defined code has already executed the WAIT\r
267                 instruction. */\r
268                 if( xModifiableIdleTime > 0 )\r
269                 {\r
270                         __asm volatile( "dsb" );\r
271                         SLEEP_Sleep();\r
272                         __asm volatile( "isb" );\r
273                 }\r
274 \r
275                 /* Allow the application to define some post sleep processing. */\r
276                 configPOST_SLEEP_PROCESSING( xModifiableIdleTime );\r
277 \r
278                 /* Stop RTC.  Again, the time the SysTick is stopped for is accounted\r
279                 for as best it can be, but using the tickless mode will inevitably\r
280                 result in some tiny drift of the time maintained by the kernel with\r
281                 respect to calendar time.  The count value is latched before stopping\r
282                 the timer as stopping the timer appears to clear the count. */\r
283                 ulCountAfterSleep = RTC_CounterGet();\r
284                 RTC_Enable( false );\r
285 \r
286                 /* Re-enable interrupts - see comments above the cpsid instruction()\r
287                 above. */\r
288                 INT_Enable();\r
289                 __asm volatile( "dsb" );\r
290                 __asm volatile( "isb" );\r
291 \r
292                 if( ulTickFlag != pdFALSE )\r
293                 {\r
294                         /* The tick interrupt has already executed, although because this\r
295                         function is called with the scheduler suspended the actual tick\r
296                         processing will not occur until after this function has exited.\r
297                         Reset the reload value with whatever remains of this tick period. */\r
298                         ulReloadValue = ulReloadValueForOneTick - ulCountAfterSleep;\r
299                         RTC_CompareSet( 0, ulReloadValue );\r
300 \r
301                         /* The tick interrupt handler will already have pended the tick\r
302                         processing in the kernel.  As the pending tick will be processed as\r
303                         soon as this function exits, the tick value     maintained by the tick\r
304                         is stepped forward by one less than the time spent sleeping.  The\r
305                         actual stepping of the tick appears later in this function. */\r
306                         ulCompleteTickPeriods = xExpectedIdleTime - 1UL;\r
307                 }\r
308                 else\r
309                 {\r
310                         /* Something other than the tick interrupt ended the sleep.  How\r
311                         many complete tick periods passed while the processor was\r
312                         sleeping?  Add back in the adjustment that was made to the reload\r
313                         value to account for the fact that a time slice was part way through\r
314                         when this function was called. */\r
315                         ulCountAfterSleep += ulCountBeforeSleep;\r
316                         ulCompleteTickPeriods = ulCountAfterSleep / ulReloadValueForOneTick;\r
317 \r
318                         /* The reload value is set to whatever fraction of a single tick\r
319                         period remains. */\r
320                         ulCountAfterSleep -= ( ulCompleteTickPeriods * ulReloadValueForOneTick );\r
321                         ulReloadValue = ulReloadValueForOneTick - ulCountAfterSleep;\r
322 \r
323                         if( ulReloadValue == 0 )\r
324                         {\r
325                                 /* There is no fraction remaining. */\r
326                                 ulReloadValue = ulReloadValueForOneTick;\r
327                                 ulCompleteTickPeriods++;\r
328                         }\r
329 \r
330                         RTC_CompareSet( 0, ulReloadValue );\r
331                 }\r
332 \r
333                 /* Restart the RTC so it runs up to the alarm value.  The alarm value\r
334                 will get set to the value required to generate exactly one tick period\r
335                 the next time the RTC interrupt executes. */\r
336                 RTC_Enable( true );\r
337 \r
338                 /* Wind the tick forward by the number of tick periods that the CPU\r
339                 remained in a low power state. */\r
340                 vTaskStepTick( ulCompleteTickPeriods );\r
341         }\r
342 }\r
343 /*-----------------------------------------------------------*/\r
344 \r
345 void RTC_IRQHandler( void )\r
346 {\r
347         ulTickFlag = pdTRUE;\r
348 \r
349         if( RTC_CompareGet( 0 ) != ulReloadValueForOneTick )\r
350         {\r
351                 /* Set RTC interrupt to one RTOS tick period. */\r
352                 RTC_Enable( false );\r
353                 RTC_CompareSet( 0, ulReloadValueForOneTick );\r
354                 RTC_Enable( true );\r
355         }\r
356 \r
357         RTC_IntClear( _RTC_IFC_MASK );\r
358 \r
359         /* Critical section which protect incrementing the tick*/\r
360         ( void ) portSET_INTERRUPT_MASK_FROM_ISR();\r
361         {\r
362                 if( xTaskIncrementTick() != pdFALSE )\r
363                 {\r
364                         /* Pend a context switch. */\r
365                         portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;\r
366                 }\r
367         }\r
368         portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 );\r
369 }\r
370 /*-----------------------------------------------------------*/\r
371 \r
372 #if( lpINCLUDE_TEST_TIMER == 1 )\r
373 \r
374         /* If lpINCLUDE_TEST_TIMER is defined then the BURTC is used to generate\r
375         interrupts that will wake the processor prior to the expected idle time\r
376         completing.  The timer interval can be altered to test different\r
377         scenarios. */\r
378         static void prvSetupTestTimer( void )\r
379         {\r
380         BURTC_Init_TypeDef xBURTCInitStruct = BURTC_INIT_DEFAULT;\r
381         const uint32_t ulBURTClockHz = 2000UL, ulInterruptFrequency = 1000UL;\r
382         const uint32_t ulReload = ( ulBURTClockHz / ulInterruptFrequency );\r
383 \r
384                 /* Ensure LE modules are accessible. */\r
385                 CMU_ClockEnable( cmuClock_CORELE, true );\r
386 \r
387                 /* Enable access to BURTC registers. */\r
388                 RMU_ResetControl( rmuResetBU, false );\r
389 \r
390                 /* Generate periodic interrupts from BURTC. */\r
391                 xBURTCInitStruct.mode   = burtcModeEM3;         /* Operational in EM3. */\r
392                 xBURTCInitStruct.clkSel = burtcClkSelULFRCO;/* ULFRCO clock. */\r
393                 xBURTCInitStruct.clkDiv = burtcClkDiv_1;        /* 2kHz ULFRCO clock. */\r
394                 xBURTCInitStruct.compare0Top = true;            /* Wrap on COMP0. */\r
395                 BURTC_IntDisable( BURTC_IF_COMP0 );\r
396                 BURTC_Init( &xBURTCInitStruct );\r
397 \r
398                 NVIC_SetPriority( BURTC_IRQn, configLIBRARY_LOWEST_INTERRUPT_PRIORITY );\r
399                 NVIC_ClearPendingIRQ( BURTC_IRQn );\r
400                 NVIC_EnableIRQ( BURTC_IRQn );\r
401                 BURTC_CompareSet( 0, ulReload );\r
402                 BURTC_IntClear( BURTC_IF_COMP0 );\r
403                 BURTC_IntEnable( BURTC_IF_COMP0 );\r
404                 BURTC_CounterReset();\r
405         }\r
406 \r
407 #endif\r
408 /*-----------------------------------------------------------*/\r
409 \r
410 #if( lpINCLUDE_TEST_TIMER == 1 )\r
411 \r
412         /* If lpINCLUDE_TEST_TIMER is defined then the BURTC is used to generate\r
413         interrupts that will wake the processor prior to the expected idle time\r
414         completing.  The timer interval can be altered to test different\r
415         scenarios. */\r
416         volatile uint32_t ulTestTimerCounts = 0;\r
417 \r
418         void BURTC_IRQHandler( void )\r
419         {\r
420                 /* Nothing to do here - just testing the code in the scenario where a\r
421                 tickless idle period is ended prior to the expected maximum idle time\r
422                 expiring. */\r
423                 BURTC_IntClear( _RTC_IFC_MASK );\r
424                 ulTestTimerCounts++;\r
425         }\r
426 \r
427 #endif\r
428 /*-----------------------------------------------------------*/\r
429 \r
430 #endif /* ( configCREATE_LOW_POWER_DEMO == 2 ) */\r