/
Timeout.cpp
109 lines (91 loc) · 2.18 KB
/
Timeout.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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include "Timeout.h"
#include <avr/interrupt.h>
#define F_CPU 16000000
#define SET(V,bit) V|=(1<<bit)
#define RESET(V,bit) V&=~(1<<bit)
#define interrupts() sei()
#define noInterrupts() cli()
static void (*timeoutCallback)();
static void stub(){}
Timeout Timer1;
ISR(TIMER1_COMPA_vect)
{
// Calling timeoutCallback can damage
// some data, because of changing data in
// status register. So, we need to save SREG
short SREG_before_interrupt = SREG;
(*timeoutCallback)();
if(!Timer1.every_flag){
Timer1.kill();}
SREG = SREG_before_interrupt;
}
Timeout::Timeout(){
configure(1000, stub);
}
Timeout Timeout::delay(short delay_ms, void (*callback)()){
configure(delay_ms, callback, false);
return Timer1;
}
Timeout Timeout::every(short delay_ms, void (*callback)()){
configure(delay_ms, callback, true);
return Timer1;
}
void Timeout::configure(short delay_ms, void (*callback)(), bool every_arg){
short ms = (delay_ms<8000 && delay_ms > 1)? delay_ms : 100;
every_flag = every_arg;
prescaler = 1024;
timeoutCallback = callback;
noInterrupts();
// Clear registers to avoid bugs
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
SET(TCCR1B, WGM12); // CTC mode
SET(TIMSK1, OCIE1A); // allow interrupt on compare
OCR1A = int(ms*F_CPU/prescaler/1000);
interrupts();
}
void Timeout::setPrescaler(short value){
switch(value){
case 1:
RESET(TCCR1B, CS12);
RESET(TCCR1B, CS11);
SET(TCCR1B, CS10);
break;
case 8:
RESET(TCCR1B, CS12);
SET(TCCR1B, CS11);
RESET(TCCR1B, CS10);
break;
case 64:
RESET(TCCR1B, CS12);
SET(TCCR1B, CS11);
SET(TCCR1B, CS10);
break;
case 256:
SET(TCCR1B, CS12);
RESET(TCCR1B, CS11);
RESET(TCCR1B, CS10);
break;
case 1024:
SET(TCCR1B, CS12);
RESET(TCCR1B, CS11);
SET(TCCR1B, CS10);
break;
default:
RESET(TCCR1B, CS12);
RESET(TCCR1B, CS11);
RESET(TCCR1B, CS10);
break;
}
}
void Timeout::start(){
noInterrupts();
setPrescaler(prescaler);
interrupts();
}
void Timeout::kill(){
setPrescaler(0);
}
#undef interrupts
#undef noInterrupts