Added Restart(by RESET) function from Standby mode only for some Nucleo boards (STM32 series)
Dependencies: LPC1114_WakeInterruptIn
Fork of WakeUp by
Example program using "Standby function" for Nucleo series is here.
/users/kenjiArai/code/Check_StandBy/
Device/WakeUp_STM32_others.cpp
- Committer:
- kenjiArai
- Date:
- 2017-09-21
- Revision:
- 25:2bd9df8c3ac8
- Child:
- 26:df9d01556394
File content as of revision 25:2bd9df8c3ac8:
// Created new file only for STM32/Nucleo boards which I have own it // by JH1PJL 2017-9-21 #if defined(TARGET_NUCLEO_F446RE) || defined(TARGET_NUCLEO_F411RE)\ || defined(TARGET_NUCLEO_F401RE)\ || defined(TARGET_NUCLEO_L152RE) || defined(TARGET_NUCLEO_L073RZ)\ || defined(TARGET_NUCLEO_L053R8) #include "WakeUp.h" #include "rtc_api.h" #define DEBUG extern Serial pc; #if 1 #define DBG(...) pc.printf(__VA_ARGS__) #else #define DBG(...) {;} #endif #if 1 #define DBGP(...) pc.printf(__VA_ARGS__) #else #define DBGP(...) {;} #endif #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 Callback<void()> WakeUp::callback; bool WakeUp::use_reset = false; 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)); DBG("Step(%u)\r\n", __LINE__); //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(); DBG("Step(%u),secs:%d,subsecs:%d\r\n", __LINE__, secs, subsecs); //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); DBG("Step(%u),secs:%d\r\n", __LINE__, 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) DBG("Step(%u),alarmreg:0x%08x\r\n", __LINE__, alarmreg); DBG("Step(%u),RTC->ISR:0x%08x\r\n", __LINE__, RTC->ISR); RTC->ALRMAR = alarmreg; RTC->ALRMASSR = subsecs | RTC_ALRMASSR_MASKSS; //Mask no subseconds DBG("Step(%u),alarmreg(reg):0x%08x\r\n", __LINE__, RTC->ALRMAR); RTC->CR |= RTC_CR_ALRAE | RTC_CR_ALRAIE; //Enable Alarm-A DBG("Step(%u),RTC->CR:0x%08x\r\n", __LINE__, RTC->CR); 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); use_reset = false; //---- Only for Debug purpose DBGP("PWR->CR 0x%08x:0x%08x\r\n",&PWR->CR, PWR->CR); DBGP("PWR->CSR 0x%08x:0x%08x\r\n",&PWR->CSR, PWR->CSR); DBGP("SCB->SCR 0x%08x:0x%08x\r\n",&SCB->SCR, SCB->SCR); DBGP("SCB->AIRCR 0x%08x:0x%08x\r\n",&SCB->AIRCR, SCB->AIRCR); DBGP("EXTI->IMR 0x%08x:0x%08x\r\n",&EXTI->IMR, EXTI->IMR); DBGP("EXTI->EMR 0x%08x:0x%08x\r\n",&EXTI->EMR, EXTI->EMR); DBGP("EXTI->RTSR 0x%08x:0x%08x\r\n",&EXTI->RTSR, EXTI->RTSR); DBGP("EXTI->FTSR 0x%08x:0x%08x\r\n",&EXTI->FTSR, EXTI->FTSR); DBGP("RTC->TR 0x%08x:0x%08x\r\n",&RTC->TR,RTC->TR); DBGP("RTC->DR 0x%08x:0x%08x\r\n",&RTC->DR,RTC->DR); DBGP("RTC->CR 0x%08x:0x%08x\r\n",&RTC->CR,RTC->CR); DBGP("RTC->ISR 0x%08x:0x%08x\r\n",&RTC->ISR,RTC->ISR); DBGP("RTC->ALRMAR 0x%08x:0x%08x\r\n",&RTC->ALRMAR,RTC->ALRMAR); } void WakeUp::standby_then_reset(uint32_t ms) { DBG("Step(%u),ms:%d\r\n", __LINE__, ms); if (ms == 0){ // just go to Reset __NVIC_SystemReset(); } set_ms(ms); use_reset = true; 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 if (use_reset == true){ NVIC_SystemReset(); } else { if (callback){ callback.call(); } } } void WakeUp::calibrate(void) { //RTC, we assume it is accurate enough without calibration } #endif