Author Topic: Going back to sleep after an interrupt  (Read 2764 times)

stevenfalco

  • NewMember
  • *
  • Posts: 11
  • Country: us
Going back to sleep after an interrupt
« on: May 23, 2017, 12:10:22 PM »
I'm working on an app that uses the LowPower library to put the processor to sleep.  The app also uses interrupts from contact closures.  When one of those interrupts happens, the processor wakes up.  I want it to go right back to sleep until the actual watchdog interrupt happens.

I've made a change to the library to add functions like LowPower.powerExtStandbyWait();  The new functions each take a bool that specifies whether to 1) wait for a watchdog interrupt or to 2) wake up on any interrupt.

I've attached a proposed patch that adds these functions.  Is there any interest in adding something like this to the upstream library?  If so, what is the best way to submit it?

Steve

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: Going back to sleep after an interrupt
« Reply #1 on: May 23, 2017, 02:31:00 PM »
Not to rain on original work, but, IMO, what exists works.  You can test the type of interrupt on return from LowPower.powerdown() and simply loop back if it's not matching your needs - much more flexible and proven.

Tom

stevenfalco

  • NewMember
  • *
  • Posts: 11
  • Country: us
Re: Going back to sleep after an interrupt
« Reply #2 on: May 23, 2017, 04:25:20 PM »
The problem is that when you return from LowPower.powerdown() you don't know how long you have slept for, so you don't know how much time remains.  I.e., say you mean to sleep for 8 seconds and that is what you set the watchdog for.  Now an interrupt wakes you up after 3 seconds.  How do you know there are 5 seconds left to sleep, and how would you do that, given that you cannot set the watchdog for 5 seconds.  Yes there are ways to do all that, for example using separate asynchronous timers, but that is a lot harder, IMO.

Also, my method avoids turning on and turning off the timers, adc, etc, because I have the loop inside LowPower.powerdown().  Thus I can go right back to sleep without touching any of the clock enables.  That makes things slightly more efficient.

That said, if folks don't see a value in the changes, that is fine.  They meet my needs.  :)

If anybody does want them, they are available here: https://github.com/stevefalco/LowPower

Steve

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: Going back to sleep after an interrupt
« Reply #3 on: May 23, 2017, 05:53:18 PM »
The problem is that when you return from LowPower.powerdown() you don't know how long you have slept for, so you don't know how much time remains.  I.e., say you mean to sleep for 8 seconds and that is what you set the watchdog for.  Now an interrupt wakes you up after 3 seconds.  How do you know there are 5 seconds left to sleep, and how would you do that, given that you cannot set the watchdog for 5 seconds.  Yes there are ways to do all that, for example using separate asynchronous timers, but that is a lot harder, IMO.

Also, my method avoids turning on and turning off the timers, adc, etc, because I have the loop inside LowPower.powerdown().  Thus I can go right back to sleep without touching any of the clock enables.  That makes things slightly more efficient.

That said, if folks don't see a value in the changes, that is fine.  They meet my needs.  :)

If anybody does want them, they are available here: https://github.com/stevefalco/LowPower

Steve
Fair enough and thank you for contributing.   Hopefully others can use this and contribute as well.

You are correct about the timing uncertainty if you are relying entirely on the WDT and you have other interrupt sources.   Check around the forum, there has been some good work on quantifying the accuracy of the WDT and a few proposals on how to deal with this using various hardware methods as alternative timing sources.

And welcome to the forum!

Tom

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: Going back to sleep after an interrupt
« Reply #4 on: May 24, 2017, 09:56:34 AM »
Steve,
Thanks a ton for this work, it's something a few folks have been struggling with for sure.
Since the changes are so extensive (I just looked at diffs) I will have to do testing on my own to ensure there's no breakage and the lib still behaves the same with the old functions (no breaking changes introduced), before I would merge back into the master.

stevenfalco

  • NewMember
  • *
  • Posts: 11
  • Country: us
Re: Going back to sleep after an interrupt
« Reply #5 on: May 24, 2017, 10:01:11 AM »
Understood.  In order to avoid lots of duplicated code, I moved the logic into my new routines, which is why the diffs are large.  The old routines now just call the new ones, and pass in "0" for the "wait" flag.

If you see any problems, please let me know so I can make any appropriate changes.  I've tested with a moteino mega clone that I'm designing for a weather station.  So I've only tested with a 1284p.

phibes4000

  • NewMember
  • *
  • Posts: 8
  • Country: au
Re: Going back to sleep after an interrupt
« Reply #6 on: May 26, 2017, 05:55:36 AM »
In this vein, if implementing a crude clock using millis() is it possible to read the WDT at wakeup from an interrupt to determine the time lapsed since the MCU went to sleep? 

I.e. without adding an RTC or Steve's mods is there same way of using the current library to keep reasonably accurate time if calling LowPower.powerDown() but with an interrupt set (say a button push)?

Thanks,

Lee

stevenfalco

  • NewMember
  • *
  • Posts: 11
  • Country: us
