RTC alarm to wake up from deep sleep?

Status
Not open for further replies.

WMXZ

Well-known member
I face the following task:

1) run program for say 1 min.
2) stop Teensy for say 10 min (lowest possible consumption)
3) wakeup using RTC alarm
4) continue with 1)

OK, I could use Snooze.
BUT, I wanted to understand first the principle.
Unfortunately, Snooze handles all possible wake-up sources and becomes difficult to understand.
So, I searched the forum and Freescale's AN4503 among others and generated the following program

Code:
/* Seting Alarm */
#define RTC_IER_TAIE_MASK       0x4u

static void rtcISR( void ) 
{
   digitalWrite(LED_BUILTIN, HIGH);
   delay(200);
   digitalWrite(LED_BUILTIN, LOW);

   NVIC_DISABLE_IRQ( IRQ_RTC_ALARM );
   RTC_IER = 0;
}

void rtcSetup(void)
{
   SIM_SCGC6 |= SIM_SCGC6_RTC;// enable RTC clock
   RTC_CR |= RTC_CR_OSCE;// enable RTC
  
   attachInterruptVector( IRQ_RTC_ALARM, rtcISR );
}

void rtcSetAlarm(uint32_t nsec)
{
   RTC_TAR = RTC_TSR + nsec;
   RTC_IER = RTC_IER_TAIE_MASK;
   NVIC_ENABLE_IRQ( IRQ_RTC_ALARM );
}

/******************LLWU********************/
#define LLWU_PIN_ANY             0x03

#define LLWU_PE3_WUPE9_MASK      0xCu
#define LLWU_PE3_WUPE9_SHIFT     2
#define LLWU_PE3_WUPE9(x)        ((uint8_t)(((uint8_t)(x))<<LLWU_PE3_WUPE9_SHIFT))

#define LLWU_ME_WUME5_MASK       0x20u

static void llwuISR(void)
{
    NVIC_DISABLE_IRQ( IRQ_LLWU ); // disable wakeup isr
    rtcISR();
}

void llwuSetup(void)
{
   attachInterruptVector( IRQ_LLWU, llwuISR );
   NVIC_SET_PRIORITY( IRQ_LLWU, 2*16 );
}

void llwuEnable(void)
{  
   NVIC_CLEAR_PENDING( IRQ_LLWU );
   NVIC_ENABLE_IRQ( IRQ_LLWU );
        //
   LLWU_PE1 = 0;
   LLWU_PE2 = 0;
   LLWU_PE3 = LLWU_PE3_WUPE9 ( LLWU_PIN_ANY ); // rtc alarm
   LLWU_PE4 = 0;
   LLWU_ME  = LLWU_ME_WUME5_MASK; //rtc alarm
}

void llwuDisable(void)
{  LLWU_PE1 = 0;
   LLWU_PE2 = 0;
   LLWU_PE3 = 0;
   LLWU_PE4 = 0;
   LLWU_ME  = 0;
}

/********************* go to deep sleep *********************/
#define SMC_PMPROT_AVLLS_MASK   0x2u
#define SMC_PMCTRL_STOPM_MASK   0x7u

#define SCB_SCR_SLEEPDEEP_MASK  0x4u

void goSleep(void)
{   /* Make sure clock monitor is off so we don't get spurious reset */
    MCG_C6 &= ~MCG_C6_CME0;

   /* 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(0x3); // VLLS3
   /*wait for write to complete to SMC before stopping core */
   (void) SMC_VLLSCTRL;

   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)
       asm volatile( "wfi" );  // WFI instruction will start entry into STOP mode
   SCB_SCR &= ~SCB_SCR_SLEEPDEEP_MASK; // Clear the SLEEPDEEP bit
   SYST_CSR |= SYST_CSR_TICKINT;       // renable systick timer interrupt
   //
   /* Turn back on external clock monitor */
   MCG_C6 |= MCG_C6_CME0;

}

