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
//! 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);

    /// 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);
}

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 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,
        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());
            }

            #[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();
                })
            }
        }
    };
}