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
//! # Watchdog
//!
//! ## Examples
//!
//! A usage example of the watchdog can be found at [examples/can.rs]
//!
//! [examples/can.rs]: https://github.com/stm32-rs/stm32f3xx-hal/blob/v0.10.0/examples/can.rs

use core::fmt;
use embedded_time::fixed_point::FixedPoint;

use crate::hal::watchdog::{Watchdog, WatchdogEnable};

use crate::pac::{iwdg::pr::PR_A, DBGMCU, IWDG};
use crate::time::duration::Milliseconds;
use crate::time::rate::Kilohertz;

/// Frequency of the watchdog peripheral clock
const LSI: Kilohertz = Kilohertz(40);
// const MAX_PRESCALER: u8 = 0b0111;
const MAX_PRESCALER: PR_A = PR_A::DivideBy256;
const MAX_RELOAD: u32 = 0x0FFF;

/// Independent Watchdog Peripheral
pub struct IndependentWatchDog {
    iwdg: IWDG,
}

#[cfg(feature = "defmt")]
impl defmt::Format for IndependentWatchDog {
    fn format(&self, f: defmt::Formatter) {
        defmt::write!(f, "IndependentWatchDog {{ iwdg: IWDG }}");
    }
}

impl fmt::Debug for IndependentWatchDog {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("IndependentWatchDog")
            .field("iwdg", &"IWDG")
            .finish()
    }
}

fn into_division_value(psc: PR_A) -> u32 {
    match psc {
        PR_A::DivideBy4 => 4,
        PR_A::DivideBy8 => 8,
        PR_A::DivideBy16 => 16,
        PR_A::DivideBy32 => 32,
        PR_A::DivideBy64 => 64,
        PR_A::DivideBy128 => 128,
        PR_A::DivideBy256 | PR_A::DivideBy256bis => 256,
    }
}

impl IndependentWatchDog {
    /// Creates a new [`IndependentWatchDog`] without starting it.
    ///
    /// Call [`start`](WatchdogEnable::start) to start the watchdog.
    ///
    /// See [`WatchdogEnable`] and [`Watchdog`] for more info.
    #[must_use]
    pub fn new(iwdg: IWDG) -> Self {
        IndependentWatchDog { iwdg }
    }

    /// Set the watchdog to stop when a breakpoint is hit while debugging
    pub fn stop_on_debug(&self, dbg: &DBGMCU, stop: bool) {
        dbg.apb1_fz.modify(|_, w| w.dbg_iwdg_stop().bit(stop));
    }

    /// Find and setup the next best prescaler and reload value for the selected timeout
    fn setup(&self, timeout: Milliseconds) {
        let mut reload: u32 =
            timeout.integer().saturating_mul(LSI.integer()) / into_division_value(PR_A::DivideBy4);

        // Reload is potentially to high to be stored in the register.
        // The goal of this loop is to find the maximum possible reload value,
        // which can be stored in the register, while still guaranteeing the wanted timeout.
        //
        // This is achived by increasing the prescaler value.
        let mut psc = 0;
        loop {
            if psc >= MAX_PRESCALER as u8 {
                // ceil the value to the maximum allow reload value
                if reload > MAX_RELOAD {
                    reload = MAX_RELOAD;
                }
                break;
            }
            if reload <= MAX_RELOAD {
                break;
            }
            psc += 1;
            // When the precaler value incresed, the reload value has to be halfed
            // so that the timeout stays the same.
            reload /= 2;
        }

        self.access_registers(|iwdg| {
            iwdg.pr.modify(|_, w| w.pr().bits(psc));
            #[allow(clippy::cast_possible_truncation)]
            iwdg.rlr.modify(|_, w| w.rl().bits(reload as u16));
        });

        // NOTE: As the watchdog can not be stopped once started,
        // a free method is not provided.
        // pub fn free(self) -> IWDG {}
    }

    /// Get access to the underlying register block.
    ///
    /// # Safety
    ///
    /// This function is not _memory_ unsafe per se, but does not guarantee
    /// anything about assumptions of invariants made in this implementation.
    ///
    /// Changing specific options can lead to un-expected behavior and nothing
    /// is guaranteed.
    pub unsafe fn peripheral(&mut self) -> &mut IWDG {
        &mut self.iwdg
    }

    /// Returns the currently set interval
    #[must_use]
    pub fn interval(&self) -> Milliseconds {
        // If the prescaler was changed wait until the change procedure is finished.
        while self.iwdg.sr.read().pvu().bit() {}

        let psc = self.iwdg.pr.read().pr().variant();
        let reload = self.iwdg.rlr.read().rl().bits();

        Milliseconds((into_division_value(psc) * u32::from(reload)) / LSI.integer())
    }

    fn access_registers<A, F: FnMut(&IWDG) -> A>(&self, mut f: F) -> A {
        // Unprotect write access to registers
        self.iwdg.kr.write(|w| w.key().enable());
        let a = f(&self.iwdg);

        // Protect again
        self.iwdg.kr.write(|w| w.key().reset());
        a
    }
}

impl WatchdogEnable for IndependentWatchDog {
    type Time = Milliseconds;

    fn start<T: Into<Self::Time>>(&mut self, period: T) {
        self.setup(period.into());

        self.iwdg.kr.write(|w| w.key().start());
    }
}

impl Watchdog for IndependentWatchDog {
    fn feed(&mut self) {
        self.iwdg.kr.write(|w| w.key().reset());
    }
}