issue to reporogram T4.0

What I not understand is the following:
I tell SNVS to wake-up the processor, say in 1 minute.
I expect that alarm ISR is called after 1 minute, but MCU is not running, therefore alarm ISR can not run.
Also, power off means all RAM (including alarm ISR code) is lost.
So what the purpose of ISR?
commenting attachInterruptVector seems not work (no wake-up). I.e. the default ISR that has no "SNVS_LPSR |= 1;" and therefore seems not to work.
It seems that the alarm interrupt is called immediately after being enabled, but the wake-up is at the indicated time.

Edit, as '+' member could you please edit the title of this thread and remove the disturbing 'o'?

+Not: Thread titles are set in stone as they are the root of any link I suppose … Sorry the 'o' stays :(

I wasn't sure about the _isr() - it fires immediately on/at EXIT? Maybe that is the MCU giving an ACK that the timer has been activated? Firing going into Sleep means the CPU and RAM are still active - but it is about to sleep.

Putting more BLINK code in _isr() - the ISR does not fire the first time after programming - but it does each time after that? And it fires before Sleep not on Wake.

Also until the rtc_isr() exits - when called after the first time - the MCU does not enter sleep.

Here is edited code - will Sleep when any USB char is sent - not on a timer. Altered blink(#) calls for flash:
Code:
#include "core_pins.h"
#include "TimeLib.h"

/******************* Seting Alarm ***************/
#define SNVS_LPCR_LPTA_EN_MASK          (0x2U)

void rtc_stopAlarm()
{
   SNVS_LPSR |= 1;

   // disable alarm
   SNVS_LPCR &= ~SNVS_LPCR_LPTA_EN_MASK;
   while (SNVS_LPCR & SNVS_LPCR_LPTA_EN_MASK);
   // clear alarm value
   SNVS_LPTAR = 0;
   while (SNVS_LPTAR != 0);
}

void rtc_isr(void)
{
   digitalWriteFast(13, 1);
   delayMicroseconds(2000000); // to let USB write last line
      for ( int ic = 0; ic < 12; ic++ ) {
         blink( 40 );
         delay(50); // to let USB write last line
      }
   SNVS_LPSR |= 1;
   Serial.println("Alarm"); Serial.flush();// only to tell if ISR is called
   asm volatile ("DSB");
}

void rtc_setAlarm(uint32_t alarmSeconds, uint32_t prio = 13)
{  uint32_t tmp = SNVS_LPCR; //save control register

   /* disable SRTC alarm interrupt */
   rtc_stopAlarm();

   SNVS_LPTAR = alarmSeconds;
   while (SNVS_LPTAR != alarmSeconds);

   attachInterruptVector(IRQ_SNVS_IRQ, rtc_isr);
   NVIC_SET_PRIORITY(IRQ_SNVS_IRQ, prio * 16); // 8 is normal priority
   NVIC_DISABLE_IRQ(IRQ_SNVS_IRQ);
   NVIC_ENABLE_IRQ(IRQ_SNVS_IRQ);

   SNVS_LPCR = tmp | SNVS_LPCR_LPTA_EN_MASK; // restore control register and set alarm
   while (!(SNVS_LPCR & SNVS_LPCR_LPTA_EN_MASK));
}

uint32_t rtc_getAlarm()
{
   return SNVS_LPTAR;
}

void doShutdown(void)
{
   SNVS_LPCR |= (1 << 6) ; // turn off power
}

void setWakeupCallandSleep(uint32_t nsec)
{
   uint32_t to = now();
   rtc_setAlarm(to + nsec, 4);
   doShutdown();
   while (1) asm("wfi");
}


uint32_t blink( uint32_t tWait)
{  static uint32_t tx0;
   if (millis() > tx0 + tWait)
   {  digitalWriteFast(13, !digitalReadFast(13));
      tx0 = millis();
      Serial.println("blink");
      return 1;
   }
   return 0;
}

void setup() {
   Serial.begin(9600);
   while (!Serial);

   // set the Time library to use Teensy 3.0's RTC to keep time
   setSyncProvider((getExternalTime)Teensy3Clock.get);
   delay(100);
   if (timeStatus() != timeSet)
      Serial.println("Unable to sync with the RTC");
   else
      Serial.println("RTC has set the system time");
   //
   Serial.println("Start");
   rtc_stopAlarm();
   pinMode(13, OUTPUT);

}

void loop() {
   static uint32_t ic = 0;
   ic += blink( 1000 );
   if (Serial.available())
   //if (ic >= 20)
   {  Serial.println("End"); Serial.flush();
      for ( ic = 0; ic < 12; ic++ ) {
         blink( 40 );
         delay(50); // to let USB write last line
      }
      digitalWriteFast(13, 0);
      delay(400); // to let USB write last line
      setWakeupCallandSleep(2);
   }
}
 
I don't know if Arduino IDE users can make use of a .a library, but if so, here's a simple reboot() implementation based on the code above.

I assume that the NVIC_DISABLE_IRQ(IRQ_SNVS_IRQ) call in rtc_SetAlarm() would be better placed at the beginning of rtc_StopAlarm() instead. Otherwise there are no material changes in this version, other than organizing the code to remove Serial calls, loop/setup functions, etc.
 

Attachments

  • reboot.zip
    39.6 KB · Views: 92
I don't know if Arduino IDE users can make use of a .a library, but if so, here's a simple reboot() implementation based on the code above.

I assume that the NVIC_DISABLE_IRQ(IRQ_SNVS_IRQ) call in rtc_SetAlarm() would be better placed at the beginning of rtc_StopAlarm() instead. Otherwise there are no material changes in this version, other than organizing the code to remove Serial calls, loop/setup functions, etc.

No, that as .a doesn't work as I tried it.

Normal code as :: sketchbook\libraries\reboot holding the reboot.h and .cpp instead of .a does work with this sketch:
Code:
#include "reboot.h"

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 4000 );
  Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
}

