Skip to content

Commit 528fe93

Browse files
authored
Merge pull request #82 from usbalbin/wdt
IWDT - Independent watchdog timer
2 parents d30246f + 6b1c43d commit 528fe93

File tree

3 files changed

+197
-0
lines changed

3 files changed

+197
-0
lines changed

examples/independent_watchdog.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Originaly from stm32h7xx-hal, adapted for stm32g4xx-hal
2+
3+
#![no_main]
4+
#![no_std]
5+
6+
//#[macro_use]
7+
//mod utils;
8+
use stm32g4xx_hal::{independent_watchdog::IndependentWatchdog, prelude::*, stm32::Peripherals};
9+
10+
use cortex_m_rt::entry;
11+
12+
// TODO: Use utils instead
13+
use defmt::Logger;
14+
use defmt_rtt as _; // global logger
15+
use panic_probe as _;
16+
17+
use defmt::info; // TODO: Use utils::logger instead
18+
19+
#[entry]
20+
fn main() -> ! {
21+
//utils::logger::init();
22+
let dp = Peripherals::take().unwrap();
23+
24+
let mut watchdog = IndependentWatchdog::new(dp.IWDG);
25+
26+
info!("");
27+
info!("stm32g4xx-hal example - Watchdog");
28+
info!("");
29+
30+
// If the watchdog is working correctly this print should
31+
// appear again and again as the chip gets restarted
32+
info!("Watchdog restarted! ");
33+
34+
// Enable the watchdog with a limit of 32.76 seconds (which is the maximum this watchdog can do) and wait forever
35+
// -> restart the chip
36+
watchdog.start(32_760.ms());
37+
38+
// Alternatively, there's also a windowed option where if the watchdog is fed before the window time, it will reset the chip as well
39+
// watchdog.start_windowed(100.millis(), 200.millis());
40+
41+
loop {
42+
// We can feed the watchdog like this:
43+
// watchdog.feed();
44+
cortex_m::asm::nop()
45+
}
46+
}