void setup() {
   // put your setup code here, to run once:
   pinMode(LED_BUILTIN, OUTPUT);
   rtcSetup();
   llwuSetup();
}

void loop() {
   // put your main code here, to run repeatedly:
   //
   rtcSetAlarm(5);
   //
   llwuEnable();
     goSleep();
   llwuDisable();
   //
   delay(10000);
   digitalWrite(LED_BUILTIN, HIGH);
   delay(1000);
   digitalWrite(LED_BUILTIN, LOW);
}

Obviously, program does not work. The program never continues after the "wfi" statement.
The LED flashes once for 200 ms so the rtcISR is called, but goSleep never returns and the 1s LED is never happening.
Commenting the goSleep() statement in loop lets the led flash as designed, so RTC-Alarm setup is working.
I have the feeling that I miss somewhere an instruction to tell the system to use the RTC alarm as wakeup source.
Unfortunately, the simple 'tutorials' or examples, I found, use mostly external interrupts, and not RTC alarms.

Any help to get this program running is appreciated.
 
Solved

I face the following task:

1) run program for say 1 min.
2) stop Teensy for say 10 min (lowest possible consumption)
3) wakeup using RTC alarm
4) continue with 1)

OK, I could use Snooze.
BUT, I wanted to understand first the principle.
Unfortunately, Snooze handles all possible wake-up sources and becomes difficult to understand.
So, I searched the forum and Freescale's AN4503 among others and generated the following program

.....

Obviously, program does not work. The program never continues after the "wfi" statement.
The LED flashes once for 200 ms so the rtcISR is called, but goSleep never returns and the 1s LED is never happening.
Commenting the goSleep() statement in loop lets the led flash as designed, so RTC-Alarm setup is working.
I have the feeling that I miss somewhere an instruction to tell the system to use the RTC alarm as wakeup source.
Unfortunately, the simple 'tutorials' or examples, I found, use mostly external interrupts, and not RTC alarms.

Any help to get this program running is appreciated.

OK,
solved it.

here comes code that runs forever
Code:
void flashLed(int del)
{
   pinMode(LED_BUILTIN, OUTPUT);
   digitalWrite(LED_BUILTIN, HIGH);
   delay(del);
   digitalWrite(LED_BUILTIN, LOW);
   pinMode(LED_BUILTIN, INPUT);
}

/******************* Seting Alarm **************************/
#define RTC_IER_TAIE_MASK       0x4u
#define RTC_SR_TAF_MASK         0x4u

void rtcSetup(void)
{
   SIM_SCGC6 |= SIM_SCGC6_RTC;// enable RTC clock
   RTC_CR |= RTC_CR_OSCE;// enable RTC
}

void rtcSetAlarm(uint32_t nsec)
{
   RTC_TAR = RTC_TSR + nsec;
   RTC_IER |= RTC_IER_TAIE_MASK;
   NVIC_CLEAR_PENDING( IRQ_RTC_ALARM );
   NVIC_ENABLE_IRQ( IRQ_RTC_ALARM );
   NVIC_SET_PRIORITY( IRQ_RTC_ALARM, 2*16 );
}

/********************LLWU**********************************/
#define LLWU_ME_WUME5_MASK       0x20u
#define LLWU_F3_MWUF5_MASK       0x20u

static void llwuISR(void)
{
    //
    LLWU_F3 |= LLWU_F3_MWUF5_MASK; // clear source in LLWU Flag register
    //
    RTC_IER = 0;// clear RTC interrupts
}

void llwuSetup(void)
{
   attachInterruptVector( IRQ_LLWU, llwuISR );
   NVIC_SET_PRIORITY( IRQ_LLWU, 2*16 );
//
   NVIC_CLEAR_PENDING( IRQ_LLWU );
   NVIC_ENABLE_IRQ( IRQ_LLWU );
//
   LLWU_PE1 = 0;
   LLWU_PE2 = 0;
   LLWU_PE3 = 0;
   LLWU_PE4 = 0;
   LLWU_ME  = LLWU_ME_WUME5_MASK; //rtc alarm
}

