Browse Source

Implement watchdog

With example and doc comments
features/pwm
David Sawatzke 4 years ago
parent
commit
02c01cd7e0
  1. 60
      examples/watchdog.rs
  2. 1
      src/lib.rs
  3. 3
      src/prelude.rs
  4. 121
      src/watchdog.rs

60
examples/watchdog.rs

@ -0,0 +1,60 @@
#![no_main]
#![no_std]
use panic_halt;
use core::fmt::Write;
use stm32f0xx_hal as hal;
use crate::hal::delay::Delay;
use crate::hal::prelude::*;
use crate::hal::serial::Serial;
use crate::hal::stm32;
use crate::hal::time::Hertz;
use crate::hal::watchdog::Watchdog;
use cortex_m::peripheral::Peripherals;
use cortex_m_rt::entry;
#[entry]
fn main() -> ! {
if let (Some(p), Some(cp)) = (stm32::Peripherals::take(), Peripherals::take()) {
let gpioa = p.GPIOA.split();
let rcc = p.RCC.constrain();
let dbgmcu = p.DBGMCU;
// Disable the watchdog when the cpu is stopped under debug
dbgmcu.apb1_fz.modify(|_, w| w.dbg_iwdg_stop().set_bit());
let mut watchdog = Watchdog::new(p.IWDG);
let clocks = rcc.cfgr.sysclk(8.mhz()).freeze();
// Get delay provider
let mut delay = Delay::new(cp.SYST, clocks);
let tx = gpioa.pa9.into_alternate_af1();
let rx = gpioa.pa10.into_alternate_af1();
let serial = Serial::usart1(p.USART1, (tx, rx), 115_200.bps(), clocks);
let (mut tx, _rx) = serial.split();
tx.write_str("RESET \r\n").ok();
watchdog.start(Hertz(1));
delay.delay_ms(500_u16);
watchdog.feed();
delay.delay_ms(500_u16);
watchdog.feed();
delay.delay_ms(500_u16);
tx.write_str("This will get printed \r\n").ok();
watchdog.feed();
// Now a reset happens while delaying
delay.delay_ms(1500_u16);
tx.write_str("This won't\r\n").ok();
}
loop {
continue;
}
}

1
src/lib.rs

@ -18,3 +18,4 @@ pub mod serial;
pub mod spi;
pub mod time;
pub mod timers;
pub mod watchdog;

3
src/prelude.rs

@ -1,4 +1,7 @@
pub use embedded_hal::prelude::*;
// TODO for some reason, watchdog isn't in the embedded_hal prelude
pub use embedded_hal::watchdog::Watchdog as _stm32f0xx_hal_embedded_hal_watchdog_Watchdog;
pub use embedded_hal::watchdog::WatchdogEnable as _stm32f0xx_hal_embedded_hal_watchdog_WatchdogEnable;
pub use crate::gpio::GpioExt as _stm32f0xx_hal_gpio_GpioExt;
pub use crate::rcc::RccExt as _stm32f0xx_hal_rcc_RccExt;

121
src/watchdog.rs

@ -0,0 +1,121 @@
//! 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();
//! ```
use embedded_hal::watchdog;
use crate::stm32::IWDG;
use crate::time::Hertz;
/// Watchdog instance
pub struct Watchdog {
iwdg: IWDG,
}
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 }
}
}
impl Watchdog {
pub fn new(iwdg: IWDG) -> Self {
Self { iwdg }
}
}
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().pvu().bit() {}
while self.iwdg.sr.read().rvu().bit() {}
self.iwdg.kr.write(|w| w.key().reset());
}
}
Loading…
Cancel
Save