| /* | |
| * 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" | |
| /* Library includes. */ | |
| #include "htimer.h" | |
| /* This file contains functions that will override the default implementations | |
| in the RTOS port layer. Therefore only build this file if the low power demo | |
| is being built. */ | |
| #if( configCREATE_LOW_POWER_DEMO == 1 ) | |
| /* ID of the hibernation timer used to generate the tick. */ | |
| #define mainTICK_HTIMER_ID 0 | |
| /* Written to the hibernation timer control register to configure the timer for | |
| its higher resolution. */ | |
| #define mainHTIMER_HIGH_RESOLUTION 0 | |
| /* The frequency of the hibernation timer when it is running at its higher | |
| resolution and low resolution respectively. */ | |
| #define mainHIGHER_RESOLUTION_TIMER_HZ ( 32787 ) /* (1000000us / 30.5us) as each LSB is 30.5us. */ | |
| #define mainLOW_RESOLUTION_TIMER_HZ ( 8UL ) /* ( 1000ms / 125ms ) as each LSB is 0.125s. */ | |
| /* Some registers are accessed directly as the library is not compatible with | |
| all the compilers used. */ | |
| #define lpHTIMER_PRELOAD_REGISTER ( * ( volatile uint16_t * ) 0x40009800 ) | |
| #define lpHTIMER_COUNT_REGISTER ( * ( volatile uint16_t * ) 0x40009808 ) | |
| #define lpEC_GIRQ17_ENABLE_SET ( * ( volatile uint32_t * ) 0x4000C0B8 ) | |
| #define lpHTIMER_INTERRUPT_CONTROL_BIT ( 1UL << 20UL ) | |
| /* | |
| * The low power demo does not use the SysTick, so override the | |
| * vPortSetupTickInterrupt() function with an implementation that configures | |
| * the low power clock. NOTE: This function name must not be changed as it | |
| * is called from the RTOS portable layer. | |
| */ | |
| void vPortSetupTimerInterrupt( void ); | |
| /*-----------------------------------------------------------*/ | |
| /* The reload value to use in the timer to generate the tick interrupt - | |
| assumes the timer is running at its higher resolution. */ | |
| static const uint16_t usHighResolutionReloadValue = ( mainHIGHER_RESOLUTION_TIMER_HZ / ( uint16_t ) configTICK_RATE_HZ ); | |
| /* Calculate how many clock increments make up a single tick period. */ | |
| static const uint32_t ulReloadValueForOneHighResolutionTick = ( mainHIGHER_RESOLUTION_TIMER_HZ / configTICK_RATE_HZ ); | |
| /* Calculate the maximum number of ticks that can be suppressed when using the | |
| high resolution clock and low resolution clock respectively. */ | |
| static uint32_t ulMaximumPossibleSuppressedHighResolutionTicks = 0; | |
| /* As the clock is only 2KHz, it is likely a value of 1 will be too much, so | |
| use zero - but leave the value here to assist porting to different clock | |
| speeds. */ | |
| static const uint32_t ulStoppedTimerCompensation = 0UL; | |
| /* Flag set from the tick interrupt to allow the sleep processing to know if | |
| sleep mode was exited because of an timer interrupt or a different interrupt. */ | |
| static volatile uint32_t ulTickFlag = pdFALSE; | |
| /*-----------------------------------------------------------*/ | |
| void NVIC_Handler_HIB_TMR( void ) iv IVT_INT_HTIMER ics ICS_AUTO | |
| { | |
| lpHTIMER_PRELOAD_REGISTER = usHighResolutionReloadValue; | |
| /* Increment the RTOS tick. */ | |
| if( xTaskIncrementTick() != pdFALSE ) | |
| { | |
| /* A context switch is required. Context switching is performed in | |
| the PendSV interrupt. Pend the PendSV interrupt. */ | |
| portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; | |
| } | |
| /* The CPU woke because of a tick. */ | |
| ulTickFlag = pdTRUE; | |
| } | |
| /*-----------------------------------------------------------*/ | |
| void vPortSetupTimerInterrupt( void ) | |
| { | |
| /* Cannot be a const when using the MikroC compiler. */ | |
| ulMaximumPossibleSuppressedHighResolutionTicks = ( ( uint32_t ) USHRT_MAX ) / ulReloadValueForOneHighResolutionTick; | |
| /* Set up the hibernation timer to start at the value required by the | |
| tick interrupt. */ | |
| htimer_enable( mainTICK_HTIMER_ID, usHighResolutionReloadValue, mainHTIMER_HIGH_RESOLUTION ); | |
| /* Enable the HTIMER interrupt. Equivalent to enable_htimer0_irq(); */ | |
| lpEC_GIRQ17_ENABLE_SET |= lpHTIMER_INTERRUPT_CONTROL_BIT; | |
| /* The hibernation timer is not an auto-reload timer, so gets reset | |
| from within the ISR itself. For that reason it's interrupt is set | |
| to the highest possible priority to ensure clock slippage is minimised. */ | |
| NVIC_SetIntPriority( IVT_INT_HTIMER, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ); | |
| NVIC_IntEnable( IVT_INT_HTIMER ); | |
| } | |
| /*-----------------------------------------------------------*/ | |
| /* Override the default definition of vPortSuppressTicksAndSleep() that is | |
| weakly defined in the FreeRTOS Cortex-M port layer with a version that manages | |
| the hibernation timer, as the tick is generated from the low power hibernation | |
| timer and not the SysTick as would normally be the case on a Cortex-M. */ | |
| void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) | |
| { | |
| uint32_t ulCompleteTickPeriods, ulReloadValue, ulCompletedTimerDecrements, ulCountAfterSleep, ulCountBeforeSleep; | |
| eSleepModeStatus eSleepAction; | |
| TickType_t xModifiableIdleTime; | |
| /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */ | |
| /* Make sure the hibernation timer reload value does not overflow the | |
| counter. */ | |
| if( xExpectedIdleTime > ( TickType_t ) ulMaximumPossibleSuppressedHighResolutionTicks ) | |
| { | |
| xExpectedIdleTime = ( TickType_t ) ulMaximumPossibleSuppressedHighResolutionTicks; | |
| } | |
| /* Stop the timer momentarily. The time the timer 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. Take the count value first as clearing | |
| the preload value also seems to clear the count. */ | |
| ulCountBeforeSleep = ( uint32_t ) lpHTIMER_COUNT_REGISTER; | |
| lpHTIMER_PRELOAD_REGISTER = 0; | |
| /* Calculate the reload value required to wait xExpectedIdleTime tick | |
| periods. -1 is used as the current time slice will already be part way | |
| through, the part value coming from the current timer count value. */ | |
| ulReloadValue = ulCountBeforeSleep + ( ulReloadValueForOneHighResolutionTick * ( xExpectedIdleTime - 1UL ) ); | |
| if( ulReloadValue > ulStoppedTimerCompensation ) | |
| { | |
| /* Compensate for the fact that the timer is going to be stopped | |
| momentarily. */ | |
| ulReloadValue -= ulStoppedTimerCompensation; | |
| } | |
| /* Enter a critical section but don't use the taskENTER_CRITICAL() method as | |
| that will mask interrupts that should exit sleep mode. */ | |
| __asm { cpsid i | |
| dsb | |
| 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 ) | |
| { | |
| /* Resetart the timer from whatever remains in the counter register, | |
| but 0 is not a valid value. */ | |
| ulReloadValue = ulCountBeforeSleep - ulStoppedTimerCompensation; | |
| if( ulReloadValue == 0 ) | |
| { | |
| ulReloadValue = ulReloadValueForOneHighResolutionTick; | |
| ulCompleteTickPeriods = 1UL; | |
| } | |
| else | |
| { | |
| ulCompleteTickPeriods = 0UL; | |
| } | |
| lpHTIMER_PRELOAD_REGISTER = ( uint16_t ) ulReloadValue; | |
| /* Re-enable interrupts - see comments above the cpsid instruction() | |
| above. */ | |
| __asm { cpsie i | |
| dsb | |
| isb }; | |
| } | |
| else | |
| { | |
| /* Write the calculated reload value, which will also start the | |
| timer. */ | |
| lpHTIMER_PRELOAD_REGISTER = ( uint16_t ) ulReloadValue; | |
| /* 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 sleep | |
| instructions. */ | |
| if( xModifiableIdleTime > 0 ) | |
| { | |
| __asm { dsb | |
| wfi | |
| isb }; | |
| } | |
| /* Allow the application to define some post sleep processing. */ | |
| configPOST_SLEEP_PROCESSING( xModifiableIdleTime ); | |
| /* Stop the hibernation timer. Again, the time the tiemr 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. Take the count value first as | |
| setting the preload to zero also seems to clear the count. */ | |
| ulCountAfterSleep = ( uint32_t ) lpHTIMER_COUNT_REGISTER; | |
| lpHTIMER_PRELOAD_REGISTER = 0; | |
| /* Re-enable interrupts - see comments above the cpsid instruction() | |
| above. */ | |
| __asm { cpsie i | |
| dsb | |
| 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. | |
| The timer has already been reloaded to count in ticks, and can just | |
| continue counting down from its current value. */ | |
| ulReloadValue = ulCountAfterSleep; | |
| /* Sanity check that the timer's reload value has indeed been | |
| reset. */ | |
| configASSERT( ( uint32_t ) lpHTIMER_PRELOAD_REGISTER == ulReloadValueForOneHighResolutionTick ); | |
| /* 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? */ | |
| ulCompletedTimerDecrements = ulReloadValue - ulCountAfterSleep; | |
| /* Undo the adjustment that was made to the reload value to account | |
| for the fact that a time slice was part way through when this | |
| function was called before working out how many complete tick | |
| periods this represents. (could have used [ulExpectedIdleTime * | |
| ulReloadValueForOneHighResolutionTick] instead of ulReloadValue on | |
| the previous line, but this way avoids the multiplication). */ | |
| ulCompletedTimerDecrements += ( ulReloadValueForOneHighResolutionTick - ulCountBeforeSleep ); | |
| ulCompleteTickPeriods = ulCompletedTimerDecrements / ulReloadValueForOneHighResolutionTick; | |
| /* The reload value is set to whatever fraction of a single tick | |
| period remains. */ | |
| ulReloadValue = ( ( ulCompleteTickPeriods + 1UL ) * ulReloadValueForOneHighResolutionTick ) - ulCompletedTimerDecrements; | |
| } | |
| /* Cannot use a reload value of 0 - it will not start the timer. */ | |
| if( ulReloadValue == 0 ) | |
| { | |
| /* There is no fraction remaining. */ | |
| ulReloadValue = ulReloadValueForOneHighResolutionTick; | |
| ulCompleteTickPeriods++; | |
| } | |
| /* Restart the timer so it runs down from the reload value. The reload | |
| value will get set to the value required to generate exactly one tick | |
| period the next time the tick interrupt executes. */ | |
| lpHTIMER_PRELOAD_REGISTER = ( uint16_t ) ulReloadValue; | |
| } | |
| /* 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 */ | |