-
Notifications
You must be signed in to change notification settings - Fork 259
Expand file tree
/
Copy pathwdt.rs
More file actions
200 lines (178 loc) · 6.75 KB
/
Copy pathwdt.rs
File metadata and controls
200 lines (178 loc) · 6.75 KB
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//! WDT Implementation
use core::marker::PhantomData;
/// Watchdog Timeout
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Timeout {
/// 16 milliseconds
Ms16,
/// 32 milliseconds
Ms32,
/// 64 milliseconds
Ms64,
/// 125 milliseconds
Ms125,
/// 250 milliseconds
Ms250,
/// 500 milliseconds
Ms500,
/// 1 second
Ms1000,
/// 2 seconds
Ms2000,
/// 4 seconds
Ms4000,
/// 8 seconds
Ms8000,
}
/// Internal trait for low-level watchdog operations.
///
/// **HAL users should use the [`Wdt`] type instead.**
pub trait WdtOps<H> {
type MCUSR;
/// Initialize the watchdog timer.
///
/// **Warning**: This is a low-level method and should not be called directly from user code.
fn raw_init(&mut self, m: &Self::MCUSR);
/// Sets the mode for the watchdog timer.
/// Refer to the `WdtMode` enum for more infos.
///
/// **Warning**: This is a low-level method and should not be called directly from user code.
fn raw_set_mode(&mut self, mode: WdtMode);
/// Start the watchdog timer with the specified timeout.
///
/// If the timeout value is not supported, `Err(())` should be returned.
///
/// **Warning**: This is a low-level method and should not be called directly from user code.
fn raw_start(&mut self, timeout: Timeout) -> Result<(), ()>;
/// Feed this watchdog, to reset its period.
///
/// **Warning**: This is a low-level method and should not be called directly from user code.
fn raw_feed(&mut self);
/// Disable/stop this watchdog again.
///
/// **Warning**: This is a low-level method and should not be called directly from user code.
fn raw_stop(&mut self);
}
#[derive(Copy, Clone)]
/// The mode that dictates how the watchdog should behave.
/// This needs to be called **after** the `start` function.
pub enum WdtMode {
/// Trigger the interrupt `WDT`, but do not perform a softreset.
/// **Warning**: On many `atmega` devices, a watchdog timeout resets the mode automatically, to
/// prevent the microcontroller from getting stuck in a infinite loop. So subsequent timeouts
/// will cause a softreset. When you want to trigger an interrupt multiple times,
/// you need to call the `rearm` method after every timeout.
Interrupt,
/// Softreset the microcontroller.
/// This is the default on many microcontrollers.
SystemReset,
}
pub struct Wdt<H, WDT> {
p: WDT,
_h: PhantomData<H>,
}
impl<H, WDT: WdtOps<H>> Wdt<H, WDT> {
pub fn new(mut p: WDT, m: &WDT::MCUSR) -> Self {
p.raw_init(m);
Self { p, _h: PhantomData }
}
pub fn rearm(&mut self, m: &WDT::MCUSR) {
self.p.raw_init(m);
}
pub fn set_mode(&mut self, mode: WdtMode) {
self.p.raw_set_mode(mode);
}
pub fn start(&mut self, timeout: Timeout) -> Result<(), ()> {
self.p.raw_start(timeout)
}
pub fn feed(&mut self) {
self.p.raw_feed()
}
pub fn stop(&mut self) {
self.p.raw_stop()
}
}
#[macro_export]
macro_rules! impl_wdt {
(
hal: $HAL:ty,
peripheral: $WDT:ty,
mcusr: $MCUSR:ty,
wdtcsr_name: $wdtcsr:ident,
timeout: |$to:ident, $w:ident| $to_match:expr,
) => {
impl $crate::wdt::WdtOps<$HAL> for $WDT {
type MCUSR = $MCUSR;
#[inline]
fn raw_init(&mut self, m: &Self::MCUSR) {
/// If a prior reset was provided by the watchdog, the WDRF in MCUSR would be set,
/// so WDRF is also cleared to allow for re-enabling the watchdog.
m.modify(|_, w| w.wdrf().clear_bit());
self.$wdtcsr.modify(|_, w| w.wde().clear_bit());
}
#[inline]
fn raw_set_mode(&mut self, mode: $crate::wdt::WdtMode) {
// Enable watchdog configuration mode.
self.$wdtcsr.modify(|_, w| w.wdce().set_bit());
match mode {
$crate::wdt::WdtMode::Interrupt => {
self.$wdtcsr
.modify(|_, w| w.wde().clear_bit().wdie().set_bit());
}
$crate::wdt::WdtMode::SystemReset => {
self.$wdtcsr
.modify(|_, w| w.wde().set_bit().wdie().clear_bit());
}
}
}
#[inline]
fn raw_start(&mut self, timeout: Timeout) -> Result<(), ()> {
// The sequence for changing time-out configuration is as follows:
//
// 1. In the same operation, write a logic one to the Watchdog change enable bit
// (WDCE) and WDE. A logic one must be written to WDE regardless of the
// previous value of the WDE bit.
// 2. Within the next four clock cycles, write the WDE and Watchdog prescaler
// bits (WDP) as desired, but with the WDCE bit cleared. This must be done in
// one operation.
$crate::avr_device::interrupt::free(|_| {
// Reset the watchdog timer.
self.raw_feed();
// Enable watchdog configuration mode.
self.$wdtcsr
.modify(|_, w| w.wdce().set_bit().wde().set_bit());
// Enable watchdog and set interval.
self.$wdtcsr.write(|w| {
let $to = timeout;
let $w = w;
($to_match).wde().set_bit().wdce().clear_bit()
});
Ok(())
})
}
#[inline]
fn raw_feed(&mut self) {
avr_device::asm::wdr();
}
#[inline]
fn raw_stop(&mut self) {
// The sequence for clearing WDE is as follows:
//
// 1. In the same operation, write a logic one to the Watchdog change enable bit
// (WDCE) and WDE. A logic one must be written to WDE regardless of the
// previous value of the WDE bit.
// 2. Within the next four clock cycles, clear the WDE and WDCE bits.
// This must be done in one operation.
$crate::avr_device::interrupt::free(|_| {
// Reset the watchdog timer.
self.raw_feed();
// Enable watchdog configuration mode.
self.$wdtcsr
.modify(|_, w| w.wdce().set_bit().wde().set_bit());
// Disable watchdog.
self.$wdtcsr.reset();
})
}
}
};
}