Enter Standby mode then wake up(only restart) by RTC timer
Dependents: Check_StandBy_os2 Check_StandBy_os5 Check_StandBy_os6
see /users/kenjiArai/notebook/standby-mode-current-consumption-on-nucleo-f446re/
WakeUp_STM32.cpp
- Committer:
- kenjiArai
- Date:
- 2021-01-17
- Revision:
- 1:bbc6b5bdd75b
- Parent:
- 0:abaa4fd0c727
File content as of revision 1:bbc6b5bdd75b:
/* Original idea & program https://os.mbed.com/users/Sissors/code/WakeUp/ by Erik modified version https://os.mbed.com/users/kenjiArai/code/WakeUp/ */ /* * Modified only for STM CPU * by Kenji Arai / JH1PJL * * http://www7b.biglobe.ne.jp/~kenjia/ * http://mbed.org/users/kenjiArai/ * Created: September 21st, 2017 * Revised: January 17th, 2021 */ #if \ defined(TARGET_NUCLEO_F334R8) # error Not support yet #elif \ defined(TARGET_NUCLEO_F401RE)\ || defined(TARGET_NUCLEO_F411RE)\ || defined(TARGET_NUCLEO_F446RE)\ || defined(TARGET_NUCLEO_L053R8)\ || defined(TARGET_NUCLEO_L073RZ)\ || defined(TARGET_NUCLEO_L152RE)\ || defined(TARGET_NUCLEO_L476RG)\ || defined(TARGET_DISCO_L475VG_IOT01A) #include "WakeUp.h" #include "rtc_api.h" #define BYTE2BCD(byte) ((byte % 10) | ((byte / 10) << 4)) //Most things are pretty similar between the different STM targets. //Only the IRQ number the alarm is connected to differs. Any errors //with RTC_IRQn/RTC_Alarm_IRQn in them are related to this #if defined(TARGET_M4) || defined(TARGET_M3) #define RTC_IRQ RTC_Alarm_IRQn #else #define RTC_IRQ RTC_IRQn #endif // Some things to handle Disco L476VG (and similar ones) #if defined(TARGET_STM32L4) #define IMR IMR1 #define EMR EMR1 #define RTSR RTSR1 #define FTSR FTSR2 #define PR PR1 #endif void WakeUp::set_ms(uint32_t ms) { if (ms == 0) { //Just disable alarm return; } if (!rtc_isenabled()) { //Make sure RTC is running rtc_init(); wait_us(250); //The f401 seems to want a delay after init } #if defined(TARGET_NUCLEO_L476RG) || defined(TARGET_DISCO_L475VG_IOT01A) PWR->CR1 |= PWR_CR1_DBP; //Enable backup domain #else PWR->CR |= PWR_CR_DBP; //Enable backup domain #endif RTC->WPR = 0xCA; //Disable RTC write protection RTC->WPR = 0x53; //Alarm must be disabled to change anything RTC->CR &= ~RTC_CR_ALRAE; RTC->CR &= 0x00ff00ff; while(!(RTC->ISR & RTC_ISR_ALRAWF)); //RTC prescaler + calculate how many sub-seconds should be added uint32_t prescaler = (RTC->PRER & 0x7FFF) + 1; uint32_t subsecsadd = ((ms % 1000) * prescaler) / 1000; if ((ms < 1000) && (subsecsadd < 2)) subsecsadd = 2;//At least 5 subsecs delay to be sure interrupt is called __disable_irq(); //At this point we don't want IRQs anymore //Get current time uint32_t subsecs = RTC->SSR; time_t secs = rtc_read(); //Calculate alarm values //Subseconds is countdown, // so substract the 'added' sub-seconds and prevent underflow if (subsecs < subsecsadd) { subsecs += prescaler; secs++; } subsecs -= subsecsadd; //Set seconds correctly secs += ms / 1000; struct tm *timeinfo = localtime(&secs); //Enable rising edge EXTI interrupt of the RTC EXTI->IMR |= RTC_EXTI_LINE_ALARM_EVENT; // enable it EXTI->EMR &= ~RTC_EXTI_LINE_ALARM_EVENT; // disable event EXTI->RTSR |= RTC_EXTI_LINE_ALARM_EVENT; // enable rising edge EXTI->FTSR &= ~RTC_EXTI_LINE_ALARM_EVENT; // disable falling edge //Calculate alarm register values uint32_t alarmreg = 0; alarmreg |= BYTE2BCD(timeinfo->tm_sec) << 0; alarmreg |= BYTE2BCD(timeinfo->tm_min) << 8; alarmreg |= BYTE2BCD(timeinfo->tm_hour) << 16; alarmreg |= BYTE2BCD(timeinfo->tm_mday) << 24; alarmreg &= 0x3f3f7f7f; // All MSKx & WDSEL = 0 //Enable RTC interrupt (use Alarm-A) RTC->ALRMAR = alarmreg; RTC->ALRMASSR = subsecs | RTC_ALRMASSR_MASKSS; //Mask no subseconds RTC->CR |= RTC_CR_ALRAE | RTC_CR_ALRAIE; //Enable Alarm-A RTC->WPR = 0xFF; //Enable RTC write protection #if defined(TARGET_NUCLEO_L476RG) || defined(TARGET_DISCO_L475VG_IOT01A) PWR->CR1 &= ~PWR_CR1_DBP; //Disable backup domain #else PWR->CR &= ~PWR_CR_DBP; //Disable backup domain #endif __enable_irq(); //Alarm is set, so irqs can be enabled again //Enable everything else NVIC_SetVector(RTC_IRQ, (uint32_t)WakeUp::irq_handler); NVIC_EnableIRQ(RTC_IRQ); } void WakeUp::standby_then_reset(uint32_t ms) { if (ms == 0) { // just go to Reset system_reset(); } set_ms(ms); #if defined(TARGET_NUCLEO_L476RG) || defined(TARGET_DISCO_L475VG_IOT01A) __HAL_RCC_PWR_CLK_ENABLE(); HAL_PWREx_EnterSHUTDOWNMode(); #else PWR->CR |= PWR_CR_CWUF; HAL_PWR_EnterSTANDBYMode(); #endif } void WakeUp::irq_handler(void) { //Clear RTC + EXTI interrupt flags #if defined(TARGET_NUCLEO_L476RG) || defined(TARGET_DISCO_L475VG_IOT01A) PWR->CR1 |= PWR_CR1_DBP; // Enable power domain #else PWR->CR |= PWR_CR_DBP; // Enable power domain #endif RTC->ISR &= ~RTC_ISR_ALRAF; #if defined(TARGET_NUCLEO_L476RG) || defined(TARGET_DISCO_L475VG_IOT01A) //RTC->CR &= 0x00ff00ff; // just in case #else RTC->CR &= 0x00ff00ff; // just in case #endif RTC->WPR = 0xCA; // Disable RTC write protection RTC->WPR = 0x53; RTC->CR &= ~(RTC_CR_ALRAE | RTC_CR_ALRAIE); //Disable Alarm-A RTC->WPR = 0xFF; // Enable RTC write protection EXTI->PR = RTC_EXTI_LINE_ALARM_EVENT; #if defined(TARGET_NUCLEO_L476RG) || defined(TARGET_DISCO_L475VG_IOT01A) PWR->CR1 &= ~PWR_CR1_DBP; // Disable power domain #else PWR->CR &= ~PWR_CR_DBP; // Disable power domain #endif system_reset(); } #endif