void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { TickType_t wakeupTime; /* Make sure the SysTick reload value does not overflow the counter. */ if( xExpectedIdleTime > portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) { xExpectedIdleTime = portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP; } /* Block the scheduler now */ portDISABLE_INTERRUPTS(); /* Stop tick events */ nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK); /* Configure CTC interrupt */ wakeupTime = nrf_rtc_counter_get(portNRF_RTC_REG) + xExpectedIdleTime; wakeupTime &= portNRF_RTC_MAXTICKS; nrf_rtc_cc_set(portNRF_RTC_REG, 0, wakeupTime); nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_COMPARE_0); nrf_rtc_int_enable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK); if( eTaskConfirmSleepModeStatus() == eAbortSleep ) { portENABLE_INTERRUPTS(); } else { TickType_t xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); if( xModifiableIdleTime > 0 ) { __DSB(); #ifdef SOFTDEVICE_PRESENT /* With SD there is no problem with possibility of interrupt lost. * every interrupt is counted and the counter is processed inside * sd_app_evt_wait function. */ portENABLE_INTERRUPTS(); sd_app_evt_wait(); #else /* No SD - we would just block interrupts globally. * BASEPRI cannot be used for that because it would prevent WFE from wake up. */ __disable_irq(); portENABLE_INTERRUPTS(); do{ __WFE(); } while(0 == (NVIC->ISPR[0] | NVIC->ISPR[1])); __enable_irq(); #endif } configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); portENABLE_INTERRUPTS(); } // We can do operations below safely, because when we are inside vPortSuppressTicksAndSleep // scheduler is already suspended. nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK); nrf_rtc_int_enable (portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK); }
/* Override the default definition of vPortSuppressTicksAndSleep() that is weakly defined in the FreeRTOS Cortex-M3 port layet with a version that manages the asynchronous timer (AST), as the tick is generated from the low power AST and not the SysTick as would normally be the case on a Cortex-M. */ void vPortSuppressTicksAndSleep( portTickType xExpectedIdleTime ) { uint32_t ulAlarmValue, ulCompleteTickPeriods; eSleepModeStatus eSleepAction; portTickType xModifiableIdleTime; enum sleepmgr_mode xSleepMode; /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */ /* Make sure the AST reload value does not overflow the counter. */ if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) { xExpectedIdleTime = xMaximumPossibleSuppressedTicks; } /* Calculate the reload value required to wait xExpectedIdleTime tick periods. */ ulAlarmValue = ulAlarmValueForOneTick * xExpectedIdleTime; if( ulAlarmValue > ulStoppedTimerCompensation ) { /* Compensate for the fact that the AST is going to be stopped momentarily. */ ulAlarmValue -= ulStoppedTimerCompensation; } /* Stop the AST momentarily. The time the AST 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. */ prvDisableAST(); /* 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 \n\t" "dsb \n\t" ); /* 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. */ prvEnableAST(); /* Re-enable interrupts - see comments above the cpsid instruction() above. */ __asm volatile( "cpsie i" ); }
__attribute__((weak)) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) { xExpectedIdleTime = xMaximumPossibleSuppressedTicks; } /* Stop the SysTick momentarily. 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. */ portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT; /* Calculate the reload value required to wait xExpectedIdleTime tick periods. -1 is used because this code will execute part way through one of the tick periods. */ ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); if( ulReloadValue > ulStoppedTimerCompensation ) { 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 volatile( "cpsid i" ); __asm volatile( "dsb" ); __asm volatile( "isb" ); /* If a context switch is pending or a task is waiting for the scheduler to be unsuspended then abandon the low power entry. */ if( eTaskConfirmSleepModeStatus() == eAbortSleep ) { /* Restart from whatever is left in the count register to complete this tick period. */ portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; /* Restart SysTick. */ portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; /* Reset the reload register to the value required for normal tick periods. */ portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Re-enable interrupts - see comments above the cpsid instruction() above. */ __asm volatile( "cpsie i" ); }
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { uint32_t ulReloadValue, ulCompleteTickPeriods; TickType_t xModifiableIdleTime; LPTMR_Type *pxLptmrBase; pxLptmrBase = vPortGetLptrmBase(); if (pxLptmrBase == 0) return; /* Make sure the SysTick reload value does not overflow the counter. */ if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) { xExpectedIdleTime = xMaximumPossibleSuppressedTicks; } if (xExpectedIdleTime == 0) return; /* Calculate the reload value required to wait xExpectedIdleTime tick periods. -1 is used because this code will execute part way through one of the tick periods. */ ulReloadValue = LPTMR_GetCurrentTimerCount(pxLptmrBase) + ( ulLPTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); /* Stop the LPTMR and systick momentarily. The time the LPTMR and 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. */ LPTMR_StopTimer(pxLptmrBase); *(portNVIC_SYSTICK_CTRL) &= ~portNVIC_SYSTICK_ENABLE_BIT; /* 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" ); /* If a context switch is pending or a task is waiting for the scheduler to be unsuspended then abandon the low power entry. */ if( eTaskConfirmSleepModeStatus() == eAbortSleep ) { /* Restart from whatever is left in the count register to complete this tick period. */ *(portNVIC_SYSTICK_LOAD) = portNVIC_SYSTICK_CURRENT_VALUE_REG; /* Restart SysTick. */ *(portNVIC_SYSTICK_CTRL) |= portNVIC_SYSTICK_ENABLE_BIT; /* Reset the reload register to the value required for normal tick periods. */ *(portNVIC_SYSTICK_LOAD) = ulTimerCountsForOneTick - 1UL; /* Re-enable interrupts - see comments above __disable_interrupt() call above. */ __asm volatile( "cpsie i" ); }
/* 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" ); }
__attribute__((weak)) void vPortSuppressTicksAndSleep( portTickType xExpectedIdleTime ) { unsigned long ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickIncrements; portTickType xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) { xExpectedIdleTime = xMaximumPossibleSuppressedTicks; } /* Calculate the reload value required to wait xExpectedIdleTime tick periods. -1 is used because this code will execute part way through one of the tick periods, and the fraction of a tick period is accounted for later. */ ulReloadValue = ( ulTimerReloadValueForOneTick * ( xExpectedIdleTime - 1UL ) ); if( ulReloadValue > ulStoppedTimerCompensation ) { ulReloadValue -= ulStoppedTimerCompensation; } /* Stop the SysTick momentarily. 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. */ portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT; /* Adjust the reload value to take into account that the current time slice is already partially complete. */ ulReloadValue += ( portNVIC_SYSTICK_LOAD_REG - ( portNVIC_SYSTICK_LOAD_REG - portNVIC_SYSTICK_CURRENT_VALUE_REG ) ); /* 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" ); /* If a context switch is pending or a task is waiting for the scheduler to be unsuspended then abandon the low power entry. */ if( eTaskConfirmSleepModeStatus() == eAbortSleep ) { /* Restart SysTick. */ portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; /* Re-enable interrupts - see comments above the cpsid instruction() above. */ __asm volatile( "cpsie i" ); }
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { TickType_t wakeupTime; /* Make sure the SysTick reload value does not overflow the counter. */ if( xExpectedIdleTime > portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) { xExpectedIdleTime = portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP; } /* Block the scheduler now */ portDISABLE_INTERRUPTS(); /* Stop tick events */ nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK); /* Configure CTC interrupt */ wakeupTime = nrf_rtc_counter_get(portNRF_RTC_REG) + xExpectedIdleTime; wakeupTime &= portNRF_RTC_MAXTICKS; nrf_rtc_cc_set(portNRF_RTC_REG, 0, wakeupTime); nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_COMPARE_0); nrf_rtc_int_enable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK); if( eTaskConfirmSleepModeStatus() == eAbortSleep ) { portENABLE_INTERRUPTS(); } else { TickType_t xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); if( xModifiableIdleTime > 0 ) { __DSB(); do{ __WFE(); } while(0 == (NVIC->ISPR[0])); } configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); portENABLE_INTERRUPTS(); } // We can do operations below safely, because when we are inside vPortSuppressTicksAndSleep // scheduler is already suspended. nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK); nrf_rtc_int_enable (portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK); }
/**************************************************************************//** * @brief vPortSetupTimerInterrupt * Override the default definition of vPortSuppressTicksAndSleep() that is weakly * defined in the FreeRTOS Cortex-M3 port layer layer *****************************************************************************/ void vPortSuppressTicksAndSleep(portTickType xExpectedIdleTime) { unsigned long ulReloadValue, ulCompleteTickPeriods; portTickType xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ if (xExpectedIdleTime > xMaximumPossibleSuppressedTicks) { xExpectedIdleTime = xMaximumPossibleSuppressedTicks; } /* Calculate the reload value required to wait xExpectedIdleTime * tick periods. -1 is used because this code will execute part way * through one of the tick periods, and the fraction of a tick period is * accounted for later. */ ulReloadValue = (ulTimerReloadValueForOneTick * (xExpectedIdleTime )); if (ulReloadValue > ulStoppedTimerCompensation) { ulReloadValue -= ulStoppedTimerCompensation; } /* Stop the SysTick momentarily. 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. */ /* Stop the RTC clock*/ BURTC_Enable(false); /* Enter a critical section but don't use the taskENTER_CRITICAL() * method as that will mask interrupts that should exit sleep mode. */ INT_Disable(); /* 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. */ intTickFlag = false; /* If a context switch is pending or a task is waiting for the scheduler * to be unsuspended then abandon the low power entry. */ if (eTaskConfirmSleepModeStatus() == eAbortSleep) { BURTC_Enable(true); /* Re-enable interrupts */ INT_Enable(); } else { /* Set the new reload value. */ ulReloadValue -= BURTC_CounterGet(); BURTC_CompareSet(0, ulReloadValue); /* Restart the counter*/ BURTC_CounterReset(); /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can * set its parameter to 0 to indicate that its implementation contains * its own wait for interrupt or wait for event instruction, and so wfi * should not be executed again. However, the original expected idle * time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING(xModifiableIdleTime); if (xModifiableIdleTime > 0) { SLEEP_Sleep(); __DSB(); __ISB(); } configPOST_SLEEP_PROCESSING(xExpectedIdleTime); /* Stop SysTick. 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. */ BURTC_Enable(false); /* Re-enable interrupts - see comments above __disable_interrupt() * call above. */ INT_Enable(); if (intTickFlag != false) { /* The tick interrupt has already executed, * Reset the alarm value with whatever remains of this tick period. */ BURTC_CompareSet(0, TIMER_CAPACITY & (ulTimerReloadValueForOneTick - BURTC_CounterGet())); /* 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 waiting. */ ulCompleteTickPeriods = xExpectedIdleTime - 1UL; } else { /* Some other interrupt than system tick ended the sleep. * Calculate how many tick periods passed while the processor * was waiting */ ulCompleteTickPeriods = BURTC_CounterGet() / ulTimerReloadValueForOneTick; /* The reload value is set to whatever fraction of a single tick * period remains. */ if (ulCompleteTickPeriods == 0) { ulReloadValue = ulTimerReloadValueForOneTick - BURTC_CounterGet(); } else { ulReloadValue = BURTC_CounterGet() - (ulCompleteTickPeriods * ulTimerReloadValueForOneTick); } BURTC_CompareSet(0, ulReloadValue); } /* Restart the RTCounter*/ BURTC_CounterReset(); /* The tick forward by the number of tick periods that * remained in a low power state. */ vTaskStepTick(ulCompleteTickPeriods); } }
__attribute__((weak)) void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime) { unsigned long ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickIncrements; TickCounter_t tmp; /* because of how we get the current tick counter */ bool tickISRfired; /* Make sure the tick timer reload value does not overflow the counter. */ if(xExpectedIdleTime>xMaximumPossibleSuppressedTicks) { xExpectedIdleTime = xMaximumPossibleSuppressedTicks; } /* Stop the tick timer momentarily. The time the counter 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. */ DISABLE_TICK_COUNTER(); /* Calculate the reload value required to wait xExpectedIdleTime * tick periods. -1 is used because this code will execute part way * through one of the tick periods. */ GET_TICK_CURRENT_VAL(&tmp); ulReloadValue = tmp+(UL_TIMER_COUNTS_FOR_ONE_TICK*(xExpectedIdleTime-1UL)); if (ulReloadValue>ulStoppedTimerCompensation) { ulReloadValue -= ulStoppedTimerCompensation; } /* Enter a critical section but don't use the taskENTER_CRITICAL() * method as that will mask interrupts that should exit sleep mode. */ TICKLESS_DISABLE_INTERRUPTS(); /* If a context switch is pending or a task is waiting for the scheduler * to be unsuspended then abandon the low power entry. */ if (eTaskConfirmSleepModeStatus()==eAbortSleep) { ENABLE_TICK_COUNTER(); /* Restart SysTick. */ TICKLESS_ENABLE_INTERRUPTS(); } else { #if configUSE_LP_TIMER DisableDevice(); ClearInterruptFlag(); WriteCompareReg(xExpectedIdleTime-1); EnableDevice(); /* start timer */ #else SET_TICK_DURATION(ulReloadValue); /* Set the new reload value. */ RESET_TICK_COUNTER_VAL(); /* Reset the counter. */ ENABLE_TICK_COUNTER(); /* Restart tick timer. */ TICK_INTERRUPT_FLAG_RESET(); /* reset flag so we know later if it has fired */ #endif /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can * set its parameter to 0 to indicate that its implementation contains * its own wait for interrupt or wait for event instruction, and so wfi * should not be executed again. However, the original expected idle * time variable must remain unmodified, so a copy is taken. */ /* CPU *HAS TO WAIT* in the sequence below for an interrupt. If vOnPreSleepProcessing() is not used, a default implementation is provided */ /* default wait/sleep code */ __asm volatile("dsb"); __asm volatile("wfi"); __asm volatile("isb"); /* ---------------------------------------------------------------------------- * Here the CPU *HAS TO BE* low power mode, waiting to wake up by an interrupt * ----------------------------------------------------------------------------*/ /* Stop tick counter. Again, the time the tick counter 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. */ tickISRfired = TICK_INTERRUPT_HAS_FIRED(); /* need to check Interrupt flag here, as might be modified below */ DISABLE_TICK_COUNTER(); TICKLESS_ENABLE_INTERRUPTS();/* Re-enable interrupts */ if (tickISRfired) { /* The tick interrupt has already executed, and the timer * count reloaded with the modulo/match value. * Reset the counter register with whatever remains of * this tick period. */ GET_TICK_CURRENT_VAL(&tmp); #if COUNTS_UP SET_TICK_DURATION((UL_TIMER_COUNTS_FOR_ONE_TICK-1UL)-tmp); #else SET_TICK_DURATION((UL_TIMER_COUNTS_FOR_ONE_TICK-1UL)-(ulReloadValue-tmp)); #endif /* 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 waiting. */ ulCompleteTickPeriods = xExpectedIdleTime-1UL; } else { /* Something other than the tick interrupt ended the sleep. * Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part ticks). */ GET_TICK_CURRENT_VAL(&tmp); ulCompletedSysTickIncrements = (xExpectedIdleTime*UL_TIMER_COUNTS_FOR_ONE_TICK)-tmp; /* How many complete tick periods passed while the processor was waiting? */ ulCompleteTickPeriods = ulCompletedSysTickIncrements/UL_TIMER_COUNTS_FOR_ONE_TICK; /* The reload value is set to whatever fraction of a single tick period remains. */ SET_TICK_DURATION(((ulCompleteTickPeriods+1)*UL_TIMER_COUNTS_FOR_ONE_TICK)-ulCompletedSysTickIncrements); } /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again, then set portNVIC_SYSTICK_LOAD_REG back to its standard value. The critical section is used to ensure the tick interrupt can only execute once in the case that the reload register is near zero. */ RESET_TICK_COUNTER_VAL(); portENTER_CRITICAL(); { ENABLE_TICK_COUNTER(); vTaskStepTick(ulCompleteTickPeriods); SET_TICK_DURATION(UL_TIMER_COUNTS_FOR_ONE_TICK-1UL); } portEXIT_CRITICAL(); } }
void prvSystemSleep( TickType_t xExpectedIdleTime ) { uint32_t ulSleepTime; eSleepModeStatus eSleepStatus; /* A simple WFI() is executed in any of the cases below: * 1. the system has just booted and the dg_configINITIAL_SLEEP_DELAY_TIME has not yet * passed * 2. the XTAL32K is used as the LP clock, the system has just woke up after clockless * sleep and the LP clock has not yet settled. */ if( !cm_lp_clk_is_avail() ) { __WFI(); // Wait for an interrupt... return; } if (dg_configUSE_LP_CLK == LP_CLK_RCX) { // Update if a previous calibration was running and is finished. if (cm_rcx_calibration_is_on) { if (cm_calibrate_rcx_update()) { return; } } } /* * Calculate the sleep time */ ulSleepTime = pm_conv_ticks_2_prescaled_lpcycles(xExpectedIdleTime); /* Enter a critical section that will not effect interrupts bringing the MCU * out of sleep mode. */ taskDISABLE_INTERRUPTS(); DBG_CONFIGURE_LOW(CMN_TIMING_DEBUG, CMNDBG_CRITICAL_SECTION); DBG_SET_HIGH(CPM_USE_TIMING_DEBUG, CPMDBG_SLEEP_ENTER); /* Ensure it is still ok to enter the sleep mode. */ eSleepStatus = eTaskConfirmSleepModeStatus(); if( eSleepStatus == eAbortSleep ) { DBG_SET_LOW(CPM_USE_TIMING_DEBUG, CPMDBG_SLEEP_ENTER); /* A task has been moved out of the Blocked state since this macro was * executed, or a context switch is being held pending. Do not enter a * sleep state. Restart the tick and exit the critical section. */ taskENABLE_INTERRUPTS(); } else { #if (dg_configIMAGE_SETUP == DEVELOPMENT_MODE) uint32_t primask; #endif if( eSleepStatus == eNoTasksWaitingTimeout ) { /* It is not necessary to configure an interrupt to bring the * microcontroller out of its low power state at a fixed time in the * future. * Enter the low power state. */ pm_sleep_enter( 0 ); } else { /* Configure an interrupt to bring the microcontroller out of its low * power state at the time the kernel next needs to execute. * Enter the low power state. */ pm_sleep_enter( ulSleepTime ); } #if (dg_configIMAGE_SETUP == DEVELOPMENT_MODE) /* If the code stops at this point then the interrupts were enabled while they * shouldn't be so. */ primask = __get_PRIMASK(); ASSERT_WARNING(primask == 1); #endif /* Wake-up! */ pm_system_wake_up(); } }
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { /* * Implementation note: * * To help debugging the option configUSE_TICKLESS_IDLE_SIMPLE_DEBUG was presented. * This option would make sure that even if program execution was stopped inside * this function no more than expected number of ticks would be skipped. * * Normally RTC works all the time even if firmware execution was stopped * and that may lead to skipping too much of ticks. */ TickType_t enterTime; /* Make sure the SysTick reload value does not overflow the counter. */ if ( xExpectedIdleTime > portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) { xExpectedIdleTime = portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP; } /* Block the scheduler now */ portDISABLE_INTERRUPTS(); /* Configure CTC interrupt */ enterTime = nrf_rtc_counter_get(portNRF_RTC_REG); if ( eTaskConfirmSleepModeStatus() == eAbortSleep ) { portENABLE_INTERRUPTS(); } else { TickType_t xModifiableIdleTime; TickType_t wakeupTime = (enterTime + xExpectedIdleTime) & portNRF_RTC_MAXTICKS; /* Stop tick events */ nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK); /* Configure CTC interrupt */ nrf_rtc_cc_set(portNRF_RTC_REG, 0, wakeupTime); nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_COMPARE_0); nrf_rtc_int_enable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK); __DSB(); /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can * set its parameter to 0 to indicate that its implementation contains * its own wait for interrupt or wait for event instruction, and so wfi * should not be executed again. However, the original expected idle * time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); if ( xModifiableIdleTime > 0 ) { #ifdef SOFTDEVICE_PRESENT sd_app_evt_wait(); #else do{ __WFE(); } while (0 == (NVIC->ISPR[0])); #endif } configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK); portENABLE_INTERRUPTS(); /* Correct the system ticks */ portENTER_CRITICAL(); { TickType_t diff; TickType_t hwTicks = nrf_rtc_counter_get(portNRF_RTC_REG); nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_TICK); nrf_rtc_int_enable (portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK); if(enterTime > hwTicks) { hwTicks += portNRF_RTC_MAXTICKS + 1U; } diff = (hwTicks - enterTime); if((configUSE_TICKLESS_IDLE_SIMPLE_DEBUG) && (diff > xExpectedIdleTime)) { diff = xExpectedIdleTime; } if (diff > 0) { vTaskStepTick(diff); } } portEXIT_CRITICAL(); } }
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { uint32_t ulReloadValue, ulCompleteTickPeriods, ulCountAfterSleep; eSleepModeStatus eSleepAction; TickType_t xModifiableIdleTime; /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */ /* Make sure the RTC reload value does not overflow the counter. */ if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) { xExpectedIdleTime = xMaximumPossibleSuppressedTicks; } /* Calculate the reload value required to wait xExpectedIdleTime tick periods. */ ulReloadValue = ulReloadValueForOneTick * xExpectedIdleTime; if( ulReloadValue > ulStoppedTimerCompensation ) { /* Compensate for the fact that the RTC is going to be stopped momentarily. */ ulReloadValue -= ulStoppedTimerCompensation; } /* Stop the RTC momentarily. The time the RTC 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. */ RTCC_Enable( false ); /* Enter a critical section but don't use the taskENTER_CRITICAL() method as that will mask interrupts that should exit sleep mode. */ INT_Disable(); __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 and continue counting to complete the current time slice. */ RTCC_Enable( true ); /* Re-enable interrupts - see comments above the RTCC_Enable() call above. */ INT_Enable(); } else { RTCC_ChannelCCVSet( lpRTCC_CHANNEL, ulReloadValue ); /* Restart the RTC. */ RTCC_Enable( true ); /* 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 ) { __asm volatile( "dsb" ); SLEEP_Sleep(); __asm volatile( "isb" ); } /* Allow the application to define some post sleep processing. */ configPOST_SLEEP_PROCESSING( xModifiableIdleTime ); /* Stop RTC. 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. */ RTCC_Enable( false ); ulCountAfterSleep = RTCC_CounterGet(); /* Re-enable interrupts - see comments above the INT_Enable() call above. */ INT_Enable(); __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. 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; /* The interrupt should have reset the CCV value. */ configASSERT( RTCC_ChannelCCVGet( lpRTCC_CHANNEL ) == ulReloadValueForOneTick ); } else { /* Something other than the tick interrupt ended the sleep. How many complete tick periods passed while the processor was sleeping? */ ulCompleteTickPeriods = ulCountAfterSleep / ulReloadValueForOneTick; /* The next interrupt is configured to occur at whatever fraction of the current tick period remains by setting the reload value back to that required for one tick, and truncating the count to remove the counts that are greater than the reload value. */ RTCC_ChannelCCVSet( lpRTCC_CHANNEL, ulReloadValueForOneTick ); ulCountAfterSleep %= ulReloadValueForOneTick; RTCC_CounterSet( ulCountAfterSleep ); } /* Restart the RTC so it runs up to the alarm value. The alarm value will get set to the value required to generate exactly one tick period the next time the RTC interrupt executes. */ RTCC_Enable( true ); /* Wind the tick forward by the number of tick periods that the CPU remained in a low power state. */ vTaskStepTick( ulCompleteTickPeriods ); }
/* 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 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 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 volatile( "cpsie i" ); __asm volatile( "dsb" ); __asm volatile( "isb" ); }
/* Override the default definition of vPortSuppressTicksAndSleep() that is weakly defined in the FreeRTOS Cortex-M3 port layer with a version that manages the asynchronous timer (AST), as the tick is generated from the low power AST and not the SysTick as would normally be the case on a Cortex-M. */ void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { uint32_t ulAlarmValue, ulCompleteTickPeriods, ulInterruptStatus; eSleepModeStatus eSleepAction; TickType_t xModifiableIdleTime; enum sleepmgr_mode xSleepMode; /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */ /* Make sure the AST reload value does not overflow the counter. */ if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) { xExpectedIdleTime = xMaximumPossibleSuppressedTicks; } /* Calculate the reload value required to wait xExpectedIdleTime tick periods. */ ulAlarmValue = ulAlarmValueForOneTick * xExpectedIdleTime; if( ulAlarmValue > ulStoppedTimerCompensation ) { /* Compensate for the fact that the AST is going to be stopped momentarily. */ ulAlarmValue -= ulStoppedTimerCompensation; } /* Stop the AST momentarily. The time the AST 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. */ prvDisableAST(); /* Enter a critical section but don't use the taskENTER_CRITICAL() method as that will mask interrupts that should exit sleep mode. */ ulInterruptStatus = cpu_irq_save(); /* 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. */ prvEnableAST(); /* Re-enable interrupts - see comments above the cpsid instruction() above. */ cpu_irq_restore( ulInterruptStatus ); } else { /* Adjust the alarm value to take into account that the current time slice is already partially complete. */ ulAlarmValue -= ast_read_counter_value( AST ); ast_write_alarm0_value( AST, ulAlarmValue ); /* Restart the AST. */ prvEnableAST(); /* 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 ) { /* Find the deepest allowable sleep mode. */ xSleepMode = sleepmgr_get_sleep_mode(); if( xSleepMode != SLEEPMGR_ACTIVE ) { /* Sleep until something happens. */ bpm_sleep( BPM, xSleepMode ); } } /* Allow the application to define some post sleep processing. */ configPOST_SLEEP_PROCESSING( xModifiableIdleTime ); /* Stop AST. 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. */ prvDisableAST(); /* Re-enable interrupts - see comments above the cpsid instruction() above. */ cpu_irq_restore( ulInterruptStatus ); 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 alarm value with whatever remains of this tick period. */ ulAlarmValue = ulAlarmValueForOneTick - ast_read_counter_value( AST ); ast_write_alarm0_value( AST, ulAlarmValue ); /* 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 = ast_read_counter_value( AST ) / ulAlarmValueForOneTick; /* The alarm value is set to whatever fraction of a single tick period remains. */ ulAlarmValue = ast_read_counter_value( AST ) - ( ulCompleteTickPeriods * ulAlarmValueForOneTick ); if( ulAlarmValue == 0 ) { /* There is no fraction remaining. */ ulAlarmValue = ulAlarmValueForOneTick; ulCompleteTickPeriods++; } ast_write_counter_value( AST, 0 ); ast_write_alarm0_value( AST, ulAlarmValue ); } /* Restart the AST so it runs up to the alarm value. The alarm value will get set to the value required to generate exactly one tick period the next time the AST interrupt executes. */ prvEnableAST(); /* Wind the tick forward by the number of tick periods that the CPU remained in a low power state. */ vTaskStepTick( ulCompleteTickPeriods ); } }
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { uint32_t ulMatchValue, ulCompleteTickPeriods, ulCurrentCount; eSleepModeStatus eSleepAction; /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */ /* Make sure the CMT reload value does not overflow the counter. */ if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) { xExpectedIdleTime = xMaximumPossibleSuppressedTicks; } /* Calculate the reload value required to wait xExpectedIdleTime tick periods. */ ulMatchValue = ulMatchValueForOneTick * xExpectedIdleTime; if( ulMatchValue > ulStoppedTimerCompensation ) { /* Compensate for the fact that the CMT is going to be stopped momentarily. */ ulMatchValue -= ulStoppedTimerCompensation; } /* Stop the CMT momentarily. The time the CMT 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. */ CMT.CMSTR0.BIT.STR0 = 0; while( CMT.CMSTR0.BIT.STR0 == 1 ) { /* Nothing to do here. */ } /* Critical section using the global interrupt bit as the i bit is automatically reset by the WAIT instruction. */ __disable_interrupt(); /* 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. */ CMT.CMSTR0.BIT.STR0 = 1; __enable_interrupt(); } else if( eSleepAction == eNoTasksWaitingTimeout ) { /* Protection off. */ SYSTEM.PRCR.WORD = portUNLOCK_KEY; /* Ready for software standby with all clocks stopped. */ SYSTEM.SBYCR.BIT.SSBY = 1; /* Protection on. */ SYSTEM.PRCR.WORD = portLOCK_KEY; /* Sleep until something happens. Calling prvSleep() will automatically reset the i bit in the PSW. */ prvSleep( xExpectedIdleTime ); /* Restart the CMT. */ CMT.CMSTR0.BIT.STR0 = 1; } else { /* Protection off. */ SYSTEM.PRCR.WORD = portUNLOCK_KEY; /* Ready for deep sleep mode. */ SYSTEM.MSTPCRC.BIT.DSLPE = 1; SYSTEM.MSTPCRA.BIT.MSTPA28 = 1; SYSTEM.SBYCR.BIT.SSBY = 0; /* Protection on. */ SYSTEM.PRCR.WORD = portLOCK_KEY; /* Adjust the match value to take into account that the current time slice is already partially complete. */ ulMatchValue -= ( uint32_t ) CMT0.CMCNT; CMT0.CMCOR = ( uint16_t ) ulMatchValue; /* Restart the CMT to count up to the new match value. */ CMT0.CMCNT = 0; CMT.CMSTR0.BIT.STR0 = 1; /* Sleep until something happens. Calling prvSleep() will automatically reset the i bit in the PSW. */ prvSleep( xExpectedIdleTime ); /* Stop CMT. 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. */ CMT.CMSTR0.BIT.STR0 = 0; while( CMT.CMSTR0.BIT.STR0 == 1 ) { /* Nothing to do here. */ } ulCurrentCount = ( uint32_t ) CMT0.CMCNT; 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 match value with whatever remains of this tick period. */ ulMatchValue = ulMatchValueForOneTick - ulCurrentCount; CMT0.CMCOR = ( uint16_t ) ulMatchValue; /* 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 = ulCurrentCount / ulMatchValueForOneTick; /* The match value is set to whatever fraction of a single tick period remains. */ ulMatchValue = ulCurrentCount - ( ulCompleteTickPeriods * ulMatchValueForOneTick ); CMT0.CMCOR = ( uint16_t ) ulMatchValue; } /* Restart the CMT so it runs up to the match value. The match value will get set to the value required to generate exactly one tick period the next time the CMT interrupt executes. */ CMT0.CMCNT = 0; CMT.CMSTR0.BIT.STR0 = 1; /* Wind the tick forward by the number of tick periods that the CPU remained in a low power state. */ vTaskStepTick( ulCompleteTickPeriods ); } }
__weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) { xExpectedIdleTime = xMaximumPossibleSuppressedTicks; } /* Stop the SysTick momentarily. 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. */ portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT; /* Calculate the reload value required to wait xExpectedIdleTime tick periods. -1 is used because this code will execute part way through one of the tick periods. */ ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); if( ulReloadValue > ulStoppedTimerCompensation ) { ulReloadValue -= ulStoppedTimerCompensation; } /* Enter a critical section but don't use the taskENTER_CRITICAL() method as that will mask interrupts that should exit sleep mode. */ __disable_irq(); /* If a context switch is pending or a task is waiting for the scheduler to be unsuspended then abandon the low power entry. */ if( eTaskConfirmSleepModeStatus() == eAbortSleep ) { /* Restart from whatever is left in the count register to complete this tick period. */ portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; /* Restart SysTick. */ portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; /* Reset the reload register to the value required for normal tick periods. */ portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Re-enable interrupts - see comments above __disable_irq() call above. */ __enable_irq(); } else { /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; /* Clear the SysTick count flag and set the count value back to zero. */ portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; /* Restart SysTick. */ portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can set its parameter to 0 to indicate that its implementation contains its own wait for interrupt or wait for event instruction, and so wfi should not be executed again. However, the original expected idle time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); if( xModifiableIdleTime > 0 ) { __dsb( portSY_FULL_READ_WRITE ); __wfi(); __isb( portSY_FULL_READ_WRITE ); } configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Stop SysTick. 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. */ ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG; portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT ); /* Re-enable interrupts - see comments above __disable_irq() call above. */ __enable_irq(); if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; /* The tick interrupt has already executed, and the SysTick count reloaded with ulReloadValue. Reset the portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow underflowed because the post sleep hook did something that took too long. */ if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; /* 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 waiting. */ ulCompleteTickPeriods = xExpectedIdleTime - 1UL; } else { /* Something other than the tick interrupt ended the sleep. Work out how long the sleep lasted rounded to complete tick periods (not the ulReload value which accounted for part ticks). */ ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; /* How many complete tick periods passed while the processor was waiting? */ ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; /* The reload value is set to whatever fraction of a single tick period remains. */ portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1 ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; } /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again, then set portNVIC_SYSTICK_LOAD_REG back to its standard value. The critical section is used to ensure the tick interrupt can only execute once in the case that the reload register is near zero. */ portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; portENTER_CRITICAL(); { portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; vTaskStepTick( ulCompleteTickPeriods ); portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; } portEXIT_CRITICAL(); } }
/* * \brief Configure sleep timer and sleep * * Function to configure timer for sleep, and calculate time slept. */ void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { // Are we running tickless now? if (!tickless_enable) return; // Reconfigure the timer to act as sleep timer tc_disable_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_unregister_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_register_callback(&tc, empty_callback, TC_CALLBACK_CC_CHANNEL0); tc_enable_callback(&tc, TC_CALLBACK_CC_CHANNEL0); // Check that the offset is not greater than the range of the timer if (xExpectedIdleTime > TIMER_MAX_POSSIBLE_SUPPRESSED_TICKS) { xExpectedIdleTime = TIMER_MAX_POSSIBLE_SUPPRESSED_TICKS; } // Set sleep time, -1 because we want to wake up before the last tick tc_set_top_value(&tc, (xExpectedIdleTime - 1) * TIMER_RELOAD_VALUE_ONE_TICK); // Clear overflow interrupt flag tc.hw->COUNT32.INTFLAG.bit.OVF = 1; // Check if we still should sleep if (eTaskConfirmSleepModeStatus() == eAbortSleep) { // Reset the timer to act as SysTick tc_disable_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_unregister_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_register_callback(&tc, (tc_callback_t) xPortSysTickHandler, TC_CALLBACK_CC_CHANNEL0); tc_enable_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_set_top_value(&tc, TIMER_RELOAD_VALUE_ONE_TICK); } else { if (xExpectedIdleTime > 0) { // Data sync barrier before sleep __asm volatile ("dsb"); // Go to sleep __asm volatile ("wfi"); // If OVF interrupt flag is set, we know the timer has wrapped if (tc.hw->COUNT32.INTFLAG.bit.OVF) { vTaskStepTick(xExpectedIdleTime - 1); } // We do not know how long we've slept else { // Calculate from Counter how long we've slept // Reset counter to less than one os tick // This might result in a tiny drift in time. uint32_t count_val = tc_get_count_value(&tc); vTaskStepTick(count_val / TIMER_RELOAD_VALUE_ONE_TICK); tc_set_count_value(&tc, count_val % TIMER_RELOAD_VALUE_ONE_TICK); } } // Reset the timer to act as SysTick tc_disable_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_unregister_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_register_callback(&tc, (tc_callback_t) xPortSysTickHandler, TC_CALLBACK_CC_CHANNEL0); tc_enable_callback(&tc, TC_CALLBACK_CC_CHANNEL0); tc_set_top_value(&tc, TIMER_RELOAD_VALUE_ONE_TICK); // Make sure that the counter hasn't passed the CC before callback was registered if ( tc_get_count_value(&tc) > TIMER_RELOAD_VALUE_ONE_TICK ) { // If so, reload count value, and step one tick */ tc_set_count_value(&tc, tc_get_count_value(&tc) % TIMER_RELOAD_VALUE_ONE_TICK); vTaskStepTick(1); } }