Added Restart(by RESET) function from Standby mode only for some Nucleo boards (STM32 series)

Dependencies:   LPC1114_WakeInterruptIn

Dependents:   Check_StandBy

Fork of WakeUp by Erik -

Example program using "Standby function" for Nucleo series is here.
/users/kenjiArai/code/Check_StandBy/

Device/WakeUp_STM32_others.cpp

Committer:
kenjiArai
Date:
2017-10-03
Revision:
26:df9d01556394
Parent:
25:2bd9df8c3ac8

File content as of revision 26:df9d01556394:

// 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 0
#define DBG(...)   pc.printf(__VA_ARGS__)
#else
#define DBG(...)   {;}
#endif

#if 0
#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