]> git.sur5r.net Git - freertos/blob
8eef5b71230d6e267a893ac2ded21e798c522924
[freertos] /
1 /*\r
2  * FreeRTOS Kernel V10.0.1\r
3  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21  *\r
22  * http://www.FreeRTOS.org\r
23  * http://aws.amazon.com/freertos\r
24  *\r
25  * 1 tab == 4 spaces!\r
26  */\r
27 \r
28 /* Standard inlcludes. */\r
29 #include <limits.h>\r
30 \r
31 /* FreeRTOS includes. */\r
32 #include "FreeRTOS.h"\r
33 #include "task.h"\r
34 \r
35 /* SiLabs library includes. */\r
36 #include "em_cmu.h"\r
37 #include "em_rtcc.h"\r
38 #include "em_rmu.h"\r
39 #include "em_int.h"\r
40 #include "em_letimer.h"\r
41 #include "sleep.h"\r
42 \r
43 /* SEE THE COMMENTS ABOVE THE DEFINITION OF configCREATE_LOW_POWER_DEMO IN\r
44 FreeRTOSConfig.h\r
45 This file contains functions that will override the default implementations\r
46 in the RTOS port layer.  Therefore only build this file if the low power demo\r
47 is being built. */\r
48 #if( configCREATE_LOW_POWER_DEMO == 1 )\r
49 \r
50 /* When lpUSE_TEST_TIMER is 1 a second timer will be used to bring the MCU out\r
51 of its low power state before the expected idle time has completed.  This is\r
52 done purely for test coverage purposes. */\r
53 #define lpUSE_TEST_TIMER        ( 0 )\r
54 \r
55 /* The RTCC channel used to generate the tick interrupt. */\r
56 #define lpRTCC_CHANNEL          ( 1 )\r
57 \r
58 /* 32768 clock divided by 1.  Don't use a prescale if errata RTCC_E201\r
59 applies. */\r
60 #define mainTIMER_FREQUENCY_HZ  ( 32768UL )\r
61 \r
62 /*\r
63  * The low power demo does not use the SysTick, so override the\r
64  * vPortSetupTickInterrupt() function with an implementation that configures\r
65  * a low power clock source.  NOTE:  This function name must not be changed as\r
66  * it is called from the RTOS portable layer.\r
67  */\r
68 void vPortSetupTimerInterrupt( void );\r
69 \r
70 /*\r
71  * Override the default definition of vPortSuppressTicksAndSleep() that is\r
72  * weakly defined in the FreeRTOS Cortex-M port layer with a version that\r
73  * manages the RTC clock, as the tick is generated from the low power RTC\r
74  * and not the SysTick as would normally be the case on a Cortex-M.\r
75  */\r
76 void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime );\r
77 \r
78 /*-----------------------------------------------------------*/\r
79 \r
80 /* Calculate how many clock increments make up a single tick period. */\r
81 static const uint32_t ulReloadValueForOneTick = ( mainTIMER_FREQUENCY_HZ / configTICK_RATE_HZ );\r
82 \r
83 /* Will hold the maximum number of ticks that can be suppressed. */\r
84 static uint32_t xMaximumPossibleSuppressedTicks = 0;\r
85 \r
86 /* Flag set from the tick interrupt to allow the sleep processing to know if\r
87 sleep mode was exited because of a timer interrupt or a different interrupt. */\r
88 static volatile uint32_t ulTickFlag = pdFALSE;\r
89 \r
90 /* As the clock is only 32KHz, it is likely a value of 1 will be enough. */\r
91 static const uint32_t ulStoppedTimerCompensation = 0UL;\r
92 \r
93 /* RTCC configuration structures. */\r
94 static const RTCC_Init_TypeDef xRTCInitStruct =\r
95 {\r
96         false,                /* Don't start counting when init complete. */\r
97         false,                /* Disable counter during debug halt. */\r
98         false,                /* Don't care. */\r
99         true,                 /* Enable counter wrap on ch. 1 CCV value. */\r
100         rtccCntPresc_1,       /* NOTE:  Do not use a pre-scale if errata RTCC_E201 applies. */\r
101         rtccCntTickPresc,     /* Count using the clock input directly. */\r
102 #if defined(_RTCC_CTRL_BUMODETSEN_MASK)\r
103         false,                /* Disable storing RTCC counter value in RTCC_CCV2 upon backup mode entry. */\r
104 #endif\r
105         false,                /* Oscillator fail detection disabled. */\r
106         rtccCntModeNormal,    /* Use RTCC in normal mode (increment by 1 on each tick) and not in calendar mode. */\r
107         false                 /* Don't care. */\r
108 };\r
109 \r
110 static const RTCC_CCChConf_TypeDef xRTCCChannel1InitStruct =\r
111 {\r
112         rtccCapComChModeCompare,    /* Use Compare mode. */\r
113         rtccCompMatchOutActionPulse,/* Don't care. */\r
114         rtccPRSCh0,                 /* PRS not used. */\r
115         rtccInEdgeNone,             /* Capture Input not used. */\r
116         rtccCompBaseCnt,            /* Compare with Base CNT register. */\r
117         0,                          /* Compare mask. */\r
118         rtccDayCompareModeMonth     /* Don't care. */\r
119 };\r
120 \r
121 /*-----------------------------------------------------------*/\r
122 \r
123 void vPortSetupTimerInterrupt( void )\r
124 {\r
125         /* Configure the RTCC to generate the RTOS tick interrupt. */\r
126 \r
127         /* The maximum number of ticks that can be suppressed depends on the clock\r
128         frequency. */\r
129         xMaximumPossibleSuppressedTicks = ULONG_MAX / ulReloadValueForOneTick;\r
130 \r
131         /* Ensure LE modules are accessible. */\r
132         CMU_ClockEnable( cmuClock_CORELE, true );\r
133 \r
134         /* Use LFXO. */\r
135         CMU_ClockSelectSet( cmuClock_LFE, cmuSelect_LFXO );\r
136 \r
137         /* Enable clock to the RTC module. */\r
138         CMU_ClockEnable( cmuClock_RTCC, true );\r
139 \r
140         /* Use channel 1 to generate the RTOS tick interrupt. */\r
141         RTCC_ChannelCCVSet( lpRTCC_CHANNEL, ulReloadValueForOneTick );\r
142 \r
143         RTCC_Init( &xRTCInitStruct );\r
144         RTCC_ChannelInit( lpRTCC_CHANNEL, &xRTCCChannel1InitStruct );\r
145         RTCC_EM4WakeupEnable( true );\r
146 \r
147         /* Disable RTCC interrupt. */\r
148         RTCC_IntDisable( _RTCC_IF_MASK );\r
149         RTCC_IntClear( _RTCC_IF_MASK );\r
150         RTCC->CNT = _RTCC_CNT_RESETVALUE;\r
151 \r
152         /* The tick interrupt must be set to the lowest priority possible. */\r
153         NVIC_SetPriority( RTCC_IRQn, configLIBRARY_LOWEST_INTERRUPT_PRIORITY );\r
154         NVIC_ClearPendingIRQ( RTCC_IRQn );\r
155         NVIC_EnableIRQ( RTCC_IRQn );\r
156         RTCC_IntEnable( RTCC_IEN_CC1 );\r
157         RTCC_Enable( true );\r
158 \r
159         #if( lpUSE_TEST_TIMER == 1 )\r
160         {\r
161                 void prvSetupTestTimer( void );\r
162 \r
163                 /* A second timer is used to test the path where the MCU is brought out\r
164                 of a low power state by a timer other than the tick timer. */\r
165                 prvSetupTestTimer();\r
166         }\r
167         #endif\r
168 }\r
169 /*-----------------------------------------------------------*/\r
170 \r
171 void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )\r
172 {\r
173 uint32_t ulReloadValue, ulCompleteTickPeriods, ulCountAfterSleep;\r
174 eSleepModeStatus eSleepAction;\r
175 TickType_t xModifiableIdleTime;\r
176 \r
177         /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */\r
178 \r
179         /* Make sure the RTC reload value does not overflow the counter. */\r
180         if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )\r
181         {\r
182                 xExpectedIdleTime = xMaximumPossibleSuppressedTicks;\r
183         }\r
184 \r
185         /* Calculate the reload value required to wait xExpectedIdleTime tick\r
186         periods. */\r
187         ulReloadValue = ulReloadValueForOneTick * xExpectedIdleTime;\r
188         if( ulReloadValue > ulStoppedTimerCompensation )\r
189         {\r
190                 /* Compensate for the fact that the RTC is going to be stopped\r
191                 momentarily. */\r
192                 ulReloadValue -= ulStoppedTimerCompensation;\r
193         }\r
194 \r
195         /* Stop the RTC momentarily.  The time the RTC is stopped for is accounted\r
196         for as best it can be, but using the tickless mode will inevitably result\r
197         in some tiny drift of the time maintained by the kernel with respect to\r
198         calendar time. */\r
199         RTCC_Enable( false );\r
200 \r
201         /* Enter a critical section but don't use the taskENTER_CRITICAL() method as\r
202         that will mask interrupts that should exit sleep mode. */\r
203         INT_Disable();\r
204         __asm volatile( "dsb" );\r
205         __asm volatile( "isb" );\r
206 \r
207         /* The tick flag is set to false before sleeping.  If it is true when sleep\r
208         mode is exited then sleep mode was probably exited because the tick was\r
209         suppressed for the entire xExpectedIdleTime period. */\r
210         ulTickFlag = pdFALSE;\r
211 \r
212         /* If a context switch is pending then abandon the low power entry as the\r
213         context switch might have been pended by an external interrupt that     requires\r
214         processing. */\r
215         eSleepAction = eTaskConfirmSleepModeStatus();\r
216         if( eSleepAction == eAbortSleep )\r
217         {\r
218                 /* Restart tick and continue counting to complete the current time\r
219                 slice. */\r
220                 RTCC_Enable( true );\r
221 \r
222                 /* Re-enable interrupts - see comments above the RTCC_Enable() call\r
223                 above. */\r
224                 INT_Enable();\r
225         }\r
226         else\r
227         {\r
228                 RTCC_ChannelCCVSet( lpRTCC_CHANNEL, ulReloadValue );\r
229 \r
230                 /* Restart the RTC. */\r
231                 RTCC_Enable( true );\r
232 \r
233                 /* Allow the application to define some pre-sleep processing. */\r
234                 xModifiableIdleTime = xExpectedIdleTime;\r
235                 configPRE_SLEEP_PROCESSING( xModifiableIdleTime );\r
236 \r
237                 /* xExpectedIdleTime being set to 0 by configPRE_SLEEP_PROCESSING()\r
238                 means the application defined code has already executed the WAIT\r
239                 instruction. */\r
240                 if( xModifiableIdleTime > 0 )\r
241                 {\r
242                         __asm volatile( "dsb" );\r
243                         SLEEP_Sleep();\r
244                         __asm volatile( "isb" );\r
245                 }\r
246 \r
247                 /* Allow the application to define some post sleep processing. */\r
248                 configPOST_SLEEP_PROCESSING( xModifiableIdleTime );\r
249 \r
250                 /* Stop RTC.  Again, the time the SysTick is stopped for is accounted\r
251                 for as best it can be, but using the tickless mode will inevitably\r
252                 result in some tiny drift of the time maintained by the kernel with\r
253                 respect to calendar time. */\r
254                 RTCC_Enable( false );\r
255                 ulCountAfterSleep = RTCC_CounterGet();\r
256 \r
257                 /* Re-enable interrupts - see comments above the INT_Enable() call\r
258                 above. */\r
259                 INT_Enable();\r
260                 __asm volatile( "dsb" );\r
261                 __asm volatile( "isb" );\r
262 \r
263                 if( ulTickFlag != pdFALSE )\r
264                 {\r
265                         /* The tick interrupt has already executed, although because this\r
266                         function is called with the scheduler suspended the actual tick\r
267                         processing will not occur until after this function has exited.\r
268                         The tick interrupt handler will already have pended the tick\r
269                         processing in the kernel.  As the pending tick will be processed as\r
270                         soon as this function exits, the tick value     maintained by the tick\r
271                         is stepped forward by one less than the time spent sleeping.  The\r
272                         actual stepping of the tick appears later in this function. */\r
273                         ulCompleteTickPeriods = xExpectedIdleTime - 1UL;\r
274 \r
275                         /* The interrupt should have reset the CCV value. */\r
276                         configASSERT( RTCC_ChannelCCVGet( lpRTCC_CHANNEL ) == ulReloadValueForOneTick );\r
277                 }\r
278                 else\r
279                 {\r
280                         /* Something other than the tick interrupt ended the sleep.  How\r
281                         many complete tick periods passed while the processor was\r
282                         sleeping? */\r
283                         ulCompleteTickPeriods = ulCountAfterSleep / ulReloadValueForOneTick;\r
284 \r
285                         /* The next interrupt is configured to occur at whatever fraction of\r
286                         the current tick period remains by setting the reload value back to\r
287                         that required for one tick, and truncating the count to remove the\r
288                         counts that are greater than the reload value. */\r
289                         RTCC_ChannelCCVSet( lpRTCC_CHANNEL, ulReloadValueForOneTick );\r
290                         ulCountAfterSleep %= ulReloadValueForOneTick;\r
291                         RTCC_CounterSet( ulCountAfterSleep );\r
292                 }\r
293 \r
294                 /* Restart the RTC so it runs up to the alarm value.  The alarm value\r
295                 will get set to the value required to generate exactly one tick period\r
296                 the next time the RTC interrupt executes. */\r
297                 RTCC_Enable( true );\r
298 \r
299                 /* Wind the tick forward by the number of tick periods that the CPU\r
300                 remained in a low power state. */\r
301                 vTaskStepTick( ulCompleteTickPeriods );\r
302         }\r
303 }\r
304 /*-----------------------------------------------------------*/\r
305 \r
306 void RTCC_IRQHandler( void )\r
307 {\r
308         ulTickFlag = pdTRUE;\r
309 \r
310         if( RTCC_ChannelCCVGet( lpRTCC_CHANNEL ) != ulReloadValueForOneTick )\r
311         {\r
312                 /* Set RTC interrupt to one RTOS tick period. */\r
313                 RTCC_Enable( false );\r
314                 RTCC_ChannelCCVSet( lpRTCC_CHANNEL, ulReloadValueForOneTick );\r
315                 RTCC_Enable( true );\r
316         }\r
317 \r
318         RTCC_IntClear( _RTCC_IF_MASK );\r
319 \r
320         /* Critical section which protect incrementing the tick*/\r
321         portDISABLE_INTERRUPTS();\r
322         {\r
323                 if( xTaskIncrementTick() != pdFALSE )\r
324                 {\r
325                         /* Pend a context switch. */\r
326                         portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;\r
327                 }\r
328         }\r
329         portENABLE_INTERRUPTS();\r
330 }\r
331 /*-----------------------------------------------------------*/\r
332 \r
333 #if( lpUSE_TEST_TIMER == 1 )\r
334 \r
335         /* Juse used to ensure the second timer is executing. */\r
336         volatile uint32_t ulLETimerIncrements = 0;\r
337 \r
338         void LETIMER0_IRQHandler( void )\r
339         {\r
340                 /* This ISR is used purely to bring the MCU out of sleep mode - it has\r
341                 no other purpose. */\r
342                 ulLETimerIncrements++;\r
343                 LETIMER_IntClear( LETIMER0, LETIMER_IF_COMP0 );\r
344         }\r
345 \r
346 #endif /* lpUSE_TEST_TIMER == 1 */\r
347 /*-----------------------------------------------------------*/\r
348 \r
349 #if( lpUSE_TEST_TIMER == 1 )\r
350 \r
351         /* Set up a timer that used used to bring the MCU out of sleep mode using\r
352         an interrupt other than the tick interrupt.  This is done for code coverage\r
353         puposes only. */\r
354         void prvSetupTestTimer( void )\r
355         {\r
356         static const LETIMER_Init_TypeDef xLETimerInitStruct =\r
357         {\r
358                 true,               /* Enable timer when init complete. */\r
359                 false,              /* Stop counter during debug halt. */\r
360                 true,               /* Load COMP0 into CNT on underflow. */\r
361                 false,              /* Do not load COMP1 into COMP0 when REP0 reaches 0. */\r
362                 0,                  /* Idle value 0 for output 0. */\r
363                 0,                  /* Idle value 0 for output 1. */\r
364                 letimerUFOANone,    /* No action on underflow on output 0. */\r
365                 letimerUFOANone,    /* No action on underflow on output 1. */\r
366                 letimerRepeatFree   /* Count until stopped by SW. */\r
367         };\r
368         const uint32_t ulCompareMatch = 32768UL / 10UL;\r
369 \r
370                 CMU_ClockSelectSet( cmuClock_LFA, cmuSelect_LFXO );\r
371                 CMU_ClockEnable( cmuClock_LETIMER0, true );\r
372 \r
373                 LETIMER_CompareSet( LETIMER0, 0, ulCompareMatch );\r
374                 LETIMER_IntEnable( LETIMER0, LETIMER_IF_COMP0 );\r
375                 NVIC_EnableIRQ( LETIMER0_IRQn );\r
376                 LETIMER_Init( LETIMER0, &xLETimerInitStruct);\r
377         }\r
378 \r
379 #endif /* lpUSE_TEST_TIMER == 1 */\r
380 \r
381 \r
382 \r
383 \r
384 #endif /* ( configCREATE_LOW_POWER_DEMO == 1 ) */\r