Skip to content

Commit 60b429b

Browse files
authored
Merge pull request #113 from MathiasKoch/feature/watchdog
Feature/Independent Watchdog Timer
2 parents b4873c1 + 17c0b87 commit 60b429b

File tree

4 files changed

+207
-2
lines changed

4 files changed

+207
-2
lines changed

examples/watchdog.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//! Example of watchdog timer
2+
#![deny(unsafe_code)]
3+
// #![deny(warnings)]
4+
#![no_std]
5+
#![no_main]
6+
7+
extern crate cortex_m;
8+
#[macro_use]
9+
extern crate cortex_m_rt as rt;
10+
extern crate cortex_m_semihosting as sh;
11+
extern crate panic_semihosting;
12+
extern crate stm32l4xx_hal as hal;
13+
// #[macro_use(block)]
14+
// extern crate nb;
15+
16+
use crate::hal::delay::Delay;
17+
use crate::hal::prelude::*;
18+
use crate::hal::rcc::{CrystalBypass, ClockSecuritySystem};
19+
use crate::hal::time::MilliSeconds;
20+
use crate::hal::watchdog::IndependentWatchdog;
21+
use crate::rt::ExceptionFrame;
22+
23+
use crate::sh::hio;
24+
use core::fmt::Write;
25+
26+
#[entry]
27+
fn main() -> ! {
28+
let mut hstdout = hio::hstdout().unwrap();
29+
30+
writeln!(hstdout, "Hello, world!").unwrap();
31+
32+
let cp = cortex_m::Peripherals::take().unwrap();
33+
let dp = hal::stm32::Peripherals::take().unwrap();
34+
35+
let mut flash = dp.FLASH.constrain();
36+
let mut rcc = dp.RCC.constrain();
37+
let mut pwr = dp.PWR.constrain(&mut rcc.apb1r1);
38+
39+
// Try a different clock configuration
40+
let clocks = rcc
41+
.cfgr
42+
.lsi(true)
43+
.freeze(&mut flash.acr, &mut pwr);
44+
45+
let mut timer = Delay::new(cp.SYST, clocks);
46+
47+
// Initiate the independent watchdog timer
48+
let mut watchdog = IndependentWatchdog::new(dp.IWDG);
49+
watchdog.stop_on_debug(&dp.DBGMCU, true);
50+
51+
// Start the independent watchdog timer
52+
watchdog.start(MilliSeconds(1020));
53+
timer.delay_ms(1000_u32);
54+
55+
// Feed the independent watchdog timer
56+
watchdog.feed();
57+
timer.delay_ms(1000_u32);
58+
59+
watchdog.feed();
60+
timer.delay_ms(1000_u32);
61+
62+
watchdog.feed();
63+
writeln!(hstdout, "Good bye!").unwrap();
64+
loop {}
65+
}
66+
67+
#[exception]
68+
fn HardFault(ef: &ExceptionFrame) -> ! {
69+
panic!("{:#?}", ef);
70+
}

src/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ pub use crate::pac as device;
5858
))]
5959
pub use crate::pac as stm32;
6060

61-
6261
pub mod traits;
6362

6463
#[cfg(any(
@@ -206,8 +205,17 @@ pub mod timer;
206205
feature = "stm32l4x6"
207206
))]
208207
pub mod tsc;
208+
209209
#[cfg(all(
210210
feature = "stm32-usbd",
211211
any(feature = "stm32l4x2", feature = "stm32l4x3")
212212
))]
213213
pub mod usb;
214+
#[cfg(any(
215+
feature = "stm32l4x1",
216+
feature = "stm32l4x2",
217+
feature = "stm32l4x3",
218+
feature = "stm32l4x5",
219+
feature = "stm32l4x6"
220+
))]
221+
pub mod watchdog;

src/time.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Time units
22
3-
use cortex_m::peripheral::DWT;
43
use crate::rcc::Clocks;
4+
use cortex_m::peripheral::DWT;
55

66
/// Bits per second
77
#[derive(Clone, Copy, Debug)]
@@ -32,6 +32,9 @@ pub trait U32Ext {
3232

3333
/// Wrap in `MegaHertz`
3434
fn mhz(self) -> MegaHertz;
35+
36+
/// Wrap in `MilliSeconds`
37+
fn milliseconds(self) -> MilliSeconds;
3538
}
3639

