Skip to content

Commit 108319f

Browse files
authored
Merge pull request #12 from david-sawatzke/iwdg
Add a watchdog implementation
2 parents 386b24b + 6a02615 commit 108319f

File tree

4 files changed

+184
-0
lines changed

4 files changed

+184
-0
lines changed

examples/watchdog.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
use panic_halt;
5+
6+
use core::fmt::Write;
7+
use stm32f0xx_hal as hal;
8+
9+
use crate::hal::delay::Delay;
10+
use crate::hal::prelude::*;
11+
use crate::hal::serial::Serial;
12+
use crate::hal::stm32;
13+
use crate::hal::time::Hertz;
14+
use crate::hal::watchdog::Watchdog;
15+
16+
use cortex_m::peripheral::Peripherals;
17+
use cortex_m_rt::entry;
18+
19+
#[entry]
20+
fn main() -> ! {
21+
if let (Some(p), Some(cp)) = (stm32::Peripherals::take(), Peripherals::take()) {
22+
let gpioa = p.GPIOA.split();
23+
let rcc = p.RCC.constrain();
24+
let dbgmcu = p.DBGMCU;
25+
26+
// Disable the watchdog when the cpu is stopped under debug
27+
dbgmcu.apb1_fz.modify(|_, w| w.dbg_iwdg_stop().set_bit());
28+
29+
let mut watchdog = Watchdog::new(p.IWDG);
30+
let clocks = rcc.cfgr.sysclk(8.mhz()).freeze();
31+
32+
// Get delay provider
33+
let mut delay = Delay::new(cp.SYST, clocks);
34+
35+
let tx = gpioa.pa9.into_alternate_af1();
36+
let rx = gpioa.pa10.into_alternate_af1();
37+
38+
let serial = Serial::usart1(p.USART1, (tx, rx), 115_200.bps(), clocks);
39+
40+
let (mut tx, _rx) = serial.split();
41+
tx.write_str("RESET \r\n").ok();
42+
43+
watchdog.start(Hertz(1));
44+
delay.delay_ms(500_u16);
45+
watchdog.feed();
46+
delay.delay_ms(500_u16);
47+
watchdog.feed();
48+
delay.delay_ms(500_u16);
49+
tx.write_str("This will get printed \r\n").ok();
50+
watchdog.feed();
51+
52+
// Now a reset happens while delaying
53+
delay.delay_ms(1500_u16);
54+
tx.write_str("This won't\r\n").ok();
55+
}
56+
57+
loop {
58+
continue;
59+
}
60+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ pub mod serial;
1818
pub mod spi;
1919
pub mod time;
2020
pub mod timers;
21+
pub mod watchdog;

src/prelude.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
pub use embedded_hal::prelude::*;
2+
// TODO for some reason, watchdog isn't in the embedded_hal prelude
3+
pub use embedded_hal::watchdog::Watchdog as _stm32f0xx_hal_embedded_hal_watchdog_Watchdog;
4+
pub use embedded_hal::watchdog::WatchdogEnable as _stm32f0xx_hal_embedded_hal_watchdog_WatchdogEnable;
25

36
pub use crate::gpio::GpioExt as _stm32f0xx_hal_gpio_GpioExt;
47
pub use crate::rcc::RccExt as _stm32f0xx_hal_rcc_RccExt;

src/watchdog.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
//! API for the IWDG
2+
//!
3+
//! You can activate the watchdog by calling `start` or the setting appropriate
4+
//! device option bit when programming.
5+
//!
6+
//! After activating the watchdog, you'll have to regularly `feed` the watchdog.
7+
//! If more time than `timeout` has gone by since the last `feed`, your
8+
//! microcontroller will be reset.
9+
//!
10+
//! This is useful if you fear that your program may get stuck. In that case it
11+
//! won't feed the watchdog anymore, the watchdog will reset the microcontroller
12+
//! and thus your program will function again.
13+
//!
14+
//! **Attention**:
15+
//!
16+
//! The IWDG runs on a separate 40kHz low-accuracy clock (30kHz-60kHz). You may
17+
//! want to some buffer in your interval.
18+
//!
19+
//! Per default the iwdg continues to run even when you stopped execution of code via a debugger.
20+
//! You may want to disable the watchdog when the cpu is stopped
21+
//!
22+
//! ``` ignore
23+
//! let dbgmcu = p.DBGMCU;
24+
//! dbgmcu.apb1_fz.modify(|_, w| w.dbg_iwdg_stop().set_bit());
25+
//! ```
26+
//!
27+
//! # Example
28+
//! ``` no_run
29+
//! use stm32f0xx_hal as hal;
30+
//!
31+
//! use crate::hal::stm32;
32+
//! use crate::hal::prelude::*;
33+
//! use crate::hal:watchdog::Watchdog;
34+
//! use crate::hal:time::Hertz;
35+
//!
36+
//! let mut p = stm32::Peripherals::take().unwrap();
37+
//!
38+
//! let mut iwdg = Watchdog::new(p.iwdg);
39+
//! iwdg.start(Hertz(100));
40+
//! loop {}
41+
//! // Whoops, got stuck, the watchdog issues a reset after 10 ms
42+
//! iwdg.feed();
43+
//! ```
44+
use embedded_hal::watchdog;
45+
46+
use crate::stm32::IWDG;
47+
use crate::time::Hertz;
48+
49+
/// Watchdog instance
50+
pub struct Watchdog {
51+
iwdg: IWDG,
52+
}
53+
54+
impl watchdog::Watchdog for Watchdog {
55+
/// Feed the watchdog, so that at least one `period` goes by before the next
56+
/// reset
57+
fn feed(&mut self) {
58+
self.iwdg.kr.write(|w| w.key().reset());
59+
}
60+
}
61+
62+
/// Timeout configuration for the IWDG
63+
#[derive(PartialEq, PartialOrd, Clone, Copy)]
64+
pub struct IwdgTimeout {
65+
psc: u8,
66+
reload: u16,
67+
}
68+
69+
impl Into<IwdgTimeout> for Hertz {
70+
/// This converts the value so it's usable by the IWDG
71+
/// Due to conversion losses, the specified frequency is a maximum
72+
///
73+
/// It can also only represent values < 10000 Hertz
74+
fn into(self) -> IwdgTimeout {
75+
let mut time = 40_000 / 4 / self.0;
76+
let mut psc = 0;
77+
let mut reload = 0;
78+
while psc < 7 {
79+
reload = time;
80+
if reload < 0x1000 {
81+
break;
82+
}
83+
psc += 1;
84+
time /= 2;
85+
}
86+
// As we get an integer value, reload is always below 0xFFF
87+
let reload = reload as u16;
88+
IwdgTimeout { psc, reload }
89+
}
90+
}
91+
impl Watchdog {
92+
pub fn new(iwdg: IWDG) -> Self {
93+
Self { iwdg }
94+
}
95+
}
96+
97+
impl watchdog::WatchdogEnable for Watchdog {
98+
type Time = IwdgTimeout;
99+
fn start<T>(&mut self, period: T)
100+
where
101+
T: Into<IwdgTimeout>,
102+
{
103+
let time: IwdgTimeout = period.into();
104+
// Feed the watchdog in case it's already running
105+
// (Waiting for the registers to update takes sometime)
106+
self.iwdg.kr.write(|w| w.key().reset());
107+
// Enable the watchdog
108+
self.iwdg.kr.write(|w| w.key().start());
109+
self.iwdg.kr.write(|w| w.key().enable());
110+
// Wait until it's safe to write to the registers
111+
while self.iwdg.sr.read().pvu().bit() {}
112+
self.iwdg.pr.write(|w| w.pr().bits(time.psc));
113+
while self.iwdg.sr.read().rvu().bit() {}
114+
self.iwdg.rlr.write(|w| w.rl().bits(time.reload));
115+
// Wait until the registers are updated before issuing a reset with
116+
// (potentially false) values
117+
while self.iwdg.sr.read().bits() != 0 {}
118+
self.iwdg.kr.write(|w| w.key().reset());
119+
}
120+
}

0 commit comments

Comments
 (0)