Go Down

Topic: Any library with delay() implemented by switching to low-power-mode w/ WDT (Read 681 times) previous topic - next topic

falcon74

Is there any simple/easy-to-use library that implements delay() (or similar function, that allows millisecond resolution sleeps), which would switch Arduino to low-power sleep mode. I'd expect it to be implemented using the WDT, to wake up and resume functioning.

br,
-f74

MarkT

The watchdog timer is not very accurate, so it depends what you mean by "millisecond resolution", but it ought to be possible by rewriting the code for delay() or by using something like Jeelabs Sleep class.
[ I won't respond to messages, use the forum please ]

falcon74

Thanks @MarkT. Had only read some fleeting mention of WDT in the scope of low-power operation, and wasn't aware that they aren't very accurate.
Found very useful information here thanks to your reference.

Maybe a dumb question - wondering if the AVR timer (Timer0?) continues ticking, when the system is in low-power mode ? I.e. will the standard Arduino time functions, like -- millis() & micros() return the elapsed time since the sketch started, or they would report elapsed time while system is in full power mode ? If it is the former, i.e. Timer0 ticks even in low-power mode, the Jeenode Sleepy class seems to be a fairly good enough for what I am trying to do. What I would like to do is to sleep as much as possible, not wake up early-enough (and frequently enough) to check for some data-pattern in a patch of memory. That patch gets over-written by noise all the time, and it is critical for me to check for valid data, frequently, lest I miss it.

Nick Gammon

A writeup on low power modes:

http://www.gammon.com.au/forum/?id=11497

Only IDLE mode keeps Timer 0 running. So in the lower-power modes you won't know elapsed time (without an external clock).

You could implement your own timing with Timer 2 and then use "power save" mode. In fact if you used an interrupt with that, then that is your "watchdog" timer. So just let Timer 2 wake you up at whatever intervals you (reasonably) want.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon

This is an example of what I am talking about:

Code: [Select]
#include <avr/sleep.h>

volatile unsigned long ms;

ISR (TIMER2_COMPA_vect)
{
 ++ms;
 digitalWrite (9, !digitalRead (9));
}  // end of TIMER2_COMPA_vect

void setup () {
 
  // reset Timer 2
 TCCR2A = 0;
 TCCR2B = 0;

 // Timer 2 - gives us our 1 mS counting interval
 // 16 MHz clock (62.5 nS per tick) - prescaled by 128
 //  counter increments every 8 uS.
 // So we count 125 of them, giving exactly 1000 uS (1 mS)
 TCCR2A = _BV (WGM21) ;   // CTC mode
 OCR2A  = 124;            // count up to 125  (zero relative!!!!)

 // Timer 2 - interrupt on match (ie. every 1 mS)
 TIMSK2 = _BV (OCIE2A);   // enable Timer2 Interrupt
 TCNT2 = 0;    

 // Reset prescalers
 GTCCR = _BV (PSRASY);        // reset prescaler now
 // start Timer 2
 TCCR2B =  _BV (CS20) | _BV (CS22) ;  // prescaler of 128
 
 pinMode (9, OUTPUT);
} // end of setup

void loop () {

 set_sleep_mode (SLEEP_MODE_PWR_SAVE);  
 sleep_enable();
 sleep_cpu ();  
  sleep_disable ();


 // should be here after 1 mS
   
}   // end of loop


This uses an average of around 1.7 mA. However it behaves in a slightly strange way. According to my calculations it should wake up (and leave "loop") every 1 mS, but it actually does it every 2 mS.

With the sleep_cpu() line commented out, it does it every 1 mS. So somehow sleep is causing the clock to run slower. I'm not sure why at this stage. However even as it is, it shows how you can be in low power mode most of the time, but still count elapsed time accurately.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Coding Badly

Quote
According to my calculations it should wake up (and leave "loop") every 1 mS, but it actually does it every 2 mS.


The clock select / BOD fuse settings may be the culprit.  It looks like the delay-from-wake time can be as long as 65ms.

Nick Gammon

Oh yeah, lol, it was that.

Well, this modified setup behaves as predicted:

Code: [Select]
void setup () {
 
   // reset Timer 2
  TCCR2A = 0;
  TCCR2B = 0;

  TCCR2A = _BV (WGM21) ;   // CTC mode
  OCR2A  = 155;   // zero relative

  // Timer 2 - interrupt on match
  TIMSK2 = _BV (OCIE2A);   // enable Timer2 Interrupt
  TCNT2 = 0;     

  // Reset prescalers
  GTCCR = _BV (PSRASY);        // reset prescaler now
  // start Timer 2
  TCCR2B =  _BV (CS20) | _BV (CS21) | _BV (CS22) ;  // prescaler of 1024
 
  pinMode (9, OUTPUT);
} // end of setup


Theory: 62.5 nS * 1024 * 156 = 9.984 mS. That pretty-much agrees with measured results. In other words, a wake-up 100 times a second. Count those, and you know how much time has elapsed.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Coding Badly

Quote
Oh yeah, lol, it was that.


Seriously?  I was correct?

I ask because there did not appear to be a combination that would explain exactly what you were seeing.  It looks like the possibilities are 3ms to a bit over 4ms or close to 65ms.

Nick Gammon


Seriously?  I was correct?


Of course. Trying to catch 1 mS interrupts with a 65 mS spool-up time was a bit silly of me. I'm surprised it worked as well as it did. (In fact, that is the mystery - maybe it had a maximum 65 mS start-up time).

Oh well, that will teach me to read the data sheet more closely. ;)

At least it confirms my belief that these things work, as long as you understand exactly what they are supposed to do.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Go Up