stm32f3xx-hal 0.9.1

Peripheral access API for STM32F3 series microcontrollers
Documentation
//! # 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.9.1/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.
    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() * 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));
            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
    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());
    }
}