Re: Going back to sleep after an interrupt
« Reply #7 on: May 26, 2017, 08:22:27 AM »
I don't think the watchdog is readable.  Looking at the data sheet, there is a prescaler with a number of taps, and that feeds the reset and interrupt logic directly.  I see only two registers: MCUSR and WDTCSR, neither of which allows you to look at the prescaler contents.

I'm adding a 32.768 kHz crystal to the TOSC[1:2] pins on my board so I can run timer 2 off an asynchronous clock and get RTC functionality without adding a separate chip.  For me, the crystal is a cheap and easy solution.

phibes4000

  • NewMember
  • *
  • Posts: 8
  • Country: au
Re: Going back to sleep after an interrupt
« Reply #8 on: May 26, 2017, 09:15:03 AM »
That's a great idea assuming TOSC is free/usable on these. Let me know how you go as if it works I will do the same.

perky

  • Hero Member
  • *****
  • Posts: 873
  • Country: gb
Re: Going back to sleep after an interrupt
« Reply #9 on: May 26, 2017, 09:43:09 AM »
There was a discussion some while back about this. Since each interrupt has its own ISR you can selectively reset the watchdog timer depending on whether a flag is set by the watchdog ISR when going back to sleep. In other words you still get regular watchdog interrupts that you can use to increment a time elapsed counter, while still allowing other interrupts in the meantime. You may have to roll your own routines to do this though.

Mark.

stevenfalco

  • NewMember
  • *
  • Posts: 11
  • Country: us
Re: Going back to sleep after an interrupt
« Reply #10 on: May 26, 2017, 10:53:40 AM »
That is pretty much what my code does.  I set a flag when the watchdog ISR runs, and I use that to determine whether to repeat the sleep instruction inside the LowPower library routines.  I.e. if there has not been a watchdog interrupt, then I repeat the sleep, because the requested delay time has not passed.  If there has been a watchdog interrupt, then I return from the LowPower library call, because the requested time has passed.

stevenfalco

  • NewMember
  • *
  • Posts: 11
  • Country: us
Re: Going back to sleep after an interrupt
« Reply #11 on: May 26, 2017, 12:19:16 PM »
phibes4000 - timer 2 works fine.  I hooked up a crystal (Digikey 300-8341-1-ND) which is pretty easy to tack onto my TQFP (44 lead) processor because it has little leads.  Just bend 'em a bit, and solder down.

I attached a little code to start the timer.  Just call setupT2() from your setup() routine, and the ISR will be called once per second.  Be sure to select the correct sleep mode for your program - it looks like you can use any sleep mode except "power-down" and "standby", because those two stop the timer, which kinda defeats the purpose. :-)

"power-save" mode is a reasonable choice.

phibes4000

  • NewMember
  • *
  • Posts: 8
  • Country: au
Re: Going back to sleep after an interrupt
« Reply #12 on: May 28, 2017, 06:49:41 AM »
Thanks Steve, I just realised from your post that you are using a mega - I am using a regular moteino with a 328P and a resonator at TOSC.  I will pick through your code to see if I can find any ideas, although a low-power RTC is starting to look tempting.

stevenfalco

  • NewMember
  • *
  • Posts: 11
  • Country: us
Re: Going back to sleep after an interrupt
« Reply #13 on: May 28, 2017, 08:11:20 AM »
I haven't played with the 328P much, but according to the data sheet, section 9.10:

When using the Timer/Counter Oscillator, the system clock needs to be four times the
oscillator frequency. Due to this and the pin sharing, the Timer/Counter Oscillator can only be used when the
Calibrated Internal RC Oscillator is selected as system clock source.


So I suppose you could replace the resonator with a 32.768 kHz crystal, use the internal 8 MHz RC oscillator for the main clock (with suitable fuse changes), and use the 32.768 kHz crystal for Timer 2.  But I haven't tried it so I don't know if there are any issues you would run into.

I attached a small lib I've started to manage timer 2.  It is very much a work in progress, and probably requires some #ifdef blocks to select the correct bits for 328p, but it may be useful to you.

phibes4000

  • NewMember
  • *
  • Posts: 8
  • Country: au
Re: Going back to sleep after an interrupt
« Reply #14 on: May 28, 2017, 08:53:25 AM »
I haven't played with the 328P much, but according to the data sheet, section 9.10:

When using the Timer/Counter Oscillator, the system clock needs to be four times the
oscillator frequency. Due to this and the pin sharing, the Timer/Counter Oscillator can only be used when the
Calibrated Internal RC Oscillator is selected as system clock source.


So I suppose you could replace the resonator with a 32.768 kHz crystal, use the internal 8 MHz RC oscillator for the main clock (with suitable fuse changes), and use the 32.768 kHz crystal for Timer 2.  But I haven't tried it so I don't know if there are any issues you would run into.


I haven't done it myself (yet), but searching "328p timer2 32.768" suggests this is a well-worn path.  Looks like (https://www.avrprogrammers.com/howto/atmega328-power) this will use less power than most RTCs, but obviously trading off features.  I will probably run at 8MHz in this application regardless.