]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_M4_ATSAM4L_Atmel_Studio/src/SAM4L_low_power_tick_management.c
Update comments in Atmel Studio CreateProjectDirectoryStructure.bat files to remove...
[freertos] / FreeRTOS / Demo / CORTEX_M4_ATSAM4L_Atmel_Studio / src / SAM4L_low_power_tick_management.c
1 /*\r
2     FreeRTOS V7.4.2 - 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 it can 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 = 2 / ( 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. */\r
278         ulAlarmValue = ulAlarmValueForOneTick * xExpectedIdleTime;\r
279         if( ulAlarmValue > ulStoppedTimerCompensation )\r
280         {\r
281                 /* Compensate for the fact that the AST is going to be stopped\r
282                 momentarily. */\r
283                 ulAlarmValue -= ulStoppedTimerCompensation;\r
284         }\r
285 \r
286         /* Stop the AST momentarily.  The time the AST is stopped for is accounted\r
287         for as best it can be, but using the tickless mode will inevitably result in\r
288         some tiny drift of the time maintained by the kernel with respect to\r
289         calendar time. */\r
290         prvDisableAST();\r
291 \r
292         /* Enter a critical section but don't use the taskENTER_CRITICAL() method as\r
293         that will mask interrupts that should exit sleep mode. */\r
294         __asm volatile( "cpsid i                \n\t"\r
295                                         "dsb                    \n\t" );\r
296 \r
297         /* The tick flag is set to false before sleeping.  If it is true when sleep\r
298         mode is exited then sleep mode was probably exited because the tick was\r
299         suppressed for the entire xExpectedIdleTime period. */\r
300         ulTickFlag = pdFALSE;\r
301 \r
302         /* If a context switch is pending then abandon the low power entry as\r
303         the context switch might have been pended by an external interrupt that\r
304         requires processing. */\r
305         eSleepAction = eTaskConfirmSleepModeStatus();\r
306         if( eSleepAction == eAbortSleep )\r
307         {\r
308                 /* Restart tick. */\r
309                 prvEnableAST();\r
310 \r
311                 /* Re-enable interrupts - see comments above the cpsid instruction()\r
312                 above. */\r
313                 __asm volatile( "cpsie i" );\r
314         }\r
315         else\r
316         {\r
317                 /* Adjust the alarm value to take into account that the current time\r
318                 slice is already partially complete. */\r
319                 ulAlarmValue -= ast_read_counter_value( AST );\r
320                 ast_write_alarm0_value( AST, ulAlarmValue );\r
321 \r
322                 /* Restart the AST. */\r
323                 prvEnableAST();\r
324 \r
325                 /* Allow the application to define some pre-sleep processing. */\r
326                 xModifiableIdleTime = xExpectedIdleTime;\r
327                 configPRE_SLEEP_PROCESSING( xModifiableIdleTime );\r
328 \r
329                 /* xExpectedIdleTime being set to 0 by configPRE_SLEEP_PROCESSING()\r
330                 means the application defined code has already executed the WAIT\r
331                 instruction. */\r
332                 if( xModifiableIdleTime > 0 )\r
333                 {\r
334                         /* Find the deepest allowable sleep mode. */\r
335                         xSleepMode = sleepmgr_get_sleep_mode();\r
336 \r
337                         if( xSleepMode != SLEEPMGR_ACTIVE )\r
338                         {\r
339                                 /* Sleep until something happens. */\r
340                                 bpm_sleep( BPM, xSleepMode );\r
341                         }\r
342                 }\r
343 \r
344                 /* Allow the application to define some post sleep processing. */\r
345                 configPOST_SLEEP_PROCESSING( xModifiableIdleTime );\r
346 \r
347                 /* Stop AST.  Again, the time the SysTick is stopped for is     accounted\r
348                 for as best it can be, but using the tickless mode will inevitably\r
349                 result in some tiny drift of the time maintained by the kernel with\r
350                 respect to calendar time. */\r
351                 prvDisableAST();\r
352 \r
353                 /* Re-enable interrupts - see comments above the cpsid instruction()\r
354                 above. */\r
355                 __asm volatile( "cpsie i" );\r
356 \r
357                 if( ulTickFlag != pdFALSE )\r
358                 {\r
359                         /* The tick interrupt has already executed, although because this\r
360                         function is called with the scheduler suspended the actual tick\r
361                         processing will not occur until after this function has exited.\r
362                         Reset the alarm value with whatever remains of this tick period. */\r
363                         ulAlarmValue = ulAlarmValueForOneTick - ast_read_counter_value( AST );\r
364                         ast_write_alarm0_value( AST, ulAlarmValue );\r
365 \r
366                         /* The tick interrupt handler will already have pended the tick\r
367                         processing in the kernel.  As the pending tick will be processed as\r
368                         soon as this function exits, the tick value     maintained by the tick\r
369                         is stepped forward by one less than the time spent sleeping.  The\r
370                         actual stepping of the tick appears later in this function. */\r
371                         ulCompleteTickPeriods = xExpectedIdleTime - 1UL;\r
372                 }\r
373                 else\r
374                 {\r
375                         /* Something other than the tick interrupt ended the sleep.  How\r
376                         many complete tick periods passed while the processor was\r
377                         sleeping? */\r
378                         ulCompleteTickPeriods = ast_read_counter_value( AST ) / ulAlarmValueForOneTick;\r
379 \r
380                         /* The alarm value is set to whatever fraction of a single tick\r
381                         period remains. */\r
382                         ulAlarmValue = ast_read_counter_value( AST ) - ( ulCompleteTickPeriods * ulAlarmValueForOneTick );\r
383                         ast_write_alarm0_value( AST, ulAlarmValue );\r
384                 }\r
385 \r
386                 /* Restart the AST so it runs up to the alarm value.  The alarm value\r
387                 will get set to the value required to generate exactly one tick period\r
388                 the next time the AST interrupt executes. */\r
389                 prvEnableAST();\r
390 \r
391                 /* Wind the tick forward by the number of tick periods that the CPU\r
392                 remained in a low power state. */\r
393                 vTaskStepTick( ulCompleteTickPeriods );\r
394         }\r
395 }\r
396 \r
397 \r
398 #endif /* configCREATE_LOW_POWER_DEMO == 1 */\r
399 \r