Mbed library to manage low-power modes on STM32 devices
STM32_LowPower.cpp
- Committer:
- biagiomkr
- Date:
- 2019-04-12
- Revision:
- 0:f37bc13b9bbf
File content as of revision 0:f37bc13b9bbf:
/** ****************************************************************************** * @file STM32_LowPower.cpp * @author Biagio Montaruli, STM32duino team (STMicroelectronics) * @version V1.0.0 * @date 12-April-2019 * @brief Mbed Library to manage Low Power modes on STM32 boards * ****************************************************************************** * @attention * * <h2><center>© COPYRIGHT(c) 2019 STMicroelectronics</center></h2> * <h2><center>© COPYRIGHT(c) 2019 Biagio Montaruli</center></h2> * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */ #include "STM32_LowPower.h" #include "rtc_api_hal.h" STM32_LowPower LowPower; static RTC_HandleTypeDef RtcHandle; static voidFuncPtr RTCUserCallback = NULL; static void *callbackUserData = NULL; void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc) { UNUSED(hrtc); if (RTCUserCallback != NULL) { RTCUserCallback(callbackUserData); } } void RTC_IRQHandler(void) { HAL_RTCEx_WakeUpTimerIRQHandler(&RtcHandle); } #ifdef STM32G0xx #define PWR_FLAG_WU PWR_FLAG_WUF #endif STM32_LowPower::STM32_LowPower() { _configured = false; _rtc_wakeup = false; _timer = 0; } /** * @brief Initializes the low power mode * @param None * @retval None */ void STM32_LowPower::init(void) { debug(DEBUG, "Starting STM32_LowPower::init()\n"); /* Initialize Low Power mode using STM32 HAL */ #if !defined(STM32H7xx) && !defined(STM32WBxx) /* Enable Power Clock */ __HAL_RCC_PWR_CLK_ENABLE(); debug(DEBUG, "STM32_LowPower::init() -> __HAL_RCC_PWR_CLK_ENABLE()\n"); #endif /* Allow access to Backup domain */ HAL_PWR_EnableBkUpAccess(); debug(DEBUG, "STM32_LowPower::init() -> HAL_PWR_EnableBkUpAccess()\n"); #ifdef __HAL_RCC_WAKEUPSTOP_CLK_CONFIG /* Ensure that HSI is wake-up system clock */ __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI); debug(DEBUG, "STM32_LowPower::init() -> __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI)\n"); #endif /* Check if the system was resumed from StandBy mode */ if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB) != RESET) { /* Clear Standby flag */ __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB); debug(DEBUG, "STM32_LowPower::init() -> __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB)\n"); } /* Clear all related wakeup flags */ __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); debug(DEBUG, "STM32_LowPower::init() -> __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU)\n"); /* start RTC */ set_time(0); _configured = true; debug(DEBUG, "Exiting STM32_LowPower::init()...\n"); } /** * @brief Enable the STM32 sleep mode. * Exit this mode on interrupt using the attachInterruptWakeup() method * or in timer_s seconds. * @param reg: type of power regulator. * @param timer_s: optional delay before leaving the sleep mode (default: 0). * @retval None */ void STM32_LowPower::sleep(STM32_RegulatorType reg, uint32_t timer_s) { debug(DEBUG, "Starting STM32_LowPower::sleep()...\n"); uint32_t regulator; /* Enable STM32 Sleep Mode: regulator in main mode */ if (reg == STM32_MAIN_REGULATOR) { regulator = PWR_MAINREGULATOR_ON; debug(DEBUG, "STM32_LowPower::sleep() -> regulator: PWR_MAINREGULATOR_ON\n"); } /* Enable STM32 Low-power Sleep Mode: regulator in low-power mode */ else if (reg == STM32_LOWPOWER_REGULATOR) { regulator = PWR_LOWPOWERREGULATOR_ON; debug(DEBUG, "STM32_LowPower::sleep() -> regulator: PWR_LOWPOWERREGULATOR_ON\n"); } else { regulator = PWR_MAINREGULATOR_ON; } if (timer_s > 0) { programRtcWakeUp(timer_s); debug(DEBUG, "STM32_LowPower::sleep() -> programRtcWakeUp(timer_s)\n"); } else if (_rtc_wakeup && (_timer > 0)) { programRtcWakeUp(_timer); debug(DEBUG, "STM32_LowPower::sleep() -> programRtcWakeUp(_timer)\n"); } /* * Suspend Tick increment to prevent wakeup by Systick interrupt. * Otherwise the Systick interrupt will wake up the device within * 1ms (HAL time base) */ HAL_SuspendTick(); debug(DEBUG, "STM32_LowPower::sleep() -> HAL_SuspendTick()\n"); /* Enter Sleep Mode */ debug(DEBUG, "STM32_LowPower::sleep() -> HAL_PWR_EnterSLEEPMode()\n"); HAL_PWR_EnterSLEEPMode(regulator, PWR_SLEEPENTRY_WFI); /* Resume Tick interrupt if disabled before switching into Sleep mode */ HAL_ResumeTick(); debug(DEBUG, "STM32_LowPower::sleep() -> HAL_ResumeTick()\n"); if ((timer_s > 0) || _rtc_wakeup) { rtc_deactivate_wake_up_timer(); } debug(DEBUG, "Exiting STM32_LowPower::sleep()...\n"); } /** * @brief Enable the STM32 stop mode. * Exit this mode on interrupt using the attachInterruptWakeup() method * or in timer_s seconds. * @param reg: type of power regulator. * @param timer_s: optional delay before leaving the stop mode (default: 0). * @retval None */ void STM32_LowPower::stop(STM32_RegulatorType reg, uint32_t timer_s) { debug(DEBUG, "Starting STM32_LowPower::stop()\n"); __disable_irq(); debug(DEBUG, "STM32_LowPower::stop() -> __disable_irq()\n"); uint32_t regulator; /* Use regulator in main mode */ if (reg == STM32_MAIN_REGULATOR) { regulator = PWR_MAINREGULATOR_ON; debug(DEBUG, "STM32_LowPower::stop() -> regulator: PWR_MAINREGULATOR_ON\n"); } /* Use regulator in low-power mode */ else if (reg == STM32_LOWPOWER_REGULATOR) { regulator = PWR_LOWPOWERREGULATOR_ON; debug(DEBUG, "STM32_LowPower::stop() -> regulator: PWR_LOWPOWERREGULATOR_ON\n"); } else { regulator = PWR_LOWPOWERREGULATOR_ON; } if (timer_s > 0) { programRtcWakeUp(timer_s); debug(DEBUG, "STM32_LowPower::stop() -> programRtcWakeUp(timer_s)\n"); } else if (_rtc_wakeup && (_timer > 0)) { programRtcWakeUp(_timer); debug(DEBUG, "STM32_LowPower::stop() -> programRtcWakeUp(_timer)\n"); } #if defined(STM32L0xx) || defined(STM32L1xx) /* Enable Ultra low power mode */ HAL_PWREx_EnableUltraLowPower(); debug(DEBUG, "STM32_LowPower::stop() -> HAL_PWREx_EnableUltraLowPower()\n"); /* Enable the fast wake up from Ultra low power mode */ HAL_PWREx_EnableFastWakeUp(); debug(DEBUG, "STM32_LowPower::stop() -> HAL_PWREx_EnableFastWakeUp()\n"); #endif #ifdef __HAL_RCC_WAKEUPSTOP_CLK_CONFIG /* Select HSI as system clock source after Wake Up from Stop mode */ __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI); debug(DEBUG, "STM32_LowPower::stop() -> __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI)\n"); #endif /* Enter Stop mode */ debug(DEBUG, "STM32_LowPower::stop() -> HAL_PWR_EnterSTOPMode()\n"); HAL_Delay(20); HAL_PWR_EnterSTOPMode(regulator, PWR_STOPENTRY_WFI); /* Exit Stop mode reset clocks */ SetSysClock(); __enable_irq(); debug(DEBUG, "STM32_LowPower::stop() -> __enable_irq()\n"); HAL_Delay(100); debug(DEBUG, "STM32_LowPower::stop() -> SetSysClock()\n"); if ((timer_s > 0) || _rtc_wakeup) { rtc_deactivate_wake_up_timer(); } debug(DEBUG, "Exiting STM32_LowPower::stop()...\n"); } /** * @brief Enable the STM32 shutdown or standby mode. * Exit this mode on interrupt using the attachInterruptWakeup() method * or in timer_s seconds. * @param timer_s: optional delay before leaving the standby mode (default: 0). * @retval None */ void STM32_LowPower::standby(uint32_t timer_s) { __disable_irq(); debug(DEBUG, "STM32_LowPower::standby() -> __disable_irq()\n"); debug(DEBUG, "Starting STM32_LowPower::standby()...\n"); if (timer_s > 0) { programRtcWakeUp(timer_s); debug(DEBUG, "STM32_LowPower::standby() -> programRtcWakeUp(timer_s)\n"); } else if (_rtc_wakeup && (_timer > 0)) { programRtcWakeUp(_timer); debug(DEBUG, "STM32_LowPower::standby() -> programRtcWakeUp(_timer)\n"); } #if defined(STM32L0xx) || defined(STM32L1xx) /* Enable Ultra low power mode */ HAL_PWREx_EnableUltraLowPower(); debug(DEBUG, "STM32_LowPower::standby() -> HAL_PWREx_EnableUltraLowPower()\n"); /* Enable the fast wake up from Ultra low power mode */ HAL_PWREx_EnableFastWakeUp(); debug(DEBUG, "STM32_LowPower::standby() -> HAL_PWREx_EnableFastWakeUp()\n"); #endif debug(DEBUG, "STM32_LowPower::standby() -> HAL_PWR_EnterSTANDBYMode()\n"); HAL_PWR_EnterSTANDBYMode(); } /** * @brief Enable GPIO pin in interrupt mode. If the pin is a wakeup pin, it is * configured as wakeup source. * @param pin: pin name (PX_Y, where X = GPIO port and Y = GPIO number) * @param callback: pointer to callback function. * @param mode: interrupt mode (IT_MODE_RISING, IT_MODE_FALLING, IT_MODE_RISING_FALLING) * @retval None */ void STM32_LowPower::attachInterruptWakeup(PinName pin, voidFuncPtrVoid callback, Interrupt_Mode mode) { debug(DEBUG, "Starting STM32_LowPower::attachInterruptWakeup()...\n"); debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> pin: %u\n", pin); _wakeupPin = new InterruptIn(pin); switch (mode) { case IT_MODE_RISING : _wakeupPin->rise(callback); debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wakeupPin->rise(callback)\n"); break; case IT_MODE_FALLING : _wakeupPin->fall(callback); debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wakeupPin->fall(callback)\n"); break; case IT_MODE_RISING_FALLING : default: _wakeupPin->rise(callback); _wakeupPin->fall(callback); debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wakeupPin->rise & fall\n"); break; } /* If Gpio is a Wake up pin activate it in order to wake up the STM32 MCU */ #if !defined(PWR_WAKEUP_PIN1_HIGH) UNUSED(mode); #endif uint32_t wkup_pin; if (pin != NC) { switch (pin) { #ifdef PWR_WAKEUP_PIN1 #if defined(TARGET_DISCO_F100RB) || defined(TARGET_DISCO_F407VG) || \ defined(TARGET_DISCO_F401VC) || defined(TARGET_DISCO_F429ZI) || \ defined(TARGET_NUCLEO_F103RB) || defined(TARGET_NUCLEO_F207ZG) || \ defined(TARGET_NUCLEO_F401RE) || defined(TARGET_NUCLEO_F411RE) || \ defined(TARGET_NUCLEO_F429ZI) || defined(TARGET_NUCLEO_F439ZI) || \ defined(TARGET_NUCLEO_F469NI) || defined(TARGET_MTB_STM_S2LP) case SYS_WKUP : #else case SYS_WKUP1 : #endif wkup_pin = PWR_WAKEUP_PIN1; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN1.\n"); #ifdef PWR_WAKEUP_PIN1_HIGH if (mode != IT_MODE_RISING) { wkup_pin = PWR_WAKEUP_PIN1_LOW; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN1_LOW.\n"); } #endif break; #endif /* PWR_WAKEUP_PIN1 */ #ifdef PWR_WAKEUP_PIN2 case SYS_WKUP2 : wkup_pin = PWR_WAKEUP_PIN2; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN2.\n"); #ifdef PWR_WAKEUP_PIN2_HIGH if (mode != IT_MODE_RISING) { wkup_pin = PWR_WAKEUP_PIN2_LOW; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN2_LOW\n"); } #endif break; #endif /* PWR_WAKEUP_PIN2 */ #ifdef PWR_WAKEUP_PIN3 case SYS_WKUP3 : wkup_pin = PWR_WAKEUP_PIN3; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN3\n"); #ifdef PWR_WAKEUP_PIN3_HIGH if (mode != IT_MODE_RISING) { wkup_pin = PWR_WAKEUP_PIN3_LOW; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN3_LOW\n"); } #endif break; #endif /* PWR_WAKEUP_PIN3 */ #ifdef PWR_WAKEUP_PIN4 case SYS_WKUP4 : wkup_pin = PWR_WAKEUP_PIN4; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN4\n"); #ifdef PWR_WAKEUP_PIN4_HIGH if (mode != IT_MODE_RISING) { wkup_pin = PWR_WAKEUP_PIN4_LOW; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN4_LOW\n"); } #endif break; #endif /* PWR_WAKEUP_PIN4 */ #ifdef PWR_WAKEUP_PIN5 case SYS_WKUP5 : wkup_pin = PWR_WAKEUP_PIN5; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN5\n"); #ifdef PWR_WAKEUP_PIN5_HIGH if (mode != IT_MODE_RISING) { wkup_pin = PWR_WAKEUP_PIN5_LOW; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN5_LOW\n"); } #endif break; #endif /* PWR_WAKEUP_PIN5 */ #ifdef PWR_WAKEUP_PIN6 case SYS_WKUP6 : wkup_pin = PWR_WAKEUP_PIN6; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN6\n"); #ifdef PWR_WAKEUP_PIN6_HIGH if (mode != IT_MODE_RISING) { wkup_pin = PWR_WAKEUP_PIN6_LOW; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN6_LOW\n"); } #endif break; #endif /* PWR_WAKEUP_PIN6 */ #ifdef PWR_WAKEUP_PIN7 case SYS_WKUP7 : wkup_pin = PWR_WAKEUP_PIN7; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN7\n"); break; #endif /* PWR_WAKEUP_PIN7 */ #ifdef PWR_WAKEUP_PIN8 case SYS_WKUP8 : wkup_pin = PWR_WAKEUP_PIN8; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN7\n"); break; #endif /* PWR_WAKEUP_PIN8 */ default : wkup_pin = PWR_WAKEUP_PIN1; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN1 (default case)\n"); #ifdef PWR_WAKEUP_PIN1_HIGH if (mode != IT_MODE_RISING) { wkup_pin = PWR_WAKEUP_PIN1_LOW; debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> wkup_pin = PWR_WAKEUP_PIN1_LOW (default case)\n"); } #endif break; } HAL_PWR_EnableWakeUpPin(wkup_pin); debug(DEBUG, "STM32_LowPower::attachInterruptWakeup() -> HAL_PWR_EnableWakeUpPin()\n"); debug(DEBUG, "Exiting STM32_LowPower::attachInterruptWakeup()...\n"); } } /** * @brief Configure the RTC WakeUp Interrupt. * @param millis: time (in seconds) to generate a RTC WakeUp event. * @retval None */ void STM32_LowPower::programRtcWakeUp(uint32_t timer_s) { uint32_t rtc_clock; core_util_critical_section_enter(); debug(DEBUG, "STM32_LowPower::programRtcWakeUp() -> core_util_critical_section_enter()\n"); rtc_clock = RTC_WAKEUPCLOCK_CK_SPRE_16BITS; /* Clear wakeup flag, just in case. */ SET_BIT(PWR->CR, PWR_CR_CWUF); /* HAL_RTCEx_SetWakeUpTimer_IT will assert that timer_s is 0xFFFF at max */ if (timer_s > 0xFFFF) { timer_s -= 0x10000; rtc_clock = RTC_WAKEUPCLOCK_CK_SPRE_17BITS; } RtcHandle.Instance = RTC; HAL_RTCEx_SetWakeUpTimer_IT(&RtcHandle, timer_s, rtc_clock); debug(DEBUG, "STM32_LowPower::programRtcWakeUp() -> HAL_RTCEx_SetWakeUpTimer_IT()\n"); NVIC_SetVector(RTC_WKUP_IRQn, (uint32_t)RTC_IRQHandler); NVIC_EnableIRQ(RTC_WKUP_IRQn); core_util_critical_section_exit(); debug(DEBUG, "STM32_LowPower::programRtcWakeUp() -> core_util_critical_section_exit()\n"); } /** * @brief Attach a callback to a RTC WakeUp event. * @param callback: callback function called when leaving the low power mode. * @param data: optional pointer to callback data parameters (default NULL). * @retval None */ void STM32_LowPower::enableWakeupFromRTC(voidFuncPtr callback, void *data, uint32_t timer_s) { _rtc_wakeup = true; if (timer_s > 0) { _timer = timer_s; } RTCUserCallback = callback; callbackUserData = data; }