]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_M4F_CEC1302_MikroC/main_low_power/low_power_tick_config.c
Roll up the minor changes checked into svn since V10.0.0 into new V10.0.1 ready for...
[freertos] / FreeRTOS / Demo / CORTEX_M4F_CEC1302_MikroC / main_low_power / low_power_tick_config.c
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 includes. */\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 /* Library includes. */\r
36 #include "htimer.h"\r
37 \r
38 /* This file contains functions that will override the default implementations\r
39 in the RTOS port layer.  Therefore only build this file if the low power demo\r
40 is being built. */\r
41 #if( configCREATE_LOW_POWER_DEMO == 1 )\r
42 \r
43 /* ID of the hibernation timer used to generate the tick. */\r
44 #define mainTICK_HTIMER_ID      0\r
45 \r
46 /* Written to the hibernation timer control register to configure the timer for\r
47 its higher resolution. */\r
48 #define mainHTIMER_HIGH_RESOLUTION      0\r
49 \r
50 /* The frequency of the hibernation timer when it is running at its higher\r
51 resolution and low resolution respectively. */\r
52 #define mainHIGHER_RESOLUTION_TIMER_HZ  ( 32787 ) /* (1000000us / 30.5us) as each LSB is 30.5us. */\r
53 #define mainLOW_RESOLUTION_TIMER_HZ             ( 8UL )  /* ( 1000ms / 125ms ) as each LSB is 0.125s. */\r
54 \r
55 /* Some registers are accessed directly as the library is not compatible with\r
56 all the compilers used. */\r
57 #define lpHTIMER_PRELOAD_REGISTER               ( * ( volatile uint16_t * ) 0x40009800 )\r
58 #define lpHTIMER_COUNT_REGISTER                 ( * ( volatile uint16_t * ) 0x40009808 )\r
59 #define lpEC_GIRQ17_ENABLE_SET                  ( * ( volatile uint32_t * ) 0x4000C0B8 )\r
60 #define lpHTIMER_INTERRUPT_CONTROL_BIT  ( 1UL << 20UL )\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  * the low power clock.  NOTE:  This function name must not be changed as it\r
66  * is called from the RTOS portable layer.\r
67  */\r
68 void vPortSetupTimerInterrupt( void );\r
69 \r
70 /*-----------------------------------------------------------*/\r
71 \r
72 /* The reload value to use in the timer to generate the tick interrupt -\r
73 assumes the timer is running at its higher resolution. */\r
74 static const uint16_t usHighResolutionReloadValue = ( mainHIGHER_RESOLUTION_TIMER_HZ / ( uint16_t ) configTICK_RATE_HZ );\r
75 \r
76 /* Calculate how many clock increments make up a single tick period. */\r
77 static const uint32_t ulReloadValueForOneHighResolutionTick = ( mainHIGHER_RESOLUTION_TIMER_HZ / configTICK_RATE_HZ );\r
78 \r
79 /* Calculate the maximum number of ticks that can be suppressed when using the\r
80 high resolution clock and low resolution clock respectively. */\r
81 static uint32_t ulMaximumPossibleSuppressedHighResolutionTicks = 0;\r
82 \r
83 /* As the clock is only 2KHz, it is likely a value of 1 will be too much, so\r
84 use zero - but leave the value here to assist porting to different clock\r
85 speeds. */\r
86 static const uint32_t ulStoppedTimerCompensation = 0UL;\r
87 \r
88 /* Flag set from the tick interrupt to allow the sleep processing to know if\r
89 sleep mode was exited because of an timer interrupt or a different interrupt. */\r
90 static volatile uint32_t ulTickFlag = pdFALSE;\r
91 \r
92 /*-----------------------------------------------------------*/\r
93 \r
94 void NVIC_Handler_HIB_TMR( void ) iv IVT_INT_HTIMER ics ICS_AUTO\r
95 {\r
96         lpHTIMER_PRELOAD_REGISTER = usHighResolutionReloadValue;\r
97 \r
98         /* Increment the RTOS tick. */\r
99         if( xTaskIncrementTick() != pdFALSE )\r
100         {\r
101                 /* A context switch is required.  Context switching is performed in\r
102                 the PendSV interrupt.  Pend the PendSV interrupt. */\r
103                 portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;\r
104         }\r
105 \r
106         /* The CPU woke because of a tick. */\r
107         ulTickFlag = pdTRUE;\r
108 }\r
109 /*-----------------------------------------------------------*/\r
110 \r
111 void vPortSetupTimerInterrupt( void )\r
112 {\r
113         /* Cannot be a const when using the MikroC compiler. */\r
114         ulMaximumPossibleSuppressedHighResolutionTicks = ( ( uint32_t ) USHRT_MAX ) / ulReloadValueForOneHighResolutionTick;\r
115 \r
116         /* Set up the hibernation timer to start at the value required by the\r
117         tick interrupt. */\r
118         htimer_enable( mainTICK_HTIMER_ID, usHighResolutionReloadValue, mainHTIMER_HIGH_RESOLUTION );\r
119 \r
120         /* Enable the HTIMER interrupt.  Equivalent to enable_htimer0_irq(); */\r
121         lpEC_GIRQ17_ENABLE_SET |= lpHTIMER_INTERRUPT_CONTROL_BIT;\r
122 \r
123         /* The hibernation timer is not an auto-reload timer, so gets reset\r
124         from within the ISR itself.  For that reason it's interrupt is set\r
125         to the highest possible priority to ensure clock slippage is minimised. */\r
126         NVIC_SetIntPriority( IVT_INT_HTIMER, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );\r
127         NVIC_IntEnable( IVT_INT_HTIMER );\r
128 }\r
129 /*-----------------------------------------------------------*/\r
130 \r
131 /* Override the default definition of vPortSuppressTicksAndSleep() that is\r
132 weakly defined in the FreeRTOS Cortex-M port layer with a version that manages\r
133 the hibernation timer, as the tick is generated from the low power hibernation\r
134 timer and not the SysTick as would normally be the case on a Cortex-M. */\r
135 void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )\r
136 {\r
137 uint32_t ulCompleteTickPeriods, ulReloadValue, ulCompletedTimerDecrements, ulCountAfterSleep, ulCountBeforeSleep;\r
138 eSleepModeStatus eSleepAction;\r
139 TickType_t xModifiableIdleTime;\r
140 \r
141         /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */\r
142 \r
143         /* Make sure the hibernation timer reload value does not overflow the\r
144         counter. */\r
145         if( xExpectedIdleTime > ( TickType_t ) ulMaximumPossibleSuppressedHighResolutionTicks )\r
146         {\r
147                 xExpectedIdleTime = ( TickType_t ) ulMaximumPossibleSuppressedHighResolutionTicks;\r
148         }\r
149 \r
150         /* Stop the timer momentarily.  The time the timer is stopped for is\r
151         accounted for as best it can be, but using the tickless mode will\r
152         inevitably result in some tiny drift of the time maintained by the kernel\r
153         with respect to calendar time.  Take the count value first as clearing\r
154         the preload value also seems to clear the count. */\r
155         ulCountBeforeSleep = ( uint32_t ) lpHTIMER_COUNT_REGISTER;\r
156         lpHTIMER_PRELOAD_REGISTER = 0;\r
157 \r
158         /* Calculate the reload value required to wait xExpectedIdleTime tick\r
159         periods.  -1 is used as the current time slice will already be part way\r
160         through, the part value coming from the current timer count value. */\r
161         ulReloadValue = ulCountBeforeSleep + ( ulReloadValueForOneHighResolutionTick * ( xExpectedIdleTime - 1UL ) );\r
162 \r
163         if( ulReloadValue > ulStoppedTimerCompensation )\r
164         {\r
165                 /* Compensate for the fact that the timer is going to be stopped\r
166                 momentarily. */\r
167                 ulReloadValue -= ulStoppedTimerCompensation;\r
168         }\r
169 \r
170         /* Enter a critical section but don't use the taskENTER_CRITICAL() method as\r
171         that will mask interrupts that should exit sleep mode. */\r
172         __asm { cpsid i\r
173                         dsb\r
174                         isb };\r
175 \r
176         /* The tick flag is set to false before sleeping.  If it is true when sleep\r
177         mode is exited then sleep mode was probably exited because the tick was\r
178         suppressed for the entire xExpectedIdleTime period. */\r
179         ulTickFlag = pdFALSE;\r
180 \r
181         /* If a context switch is pending then abandon the low power entry as\r
182         the context switch might have been pended by an external interrupt that\r
183         requires processing. */\r
184         eSleepAction = eTaskConfirmSleepModeStatus();\r
185         if( eSleepAction == eAbortSleep )\r
186         {\r
187                 /* Resetart the timer from whatever remains in the counter register,\r
188                 but 0 is not a valid value. */\r
189                 ulReloadValue = ulCountBeforeSleep - ulStoppedTimerCompensation;\r
190 \r
191                 if( ulReloadValue == 0 )\r
192                 {\r
193                         ulReloadValue = ulReloadValueForOneHighResolutionTick;\r
194                         ulCompleteTickPeriods = 1UL;\r
195                 }\r
196                 else\r
197                 {\r
198                         ulCompleteTickPeriods = 0UL;\r
199                 }\r
200 \r
201                 lpHTIMER_PRELOAD_REGISTER = ( uint16_t ) ulReloadValue;\r
202 \r
203                 /* Re-enable interrupts - see comments above the cpsid instruction()\r
204                 above. */\r
205                 __asm { cpsie i\r
206                                 dsb\r
207                                 isb };\r
208 \r
209         }\r
210         else\r
211         {\r
212                 /* Write the calculated reload value, which will also start the\r
213                 timer. */\r
214                 lpHTIMER_PRELOAD_REGISTER = ( uint16_t ) ulReloadValue;\r
215 \r
216                 /* Allow the application to define some pre-sleep processing. */\r
217                 xModifiableIdleTime = xExpectedIdleTime;\r
218                 configPRE_SLEEP_PROCESSING( xModifiableIdleTime );\r
219 \r
220                 /* xExpectedIdleTime being set to 0 by configPRE_SLEEP_PROCESSING()\r
221                 means the application defined code has already executed the sleep\r
222                 instructions. */\r
223                 if( xModifiableIdleTime > 0 )\r
224                 {\r
225                         __asm { dsb\r
226                                         wfi\r
227                                         isb };\r
228                 }\r
229 \r
230                 /* Allow the application to define some post sleep processing. */\r
231                 configPOST_SLEEP_PROCESSING( xModifiableIdleTime );\r
232 \r
233                 /* Stop the hibernation timer.  Again, the time the tiemr is stopped\r
234                 for is accounted for as best it can be, but using the tickless mode\r
235                 will inevitably result in some tiny drift of the time maintained by the\r
236                 kernel with respect to calendar time.  Take the count value first as\r
237                 setting the preload to zero also seems to clear the count. */\r
238                 ulCountAfterSleep = ( uint32_t ) lpHTIMER_COUNT_REGISTER;\r
239                 lpHTIMER_PRELOAD_REGISTER = 0;\r
240 \r
241                 /* Re-enable interrupts - see comments above the cpsid instruction()\r
242                 above. */\r
243                 __asm { cpsie i\r
244                                 dsb\r
245                                 isb };\r
246 \r
247 \r
248                 if( ulTickFlag != pdFALSE )\r
249                 {\r
250                         /* The tick interrupt has already executed, although because this\r
251                         function is called with the scheduler suspended the actual tick\r
252                         processing will not occur until after this function has exited.\r
253                         The timer has already been reloaded to count in ticks, and can just\r
254                         continue counting down from its current value. */\r
255                         ulReloadValue = ulCountAfterSleep;\r
256 \r
257                         /* Sanity check that the timer's reload value has indeed been\r
258                         reset. */\r
259                         configASSERT( ( uint32_t ) lpHTIMER_PRELOAD_REGISTER == ulReloadValueForOneHighResolutionTick );\r
260 \r
261                         /* The tick interrupt handler will already have pended the tick\r
262                         processing in the kernel.  As the pending tick will be processed as\r
263                         soon as this function exits, the tick value     maintained by the tick\r
264                         is stepped forward by one less than the time spent sleeping.  The\r
265                         actual stepping of the tick appears later in this function. */\r
266                         ulCompleteTickPeriods = xExpectedIdleTime - 1UL;\r
267                 }\r
268                 else\r
269                 {\r
270                         /* Something other than the tick interrupt ended the sleep.  How\r
271                         many complete tick periods passed while the processor was\r
272                         sleeping? */\r
273                         ulCompletedTimerDecrements = ulReloadValue - ulCountAfterSleep;\r
274 \r
275                         /* Undo the adjustment that was made to the reload value to account\r
276                         for the fact that a time slice was part way through when this\r
277                         function was called before working out how many complete tick\r
278                         periods this represents.  (could have used [ulExpectedIdleTime *\r
279                         ulReloadValueForOneHighResolutionTick] instead of ulReloadValue on\r
280                         the previous line, but this way avoids the multiplication). */\r
281                         ulCompletedTimerDecrements += ( ulReloadValueForOneHighResolutionTick - ulCountBeforeSleep );\r
282                         ulCompleteTickPeriods = ulCompletedTimerDecrements / ulReloadValueForOneHighResolutionTick;\r
283 \r
284                         /* The reload value is set to whatever fraction of a single tick\r
285                         period remains. */\r
286                         ulReloadValue = ( ( ulCompleteTickPeriods + 1UL ) * ulReloadValueForOneHighResolutionTick ) - ulCompletedTimerDecrements;\r
287                 }\r
288 \r
289                 /* Cannot use a reload value of 0 - it will not start the timer. */\r
290                 if( ulReloadValue == 0 )\r
291                 {\r
292                         /* There is no fraction remaining. */\r
293                         ulReloadValue = ulReloadValueForOneHighResolutionTick;\r
294                         ulCompleteTickPeriods++;\r
295                 }\r
296 \r
297                 /* Restart the timer so it runs down from the reload value.  The reload\r
298                 value will get set to the value required to generate exactly one tick\r
299                 period the next time the tick interrupt executes. */\r
300                 lpHTIMER_PRELOAD_REGISTER = ( uint16_t ) ulReloadValue;\r
301         }\r
302 \r
303         /* Wind the tick forward by the number of tick periods that the CPU\r
304         remained in a low power state. */\r
305         vTaskStepTick( ulCompleteTickPeriods );\r
306 }\r
307 /*-----------------------------------------------------------*/\r
308 \r
309 \r
310 #endif /* configCREATE_LOW_POWER_DEMO */\r
311 \r