]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_M4_ATSAM4L_Atmel_Studio/src/SAM4L_low_power_tick_management.c
Updated the sleep function to ensure it left interrupts disabled when returning.
[freertos] / FreeRTOS / Demo / CORTEX_M4_ATSAM4L_Atmel_Studio / src / SAM4L_low_power_tick_management.c
1 /*\r
2     FreeRTOS V7.4.0 - Copyright (C) 2013 Real Time Engineers Ltd.\r
3 \r
4     FEATURES AND PORTS ARE ADDED TO FREERTOS ALL THE TIME.  PLEASE VISIT\r
5     http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.\r
6 \r
7     ***************************************************************************\r
8      *                                                                       *\r
9      *    FreeRTOS tutorial books are available in pdf and paperback.        *\r
10      *    Complete, revised, and edited pdf reference manuals are also       *\r
11      *    available.                                                         *\r
12      *                                                                       *\r
13      *    Purchasing FreeRTOS documentation will not only help you, by       *\r
14      *    ensuring you get running as quickly as possible and with an        *\r
15      *    in-depth knowledge of how to use FreeRTOS, it will also help       *\r
16      *    the FreeRTOS project to continue with its mission of providing     *\r
17      *    professional grade, cross platform, de facto standard solutions    *\r
18      *    for microcontrollers - completely free of charge!                  *\r
19      *                                                                       *\r
20      *    >>> See http://www.FreeRTOS.org/Documentation for details. <<<     *\r
21      *                                                                       *\r
22      *    Thank you for using FreeRTOS, and thank you for your support!      *\r
23      *                                                                       *\r
24     ***************************************************************************\r
25 \r
26 \r
27     This file is part of the FreeRTOS distribution.\r
28 \r
29     FreeRTOS is free software; you can redistribute it and/or modify it under\r
30     the terms of the GNU General Public License (version 2) as published by the\r
31     Free Software Foundation AND MODIFIED BY the FreeRTOS exception.\r
32 \r
33     >>>>>>NOTE<<<<<< The modification to the GPL is included to allow you to\r
34     distribute a combined work that includes FreeRTOS without being obliged to\r
35     provide the source code for proprietary components outside of the FreeRTOS\r
36     kernel.\r
37 \r
38     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY\r
39     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\r
40     FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\r
41     details. You should have received a copy of the GNU General Public License\r
42     and the FreeRTOS license exception along with FreeRTOS; if not itcan be\r
43     viewed here: http://www.freertos.org/a00114.html and also obtained by\r
44     writing to Real Time Engineers Ltd., contact details for whom are available\r
45     on the FreeRTOS WEB site.\r
46 \r
47     1 tab == 4 spaces!\r
48 \r
49     ***************************************************************************\r
50      *                                                                       *\r
51      *    Having a problem?  Start by reading the FAQ "My application does   *\r
52      *    not run, what could be wrong?"                                     *\r
53      *                                                                       *\r
54      *    http://www.FreeRTOS.org/FAQHelp.html                               *\r
55      *                                                                       *\r
56     ***************************************************************************\r
57 \r
58 \r
59     http://www.FreeRTOS.org - Documentation, books, training, latest versions,\r
60     license and Real Time Engineers Ltd. contact details.\r
61 \r
62     http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,\r
63     including FreeRTOS+Trace - an indispensable productivity tool, and our new\r
64     fully thread aware and reentrant UDP/IP stack.\r
65 \r
66     http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High\r
67     Integrity Systems, who sell the code with commercial support,\r
68     indemnification and middleware, under the OpenRTOS brand.\r
69 \r
70     http://www.SafeRTOS.com - High Integrity Systems also provide a safety\r
71     engineered and independently SIL3 certified version for use in safety and\r
72     mission critical applications that require provable dependability.\r
73 */\r
74 \r
75 /* Standard includes. */\r
76 #include "limits.h"\r
77 \r
78 /* FreeRTOS includes. */\r
79 #include "FreeRTOS.h"\r
80 #include "task.h"\r
81 \r
82 /* Library includes. */\r
83 #include <asf.h>\r
84 \r
85 \r
86 /*\r
87  * When configCREATE_LOW_POWER_DEMO is set to 1 then the tick interrupt\r
88  * is generated by the AST.  The AST configuration and handling functions are\r
89  * defined in this file.\r
90  *\r
91  * When configCREATE_LOW_POWER_DEMO is set to 0 the tick interrupt is\r
92  * generated by the standard FreeRTOS Cortex-M port layer, which uses the\r
93  * SysTick timer.\r
94  */\r
95 #if configCREATE_LOW_POWER_DEMO == 1\r
96 \r
97 /* Constants required to pend a PendSV interrupt from the tick ISR if the\r
98 preemptive scheduler is being used.  These are just standard bits and registers\r
99 within the Cortex-M core itself. */\r
100 #define portNVIC_INT_CTRL_REG   ( * ( ( volatile unsigned long * ) 0xe000ed04 ) )\r
101 #define portNVIC_PENDSVSET_BIT  ( 1UL << 28UL )\r
102 \r
103 /* The alarm used to generate interrupts in the asynchronous timer. */\r
104 #define portAST_ALARM_CHANNEL   0\r
105 \r
106 /*-----------------------------------------------------------*/\r
107 \r
108 /*\r
109  * The tick interrupt is generated by the asynchronous timer.  The default tick\r
110  * interrupt handler cannot be used (even with the AST being handled from the\r
111  * tick hook function) because the default tick interrupt accesses the SysTick\r
112  * registers when configUSE_TICKLESS_IDLE set to 1.  AST_ALARM_Handler() is the\r
113  * default name for the AST alarm interrupt.  This definition overrides the\r
114  * default implementation that is weakly defined in the interrupt vector table\r
115  * file.\r
116  */\r
117 void AST_ALARM_Handler(void);\r
118 \r
119 /*-----------------------------------------------------------*/\r
120 \r
121 /* Calculate how many clock increments make up a single tick period. */\r
122 static const uint32_t ulAlarmValueForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );\r
123 \r
124 /* Holds the maximum number of ticks that can be suppressed - which is\r
125 basically how far into the future an interrupt can be generated. Set\r
126 during initialisation. */\r
127 static portTickType xMaximumPossibleSuppressedTicks = 0;\r
128 \r
129 /* Flag set from the tick interrupt to allow the sleep processing to know if\r
130 sleep mode was exited because of an AST interrupt or a different interrupt. */\r
131 static volatile uint32_t ulTickFlag = pdFALSE;\r
132 \r
133 /* The AST counter is stopped temporarily each time it is re-programmed.  The\r
134 following variable offsets the AST counter alarm value by the number of AST\r
135 counts that would typically be missed while the counter was stopped to compensate\r
136 for the lost time.  _RB_ Value needs calculating correctly. */\r
137 static uint32_t ulStoppedTimerCompensation = 10 / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );\r
138 \r
139 /*-----------------------------------------------------------*/\r
140 \r
141 /* The tick interrupt handler.  This is always the same other than the part that\r
142 clears the interrupt, which is specific to the clock being used to generate the\r
143 tick. */\r
144 void AST_ALARM_Handler(void)\r
145 {\r
146         /* If using preemption, also force a context switch by pending the PendSV\r
147         interrupt. */\r
148         #if configUSE_PREEMPTION == 1\r
149         {\r
150                 portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;\r
151         }\r
152         #endif\r
153 \r
154         /* Protect incrementing the tick with an interrupt safe critical section. */\r
155         ( void ) portSET_INTERRUPT_MASK_FROM_ISR();\r
156         {\r
157                 vTaskIncrementTick();\r
158 \r
159                 /* Just completely clear the interrupt mask on exit by passing 0 because\r
160                 it is known that this interrupt will only ever execute with the lowest\r
161                 possible interrupt priority. */\r
162         }\r
163         portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 );\r
164 \r
165         /* The CPU woke because of a tick. */\r
166         ulTickFlag = pdTRUE;\r
167 \r
168         /* If this is the first tick since exiting tickless mode then the AST needs\r
169         to be reconfigured to generate interrupts at the defined tick frequency. */\r
170         ast_write_alarm0_value( AST, ulAlarmValueForOneTick );\r
171 \r
172         /* Ensure the interrupt is clear before exiting. */\r
173         ast_clear_interrupt_flag( AST, AST_INTERRUPT_ALARM );\r
174 }\r
175 /*-----------------------------------------------------------*/\r
176 \r
177 /* Override the default definition of vPortSetupTimerInterrupt() that is weakly\r
178 defined in the FreeRTOS Cortex-M3 port layer layer with a version that\r
179 configures the asynchronous timer (AST) to generate the tick interrupt. */\r
180 void vPortSetupTimerInterrupt( void )\r
181 {\r
182 struct ast_config ast_conf;\r
183 \r
184         /* Ensure the AST can bring the CPU out of sleep mode. */\r
185         sleepmgr_lock_mode( SLEEPMGR_RET );\r
186 \r
187         /* Ensure the 32KHz oscillator is enabled. */\r
188         if( osc_is_ready( OSC_ID_OSC32 ) == pdFALSE )\r
189         {\r
190                 osc_enable( OSC_ID_OSC32 );\r
191                 osc_wait_ready( OSC_ID_OSC32 );\r
192         }\r
193 \r
194         /* Enable the AST itself. */\r
195         ast_enable( AST );\r
196 \r
197         ast_conf.mode = AST_COUNTER_MODE;  /* Simple up counter. */\r
198         ast_conf.osc_type = AST_OSC_32KHZ;\r
199         ast_conf.psel = 0; /* No prescale so the actual frequency is 32KHz/2. */\r
200         ast_conf.counter = 0;\r
201         ast_set_config( AST, &ast_conf );\r
202 \r
203         /* The AST alarm interrupt is used as the tick interrupt.  Ensure the alarm\r
204         status starts clear. */\r
205         ast_clear_interrupt_flag( AST, AST_INTERRUPT_ALARM );\r
206 \r
207         /* Enable wakeup from alarm 0 in the AST and power manager.  */\r
208         ast_enable_wakeup( AST, AST_WAKEUP_ALARM );\r
209         bpm_enable_wakeup_source( BPM, ( 1 << BPM_BKUPWEN_AST ) );\r
210 \r
211         /* Tick interrupt MUST execute at the lowest interrupt priority. */\r
212         NVIC_SetPriority( AST_ALARM_IRQn, configLIBRARY_LOWEST_INTERRUPT_PRIORITY);\r
213         ast_enable_interrupt( AST, AST_INTERRUPT_ALARM );\r
214         NVIC_ClearPendingIRQ( AST_ALARM_IRQn );\r
215         NVIC_EnableIRQ( AST_ALARM_IRQn );\r
216 \r
217         /* Automatically clear the counter on interrupt. */\r
218         ast_enable_counter_clear_on_alarm( AST, portAST_ALARM_CHANNEL );\r
219 \r
220         /* Start with the tick active and generating a tick with regular period. */\r
221         ast_write_alarm0_value( AST, ulAlarmValueForOneTick );\r
222         ast_write_counter_value( AST, 0 );\r
223 \r
224         /* See the comments where xMaximumPossibleSuppressedTicks is declared. */\r
225         xMaximumPossibleSuppressedTicks = ULONG_MAX / ulAlarmValueForOneTick;\r
226 }\r
227 /*-----------------------------------------------------------*/\r
228 \r
229 void prvDisableAST( void )\r
230 {\r
231         while( ast_is_busy( AST ) )\r
232         {\r
233                 /* Nothing to do here, just waiting. */\r
234         }\r
235         AST->AST_CR &= ~( AST_CR_EN );\r
236         while( ast_is_busy( AST ) )\r
237         {\r
238                 /* Nothing to do here, just waiting. */\r
239         }\r
240 }\r
241 /*-----------------------------------------------------------*/\r
242 \r
243 void prvEnableAST( void )\r
244 {\r
245         while( ast_is_busy( AST ) )\r
246         {\r
247                 /* Nothing to do here, just waiting. */\r
248         }\r
249         AST->AST_CR |= AST_CR_EN;\r
250         while( ast_is_busy( AST ) )\r
251         {\r
252                 /* Nothing to do here, just waiting. */\r
253         }\r
254 }\r
255 /*-----------------------------------------------------------*/\r
256 \r
257 /* Override the default definition of vPortSuppressTicksAndSleep() that is weakly\r
258 defined in the FreeRTOS Cortex-M3 port layer layer with a version that manages\r
259 the asynchronous timer (AST), as the tick is generated from the low power AST\r
260 and not the SysTick as would normally be the case on a Cortex-M. */\r
261 void vPortSuppressTicksAndSleep( portTickType xExpectedIdleTime )\r
262 {\r
263 uint32_t ulAlarmValue, ulCompleteTickPeriods;\r
264 eSleepModeStatus eSleepAction;\r
265 portTickType xModifiableIdleTime;\r
266 enum sleepmgr_mode xSleepMode;\r
267 \r
268         /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */\r
269 \r
270         /* Make sure the AST reload value does not overflow the counter. */\r
271         if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )\r
272         {\r
273                 xExpectedIdleTime = xMaximumPossibleSuppressedTicks;\r
274         }\r
275 \r
276         /* Calculate the reload value required to wait xExpectedIdleTime tick\r
277         periods.  -1 is used because this code will execute part way through one of\r
278         the tick periods, and the fraction of a tick period is accounted for\r
279         later. */\r
280         ulAlarmValue = ( ulAlarmValueForOneTick * ( xExpectedIdleTime - 1UL ) );\r
281         if( ulAlarmValue > ulStoppedTimerCompensation )\r
282         {\r
283                 /* Compensate for the fact that the AST is going to be stopped\r
284                 momentarily. */\r
285                 ulAlarmValue -= ulStoppedTimerCompensation;\r
286         }\r
287 \r
288         /* Stop the AST momentarily.  The time the AST is stopped for is accounted\r
289         for as best it can be, but using the tickless mode will inevitably result in\r
290         some tiny drift of the time maintained by the kernel with respect to\r
291         calendar time. */\r
292         prvDisableAST();\r
293 \r
294         /* Enter a critical section but don't use the taskENTER_CRITICAL() method as\r
295         that will mask interrupts that should exit sleep mode. */\r
296         __asm volatile( "cpsid i                \n\t"\r
297                                         "dsb                    \n\t" );\r
298 \r
299         /* The tick flag is set to false before sleeping.  If it is true when sleep\r
300         mode is exited then sleep mode was probably exited because the tick was\r
301         suppressed for the entire xExpectedIdleTime period. */\r
302         ulTickFlag = pdFALSE;\r
303 \r
304         /* If a context switch is pending then abandon the low power entry as\r
305         the context switch might have been pended by an external interrupt that\r
306         requires processing. */\r
307         eSleepAction = eTaskConfirmSleepModeStatus();\r
308         if( eSleepAction == eAbortSleep )\r
309         {\r
310                 /* Restart tick. */\r
311                 prvEnableAST();\r
312 \r
313                 /* Re-enable interrupts - see comments above the cpsid instruction()\r
314                 above. */\r
315                 __asm volatile( "cpsie i" );\r
316         }\r
317         else\r
318         {\r
319                 /* Adjust the alarm value to take into account that the current time\r
320                 slice is already partially complete. */\r
321                 ulAlarmValue -= ast_read_counter_value( AST );\r
322                 ast_write_alarm0_value( AST, ulAlarmValue );\r
323 \r
324                 /* Restart the AST. */\r
325                 prvEnableAST();\r
326 \r
327                 /* Allow the application to define some pre-sleep processing. */\r
328                 xModifiableIdleTime = xExpectedIdleTime;\r
329                 configPRE_SLEEP_PROCESSING( xModifiableIdleTime );\r
330 \r
331                 /* xExpectedIdleTime being set to 0 by configPRE_SLEEP_PROCESSING()\r
332                 means the application defined code has already executed the WAIT\r
333                 instruction. */\r
334                 if( xModifiableIdleTime > 0 )\r
335                 {\r
336                         /* Find the deepest allowable sleep mode. */\r
337                         xSleepMode = sleepmgr_get_sleep_mode();\r
338 \r
339                         if( xSleepMode != SLEEPMGR_ACTIVE )\r
340                         {\r
341                                 /* Sleep until something happens. */\r
342                                 bpm_sleep( BPM, xSleepMode );\r
343                         }\r
344                 }\r
345 \r
346                 /* Allow the application to define some post sleep processing. */\r
347                 configPOST_SLEEP_PROCESSING( xModifiableIdleTime );\r
348 \r
349                 /* Stop AST.  Again, the time the SysTick is stopped for is     accounted\r
350                 for as best it can be, but using the tickless mode will inevitably\r
351                 result in some tiny drift of the time maintained by the kernel with\r
352                 respect to calendar time. */\r
353                 prvDisableAST();\r
354 \r
355                 /* Re-enable interrupts - see comments above the cpsid instruction()\r
356                 above. */\r
357                 __asm volatile( "cpsie i" );\r
358 \r
359                 if( ulTickFlag != pdFALSE )\r
360                 {\r
361                         /* The tick interrupt has already executed, although because this\r
362                         function is called with the scheduler suspended the actual tick\r
363                         processing will not occur until after this function has exited.\r
364                         Reset the alarm value with whatever remains of this tick period. */\r
365                         ulAlarmValue = ulAlarmValueForOneTick - ast_read_counter_value( AST );\r
366                         ast_write_alarm0_value( AST, ulAlarmValue );\r
367 \r
368                         /* The tick interrupt handler will already have pended the tick\r
369                         processing in the kernel.  As the pending tick will be processed as\r
370                         soon as this function exits, the tick value     maintained by the tick\r
371                         is stepped forward by one less than the time spent sleeping.  The\r
372                         actual stepping of the tick appears later in this function. */\r
373                         ulCompleteTickPeriods = xExpectedIdleTime - 1UL;\r
374                 }\r
375                 else\r
376                 {\r
377                         /* Something other than the tick interrupt ended the sleep.  How\r
378                         many complete tick periods passed while the processor was\r
379                         sleeping? */\r
380                         ulCompleteTickPeriods = ast_read_counter_value( AST ) / ulAlarmValueForOneTick;\r
381 \r
382                         /* The alarm value is set to whatever fraction of a single tick\r
383                         period remains. */\r
384                         ulAlarmValue = ast_read_counter_value( AST ) - ( ulCompleteTickPeriods * ulAlarmValueForOneTick );\r
385                         ast_write_alarm0_value( AST, ulAlarmValue );\r
386                 }\r
387 \r
388                 /* Restart the AST so it runs up to the alarm value.  The alarm value\r
389                 will get set to the value required to generate exactly one tick period\r
390                 the next time the AST interrupt executes. */\r
391                 prvEnableAST();\r
392 \r
393                 /* Wind the tick forward by the number of tick periods that the CPU\r
394                 remained in a low power state. */\r
395                 vTaskStepTick( ulCompleteTickPeriods );\r
396         }\r
397 }\r
398 \r
399 \r
400 #endif /* configCREATE_LOW_POWER_DEMO == 1 */\r
401 \r