96 lines
2.6 KiB
Rust
96 lines
2.6 KiB
Rust
use core::marker::PhantomData;
|
|
|
|
use embassy::time::Duration;
|
|
use embassy_hal_common::{unborrow, Unborrow};
|
|
use stm32_metapac::iwdg::vals::{Key, Pr};
|
|
|
|
use crate::time::Hertz;
|
|
|
|
pub struct IndependentWatchdog<'d, T: Instance> {
|
|
wdg: PhantomData<&'d mut T>,
|
|
}
|
|
|
|
// Some STM32 families have 40 kHz LSI clock instead of 32 kHz
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(any(stm32f0, stm32f1))] {
|
|
pub const IWDG_FREQ: Hertz = Hertz(40_000);
|
|
} else {
|
|
pub const IWDG_FREQ: Hertz = Hertz(32_000);
|
|
}
|
|
}
|
|
|
|
// 12-bit counter
|
|
const MAX_RL: u16 = 0xFFF;
|
|
|
|
/// Calculates maximum watchdog timeout (RL = 0xFFF) for a given prescaler
|
|
const fn max_timeout(prescaler: u8) -> Duration {
|
|
Duration::from_micros(1_000_000 / (IWDG_FREQ.0 / prescaler as u32) as u64 * MAX_RL as u64)
|
|
}
|
|
|
|
/// Calculates watchdog reload value for the given prescaler and desired timeout
|
|
const fn reload_value(prescaler: u8, timeout: Duration) -> u16 {
|
|
((IWDG_FREQ.0 / prescaler as u32) as u64 * timeout.as_micros() / 1_000_000) as u16
|
|
}
|
|
|
|
impl<'d, T: Instance> IndependentWatchdog<'d, T> {
|
|
pub fn new(_instance: impl Unborrow<Target = T> + 'd, timeout: Duration) -> Self {
|
|
unborrow!(_instance);
|
|
|
|
// Find lowest prescaler value, which makes watchdog period longer or equal to timeout.
|
|
// This iterates from 4 (2^2) to 256 (2^8).
|
|
let psc_power = unwrap!((2..=8).find(|psc_power| {
|
|
let psc = 2u8.pow(*psc_power);
|
|
timeout <= max_timeout(psc)
|
|
}));
|
|
|
|
// Prescaler value
|
|
let psc = 2u8.pow(psc_power);
|
|
|
|
// Convert prescaler power to PR register value
|
|
let pr = psc_power as u8 - 2;
|
|
assert!(pr <= 0b110);
|
|
|
|
// Reload value
|
|
let rl = reload_value(psc, timeout);
|
|
|
|
let wdg = T::regs();
|
|
unsafe {
|
|
wdg.kr().write(|w| w.set_key(Key::ENABLE));
|
|
wdg.pr().write(|w| w.set_pr(Pr(pr)));
|
|
wdg.rlr().write(|w| w.set_rl(rl));
|
|
}
|
|
|
|
IndependentWatchdog {
|
|
wdg: PhantomData::default(),
|
|
}
|
|
}
|
|
|
|
pub unsafe fn unleash(&mut self) {
|
|
T::regs().kr().write(|w| w.set_key(Key::START));
|
|
}
|
|
|
|
pub unsafe fn pet(&mut self) {
|
|
T::regs().kr().write(|w| w.set_key(Key::RESET));
|
|
}
|
|
}
|
|
|
|
mod sealed {
|
|
pub trait Instance {
|
|
fn regs() -> crate::pac::iwdg::Iwdg;
|
|
}
|
|
}
|
|
|
|
pub trait Instance: sealed::Instance {}
|
|
|
|
foreach_peripheral!(
|
|
(iwdg, $inst:ident) => {
|
|
impl sealed::Instance for crate::peripherals::$inst {
|
|
fn regs() -> crate::pac::iwdg::Iwdg {
|
|
crate::pac::$inst
|
|
}
|
|
}
|
|
|
|
impl Instance for crate::peripherals::$inst {}
|
|
};
|
|
);
|