/********************* go to deep sleep *********************/
#define SMC_PMPROT_AVLLS_MASK   0x2u
#define SMC_PMCTRL_STOPM_MASK   0x7u
#define SCB_SCR_SLEEPDEEP_MASK  0x4u

void goSleep(void)
{  //volatile unsigned int dummyread;
  /* Make sure clock monitor is off so we don't get spurious reset */
   MCG_C6 &= ~MCG_C6_CME0;
   //
   /* 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(0x3); // VLLS3
   /*wait for write to complete to SMC before stopping core */
   (void) SMC_PMCTRL;

   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)
       asm volatile( "wfi" );  // WFI instruction will start entry into STOP mode
   // will never return, but generates system reset
}

void setup() {
   //
   // put your setup code here, to run once:
   flashLed(100);
   //
   rtcSetup();
   llwuSetup();
   
   rtcSetAlarm(5);
   goSleep();
}

void loop() {
   // put your main code here, to run repeatedly:
   //
}

For those that are interested:
using my own words:
VLLS deep sleep never returns from "wfi", but generates a system reset. i.e. program starts from beginning.
Edit: that is, running ResetHandler() in mk20dx128.c.

problem with earlier program was that RTC alarm interrupt request needed to be cleared before setting it a second time.
No rtcISR is needed as RTC-alarm interrupt is first handled by LLWU.

Not sure if above program is smallest and if it is robust, but demonstrates what I wanted to know. QED
 
Last edited:
Nicely done! I tried this out and got it to work for me. I added a blink led portion in setup and saw first the program led and then the builtin led (really just two of three leds I have available) blink in turn. I tried a serial write in setup but it doesn't work for some reason; maybe I didn't give it enough time (>10 s?) to instantiate.

This is akin to the deepSleep function on the ESP8266. For remote logging operation it can be very useful to have the device sleep with the lowest power consumption and then come alive for a short period of time to gather and store data.

I suppose the same functionality can be obtained using the Snooze library; I haven't really tried but the few half-hearted attempts to use this were not entirely successful. Guess it's time to take another whack at it.

I did notice that if I had the program led function time set to 100 ms the leds just cycles quietly, but when I increased the time to 1000 ms I got the thunk-kthunk of Windows minding the Teensy reset and occasionally an error from Windows that my USB device was malfunctioning!
 
Last edited:
Nicely done! I tried this out and got it to work for me. I added a blink led portion in setup and saw first the program led and then the builtin led (really just two of three leds I have available) blink in turn. I tried a serial write in setup but it doesn't work for some reason; maybe I didn't give it enough time (>10 s?) to instantiate.

This is akin to the deepSleep function on the ESP8266. For remote logging operation it can be very useful to have the device sleep with the lowest power consumption and then come alive for a short period of time to gather and store data.

I suppose the same functionality can be obtained using the Snooze library; I haven't really tried but the few half-hearted attempts to use this were not entirely successful. Guess it's time to take another whack at it.

I did notice that if I had the program led function time set to 100 ms the leds just cycles quietly, but when I increased the time to 1000 ms I got the thunk-kthunk of Windows minding the Teensy reset and occasionally an error from Windows that my USB device was malfunctioning!

I must say that after a day, or so, inverse engineering other people's programs, comparing to application notes and googling the internet, I was happy that I got a running system that, as I believe may be even close to correct. It was not my intention to replace for example duff's Snooze library, but to learn something new. So I'm not sure what the limits of this test program are, in particular in the context of a real function program with a lot of peripherals running. I'm also not sure that program handles VLLS exit properly.

There are also other modes that may return from "wfi" and not generate a system reset, but here comes duff's Snooze into play.
 
