blob: 6b0acf0e999051829c43f1df60b36bc44e505ab0 (
plain)
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
/* Pinout used:
* -----
* !reset - MCU - Vcc
* NA - MCU - NA
* NA - MCU - NA
* GND - MCU - (PB0) powerbutton
* -----
*/
static const uint8_t SLEPT_TIMEOUT = 25; /* ~2.5s */
static void init_timer()
{
/* timer_resolution = 1 / (clock_speed / prescaler)
* timer_resolution = 1 / (10**6 / 1024)
*
* in ctc mode, target counts:
*
* timer_counts = (target_time / timer_resolution) - 1
* timer_counts = (0.1 / (1/(10**6/1024))) - 1
* timer_counts = 96.65625000000001 =~ 97
*
* Why did we add the extra +1 to our number of timer counts? In CTC mode,
* when the timer matches our desired count it will reset itself to zero.
* This takes one clock cycle to perform, so we need to factor that into
* our calculations.
*
* VG notes: more simply 0 to 97 = 98 values = 98 cycles.
*
* trigger_time = (97+1) * (1/(10**6 / 1024)) =~ 0.100352s
*
* WGM0[2:0] = 010 = CTC
*/
TCCR0A =
(1 << WGM01) | // CTC
(0 << WGM00); // CTC
TCCR0B =
(0 << WGM02) | // CTC
(1 << CS02) | // CS0[2:0] = 101 => prescaler clk/1024
(0 << CS01) | // CS0[2:0] = 101 => prescaler clk/1024
(1 << CS00); // CS0[2:0] = 101 => prescaler clk/1024
OCR0A = 97;
TIMSK =
(1 << OCIE0A) | // enable CTC interrupt for compare match 0A
(0 << OCIE0B) | // disable CTC interrupt for compare match 0B
(0 << TOIE0); // disable interrupt for timer0 overflow
}
static volatile uint8_t sleep_time;
ISR(TIMER0_COMPA_vect)
{
++sleep_time;
}
static uint8_t get_sleep_time(void)
{
return sleep_time;
}
static void reset_sleep_time(void)
{
sleep_time = 0;
}
int main()
{
cli();
PORTB = 0; // outputs set to low and input not set to internal pull-up
init_timer();
sei();
// Normally a loop is not required but if somehow the mcu woke up from its
// sleep state this ensure we can do the operation again.
for (;;) {
reset_sleep_time();
// digital output, pull-down for a time then go to default PB0 = Hi-Z
DDRB |= (1 << PB0);
while (get_sleep_time() <= SLEPT_TIMEOUT);
DDRB &= ~(1 << PB0);
/* set low power mode */
power_all_disable(); // disable all peripherals
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode
sleep_enable(); // set register to enable sleep
sleep_bod_disable(); // disable bod to consume even less
//sei(); // normally set sei to ensure wakeup but I do
// not want to wake up appart from a power
// reset
sleep_cpu(); // put cpu to sleep = low power mode now
sleep_disable(); // clear register (if somehow mcu woke up)
}
return 0;
}
|