-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #82 from usbalbin/wdt
IWDT - Independent watchdog timer
- Loading branch information
Showing
3 changed files
with
197 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Originaly from stm32h7xx-hal, adapted for stm32g4xx-hal | ||
|
||
#![no_main] | ||
#![no_std] | ||
|
||
//#[macro_use] | ||
//mod utils; | ||
use stm32g4xx_hal::{independent_watchdog::IndependentWatchdog, prelude::*, stm32::Peripherals}; | ||
|
||
use cortex_m_rt::entry; | ||
|
||
// TODO: Use utils instead | ||
use defmt::Logger; | ||
use defmt_rtt as _; // global logger | ||
use panic_probe as _; | ||
|
||
use defmt::info; // TODO: Use utils::logger instead | ||
|
||
#[entry] | ||
fn main() -> ! { | ||
//utils::logger::init(); | ||
let dp = Peripherals::take().unwrap(); | ||
|
||
let mut watchdog = IndependentWatchdog::new(dp.IWDG); | ||
|
||
info!(""); | ||
info!("stm32g4xx-hal example - Watchdog"); | ||
info!(""); | ||
|
||
// If the watchdog is working correctly this print should | ||
// appear again and again as the chip gets restarted | ||
info!("Watchdog restarted! "); | ||
|
||
// Enable the watchdog with a limit of 32.76 seconds (which is the maximum this watchdog can do) and wait forever | ||
// -> restart the chip | ||
watchdog.start(32_760.ms()); | ||
|
||
// Alternatively, there's also a windowed option where if the watchdog is fed before the window time, it will reset the chip as well | ||
// watchdog.start_windowed(100.millis(), 200.millis()); | ||
|
||
loop { | ||
// We can feed the watchdog like this: | ||
// watchdog.feed(); | ||
cortex_m::asm::nop() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
//! Independent Watchdog | ||
//! | ||
//! This module implements the embedded-hal | ||
//! [Watchdog](https://docs.rs/embedded-hal/latest/embedded_hal/watchdog/trait.Watchdog.html) | ||
//! trait for the Independent Watchdog peripheral. | ||
//! | ||
//! The Independent Watchdog peripheral triggers a system reset when its internal counter expires. | ||
//! | ||
//! # Examples | ||
//! | ||
//! - [IWDG Example](todo-insert-link-here) | ||
//! | ||
//! Originally from stm32h7-hal, adapted for stm32g4xx-hal | ||
use crate::{ | ||
stm32::{iwdg::pr::PR_A, IWDG}, | ||
time::{MicroSecond, U32Ext}, | ||
}; | ||
|
||
/// The implementation of the hardware IWDG | ||
pub struct IndependentWatchdog { | ||
iwdg: IWDG, | ||
} | ||
|
||
impl IndependentWatchdog { | ||
const CLOCK_SPEED: u32 = 32000; | ||
const MAX_COUNTER_VALUE: u32 = 0x00000FFF; | ||
const MAX_MILLIS_FOR_PRESCALER: [(PR_A, u32); 8] = [ | ||
( | ||
PR_A::DivideBy4, | ||
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 4), | ||
), | ||
( | ||
PR_A::DivideBy8, | ||
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 8), | ||
), | ||
( | ||
PR_A::DivideBy16, | ||
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 16), | ||
), | ||
( | ||
PR_A::DivideBy32, | ||
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 32), | ||
), | ||
( | ||
PR_A::DivideBy64, | ||
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 64), | ||
), | ||
( | ||
PR_A::DivideBy128, | ||
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 128), | ||
), | ||
( | ||
PR_A::DivideBy256, | ||
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 256), | ||
), | ||
( | ||
PR_A::DivideBy256bis, | ||
(Self::MAX_COUNTER_VALUE * 1000) / (Self::CLOCK_SPEED / 256), | ||
), | ||
]; | ||
|
||
/// Create a new instance | ||
pub fn new(iwdg: IWDG) -> Self { | ||
Self { iwdg } | ||
} | ||
|
||
/// Feed the watchdog, resetting the timer to 0 | ||
pub fn feed(&mut self) { | ||
self.iwdg.kr.write(|w| w.key().reset()); | ||
} | ||
|
||
/// Start the watchdog where it must be fed before the max time is over and | ||
/// not before the min time has passed | ||
pub fn start_windowed<T: Into<MicroSecond>>(&mut self, min_window_time: T, max_window_time: T) { | ||
let min_window_time: MicroSecond = min_window_time.into(); | ||
let max_window_time: MicroSecond = max_window_time.into(); | ||
|
||
// Start the watchdog | ||
self.iwdg.kr.write(|w| w.key().start()); | ||
// Enable register access | ||
self.iwdg.kr.write(|w| w.key().enable()); | ||
|
||
// Set the prescaler | ||
let (prescaler, _) = Self::MAX_MILLIS_FOR_PRESCALER | ||
.iter() | ||
.find(|(_, max_millis)| *max_millis >= max_window_time.0 / 1000) | ||
.expect("IWDG max time is greater than is possible"); | ||
while self.iwdg.sr.read().pvu().bit_is_set() { | ||
cortex_m::asm::nop(); | ||
} | ||
self.iwdg.pr.write(|w| w.pr().variant(*prescaler)); | ||
|
||
// Reset the window value | ||
while self.iwdg.sr.read().wvu().bit_is_set() { | ||
cortex_m::asm::nop(); | ||
} | ||
self.iwdg | ||
.winr | ||
.write(|w| w.win().bits(Self::MAX_COUNTER_VALUE as u16)); | ||
|
||
// Calculate the counter values | ||
let reload_value = (max_window_time.0 / 1000) * (Self::CLOCK_SPEED / 1000) | ||
/ Self::get_prescaler_divider(prescaler); | ||
let window_value = (min_window_time.0 / 1000) * (Self::CLOCK_SPEED / 1000) | ||
/ Self::get_prescaler_divider(prescaler); | ||
|
||
// Set the reload value | ||
while self.iwdg.sr.read().rvu().bit_is_set() { | ||
cortex_m::asm::nop(); | ||
} | ||
self.iwdg.rlr.write(|w| w.rl().bits(reload_value as u16)); | ||
|
||
self.feed(); | ||
// Enable register access | ||
self.iwdg.kr.write(|w| w.key().enable()); | ||
|
||
// Set the window value | ||
while self.iwdg.sr.read().wvu().bit_is_set() { | ||
cortex_m::asm::nop(); | ||
} | ||
self.iwdg | ||
.winr | ||
.write(|w| w.win().bits((reload_value - window_value) as u16)); | ||
|
||
// Wait until everything is set | ||
while self.iwdg.sr.read().bits() != 0 { | ||
cortex_m::asm::nop(); | ||
} | ||
|
||
self.feed(); | ||
} | ||
|
||
/// Start the watchdog with the given max time and no minimal time | ||
pub fn start<T: Into<MicroSecond>>(&mut self, max_time: T) { | ||
self.start_windowed(0_u32.ms(), max_time.into()); | ||
} | ||
|
||
fn get_prescaler_divider(prescaler: &PR_A) -> u32 { | ||
match prescaler { | ||
PR_A::DivideBy4 => 4, | ||
PR_A::DivideBy8 => 8, | ||
PR_A::DivideBy16 => 16, | ||
PR_A::DivideBy32 => 32, | ||
PR_A::DivideBy64 => 64, | ||
PR_A::DivideBy128 => 128, | ||
PR_A::DivideBy256 => 256, | ||
PR_A::DivideBy256bis => 256, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -85,3 +85,4 @@ pub mod syscfg; | |
pub mod time; | ||
pub mod timer; | ||
// pub mod watchdog; | ||
pub mod independent_watchdog; |