void loop() {
	if ( Serial.available() ) reboot(2);
}
 
By the way, I also found that the ISR isn't necessary (and can be removed) the first time the boot cycle is performed by a given program. After the first reboot, subsequent reboots don't happen unless the ISR is installed and interrupts are enabled at the time rtc_setAlarm() programs the control register.

On further investigation, as long as somebody sets bit 0 of SNVS_LPSR, the boot cycle is consistently reliable. Whether that's done in the ISR or rtc_stopAlarm() doesn't seem to matter. SNVS_LPSR seems to be some kind of reset/acknowledge bit that is not otherwise initialized at startup time.

So it appears that the reboot() function can be further simplified below:
Code:
//
// Based on code from defragster (https://forum.pjrc.com/threads/29607-Over-the-air-updates/page3)
// and WMXZ (https://forum.pjrc.com/threads/58484-issue-to-reporogram-T4-0)
//

#include <core_pins.h>
#include <TimeLib.h>

#include <reboot.h>

#define SNVS_LPCR_LPTA_EN_MASK (0x2U)

//static void rtc_isr(void)
//{
//   SNVS_LPSR |= 1;
//   asm volatile("DSB");
//}

static void rtc_stopAlarm()      
{
//   NVIC_DISABLE_IRQ(IRQ_SNVS_IRQ);
   SNVS_LPSR |= 1;

   // disable alarm
   SNVS_LPCR &= ~SNVS_LPCR_LPTA_EN_MASK;
   while (SNVS_LPCR & SNVS_LPCR_LPTA_EN_MASK);

   // clear alarm value
   SNVS_LPTAR = 0;
   while (SNVS_LPTAR != 0);
}

static void rtc_setAlarm(uint32_t alarmSeconds, 
                         uint32_t prio=13)
{
   uint32_t tmp = SNVS_LPCR; // save control register

   rtc_stopAlarm();          // disable SRTC alarm interrupt

   SNVS_LPTAR = alarmSeconds;
   while (SNVS_LPTAR != alarmSeconds);

// attachInterruptVector(IRQ_SNVS_IRQ, rtc_isr);
//   NVIC_SET_PRIORITY(IRQ_SNVS_IRQ, prio * 16); // 8 is normal priority
//   NVIC_ENABLE_IRQ(IRQ_SNVS_IRQ);

   SNVS_LPCR = tmp | SNVS_LPCR_LPTA_EN_MASK; // restore control register and set alarm
   while (!(SNVS_LPCR & SNVS_LPCR_LPTA_EN_MASK));
}

void reboot(int delay_secs)
{
   // set the Time library to use Teensy 3/4 RTC to keep time
   setSyncProvider((getExternalTime) Teensy3Clock.get);
   delay(100);

   rtc_setAlarm(now() + delay_secs);

   __disable_irq();
   SNVS_LPCR |= (1 << 6); // turn off power

   while (1) asm("wfi");
}
 
