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/

Revision:
0:abaa4fd0c727
Child:
1:bbc6b5bdd75b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WakeUp_STM32.cpp	Thu Mar 12 09:27:30 2020 +0000
@@ -0,0 +1,155 @@
+/*
+
+    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:    March      12th, 2020
+ */
+
+#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)
+
+#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
+    }
+
+    PWR->CR |= PWR_CR_DBP;      //Enable power domain
+    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
+    PWR->CR &= ~PWR_CR_DBP; //Disable power domain
+
+    __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);
+    PWR->CR |= PWR_CR_CWUF;
+    HAL_PWR_EnterSTANDBYMode();
+}
+
+void WakeUp::irq_handler(void)
+{
+    //Clear RTC + EXTI interrupt flags
+    PWR->CR |= PWR_CR_DBP;      // Enable power domain
+    RTC->ISR &= ~RTC_ISR_ALRAF;
+    RTC->CR &= 0x00ff00ff;      // just in case
+    RTC->WPR = 0xCA;            // Disable RTC write protection
+    RTC->WPR = 0x53;
+    RTC->CR &= ~(RTC_CR_ALRAE | RTC_CR_ALRAIE); //DisEnable Alarm-A  
+    RTC->WPR = 0xFF;            // Enable RTC write protection
+    EXTI->PR = RTC_EXTI_LINE_ALARM_EVENT;  
+    PWR->CR &= ~PWR_CR_DBP;     // Disable power domain
+    system_reset();
+}
+
+#endif
\ No newline at end of file