This code looks good to me expect that you can for go enabling the RTC isr. It can mask the LLWU interrupt and it will crash, just clear all the rtc flags and such in the LLWU interrupt. LLS to VLLSx sleep modes use a different isr control unit than the NVIC.
Code:
void flashLed(int del)
{
   pinMode(LED_BUILTIN, OUTPUT);
   digitalWrite(LED_BUILTIN, HIGH);
   delay(del);
   digitalWrite(LED_BUILTIN, LOW);
   pinMode(LED_BUILTIN, INPUT);
}

/******************* Seting Alarm **************************/
#define RTC_IER_TAIE_MASK       0x4u
#define RTC_SR_TAF_MASK         0x4u

void rtcSetup(void)
{
   SIM_SCGC6 |= SIM_SCGC6_RTC;// enable RTC clock
   RTC_CR |= RTC_CR_OSCE;// enable RTC
}

void rtcSetAlarm(uint32_t nsec)
{
   RTC_TAR = RTC_TSR + nsec;
   RTC_IER |= RTC_IER_TAIE_MASK;
   NVIC_CLEAR_PENDING( IRQ_RTC_ALARM );
   NVIC_ENABLE_IRQ( IRQ_RTC_ALARM );// Don't set when using LLS or any VLLS sleep modes, use the LLWU isr to clear the flags
   NVIC_SET_PRIORITY( IRQ_RTC_ALARM, 2*16 );
}

/********************LLWU**********************************/
#define LLWU_ME_WUME5_MASK       0x20u
#define LLWU_F3_MWUF5_MASK       0x20u

static void llwuISR(void)
{
    //
    LLWU_F3 |= LLWU_F3_MWUF5_MASK; // clear source in LLWU Flag register
    //
    RTC_IER = 0;// clear RTC interrupts
}

void llwuSetup(void)
{
   attachInterruptVector( IRQ_LLWU, llwuISR );
   NVIC_SET_PRIORITY( IRQ_LLWU, 2*16 );
//
   NVIC_CLEAR_PENDING( IRQ_LLWU );
   NVIC_ENABLE_IRQ( IRQ_LLWU );
//
   LLWU_PE1 = 0;
   LLWU_PE2 = 0;
   LLWU_PE3 = 0;
   LLWU_PE4 = 0;
   LLWU_ME  = LLWU_ME_WUME5_MASK; //rtc alarm
}