3740
impl U32Ext for u32 {
@@ -50,6 +53,10 @@ impl U32Ext for u32 {
5053
fn mhz(self) -> MegaHertz {
5154
MegaHertz(self)
5255
}
56+
57+
fn milliseconds(self) -> MilliSeconds {
58+
MilliSeconds(self)
59+
}
5360
}
5461

5562
impl Into<Hertz> for KiloHertz {
@@ -124,3 +131,7 @@ impl Instant {
124131
DWT::get_cycle_count().wrapping_sub(self.now)
125132
}
126133
}
134+
135+
/// Time unit
136+
#[derive(PartialEq, PartialOrd, Clone, Copy)]
137+
pub struct MilliSeconds(pub u32);

src/watchdog.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//! Watchdog peripherals
2+
3+
use crate::{
4+
hal::watchdog::{Watchdog, WatchdogEnable},
5+
stm32::{DBGMCU, IWDG},
6+
time::MilliSeconds,
7+
};
8+
9+
/// Wraps the Independent Watchdog (IWDG) peripheral
10+
pub struct IndependentWatchdog {
11+
iwdg: IWDG,
12+
}
13+
14+
const LSI_KHZ: u32 = 32;
15+
const MAX_PR: u32 = 0b110;
16+
const MAX_RL: u16 = 0xFFF;
17+
const KR_ACCESS: u16 = 0x5555;
18+
const KR_RELOAD: u16 = 0xAAAA;
19+
const KR_START: u16 = 0xCCCC;
20+
21+
impl IndependentWatchdog {
22+
/// Creates a new `IndependentWatchDog` without starting it. Call `start` to start the watchdog.
23+
/// See `WatchdogEnable` and `Watchdog` for more info.
24+
pub fn new(iwdg: IWDG) -> Self {
25+
IndependentWatchdog { iwdg }
26+
}
27+
28+
/// Debug independent watchdog stopped when core is halted
29+
pub fn stop_on_debug(&self, dbgmcu: &DBGMCU, stop: bool) {
30+
#[cfg(any(feature = "stm32l4x1", feature = "stm32l4x2", feature = "stm32l4x3",))]
31+
dbgmcu.apb1fzr1.modify(|_, w| w.dbg_iwdg_stop().bit(stop));
32+
#[cfg(any(feature = "stm32l4x5", feature = "stm32l4x6"))]
33+
dbgmcu.apb1_fzr1.modify(|_, w| w.dbg_iwdg_stop().bit(stop));
34+
}
35+
36+
/// Sets the watchdog timer timout period. Max: 32768 ms
37+
fn setup(&self, timeout_ms: MilliSeconds) {
38+
assert!(timeout_ms.0 < (1 << 15), "Watchdog timeout to high");
39+
let pr = match timeout_ms.0 {
40+
t if t <= (MAX_PR + 1) * 4 / LSI_KHZ => 0b000,
41+
t if t <= (MAX_PR + 1) * 8 / LSI_KHZ => 0b001,
42+
t if t <= (MAX_PR + 1) * 16 / LSI_KHZ => 0b010,
43+
t if t <= (MAX_PR + 1) * 32 / LSI_KHZ => 0b011,
44+
t if t <= (MAX_PR + 1) * 64 / LSI_KHZ => 0b100,
45+
t if t <= (MAX_PR + 1) * 128 / LSI_KHZ => 0b101,
46+
_ => 0b110,
47+
};
48+
49+
let max_period = Self::timeout_period(pr, MAX_RL);
50+
let max_rl = u32::from(MAX_RL);
51+
let rl = (timeout_ms.0 * max_rl / max_period).min(max_rl) as u16;
52+
53+
self.access_registers(|iwdg| {
54+
iwdg.pr.modify(|_, w| w.pr().bits(pr));
55+
iwdg.rlr.modify(|_, w| w.rl().bits(rl));
56+
});
57+
}
58+
59+
fn is_pr_updating(&self) -> bool {
60+
self.iwdg.sr.read().pvu().bit()
61+
}
62+
63+
/// Returns the interval in ms
64+
pub fn interval(&self) -> MilliSeconds {
65+
while self.is_pr_updating() {}
66+
67+
let pr = self.iwdg.pr.read().pr().bits();
68+
let rl = self.iwdg.rlr.read().rl().bits();
69+
let ms = Self::timeout_period(pr, rl);
70+
MilliSeconds(ms)
71+
}
72+
73+
/// pr: Prescaler divider bits, rl: reload value
74+
///
75+
/// Returns timeout period in ms
76+
fn timeout_period(pr: u8, rl: u16) -> u32 {
77+
let divider: u32 = match pr {
78+
0b000 => 4,
79+
0b001 => 8,
80+
0b010 => 16,
81+
0b011 => 32,
82+
0b100 => 64,
83+
0b101 => 128,
84+
0b110 => 256,
85+
0b111 => 256,
86+
_ => unreachable!(),
87+
};
88+
(u32::from(rl) + 1) * divider / LSI_KHZ
89+
}
90+
91+
fn access_registers<A, F: FnMut(&IWDG) -> A>(&self, mut f: F) -> A {
92+
// Unprotect write access to registers
93+
self.iwdg.kr.write(|w| unsafe { w.key().bits(KR_ACCESS) });
94+
let a = f(&self.iwdg);
95+
96+
// Protect again
97+
self.iwdg.kr.write(|w| unsafe { w.key().bits(KR_RELOAD) });
98+
a
99+
}
100+
}
101+
102+
impl WatchdogEnable for IndependentWatchdog {
103+
type Time = MilliSeconds;
104+
105+
fn start<T: Into<Self::Time>>(&mut self, period: T) {
106+
self.setup(period.into());
107+
108+
self.iwdg.kr.write(|w| unsafe { w.key().bits(KR_START) });
109+
}
110+
}
111+
112+
impl Watchdog for IndependentWatchdog {
113+
fn feed(&mut self) {
114+
self.iwdg.kr.write(|w| unsafe { w.key().bits(KR_RELOAD) });
115+
}
116+
}

0 commit comments

Comments
 (0)