You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
stm32f0xx-hal/src/watchdog.rs

128 lines
3.8 KiB
Rust

//! API for the IWDG
//!
//! You can activate the watchdog by calling `start` or the setting appropriate
//! device option bit when programming.
//!
//! After activating the watchdog, you'll have to regularly `feed` the watchdog.
//! If more time than `timeout` has gone by since the last `feed`, your
//! microcontroller will be reset.
//!
//! This is useful if you fear that your program may get stuck. In that case it
//! won't feed the watchdog anymore, the watchdog will reset the microcontroller
//! and thus your program will function again.
//!
//! **Attention**:
//!
//! The IWDG runs on a separate 40kHz low-accuracy clock (30kHz-60kHz). You may
//! want to some buffer in your interval.
//!
//! Per default the iwdg continues to run even when you stopped execution of code via a debugger.
//! You may want to disable the watchdog when the cpu is stopped
//!
//! ``` ignore
//! let dbgmcu = p.DBGMCU;
//! dbgmcu.apb1_fz.modify(|_, w| w.dbg_iwdg_stop().set_bit());
//! ```
//!
//! # Example
//! ``` no_run
//! use stm32f0xx_hal as hal;
//!
//! use crate::hal::stm32;
//! use crate::hal::prelude::*;
//! use crate::hal:watchdog::Watchdog;
//! use crate::hal:time::Hertz;
//!
//! let mut p = stm32::Peripherals::take().unwrap();
//!
//! let mut iwdg = Watchdog::new(p.iwdg);
//! iwdg.start(Hertz(100));
//! loop {}
//! // Whoops, got stuck, the watchdog issues a reset after 10 ms
//! iwdg.feed();
//! ```
#[allow(unused)]
use embedded_hal::watchdog;
#[cfg(feature = "device-selected")]
use crate::stm32::IWDG;
use crate::time::Hertz;
#[cfg(feature = "device-selected")]
/// Watchdog instance
pub struct Watchdog {
iwdg: IWDG,
}
#[cfg(feature = "device-selected")]
impl watchdog::Watchdog for Watchdog {
/// Feed the watchdog, so that at least one `period` goes by before the next
/// reset
fn feed(&mut self) {
self.iwdg.kr.write(|w| w.key().reset());
}
}
/// Timeout configuration for the IWDG
#[derive(PartialEq, PartialOrd, Clone, Copy)]
pub struct IwdgTimeout {
psc: u8,
reload: u16,
}
impl Into<IwdgTimeout> for Hertz {
/// This converts the value so it's usable by the IWDG
/// Due to conversion losses, the specified frequency is a maximum
///
/// It can also only represent values < 10000 Hertz
fn into(self) -> IwdgTimeout {
let mut time = 40_000 / 4 / self.0;
let mut psc = 0;
let mut reload = 0;
while psc < 7 {
reload = time;
if reload < 0x1000 {
break;
}
psc += 1;
time /= 2;
}
// As we get an integer value, reload is always below 0xFFF
let reload = reload as u16;
IwdgTimeout { psc, reload }
}
}
#[cfg(feature = "device-selected")]
impl Watchdog {
pub fn new(iwdg: IWDG) -> Self {
Self { iwdg }
}
}
#[cfg(feature = "device-selected")]
impl watchdog::WatchdogEnable for Watchdog {
type Time = IwdgTimeout;
fn start<T>(&mut self, period: T)
where
T: Into<IwdgTimeout>,
{
let time: IwdgTimeout = period.into();
// Feed the watchdog in case it's already running
// (Waiting for the registers to update takes sometime)
self.iwdg.kr.write(|w| w.key().reset());
// Enable the watchdog
self.iwdg.kr.write(|w| w.key().start());
self.iwdg.kr.write(|w| w.key().enable());
// Wait until it's safe to write to the registers
while self.iwdg.sr.read().pvu().bit() {}
self.iwdg.pr.write(|w| w.pr().bits(time.psc));
while self.iwdg.sr.read().rvu().bit() {}
self.iwdg.rlr.write(|w| w.rl().bits(time.reload));
// Wait until the registers are updated before issuing a reset with
// (potentially false) values
while self.iwdg.sr.read().bits() != 0 {}
self.iwdg.kr.write(|w| w.key().reset());
}
}