src/independent_watchdog.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//! Independent Watchdog
2+
//!
3+
//! This module implements the embedded-hal
4+
//! [Watchdog](https://docs.rs/embedded-hal/latest/embedded_hal/watchdog/trait.Watchdog.html)
5+
//! trait for the Independent Watchdog peripheral.
6+
//!
7+
//! The Independent Watchdog peripheral triggers a system reset when its internal counter expires.
8+
//!
9+
//! # Examples
10+
//!
11+
//! - [IWDG Example](todo-insert-link-here)
12+
//!
13+
//! Originally from stm32h7-hal, adapted for stm32g4xx-hal
14+
use crate::{
15+
stm32::{iwdg::pr::PR_A, IWDG},
16+
time::{MicroSecond, U32Ext},
17+
};
18+
19+
/// The implementation of the hardware IWDG
20+
pub struct IndependentWatchdog {
21+
iwdg: IWDG,
22+
}
23+
24+
impl IndependentWatchdog {
25+
const CLOCK_SPEED: u32 = 32000;
26+
const MAX_COUNTER_VALUE: u32 = 0x00000FFF;
27+
const MAX_MILLIS_FOR_PRESCALER: [(PR_A, u32); 8] = [
28+
(
29+
PR_A::DivideBy4,
30+
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 4),
31+
),
32+
(
33+
PR_A::DivideBy8,
34+
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 8),
35+
),
36+
(
37+
PR_A::DivideBy16,
38+
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 16),
39+
),
40+
(
41+
PR_A::DivideBy32,
42+
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 32),
43+
),
44+
(
45+
PR_A::DivideBy64,
46+
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 64),
47+
),
48+
(
49+
PR_A::DivideBy128,
50+
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 128),
51+
),
52+
(
53+
PR_A::DivideBy256,
54+
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 256),
55+
),
56+
(
57+
PR_A::DivideBy256bis,
58+
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 256),
59+
),
60+
];
61+
62+
/// Create a new instance
63+
pub fn new(iwdg: IWDG) -> Self {
64+
Self { iwdg }
65+
}
66+
67+
/// Feed the watchdog, resetting the timer to 0
68+
pub fn feed(&mut self) {
69+
self.iwdg.kr.write(|w| w.key().reset());
70+
}
71+
72+
/// Start the watchdog where it must be fed before the max time is over and
73+
/// not before the min time has passed
74+
pub fn start_windowed<T: Into<MicroSecond>>(&mut self, min_window_time: T, max_window_time: T) {
75+
let min_window_time: MicroSecond = min_window_time.into();
76+
let max_window_time: MicroSecond = max_window_time.into();
77+
78+
// Start the watchdog
79+
self.iwdg.kr.write(|w| w.key().start());
80+
// Enable register access
81+
self.iwdg.kr.write(|w| w.key().enable());
82+
83+
// Set the prescaler
84+
let (prescaler, _) = Self::MAX_MILLIS_FOR_PRESCALER
85+
.iter()
86+
.find(|(_, max_millis)| *max_millis >= max_window_time.0 / 1000)
87+
.expect("IWDG max time is greater than is possible");
88+
while self.iwdg.sr.read().pvu().bit_is_set() {
89+
cortex_m::asm::nop();
90+
}
91+
self.iwdg.pr.write(|w| w.pr().variant(*prescaler));
92+
93+
// Reset the window value
94+
while self.iwdg.sr.read().wvu().bit_is_set() {
95+
cortex_m::asm::nop();
96+
}
97+
self.iwdg
98+
.winr
99+
.write(|w| w.win().bits(Self::MAX_COUNTER_VALUE as u16));
100+
101+
// Calculate the counter values
102+
let reload_value = (max_window_time.0 / 1000) * (Self::CLOCK_SPEED / 1000)
103+
/ Self::get_prescaler_divider(prescaler);
104+
let window_value = (min_window_time.0 / 1000) * (Self::CLOCK_SPEED / 1000)
105+
/ Self::get_prescaler_divider(prescaler);
106+
107+
// Set the reload value
108+
while self.iwdg.sr.read().rvu().bit_is_set() {
109+
cortex_m::asm::nop();
110+
}
111+
self.iwdg.rlr.write(|w| w.rl().bits(reload_value as u16));
112+
113+
self.feed();
114+
// Enable register access
115+
self.iwdg.kr.write(|w| w.key().enable());
116+
117+
// Set the window value
118+
while self.iwdg.sr.read().wvu().bit_is_set() {
119+
cortex_m::asm::nop();
120+
}
121+
self.iwdg
122+
.winr
123+
.write(|w| w.win().bits((reload_value - window_value) as u16));
124+
125+
// Wait until everything is set
126+
while self.iwdg.sr.read().bits() != 0 {
127+
cortex_m::asm::nop();
128+
}
129+
130+
self.feed();
131+
}
132+
133+
/// Start the watchdog with the given max time and no minimal time
134+
pub fn start<T: Into<MicroSecond>>(&mut self, max_time: T) {
135+
self.start_windowed(0_u32.ms(), max_time.into());
136+
}
137+
138+
fn get_prescaler_divider(prescaler: &PR_A) -> u32 {
139+
match prescaler {
140+
PR_A::DivideBy4 => 4,
141+
PR_A::DivideBy8 => 8,
142+
PR_A::DivideBy16 => 16,
143+
PR_A::DivideBy32 => 32,
144+
PR_A::DivideBy64 => 64,
145+
PR_A::DivideBy128 => 128,
146+
PR_A::DivideBy256 => 256,
147+
PR_A::DivideBy256bis => 256,
148+
}
149+
}
150+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,4 @@ pub mod syscfg;
8585
pub mod time;
8686
pub mod timer;
8787
// pub mod watchdog;
88+
pub mod independent_watchdog;

0 commit comments

Comments
 (0)