STM32L discovery demo is now demonstrating three low power modes - still needs clean up.
parent
9001b7b77a
commit
3d00d47239
@ -0,0 +1,385 @@
|
|||||||
|
/*
|
||||||
|
FreeRTOS V7.6.0 - Copyright (C) 2013 Real Time Engineers Ltd.
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
|
VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
|
||||||
|
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* FreeRTOS provides completely free yet professionally developed, *
|
||||||
|
* robust, strictly quality controlled, supported, and cross *
|
||||||
|
* platform software that has become a de facto standard. *
|
||||||
|
* *
|
||||||
|
* Help yourself get started quickly and support the FreeRTOS *
|
||||||
|
* project by purchasing a FreeRTOS tutorial book, reference *
|
||||||
|
* manual, or both from: http://www.FreeRTOS.org/Documentation *
|
||||||
|
* *
|
||||||
|
* Thank you! *
|
||||||
|
* *
|
||||||
|
***************************************************************************
|
||||||
|
|
||||||
|
This file is part of the FreeRTOS distribution.
|
||||||
|
|
||||||
|
FreeRTOS is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License (version 2) as published by the
|
||||||
|
Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.
|
||||||
|
|
||||||
|
>>! NOTE: The modification to the GPL is included to allow you to distribute
|
||||||
|
>>! a combined work that includes FreeRTOS without being obliged to provide
|
||||||
|
>>! the source code for proprietary components outside of the FreeRTOS
|
||||||
|
>>! kernel.
|
||||||
|
|
||||||
|
FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. Full license text is available from the following
|
||||||
|
link: http://www.freertos.org/a00114.html
|
||||||
|
|
||||||
|
1 tab == 4 spaces!
|
||||||
|
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* Having a problem? Start by reading the FAQ "My application does *
|
||||||
|
* not run, what could be wrong?" *
|
||||||
|
* *
|
||||||
|
* http://www.FreeRTOS.org/FAQHelp.html *
|
||||||
|
* *
|
||||||
|
***************************************************************************
|
||||||
|
|
||||||
|
http://www.FreeRTOS.org - Documentation, books, training, latest versions,
|
||||||
|
license and Real Time Engineers Ltd. contact details.
|
||||||
|
|
||||||
|
http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
|
||||||
|
including FreeRTOS+Trace - an indispensable productivity tool, a DOS
|
||||||
|
compatible FAT file system, and our tiny thread aware UDP/IP stack.
|
||||||
|
|
||||||
|
http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High
|
||||||
|
Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS
|
||||||
|
licenses offer ticketed support, indemnification and middleware.
|
||||||
|
|
||||||
|
http://www.SafeRTOS.com - High Integrity Systems also provide a safety
|
||||||
|
engineered and independently SIL3 certified version for use in safety and
|
||||||
|
mission critical applications that require provable dependability.
|
||||||
|
|
||||||
|
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 wakeup interrupt of the real time clock (RTC). The RTC
|
||||||
|
* configuration and handling functions are defined in this file.
|
||||||
|
*
|
||||||
|
* 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 should run. */
|
||||||
|
#define lpCLOCK_INPUT_FREQUENCY ( 1000UL )
|
||||||
|
|
||||||
|
/* Constants required to pend a PendSV interrupt from the tick ISR if the
|
||||||
|
preemptive scheduler is being used. These are just standard bits and registers
|
||||||
|
within the Cortex-M core itself. */
|
||||||
|
#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL )
|
||||||
|
|
||||||
|
#define DBGMCU_APB1_FZ ( * ( ( volatile unsigned long * ) 0xE0042008 ) )
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The tick interrupt is generated by the TIM2 timer. The default interrupt
|
||||||
|
* handler cannot be used (even with the TIM2 being handled from the tick hook
|
||||||
|
* function) because the default tick interrupt accesses the SysTick registers
|
||||||
|
* when configUSE_TICKLESS_IDLE set to 1. TIM2_IRQHandler() is the default name
|
||||||
|
* for the TIM2 interrupt handler.
|
||||||
|
*/
|
||||||
|
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 portTickType xMaximumPossibleSuppressedTicks = 0;
|
||||||
|
|
||||||
|
/* Flag set from the tick interrupt to allow the sleep processing to know if
|
||||||
|
sleep mode was exited because of an RTC interrupt or a different interrupt. */
|
||||||
|
static volatile uint32_t ulTickFlag = pdFALSE;
|
||||||
|
|
||||||
|
/* The RTC counter is stopped temporarily each time it is re-programmed. The
|
||||||
|
following variable offsets the RTC counter alarm value by the number of RTC
|
||||||
|
counts that would typically be missed while the counter was stopped to
|
||||||
|
compensate for the lost time. _RB_ Value needs calculating correctly. */
|
||||||
|
static uint32_t ulStoppedTimerCompensation = 1;//_RB_ / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* 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 )
|
||||||
|
{
|
||||||
|
TIM_ClearITPendingBit( TIM2, TIM_IT_Update );
|
||||||
|
TIM_SetAutoreload( TIM2, ( uint16_t ) ulReloadValueForOneTick );
|
||||||
|
|
||||||
|
/* Protect incrementing the tick with an interrupt safe critical section. */
|
||||||
|
( 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 );
|
||||||
|
|
||||||
|
/* 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 the
|
||||||
|
wakeup timer of the RTC to generate the tick interrupt. */
|
||||||
|
void vPortSetupTimerInterrupt( void )
|
||||||
|
{
|
||||||
|
NVIC_InitTypeDef NVIC_InitStructure;
|
||||||
|
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
|
||||||
|
|
||||||
|
/* Enable the TIM2 clock, which is used to generate long tickless periods
|
||||||
|
when the tickless period is finite. */
|
||||||
|
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 very long tickless periods can be acheived. 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 - used for the tick interrupt when the tickless
|
||||||
|
period is finite. */
|
||||||
|
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( portTickType xExpectedIdleTime )
|
||||||
|
{
|
||||||
|
uint32_t ulCounterValue, ulCompleteTickPeriods;
|
||||||
|
eSleepModeStatus eSleepAction;
|
||||||
|
portTickType xModifiableIdleTime;
|
||||||
|
const portTickType 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;
|
||||||
|
if( ulCounterValue > ulStoppedTimerCompensation )
|
||||||
|
{
|
||||||
|
/* Compensate for the fact that TIM2 is going to be stopped
|
||||||
|
momentarily. */
|
||||||
|
ulCounterValue -= ulStoppedTimerCompensation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop TIM2 momentarily. The time TIM2 is stopped for is accounted for as
|
||||||
|
best it can be, but using the tickless mode will inevitably result in some
|
||||||
|
tiny drift of the time maintained by the kernel with respect to calendar
|
||||||
|
time. */
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
/* 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. */
|
||||||
|
configPRE_STOP_PROCESSING();
|
||||||
|
PWR_EnterSTOPMode( PWR_Regulator_LowPower, PWR_SLEEPEntry_WFI );
|
||||||
|
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" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Adjust the TIM2 value to take into account that the current time
|
||||||
|
slice is already partially complete. */
|
||||||
|
configASSERT( ulCounterValue >= TIM_GetCounter( TIM2 ) );
|
||||||
|
ulCounterValue -= ( uint32_t ) TIM_GetCounter( TIM2 );
|
||||||
|
configASSERT( ulCounterValue < ( uint32_t ) USHRT_MAX );
|
||||||
|
configASSERT( ulCounterValue != 0 );
|
||||||
|
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. */
|
||||||
|
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
|
||||||
|
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. */
|
||||||
|
if( xModifiableIdleTime > xRegulatorOffIdleTime )
|
||||||
|
{
|
||||||
|
/* A slightly lower power sleep mode with a longer wake up
|
||||||
|
time. */
|
||||||
|
PWR_EnterSleepMode( PWR_Regulator_LowPower, PWR_SLEEPEntry_WFI );
|
||||||
|
}
|
||||||
|
else if( pdTRUE )
|
||||||
|
{
|
||||||
|
/* 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. */
|
||||||
|
configPOST_SLEEP_PROCESSING( xModifiableIdleTime );
|
||||||
|
|
||||||
|
/* Stop TIM2. Again, the time the SysTick is stopped for is accounted
|
||||||
|
for as best it can be, but using the tickless mode will inevitably
|
||||||
|
result in some tiny drift of the time maintained by the kernel with
|
||||||
|
respect to calendar time. */
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
/* 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. */
|
||||||
|
configASSERT( ulReloadValueForOneTick >= ( uint32_t ) TIM_GetCounter( TIM2 ) );
|
||||||
|
ulCounterValue = ulReloadValueForOneTick - ( uint32_t ) TIM_GetCounter( TIM2 );
|
||||||
|
configASSERT( ulCounterValue <= ( uint32_t ) USHRT_MAX );
|
||||||
|
configASSERT( ulCounterValue != 0 );
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* The reload value is set to whatever fraction of a single tick
|
||||||
|
period remains. */
|
||||||
|
configASSERT( ( ( uint32_t ) TIM_GetCounter( TIM2 ) ) >= ( ulCompleteTickPeriods * ulReloadValueForOneTick ) );
|
||||||
|
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 */
|
||||||
|
|
Loading…
Reference in New Issue