This should be #ifdef ?_1062_? since it is MCU specific?

Also we know restart times under 2 are suspect or fail - it would be good perhaps to add:
Code:
if ( delay_secs <2 ) delay_secs=2;

Indeed observed the _isr() trigger is not called after first 'PJRC Bootloader' programming - so if that can be set to unneeded that would be best.

However the Wake time of 2 seconds is still FIXED in the Low Power system. So every time the Power Off is issues with that button/pad - the MCU immediately WAKES for the expired timer. See prior posts this thread, until the timer is canceled on power up { with what was in rtc_stopAlarm(); called in setup() } the Power button is rendered ODD - as WMXZ first noted when creating this thread.
 
generated separated files
I tested with T4.0 and T3.6

hibernate.h
Code:
#ifndef _HIBERNATE_H_
#define _HIBERNATE_H_
    void rtc_clearAlarm();
    void setWakeupCallandSleep(uint32_t nsec);
#endif

hibernate.cpp
Code:
#include "core_pins.h"
#include "hibernate.h"

#include "TimeLib.h"
  
  #if defined (__IMXRT1062__)
    #define SNVS_LPCR_LPTA_EN_MASK          (0x2U)
    
    void rtc_clearAlarm()
    {
      SNVS_LPSR |= 1;
      SNVS_LPCR &= ~SNVS_LPCR_LPTA_EN_MASK;
      while (SNVS_LPCR & SNVS_LPCR_LPTA_EN_MASK); 
      SNVS_LPTAR=0;
    }
    
    
    void rtc_setAlarm(uint32_t alarmSeconds)
    {   uint32_t tmp = SNVS_LPCR; //save control register
    
        /* disable SRTC alarm interrupt */
        rtc_clearAlarm();
    
        SNVS_LPTAR=alarmSeconds;
        while(SNVS_LPTAR != alarmSeconds);
    
        SNVS_LPCR = tmp | SNVS_LPCR_LPTA_EN_MASK; // restore control register and set alarm
        while(!(SNVS_LPCR & SNVS_LPCR_LPTA_EN_MASK)); 
    }
    
    uint32_t rtc_getAlarm()
    {
      return SNVS_LPTAR;  
    }
    
    void setWakeupCallandSleep(uint32_t nsec)
    {
      uint32_t to=now();
      rtc_setAlarm(to+nsec);
      SNVS_LPCR |=(1<<6) ; // turn off power
      while(1) continue;
    }
    
  #elif defined(KINETISK) 
      #define RTC_IER_TAIE_MASK       0x4u
      #define RTC_SR_TAF_MASK         0x4u
      
      void rtcSetAlarm(uint32_t nsec)
      { 
         SIM_SCGC6 |= SIM_SCGC6_RTC;// enable RTC clock
         RTC_CR |= RTC_CR_OSCE;// enable RTC
         // set alarm nsec seconds in the future
         RTC_TAR = RTC_TSR + nsec;
         RTC_IER |= RTC_IER_TAIE_MASK;
         while(!(RTC_IER & RTC_IER_TAIE_MASK));
      }
      
      /********************LLWU**********************************/
      #define LLWU_ME_WUME5_MASK       0x20u
      #define LLWU_F3_MWUF5_MASK       0x20u
      #define LLWU_MF5_MWUF5_MASK      0x20u

      static void llwuSetup(void)
      {
        LLWU_PE1 = 0;
        LLWU_PE2 = 0;
        LLWU_PE3 = 0;
        LLWU_PE4 = 0;
      #if defined(HAS_KINETIS_LLWU_32CH)
        LLWU_PE5 = 0;
        LLWU_PE6 = 0;
        LLWU_PE7 = 0;
        LLWU_PE8 = 0;
      #endif
        LLWU_ME  = LLWU_ME_WUME5_MASK; //rtc alarm
        //   
        SIM_SOPT1CFG |= SIM_SOPT1CFG_USSWE;
        SIM_SOPT1 |= SIM_SOPT1_USBSSTBY;
        //
      }
      
      /********************* go to deep sleep *********************/
      #define SMC_PMPROT_AVLLS_MASK   0x2u
      #define SMC_PMCTRL_STOPM_MASK   0x7u
      #define SCB_SCR_SLEEPDEEP_MASK  0x4u
      
      // see SMC section (e.g. p 339 of K66) 
      #define VLLS3 0x3 // RAM retained I/O states held
      #define VLLS2 0x2 // RAM partially retained
      #define VLLS1 0x1 // I/O states held
      #define VLLS0 0x0 // all stop
      
      #define VLLS_MODE VLLS0
      static void gotoSleep(void)
      {  
      //  /* Make sure clock monitor is off so we don't get spurious reset */
         MCG_C6 &= ~MCG_C6_CME0;
         //
      
      // if K66 is running in highspeed mode (>120 MHz) reduce speed
      // is defined in kinetis.h and mk20dx128c
      #if defined(HAS_KINETIS_HSRUN) && F_CPU > 120000000
          kinetis_hsrun_disable( );
      #endif   
         SYST_CSR &= ~SYST_CSR_TICKINT;      // disable systick timer interrupt
         SCB_SCR |= SCB_SCR_SLEEPDEEP_MASK;  // Set the SLEEPDEEP bit to enable deep sleep mode (STOP)
         
         /* Write to PMPROT to allow all possible power modes */
         SMC_PMPROT = SMC_PMPROT_AVLLS_MASK;
         /* Set the STOPM field to 0b100 for VLLSx mode */
         SMC_PMCTRL &= ~SMC_PMCTRL_STOPM_MASK;
         SMC_PMCTRL |= SMC_PMCTRL_STOPM(0x4); // VLLSx
      
         SMC_VLLSCTRL =  SMC_VLLSCTRL_VLLSM(VLLS_MODE);
         /*wait for write to complete to SMC before stopping core */
         (void) SMC_PMCTRL;

         asm volatile( "wfi" );  // WFI instruction will start entry into STOP mode
         // will never return, but wake-up results in call to ResetHandler() in mk20dx128.c
      }
      
      void rtc_clearAlarm(){}
      
      void setWakeupCallandSleep(uint32_t nsec)
      {  // set alarm to nsec secods in future and go to hibernate
         llwuSetup();
         
         rtcSetAlarm(nsec);
         gotoSleep();
      }
    
    #endif
