Revised by I. Cserny

Dependents:   Lab04_Check_StandBy_os2 Lab04_wakeup_STM32

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers WakeUp_STM32.cpp Source File

WakeUp_STM32.cpp

00001 /*
00002 
00003     Original idea & program
00004     https://os.mbed.com/users/Sissors/code/WakeUp/
00005         by Erik
00006     
00007     modified version
00008     https://os.mbed.com/users/kenjiArai/code/WakeUp/
00009  */
00010 
00011 /*
00012  *  Modified only for STM CPU
00013  *      by Kenji Arai / JH1PJL
00014  *
00015  *  http://www7b.biglobe.ne.jp/~kenjia/
00016  *  http://mbed.org/users/kenjiArai/
00017  *      Created:    September  21st, 2017
00018  *      Revised:    March      12th, 2020
00019  */
00020 
00021 #if \
00022     defined(TARGET_NUCLEO_F334R8)
00023 #   error Not support yet
00024 #elif \
00025     defined(TARGET_NUCLEO_F401RE)\
00026  || defined(TARGET_NUCLEO_F411RE)\
00027  || defined(TARGET_NUCLEO_F446RE)\
00028  || defined(TARGET_NUCLEO_L053R8)\
00029  || defined(TARGET_NUCLEO_L073RZ)\
00030  || defined(TARGET_NUCLEO_L152RE)
00031 
00032 #include "WakeUp_STM32.h"
00033 #include "rtc_api.h"
00034 
00035 #define BYTE2BCD(byte)  ((byte % 10) | ((byte / 10) << 4))
00036 
00037 //Most things are pretty similar between the different STM targets.
00038 //Only the IRQ number the alarm is connected to differs. Any errors
00039 //with RTC_IRQn/RTC_Alarm_IRQn in them are related to this
00040 #if defined(TARGET_M4) || defined(TARGET_M3)
00041 #define RTC_IRQ     RTC_Alarm_IRQn               
00042 #else
00043 #define RTC_IRQ     RTC_IRQn
00044 #endif
00045 
00046 // Some things to handle Disco L476VG (and similar ones)
00047 #if defined(TARGET_STM32L4)
00048 #define IMR     IMR1
00049 #define EMR     EMR1
00050 #define RTSR    RTSR1
00051 #define FTSR    FTSR2
00052 #define PR      PR1
00053 #endif
00054 
00055 void WakeUp::set_ms(uint32_t ms)
00056 {
00057     if (ms == 0) {              //Just disable alarm
00058         return;
00059     }
00060     
00061     if (!rtc_isenabled()) {     //Make sure RTC is running
00062         rtc_init();
00063         wait_us(250);           //The f401 seems to want a delay after init
00064     }
00065 
00066     PWR->CR |= PWR_CR_DBP;      //Enable power domain
00067     RTC->WPR = 0xCA;            //Disable RTC write protection
00068     RTC->WPR = 0x53;
00069 
00070     //Alarm must be disabled to change anything
00071     RTC->CR &= ~RTC_CR_ALRAE;
00072     RTC->CR &= 0x00ff00ff;
00073     while(!(RTC->ISR & RTC_ISR_ALRAWF));
00074 
00075     //RTC prescaler + calculate how many sub-seconds should be added
00076     uint32_t prescaler = (RTC->PRER & 0x7FFF) + 1;
00077     uint32_t subsecsadd = ((ms % 1000) * prescaler) / 1000;
00078 
00079     if ((ms < 1000) && (subsecsadd < 2))
00080         subsecsadd = 2;//At least 5 subsecs delay to be sure interrupt is called
00081 
00082     __disable_irq();   //At this point we don't want IRQs anymore
00083 
00084     //Get current time
00085     uint32_t subsecs = RTC->SSR;
00086     time_t secs = rtc_read(); 
00087 
00088     //Calculate alarm values
00089     //Subseconds is countdown,
00090     //    so substract the 'added' sub-seconds and prevent underflow
00091     if (subsecs < subsecsadd) {
00092         subsecs += prescaler;
00093         secs++;
00094     }
00095     subsecs -= subsecsadd;
00096 
00097     //Set seconds correctly
00098     secs += ms / 1000;
00099     struct tm *timeinfo = localtime(&secs);
00100 
00101     //Enable rising edge EXTI interrupt of the RTC
00102     EXTI->IMR |= RTC_EXTI_LINE_ALARM_EVENT;     // enable it
00103     EXTI->EMR &= ~RTC_EXTI_LINE_ALARM_EVENT;    // disable event
00104     EXTI->RTSR |= RTC_EXTI_LINE_ALARM_EVENT;    // enable rising edge
00105     EXTI->FTSR &= ~RTC_EXTI_LINE_ALARM_EVENT;   // disable falling edge
00106 
00107     //Calculate alarm register values
00108     uint32_t alarmreg = 0;
00109     alarmreg |= BYTE2BCD(timeinfo->tm_sec)  << 0;
00110     alarmreg |= BYTE2BCD(timeinfo->tm_min)  << 8;
00111     alarmreg |= BYTE2BCD(timeinfo->tm_hour) << 16;
00112     alarmreg |= BYTE2BCD(timeinfo->tm_mday) << 24;
00113     alarmreg &= 0x3f3f7f7f; // All MSKx & WDSEL = 0
00114 
00115     //Enable RTC interrupt (use Alarm-A)
00116     RTC->ALRMAR = alarmreg;
00117     RTC->ALRMASSR = subsecs | RTC_ALRMASSR_MASKSS;      //Mask no subseconds
00118     RTC->CR |= RTC_CR_ALRAE | RTC_CR_ALRAIE;            //Enable Alarm-A  
00119 
00120     RTC->WPR = 0xFF;        //Enable RTC write protection
00121     PWR->CR &= ~PWR_CR_DBP; //Disable power domain
00122 
00123     __enable_irq();         //Alarm is set, so irqs can be enabled again
00124 
00125     //Enable everything else
00126     NVIC_SetVector(RTC_IRQ, (uint32_t)WakeUp::irq_handler);
00127     NVIC_EnableIRQ(RTC_IRQ);
00128 }
00129 
00130 void WakeUp::standby_then_reset(uint32_t ms)
00131 {
00132     if (ms == 0){   // just go to Reset
00133         system_reset();
00134     } 
00135     set_ms(ms);
00136     PWR->CR |= PWR_CR_CWUF;
00137     HAL_PWR_EnterSTANDBYMode();
00138 }
00139 
00140 void WakeUp::irq_handler(void)
00141 {
00142     //Clear RTC + EXTI interrupt flags
00143     PWR->CR |= PWR_CR_DBP;      // Enable power domain
00144     RTC->ISR &= ~RTC_ISR_ALRAF;
00145     RTC->CR &= 0x00ff00ff;      // just in case
00146     RTC->WPR = 0xCA;            // Disable RTC write protection
00147     RTC->WPR = 0x53;
00148     RTC->CR &= ~(RTC_CR_ALRAE | RTC_CR_ALRAIE); //DisEnable Alarm-A  
00149     RTC->WPR = 0xFF;            // Enable RTC write protection
00150     EXTI->PR = RTC_EXTI_LINE_ALARM_EVENT;  
00151     PWR->CR &= ~PWR_CR_DBP;     // Disable power domain
00152     system_reset();
00153 }
00154 
00155 #endif