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