my test program is
Code:
#include "core_pins.h"
#include "hibernate.h"

uint32_t blink(void)
{ static uint32_t tx0;
  if(millis()>tx0+1000) 
  { digitalWriteFast(13,!digitalReadFast(13));
    tx0=millis();
    Serial.println("blink");
    return 1;
  }
  return 0;
}

void setup() {
  Serial.begin(9600);
  while (!Serial);
  Serial.println("\n" __FILE__ "\n " __DATE__ " " __TIME__);
  
  // set the Time library to use Teensy 3.0's RTC to keep time
  setSyncProvider(Teensy3Clock.get);
  delay(100);
  if (timeStatus()!= timeSet) 
    Serial.println("Unable to sync with the RTC");
  else 
    Serial.println("RTC has set the system time");
  //
  Serial.println("Start");
  rtc_clearAlarm(); // just in case
  pinMode(13,OUTPUT);
}

void loop() {
  static uint32_t ic=0;
  ic += blink();
  if(ic>=20)
  { Serial.println("End"); Serial.flush();
    delay(1000); // to let USB write last line
    setWakeupCallandSleep(2);
  }
}

I stick to hibernate and "setWakeupCallandSleep" for historic reason.
Everyone may feel free to change filename and content.
 
For the records, this does the same, but without inlcuding "timelib", is shorter and is a single function:
Code:
void arm_reset() {
  uint32_t tmp = SNVS_LPCR; // save control register

  SNVS_LPSR |= 1;

  // disable alarm
  SNVS_LPCR &= ~0x02;
  while (SNVS_LPCR & 0x02);

  __disable_irq();

  //get Time:
  uint32_t lsb, msb;
  do {
    msb = SNVS_LPSRTCMR;
    lsb = SNVS_LPSRTCLR;
  } while ( (SNVS_LPSRTCLR != lsb) | (SNVS_LPSRTCMR != msb) );
  uint32_t secs = (msb << 17) | (lsb >> 15);

  //set alarm
  secs += 2;
  SNVS_LPTAR = secs;
  while (SNVS_LPTAR != secs);

  SNVS_LPCR = tmp | 0x02; // restore control register and set alarm
  while (!(SNVS_LPCR & 0x02));

  SNVS_LPCR |= (1 << 6); // turn off power
  while (1) asm("wfi");  
}

I wished there was a way without using the RTC(??!)
 
Back
Top