Skip to content

Commit

Permalink
Merge pull request #82 from usbalbin/wdt
Browse files Browse the repository at this point in the history
IWDT - Independent watchdog timer
  • Loading branch information
no111u3 authored Nov 28, 2023
2 parents d30246f + 6b1c43d commit 528fe93
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 0 deletions.
46 changes: 46 additions & 0 deletions examples/independent_watchdog.rs
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()
}
}
150 changes: 150 additions & 0 deletions src/independent_watchdog.rs
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,
}
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,4 @@ pub mod syscfg;
pub mod time;
pub mod timer;
// pub mod watchdog;
pub mod independent_watchdog;

0 comments on commit 528fe93

Please sign in to comment.