AnsweredAssumed Answered

RTC time calculation in stop mode.

Question asked by sandaris.marius on Jul 5, 2016
Latest reply on Jul 5, 2016 by FTITI.Walid
Hello, I'm trying to put my STM32F105 device into stop mode and it's important for me to keep calculating time. I'm trying to put system time into backup registers but then it somehow messes up my system. It seems like somewhere in the middle of stop mode the prescaler gets lost and RTC->CNT value starts increment extremely fast. To be more specific I'll give some details about my RTC configuration and entrance to stop mode code.
void RTC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure; // Configure the interrupt handler
     
        /* Enable PWR and BKP clock */
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
         
         /* Enable WKUP pin */
        //PWR_WakeUpPinCmd(ENABLE);
 
        /* Allow access to BKP Domain */
        PWR_BackupAccessCmd(ENABLE);
     
     
      //SystemTime = BKP_ReadBackupRegister(BKP_DR1);
      //SystemTime <<= 16;
      //SystemTime |= BKP_ReadBackupRegister(BKP_DR2);
     
 
 
 
        /* RTC clock source configuration ----------------------------------------*/
        /* Reset Backup Domain */
        BKP_DeInit();
 
 
     
        /* Enable LSE OSC */
        RCC_LSEConfig(RCC_LSE_ON);
        /* Wait till LSE is ready */
        osDelay(1000);
        if ( RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET )
        {
            //nepasileido
             
            DeviceState.WorkingLSI40KHZ = 1;
             
            //isjungiam isorini kvarca
            RCC_LSEConfig(RCC_LSE_OFF);
             
            //ijungiam vidini 40KHz RC rezonatoriu
            RCC_LSICmd(ENABLE);
            while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET )
            {
                osDelay(100);
            }
            RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
             
            /* Enable the RTC Clock */
            RCC_RTCCLKCmd(ENABLE);
             
            /* RTC configuration -----------------------------------------------------*/
            /* Wait for RTC APB registers synchronisation */
            RTC_WaitForSynchro();
             
            /* Set the RTC time base to 2s */
            lastPrescaler = 79999;
            RTC_SetPrescaler(lastPrescaler);
             
             
            RTC_WaitForLastTask();         
             
        }
        else
        {
            //pasileido
            /* Select the RTC Clock Source */
            RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
             
            /* Enable the RTC Clock */
            RCC_RTCCLKCmd(ENABLE);
             
            /* RTC configuration -----------------------------------------------------*/
            /* Wait for RTC APB registers synchronisation */
            RTC_WaitForSynchro();
             
            /* Set the RTC time base to 2s */
            lastPrescaler = 65535;
            RTC_SetPrescaler(lastPrescaler);
             
            RTC_WaitForLastTask();         
        }
         
         
        RTC_ClearITPendingBit(RTC_IT_OW);
    RTC_ITConfig(RTC_IT_SEC, ENABLE); // Enables overflow interrupt
    RTC_WaitForLastTask(); // Wait until last write operation on RTC registers has finished
                 
         
    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
I'm currently using external crystal oscillator for RTC clocking and I'm not sure how it works while my device goes into stop mode. Shall I change RTC's oscillator from LSE to LSI before going to low power mode? In order to synchronize time every hour I update RTC->CNT value with time from NTP servers using RTC_SetCounter(time); commands but for some reason it seems like my code works better without SetCounter commands, maybe there's a must to use commands like RTC_WaitForLastTask(); after storing time value into RTC registers ?
To give you a better view of where I struggle I'm also giving my code of where I go into stop mode and RTC interrupt handler part
void RTC_IRQHandler(void)
{
    // @todo RTC interrupt handler
    if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
    {  
        RTC_ClearITPendingBit(RTC_IT_SEC);
         
        TickFromReset++;
        if ( SystemTime > 0) SystemTime++;
         
        //BKP_WriteBackupRegister(BKP_DR2, SystemTime & 0xFFFF);
        //BKP_WriteBackupRegister(BKP_DR1, SystemTime >> 16 );     
        GPS_Coord_Sync_05Hz();
        if (Sleep.SleepingThread.IsSleeping > 0)
        {
//          if ((FixedInputs & 0x10) > 0)
//          {
////                osSemaphoreRelease(wakeUpSemaphore);
//              Sleep.WakeUp = 1;
//              Sleep.TimeTillSleep = 15;
//          }
//          // patikrinti
            #ifdef NODEBUG
            IWDG_ReloadCounter();
            #endif
        }
        else if ((FixedInputs & 0x10) == 0 && !Sleep.SleepEnabled)
        {
            if (Sleep.TimeTillSleep > 0)
                Sleep.TimeTillSleep--;
            else
            {
                Sleep.SleepEnabled = 1;
            }
        }
    }  
}//I know that some of those might not make any sense to you so just stay with hardware configuration part
 
//There's my idle os task---->
 
void os_idle_demon (void) {
  /* The idle demon is a system thread, running when no other thread is      */
  /* ready to run.                                                           */
 
  for (;;)
    {
    /* HERE: include optional user code to be executed when no thread runs.*/
        if (Sleep.SleepEnabled == 1)
        {
            if (Sleep.WakeUp == 1) //is sleeping and has to wake up now
            {
                SystemClocksInit(); //needed to configure system clocks after wake up from stop mode
                SystemTime = RTC_GetCounter(); //i think this might break my system, but has to update system time variable from RTC registers
//              RTC_WaitForLastTask(); //not sure if needed
                flash_size(); //external flash configuration
                Sleep.ADCnFlashSleep = 0; //this is basically used for "last touch" before going to stop mode
                Sleep.TimeTillSleep = 30; //sort of a timer used in order to prevent fast toggle from stop to running power modes
                Sleep.WakeUp = 0;
                Sleep.SleepEnabled = 0;
                osSemaphoreRelease(wakeUpSemaphore);               
            }
            else if (Sleep.SleepingThread.IsSleeping == 0x3FF) //check if all threads have semaphores waiting for wake up event
            {              
//              __wfi();
                if (Sleep.ADCnFlashSleep == 0) //last system configuration before going to sleep
                {
//                  adcmanager_deinit(); //this is supposed to turn of MCU's ADC1 clock and make ADC inputs go to IN_FLOATING_MODE
                    //but then for some reason after this MCU doesnt go into stop mode
                    //Not sure if RTC clock configuration is needed here
                    RTC_SetCounter(SystemTime);
//                  RTC_WaitForLastTask(); //not sure if needed
                    flash_deinit();
                    Sleep.ADCnFlashSleep = 1;
                }
                PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI); //PWR_Regulator_LowPower
            }
        }
    }
}

Would be grateful if you shared your experiences with time calculation in low power modes :)

Outcomes