avr_hal_generic/
wdt.rs

1//! WDT Implementation
2use core::marker::PhantomData;
3
4/// Watchdog Timeout
5#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
6pub enum Timeout {
7    /// 16 milliseconds
8    Ms16,
9    /// 32 milliseconds
10    Ms32,
11    /// 64 milliseconds
12    Ms64,
13    /// 125 milliseconds
14    Ms125,
15    /// 250 milliseconds
16    Ms250,
17    /// 500 milliseconds
18    Ms500,
19    /// 1 second
20    Ms1000,
21    /// 2 seconds
22    Ms2000,
23    /// 4 seconds
24    Ms4000,
25    /// 8 seconds
26    Ms8000,
27}
28
29/// Internal trait for low-level watchdog operations.
30///
31/// **HAL users should use the [`Wdt`] type instead.**
32pub trait WdtOps<H> {
33    type MCUSR;
34
35    /// Initialize the watchdog timer.
36    ///
37    /// **Warning**: This is a low-level method and should not be called directly from user code.
38    fn raw_init(&mut self, m: &Self::MCUSR);
39
40    /// Start the watchdog timer with the specified timeout.
41    ///
42    /// If the timeout value is not supported, `Err(())` should be returned.
43    ///
44    /// **Warning**: This is a low-level method and should not be called directly from user code.
45    fn raw_start(&mut self, timeout: Timeout) -> Result<(), ()>;
46
47    /// Feed this watchdog, to reset its period.
48    ///
49    /// **Warning**: This is a low-level method and should not be called directly from user code.
50    fn raw_feed(&mut self);
51
52    /// Disable/stop this watchdog again.
53    ///
54    /// **Warning**: This is a low-level method and should not be called directly from user code.
55    fn raw_stop(&mut self);
56}
57
58pub struct Wdt<H, WDT> {
59    p: WDT,
60    _h: PhantomData<H>,
61}
62
63impl<H, WDT: WdtOps<H>> Wdt<H, WDT> {
64    pub fn new(mut p: WDT, m: &WDT::MCUSR) -> Self {
65        p.raw_init(m);
66        Self { p, _h: PhantomData }
67    }
68
69    pub fn start(&mut self, timeout: Timeout) -> Result<(), ()> {
70        self.p.raw_start(timeout)
71    }
72
73    pub fn feed(&mut self) {
74        self.p.raw_feed()
75    }
76
77    pub fn stop(&mut self) {
78        self.p.raw_stop()
79    }
80}
81
82#[macro_export]
83macro_rules! impl_wdt {
84    (
85        hal: $HAL:ty,
86        peripheral: $WDT:ty,
87        mcusr: $MCUSR:ty,
88        wdtcsr_name: $wdtcsr:ident,
89        timeout: |$to:ident, $w:ident| $to_match:expr,
90    ) => {
91        impl $crate::wdt::WdtOps<$HAL> for $WDT {
92            type MCUSR = $MCUSR;
93
94            #[inline]
95            fn raw_init(&mut self, m: &Self::MCUSR) {
96                /// If a prior reset was provided by the watchdog, the WDRF in MCUSR would be set,
97                /// so WDRF is also cleared to allow for re-enabling the watchdog.
98                m.modify(|_, w| w.wdrf().clear_bit());
99            }
100
101            #[inline]
102            fn raw_start(&mut self, timeout: Timeout) -> Result<(), ()> {
103                // The sequence for changing time-out configuration is as follows:
104                //
105                //     1. In the same operation, write a logic one to the Watchdog change enable bit
106                //        (WDCE) and WDE. A logic one must be written to WDE regardless of the
107                //        previous value of the WDE bit.
108                //     2. Within the next four clock cycles, write the WDE and Watchdog prescaler
109                //        bits (WDP) as desired, but with the WDCE bit cleared. This must be done in
110                //        one operation.
111                $crate::avr_device::interrupt::free(|_| {
112                    // Reset the watchdog timer.
113                    self.raw_feed();
114                    // Enable watchdog configuration mode.
115                    self.$wdtcsr
116                        .modify(|_, w| w.wdce().set_bit().wde().set_bit());
117                    // Enable watchdog and set interval.
118                    self.$wdtcsr.write(|w| {
119                        let $to = timeout;
120                        let $w = w;
121                        ($to_match).wde().set_bit().wdce().clear_bit()
122                    });
123
124                    Ok(())
125                })
126            }
127
128            #[inline]
129            fn raw_feed(&mut self) {
130                avr_device::asm::wdr();
131            }
132
133            #[inline]
134            fn raw_stop(&mut self) {
135                // The sequence for clearing WDE is as follows:
136                //
137                //     1. In the same operation, write a logic one to the Watchdog change enable bit
138                //        (WDCE) and WDE. A logic one must be written to WDE regardless of the
139                //        previous value of the WDE bit.
140                //     2. Within the next four clock cycles, clear the WDE and WDCE bits.
141                //        This must be done in one operation.
142                $crate::avr_device::interrupt::free(|_| {
143                    // Reset the watchdog timer.
144                    self.raw_feed();
145                    // Enable watchdog configuration mode.
146                    self.$wdtcsr
147                        .modify(|_, w| w.wdce().set_bit().wde().set_bit());
148                    // Disable watchdog.
149                    self.$wdtcsr.reset();
150                })
151            }
152        }
153    };
154}