-
Notifications
You must be signed in to change notification settings - Fork 1
/
WDT.hpp
141 lines (118 loc) · 3.55 KB
/
WDT.hpp
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#pragma once
/*
* File: WDT.h
*
* Created on January 25, 2012, 11:33 AM
*/
#include "basicTypes.hpp"
#include "undefAVR.hpp"
namespace AVR {
enum class GlobalInterruptHandlingStrategy {
None,
AlwaysOn,
Maintain,
};
using Basic::u1;
class WDT {
public:
enum class AutomaticTick {
Off,
On,
};
enum class Timeout : u1 {
T__15ms = 0,
T__30ms = 1,
T__60ms = 2,
T_120ms = 3,
T_250ms = 4,
T_500ms = 5,
T1000ms = 6,
T2000ms = 7,
T4000ms = 8,
T8000ms = 9
};
inline constexpr static u1 timeoutBits(Timeout t) {
const u1 v = static_cast<u1>(t);
const u1 low = v & 0b111;
const u1 high = (v >> 3) & 0b1;
return low | (high << WDP3);
}
/**
* Resets the WDT (call this more often than the timeout!)
*/
static inline void tick() { asm volatile("wdr"); }
/**
* Starts the watchdog timer with timeout t
* @param t the timeout to wait for
*/
template <GlobalInterruptHandlingStrategy interruptStrategy = GlobalInterruptHandlingStrategy::None,
AutomaticTick handleTick = AutomaticTick::On>
static inline void start(Timeout t, bool reset = true, bool interrupt = false) {
u1 sreg;
switch (interruptStrategy) {
case GlobalInterruptHandlingStrategy::Maintain:
sreg = SREG;
// Fall through
case GlobalInterruptHandlingStrategy::AlwaysOn:
asm volatile("cli");
case GlobalInterruptHandlingStrategy::None:;
}
// Ensure a reset doesn't happen while we're stopping
if (handleTick == AutomaticTick::On) tick();
// Clear interrupt flag, enable changes, and maybe enable
WDTCSR = (interrupt << WDIF) | (1 << WDCE) | (reset << WDE);
// Maybe enable interrupt, keep changes enabled, and set timeout
WDTCSR = (interrupt << WDIE) | (1 << WDCE) | (reset << WDE) | timeoutBits(t);
switch (interruptStrategy) {
case GlobalInterruptHandlingStrategy::Maintain:
SREG = sreg;
return;
case GlobalInterruptHandlingStrategy::AlwaysOn:
asm volatile("sei");
case GlobalInterruptHandlingStrategy::None:;
}
}
/**
* Stops the WDT. May not always succeed as WDT can be forced on by fuses
*/
template <GlobalInterruptHandlingStrategy interruptStrategy = GlobalInterruptHandlingStrategy::None,
AutomaticTick handleTick = AutomaticTick::On>
static inline void stop() {
u1 sreg;
switch (interruptStrategy) {
case GlobalInterruptHandlingStrategy::Maintain:
sreg = SREG;
// Fall through
case GlobalInterruptHandlingStrategy::AlwaysOn:
asm volatile("cli");
case GlobalInterruptHandlingStrategy::None:;
}
// Ensure a reset doesn't happen while we're stopping
if (handleTick == AutomaticTick::On) tick();
// Clear WDRF in MCUSR
MCUSR &= ~(1 << WDRF);
// Write logical one to WDCE and WDE
// Keep old prescaler setting to prevent unintentional time-out
WDTCSR |= (1 << WDCE) | (1 << WDE);
// Turn off WDT
WDTCSR = 0x00;
switch (interruptStrategy) {
case GlobalInterruptHandlingStrategy::Maintain:
SREG = sreg;
return;
case GlobalInterruptHandlingStrategy::AlwaysOn:
asm volatile("sei");
case GlobalInterruptHandlingStrategy::None:;
}
}
static inline bool isEnabled() { return bit_is_set(WDTCSR, WDE); }
static inline bool isInterruptEnabled() { return bit_is_set(WDTCSR, WDIE); }
static inline void setInterruptEnable(bool enable) {
if (enable)
WDTCSR |= _BV(WDIE);
else
WDTCSR &= ~_BV(WDIE);
}
static inline void clearResetFlag() { MCUSR = ~_BV(WDRF); }
};
}; // namespace AVR