/* | |
* FreeRTOS Kernel V10.3.0 | |
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy of | |
* this software and associated documentation files (the "Software"), to deal in | |
* the Software without restriction, including without limitation the rights to | |
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | |
* the Software, and to permit persons to whom the Software is furnished to do so, | |
* subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all | |
* copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | |
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
* | |
* http://www.FreeRTOS.org | |
* http://aws.amazon.com/freertos | |
* | |
* 1 tab == 4 spaces! | |
*/ | |
/* Standard includes. */ | |
#include <limits.h> | |
/* FreeRTOS includes. */ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
/* ST library functions. */ | |
#include "stm32l1xx.h" | |
/* | |
* When configCREATE_LOW_POWER_DEMO is set to 1 then the tick interrupt | |
* is generated by the TIM2 peripheral. The TIM2 configuration and handling | |
* functions are defined in this file. Note the RTC is not used as there does | |
* not appear to be a way to read back the RTC count value, and therefore the | |
* only way of knowing exactly how long a sleep lasted is to use the very low | |
* resolution calendar time. | |
* | |
* When configCREATE_LOW_POWER_DEMO is set to 0 the tick interrupt is | |
* generated by the standard FreeRTOS Cortex-M port layer, which uses the | |
* SysTick timer. | |
*/ | |
#if configCREATE_LOW_POWER_DEMO == 1 | |
/* The frequency at which TIM2 will run. */ | |
#define lpCLOCK_INPUT_FREQUENCY ( 1000UL ) | |
/* STM32 register used to ensure the TIM2 clock stops when the MCU is in debug | |
mode. */ | |
#define DBGMCU_APB1_FZ ( * ( ( volatile unsigned long * ) 0xE0042008 ) ) | |
/*-----------------------------------------------------------*/ | |
/* | |
* The tick interrupt is generated by the TIM2 timer. | |
*/ | |
void TIM2_IRQHandler( void ); | |
/*-----------------------------------------------------------*/ | |
/* Calculate how many clock increments make up a single tick period. */ | |
static const uint32_t ulReloadValueForOneTick = ( ( lpCLOCK_INPUT_FREQUENCY / configTICK_RATE_HZ ) - 1 ); | |
/* Holds the maximum number of ticks that can be suppressed - which is | |
basically how far into the future an interrupt can be generated. Set during | |
initialisation. */ | |
static TickType_t xMaximumPossibleSuppressedTicks = 0; | |
/* Flag set from the tick interrupt to allow the sleep processing to know if | |
sleep mode was exited because of an tick interrupt or a different interrupt. */ | |
static volatile uint32_t ulTickFlag = pdFALSE; | |
/*-----------------------------------------------------------*/ | |
/* The tick interrupt handler. This is always the same other than the part that | |
clears the interrupt, which is specific to the clock being used to generate the | |
tick. */ | |
void TIM2_IRQHandler( void ) | |
{ | |
/* Clear the interrupt. */ | |
TIM_ClearITPendingBit( TIM2, TIM_IT_Update ); | |
/* The next block of code is from the standard FreeRTOS tick interrupt | |
handler. The standard handler is not called directly in case future | |
versions contain changes that make it no longer suitable for calling | |
here. */ | |
( void ) portSET_INTERRUPT_MASK_FROM_ISR(); | |
{ | |
if( xTaskIncrementTick() != pdFALSE ) | |
{ | |
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; | |
} | |
/* Just completely clear the interrupt mask on exit by passing 0 because | |
it is known that this interrupt will only ever execute with the lowest | |
possible interrupt priority. */ | |
} | |
portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 ); | |
/* In case this is the first tick since the MCU left a low power mode the | |
reload value is reset to its default. */ | |
TIM2->ARR = ( uint16_t ) ulReloadValueForOneTick; | |
/* The CPU woke because of a tick. */ | |
ulTickFlag = pdTRUE; | |
} | |
/*-----------------------------------------------------------*/ | |
/* Override the default definition of vPortSetupTimerInterrupt() that is weakly | |
defined in the FreeRTOS Cortex-M3 port layer with a version that configures TIM2 | |
to generate the tick interrupt. */ | |
void vPortSetupTimerInterrupt( void ) | |
{ | |
NVIC_InitTypeDef NVIC_InitStructure; | |
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; | |
/* Enable the TIM2 clock. */ | |
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE ); | |
/* Ensure clock stops in debug mode. */ | |
DBGMCU_APB1_FZ |= DBGMCU_APB1_FZ_DBG_TIM2_STOP; | |
/* Scale the clock so longer tickless periods can be achieved. The SysTick | |
is not used as even when its frequency is divided by 8 the maximum tickless | |
period with a system clock of 16MHz is only 8.3 seconds. Using a prescaled | |
clock on the 16-bit TIM2 allows a tickless period of nearly 66 seconds, | |
albeit at low resolution. */ | |
TIM_TimeBaseStructure.TIM_Prescaler = ( uint16_t ) ( SystemCoreClock / lpCLOCK_INPUT_FREQUENCY ); | |
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; | |
TIM_TimeBaseStructure.TIM_Period = ( uint16_t ) ( lpCLOCK_INPUT_FREQUENCY / configTICK_RATE_HZ ); | |
TIM_TimeBaseStructure.TIM_ClockDivision = 0; | |
TIM_TimeBaseInit( TIM2, &TIM_TimeBaseStructure ); | |
/* Enable the TIM2 interrupt. This must execute at the lowest interrupt | |
priority. */ | |
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; | |
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configLIBRARY_LOWEST_INTERRUPT_PRIORITY; /* Must be set to lowest priority. */ | |
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; | |
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; | |
NVIC_Init(&NVIC_InitStructure); | |
TIM_ITConfig( TIM2, TIM_IT_Update, ENABLE ); | |
TIM_SetCounter( TIM2, 0 ); | |
TIM_Cmd( TIM2, ENABLE ); | |
/* See the comments where xMaximumPossibleSuppressedTicks is declared. */ | |
xMaximumPossibleSuppressedTicks = ( ( unsigned long ) USHRT_MAX ) / ulReloadValueForOneTick; | |
} | |
/*-----------------------------------------------------------*/ | |
/* Override the default definition of vPortSuppressTicksAndSleep() that is | |
weakly defined in the FreeRTOS Cortex-M3 port layer with a version that manages | |
the TIM2 interrupt, as the tick is generated from TIM2 compare matches events. */ | |
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) | |
{ | |
uint32_t ulCounterValue, ulCompleteTickPeriods; | |
eSleepModeStatus eSleepAction; | |
TickType_t xModifiableIdleTime; | |
const TickType_t xRegulatorOffIdleTime = 30; | |
/* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */ | |
/* Make sure the TIM2 reload value does not overflow the counter. */ | |
if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) | |
{ | |
xExpectedIdleTime = xMaximumPossibleSuppressedTicks; | |
} | |
/* Calculate the reload value required to wait xExpectedIdleTime tick | |
periods. */ | |
ulCounterValue = ulReloadValueForOneTick * xExpectedIdleTime; | |
/* Stop TIM2 momentarily. The time TIM2 is stopped for is not accounted for | |
in this implementation (as it is in the generic implementation) because the | |
clock is so slow it is unlikely to be stopped for a complete count period | |
anyway. */ | |
TIM_Cmd( TIM2, DISABLE ); | |
/* Enter a critical section but don't use the taskENTER_CRITICAL() method as | |
that will mask interrupts that should exit sleep mode. */ | |
__asm volatile ( "cpsid i" ); | |
__asm volatile ( "dsb" ); | |
__asm volatile ( "isb" ); | |
/* The tick flag is set to false before sleeping. If it is true when sleep | |
mode is exited then sleep mode was probably exited because the tick was | |
suppressed for the entire xExpectedIdleTime period. */ | |
ulTickFlag = pdFALSE; | |
/* If a context switch is pending then abandon the low power entry as | |
the context switch might have been pended by an external interrupt that | |
requires processing. */ | |
eSleepAction = eTaskConfirmSleepModeStatus(); | |
if( eSleepAction == eAbortSleep ) | |
{ | |
/* Restart tick. */ | |
TIM_Cmd( TIM2, ENABLE ); | |
/* Re-enable interrupts - see comments above the cpsid instruction() | |
above. */ | |
__asm volatile ( "cpsie i" ); | |
} | |
else if( eSleepAction == eNoTasksWaitingTimeout ) | |
{ | |
/* A user definable macro that allows application code to be inserted | |
here. Such application code can be used to minimise power consumption | |
further by turning off IO, peripheral clocks, the Flash, etc. */ | |
configPRE_STOP_PROCESSING(); | |
/* There are no running state tasks and no tasks that are blocked with a | |
time out. Assuming the application does not care if the tick time slips | |
with respect to calendar time then enter a deep sleep that can only be | |
woken by (in this demo case) the user button being pushed on the | |
STM32L discovery board. If the application does require the tick time | |
to keep better track of the calender time then the RTC peripheral can be | |
used to make rough adjustments. */ | |
PWR_EnterSTOPMode( PWR_Regulator_LowPower, PWR_SLEEPEntry_WFI ); | |
/* A user definable macro that allows application code to be inserted | |
here. Such application code can be used to reverse any actions taken | |
by the configPRE_STOP_PROCESSING(). In this demo | |
configPOST_STOP_PROCESSING() is used to re-initialise the clocks that | |
were turned off when STOP mode was entered. */ | |
configPOST_STOP_PROCESSING(); | |
/* Restart tick. */ | |
TIM_SetCounter( TIM2, 0 ); | |
TIM_Cmd( TIM2, ENABLE ); | |
/* Re-enable interrupts - see comments above the cpsid instruction() | |
above. */ | |
__asm volatile ( "cpsie i" ); | |
__asm volatile ( "dsb" ); | |
__asm volatile ( "isb" ); | |
} | |
else | |
{ | |
/* Trap underflow before the next calculation. */ | |
configASSERT( ulCounterValue >= TIM_GetCounter( TIM2 ) ); | |
/* Adjust the TIM2 value to take into account that the current time | |
slice is already partially complete. */ | |
ulCounterValue -= ( uint32_t ) TIM_GetCounter( TIM2 ); | |
/* Trap overflow/underflow before the calculated value is written to | |
TIM2. */ | |
configASSERT( ulCounterValue < ( uint32_t ) USHRT_MAX ); | |
configASSERT( ulCounterValue != 0 ); | |
/* Update to use the calculated overflow value. */ | |
TIM_SetAutoreload( TIM2, ( uint16_t ) ulCounterValue ); | |
TIM_SetCounter( TIM2, 0 ); | |
/* Restart the TIM2. */ | |
TIM_Cmd( TIM2, ENABLE ); | |
/* Allow the application to define some pre-sleep processing. This is | |
the standard configPRE_SLEEP_PROCESSING() macro as described on the | |
FreeRTOS.org website. */ | |
xModifiableIdleTime = xExpectedIdleTime; | |
configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); | |
/* xExpectedIdleTime being set to 0 by configPRE_SLEEP_PROCESSING() | |
means the application defined code has already executed the wait/sleep | |
instruction. */ | |
if( xModifiableIdleTime > 0 ) | |
{ | |
/* The sleep mode used is dependent on the expected idle time | |
as the deeper the sleep the longer the wake up time. See the | |
comments at the top of main_low_power.c. Note xRegulatorOffIdleTime | |
is set purely for convenience of demonstration and is not intended | |
to be an optimised value. */ | |
if( xModifiableIdleTime > xRegulatorOffIdleTime ) | |
{ | |
/* A slightly lower power sleep mode with a longer wake up | |
time. */ | |
PWR_EnterSleepMode( PWR_Regulator_LowPower, PWR_SLEEPEntry_WFI ); | |
} | |
else | |
{ | |
/* A slightly higher power sleep mode with a faster wake up | |
time. */ | |
PWR_EnterSleepMode( PWR_Regulator_ON, PWR_SLEEPEntry_WFI ); | |
} | |
} | |
/* Allow the application to define some post sleep processing. This is | |
the standard configPOST_SLEEP_PROCESSING() macro, as described on the | |
FreeRTOS.org website. */ | |
configPOST_SLEEP_PROCESSING( xModifiableIdleTime ); | |
/* Stop TIM2. Again, the time the clock is stopped for in not accounted | |
for here (as it would normally be) because the clock is so slow it is | |
unlikely it will be stopped for a complete count period anyway. */ | |
TIM_Cmd( TIM2, DISABLE ); | |
/* Re-enable interrupts - see comments above the cpsid instruction() | |
above. */ | |
__asm volatile ( "cpsie i" ); | |
__asm volatile ( "dsb" ); | |
__asm volatile ( "isb" ); | |
if( ulTickFlag != pdFALSE ) | |
{ | |
/* Trap overflows before the next calculation. */ | |
configASSERT( ulReloadValueForOneTick >= ( uint32_t ) TIM_GetCounter( TIM2 ) ); | |
/* The tick interrupt has already executed, although because this | |
function is called with the scheduler suspended the actual tick | |
processing will not occur until after this function has exited. | |
Reset the reload value with whatever remains of this tick period. */ | |
ulCounterValue = ulReloadValueForOneTick - ( uint32_t ) TIM_GetCounter( TIM2 ); | |
/* Trap under/overflows before the calculated value is used. */ | |
configASSERT( ulCounterValue <= ( uint32_t ) USHRT_MAX ); | |
configASSERT( ulCounterValue != 0 ); | |
/* Use the calculated reload value. */ | |
TIM_SetAutoreload( TIM2, ( uint16_t ) ulCounterValue ); | |
TIM_SetCounter( TIM2, 0 ); | |
/* The tick interrupt handler will already have pended the tick | |
processing in the kernel. As the pending tick will be processed as | |
soon as this function exits, the tick value maintained by the tick | |
is stepped forward by one less than the time spent sleeping. The | |
actual stepping of the tick appears later in this function. */ | |
ulCompleteTickPeriods = xExpectedIdleTime - 1UL; | |
} | |
else | |
{ | |
/* Something other than the tick interrupt ended the sleep. How | |
many complete tick periods passed while the processor was | |
sleeping? */ | |
ulCompleteTickPeriods = ( ( uint32_t ) TIM_GetCounter( TIM2 ) ) / ulReloadValueForOneTick; | |
/* Check for over/under flows before the following calculation. */ | |
configASSERT( ( ( uint32_t ) TIM_GetCounter( TIM2 ) ) >= ( ulCompleteTickPeriods * ulReloadValueForOneTick ) ); | |
/* The reload value is set to whatever fraction of a single tick | |
period remains. */ | |
ulCounterValue = ( ( uint32_t ) TIM_GetCounter( TIM2 ) ) - ( ulCompleteTickPeriods * ulReloadValueForOneTick ); | |
configASSERT( ulCounterValue <= ( uint32_t ) USHRT_MAX ); | |
if( ulCounterValue == 0 ) | |
{ | |
/* There is no fraction remaining. */ | |
ulCounterValue = ulReloadValueForOneTick; | |
ulCompleteTickPeriods++; | |
} | |
TIM_SetAutoreload( TIM2, ( uint16_t ) ulCounterValue ); | |
TIM_SetCounter( TIM2, 0 ); | |
} | |
/* Restart TIM2 so it runs up to the reload value. The reload value | |
will get set to the value required to generate exactly one tick period | |
the next time the TIM2 interrupt executes. */ | |
TIM_Cmd( TIM2, ENABLE ); | |
/* Wind the tick forward by the number of tick periods that the CPU | |
remained in a low power state. */ | |
vTaskStepTick( ulCompleteTickPeriods ); | |
} | |
} | |
#endif /* configCREATE_LOW_POWER_DEMO == 1 */ | |