/********************* go to deep sleep *********************/
[COLOR=#ff0000]// These are defined in the core now[/COLOR]
#define SMC_PMPROT_AVLLS_MASK   0x2u
#define SMC_PMCTRL_STOPM_MASK   0x7u
#define SCB_SCR_SLEEPDEEP_MASK  0x4u

void goSleep(void)
{  //volatile unsigned int dummyread;
  /* Make sure clock monitor is off so we don't get spurious reset */
[COLOR=#ff0000]   // currently not set by anything I know, so the clock monitor is not set from reset[/COLOR]
   MCG_C6 &= ~MCG_C6_CME0;
   //
   /* Write to PMPROT to allow all possible power modes */
   SMC_PMPROT = SMC_PMPROT_AVLLS_MASK;[COLOR=#ff0000]// Not needed already taken care of in [URL="https://github.com/PaulStoffregen/cores/blob/master/teensy3/mk20dx128.c#L745"]here[/URL].[/COLOR]
   /* 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(0x3); // VLLS3
   /*wait for write to complete to SMC before stopping core */
   (void) SMC_PMCTRL;

   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), [COLOR=#ff0000]already defined in the latest core[/COLOR]
       asm volatile( "wfi" );  // WFI instruction will start entry into STOP mode
   // will never return, but generates system reset
}

void setup() {
   //
   // put your setup code here, to run once:
   flashLed(100);
   //
   rtcSetup();
   llwuSetup();
   
   rtcSetAlarm(5);
   goSleep();
}

void loop() {
   // put your main code here, to run repeatedly:
   //
}
 
This code looks good to me expect that you can for go enabling the RTC isr. It can mask the LLWU interrupt and it will crash, just clear all the rtc flags and such in the LLWU interrupt. LLS to VLLSx sleep modes use a different isr control unit than the NVIC.

Thanks duff
I did the suggested change and used
Code:
void rtcSetAlarm(uint32_t nsec)
{
   RTC_TAR = RTC_TSR + nsec;
   RTC_IER |= RTC_IER_TAIE_MASK;
}
and program continued to work.
 
The following code was tested on Teensy 3.6

Code:
void flashLed(int del)
{
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(del);
  digitalWrite(LED_BUILTIN, LOW);
  pinMode(LED_BUILTIN, INPUT);
}

/******************* Seting Alarm **************************/
#define RTC_IER_TAIE_MASK       0x4u
#define RTC_SR_TAF_MASK         0x4u

void rtcSetup(void)
{
  SIM_SCGC6 |= SIM_SCGC6_RTC;// enable RTC clock
  RTC_CR |= RTC_CR_OSCE;// enable RTC
}

void rtcSetAlarm(uint32_t nsec)
{
  RTC_TAR = RTC_TSR + nsec;
  RTC_IER |= RTC_IER_TAIE_MASK;
}

/********************LLWU**********************************/
#define WMUF5_MASK      0x20u

static void llwuISR(void)
{
#if defined(HAS_KINETIS_LLWU_32CH)
  LLWU_MF5 |= WMUF5_MASK;
#elif defined(HAS_KINETIS_LLWU_16CH)
  LLWU_F3 |= WMUF5_MASK; // clear source in LLWU Flag register
#endif
  RTC_IER = 0;// clear RTC interrupts
}

void llwuSetup(void)
{
  attachInterruptVector( IRQ_LLWU, llwuISR );
  NVIC_SET_PRIORITY( IRQ_LLWU, 2*16 );

  NVIC_CLEAR_PENDING( IRQ_LLWU );
  NVIC_ENABLE_IRQ( IRQ_LLWU );

  LLWU_PE1 = 0;
  LLWU_PE2 = 0;
  LLWU_PE3 = 0;
  LLWU_PE4 = 0;
  LLWU_ME  = LLWU_ME_WUME5; //rtc alarm
}

/********************* go to deep sleep *********************/
// These are defined in the core now
#define SMC_PMPROT_AVLLS_MASK   0x2u
#define SMC_PMCTRL_STOPM_MASK   0x7u
#define SCB_SCR_SLEEPDEEP_MASK  0x4u

void goSleep(void) {
  //volatile unsigned int dummyread;
  /* Make sure clock monitor is off so we don't get spurious reset */
  // currently not set by anything I know, so the clock monitor is not set from reset
  MCG_C6 &= ~MCG_C6_CME0;
  //
  /* Write to PMPROT to allow all possible power modes */
  SMC_PMPROT = SMC_PMPROT_AVLLS_MASK;// Not needed already taken care of in here.
  /* 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(0x3); // VLLS3
  /*wait for write to complete to SMC before stopping core */
  (void) SMC_PMCTRL;

  SYST_CSR &= ~SYST_CSR_TICKINT;      // disable systick timer interrupt
  SCB_SCR |= SCB_SCR_SLEEPDEEP_MASK;  // Set SLEEPDEEP bit to enable deep sleep mode (STOP)
  asm volatile( "wfi" );  // WFI instruction will start entry into STOP mode
  // will never return, but generates system reset
}

void setup() {
   //
   // put your setup code here, to run once:
   flashLed(100);
   //
   rtcSetup();
   llwuSetup();

   rtcSetAlarm(5);
   goSleep();
}

void loop() {
   // put your main code here, to run repeatedly:
   //
}
 
Last edited:
Status
Not open for further replies.
Back
Top