/
Sleepy.cpp
82 lines (74 loc) · 2.53 KB
/
Sleepy.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <avr/sleep.h>
#include <util/atomic.h>
#include "Sleepy.h"
static volatile byte watchdogCounter;
void Sleepy::watchdogInterrupts (char mode) {
// correct for the fact that WDP3 is *not* in bit position 3!
if (mode & bit(3))
mode ^= bit(3) | bit(WDP3);
// pre-calculate the WDTCSR value, can't do it inside the timed sequence
// we only generate interrupts, no reset
byte wdtcsr = mode >= 0 ? bit(WDIE) | mode : 0;
MCUSR &= ~(1<<WDRF);
ATOMIC_BLOCK(ATOMIC_FORCEON) {
#ifndef WDTCSR
#define WDTCSR WDTCR
#endif
WDTCSR |= (1<<WDCE) | (1<<WDE); // timed sequence
WDTCSR = wdtcsr;
}
}
/// @see http://www.nongnu.org/avr-libc/user-manual/group__avr__sleep.html
void Sleepy::powerDown () {
byte adcsraSave = ADCSRA;
ADCSRA &= ~ bit(ADEN); // disable the ADC
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
ATOMIC_BLOCK(ATOMIC_FORCEON) {
sleep_enable();
// sleep_bod_disable(); // can't use this - not in my avr-libc version!
#ifdef BODSE
MCUCR = MCUCR | bit(BODSE) | bit(BODS); // timed sequence
MCUCR = (MCUCR & ~ bit(BODSE)) | bit(BODS);
#endif
}
sleep_cpu();
sleep_disable();
// re-enable what we disabled
ADCSRA = adcsraSave;
}
byte Sleepy::loseSomeTime (word msecs) {
byte ok = 1;
word msleft = msecs;
// only slow down for periods longer than the watchdog granularity
while (msleft >= 16) {
char wdp = 0; // wdp 0..9 corresponds to roughly 16..8192 ms
// calc wdp as log2(msleft/16), i.e. loop & inc while next value is ok
for (word m = msleft; m >= 32; m >>= 1)
if (++wdp >= 9)
break;
watchdogCounter = 0;
watchdogInterrupts(wdp);
powerDown();
watchdogInterrupts(-1); // off
// when interrupted, our best guess is that half the time has passed
word halfms = 8 << wdp;
msleft -= halfms;
if (watchdogCounter == 0) {
ok = 0; // lost some time, but got interrupted
break;
}
msleft -= halfms;
}
// adjust the milli ticks, since we will have missed several
#if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) || defined (__AVR_ATtiny44__) || defined (__AVR_ATtiny45__)
extern volatile unsigned long millis_timer_millis;
millis_timer_millis += msecs - msleft;
#else
extern volatile unsigned long timer0_millis;
timer0_millis += msecs - msleft;
#endif
return ok; // true if we lost approx the time planned
}
void Sleepy::watchdogEvent() {
++watchdogCounter;
}