diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 2034c67e..c3fcdf21 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -8,6 +8,9 @@ embassy_hal_common::peripherals! { RTC0, RTC1, + // WDT + WDT, + // RNG RNG, diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 27e1f3d2..997f130f 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -8,6 +8,9 @@ embassy_hal_common::peripherals! { RTC0, RTC1, + // WDT + WDT, + // RNG RNG, diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 0d0c5ac7..7e53f734 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -8,6 +8,9 @@ embassy_hal_common::peripherals! { RTC0, RTC1, + // WDT + WDT, + // RNG RNG, diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 9b5bdef1..a0315c40 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -8,6 +8,9 @@ embassy_hal_common::peripherals! { RTC0, RTC1, + // WDT + WDT, + // RNG RNG, diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index e79dba52..6e506d3f 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -9,6 +9,9 @@ embassy_hal_common::peripherals! { RTC1, RTC2, + // WDT + WDT, + // RNG RNG, diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 7c62a7fd..037efacb 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -9,6 +9,9 @@ embassy_hal_common::peripherals! { RTC1, RTC2, + // WDT + WDT, + // RNG RNG, diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 00f6d49d..6523858d 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -9,6 +9,9 @@ embassy_hal_common::peripherals! { RTC1, RTC2, + // WDT + WDT, + // RNG RNG, diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 4cce8028..e846746b 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -40,6 +40,7 @@ pub mod spim; pub mod timer; pub mod twim; pub mod uarte; +pub mod wdt; // This mod MUST go last, so that it sees all the `impl_foo!` macros #[cfg(feature = "nrf52805")] diff --git a/embassy-nrf/src/wdt.rs b/embassy-nrf/src/wdt.rs new file mode 100644 index 00000000..0c6f2b3f --- /dev/null +++ b/embassy-nrf/src/wdt.rs @@ -0,0 +1,153 @@ +//! HAL interface to the WDT peripheral. +//! +//! This HAL implements a basic watchdog timer with 1..=8 handles. +//! Once the watchdog has been started, it cannot be stopped. + +use crate::pac::WDT; +use crate::peripherals; + +const MIN_TICKS: u32 = 15; + +#[non_exhaustive] +pub struct Config { + /// Number of 32768 Hz ticks in each watchdog period. + /// + /// Note: there is a minimum of 15 ticks (458 microseconds). If a lower + /// number is provided, 15 ticks will be used as the configured value. + pub timeout_ticks: u32, + + /// Should the watchdog continue to count during sleep modes? + pub run_during_sleep: bool, + + /// Should the watchdog continue to count when the CPU is halted for debug? + pub run_during_debug_halt: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { + timeout_ticks: 32768, // 1 second + run_during_debug_halt: true, + run_during_sleep: true, + } + } +} + +/// An interface to the Watchdog. +pub struct Watchdog { + _private: (), +} + +impl Watchdog { + /// Try to create a new watchdog instance from the peripheral. + /// + /// This function will return an error if the watchdog is already active + /// with a `config` different to the requested one, or a different number of + /// enabled handles. + /// + /// `N` must be between 1 and 8, inclusive. + #[inline] + pub fn try_new( + wdt: peripherals::WDT, + config: Config, + ) -> Result<(Self, [WatchdogHandle; N]), peripherals::WDT> { + assert!(N >= 1 && N <= 8); + + let r = unsafe { &*WDT::ptr() }; + + let crv = config.timeout_ticks.max(MIN_TICKS); + let rren = (1u32 << N) - 1; + + if r.runstatus.read().runstatus().bit() { + let curr_config = r.config.read(); + if curr_config.halt().bit() != config.run_during_debug_halt + || curr_config.sleep().bit() != config.run_during_sleep + || r.crv.read().bits() != crv + || r.rren.read().bits() != rren + { + return Err(wdt); + } + } else { + r.config.write(|w| { + w.sleep().bit(config.run_during_sleep); + w.halt().bit(config.run_during_debug_halt); + w + }); + r.intenset.write(|w| w.timeout().set_bit()); + + r.crv.write(|w| unsafe { w.bits(crv) }); + r.rren.write(|w| unsafe { w.bits(rren) }); + r.tasks_start.write(|w| unsafe { w.bits(1) }); + } + + let this = Self { _private: () }; + + const DUMMY_HANDLE: WatchdogHandle = WatchdogHandle { index: 0 }; + let mut handles = [DUMMY_HANDLE; N]; + for i in 0..N { + handles[i] = WatchdogHandle { index: i as u8 }; + handles[i].pet(); + } + + Ok((this, handles)) + } + + /// Enable the watchdog interrupt. + /// + /// NOTE: Although the interrupt will occur, there is no way to prevent + /// the reset from occurring. From the time the event was fired, the + /// system will reset two LFCLK ticks later (61 microseconds) if the + /// interrupt has been enabled. + #[inline(always)] + pub fn enable_interrupt(&mut self) { + let r = unsafe { &*WDT::ptr() }; + r.intenset.write(|w| w.timeout().set_bit()); + } + + /// Disable the watchdog interrupt. + /// + /// NOTE: This has no effect on the reset caused by the Watchdog. + #[inline(always)] + pub fn disable_interrupt(&mut self) { + let r = unsafe { &*WDT::ptr() }; + r.intenclr.write(|w| w.timeout().set_bit()); + } + + /// Is the watchdog still awaiting pets from any handle? + /// + /// This reports whether sufficient pets have been received from all + /// handles to prevent a reset this time period. + #[inline(always)] + pub fn awaiting_pets(&self) -> bool { + let r = unsafe { &*WDT::ptr() }; + let enabled = r.rren.read().bits(); + let status = r.reqstatus.read().bits(); + (status & enabled) == 0 + } +} + +pub struct WatchdogHandle { + index: u8, +} + +impl WatchdogHandle { + /// Pet the watchdog. + /// + /// This function pets the given watchdog handle. + /// + /// NOTE: All active handles must be pet within the time interval to + /// prevent a reset from occurring. + #[inline] + pub fn pet(&mut self) { + let r = unsafe { &*WDT::ptr() }; + r.rr[self.index as usize].write(|w| w.rr().reload()); + } + + /// Has this handle been pet within the current window? + pub fn is_pet(&self) -> bool { + let r = unsafe { &*WDT::ptr() }; + let rd = r.reqstatus.read().bits(); + let idx = self.index as usize; + ((rd >> idx) & 0x1) == 0 + } +} diff --git a/examples/nrf/src/bin/wdt.rs b/examples/nrf/src/bin/wdt.rs new file mode 100644 index 00000000..53df7b0b --- /dev/null +++ b/examples/nrf/src/bin/wdt.rs @@ -0,0 +1,46 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] + +#[path = "../example_common.rs"] +mod example_common; + +use defmt::*; +use embassy::executor::Spawner; +use embassy_nrf::gpio::{Input, Pull}; +use embassy_nrf::gpiote::PortInput; +use embassy_nrf::wdt::{Config, Watchdog}; +use embassy_nrf::Peripherals; +use embassy_traits::gpio::{WaitForHigh, WaitForLow}; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + info!("Hello World!"); + + let mut config = Config::default(); + config.timeout_ticks = 32768 * 3; // 3 seconds + + // This is needed for `probe-run` to be able to catch the panic message + // in the WDT interrupt. The core resets 2 ticks after firing the interrupt. + config.run_during_debug_halt = false; + + let (_wdt, [mut handle]) = match Watchdog::try_new::<1>(p.WDT, config) { + Ok(x) => x, + Err(_) => { + info!("Watchdog already active with wrong config, waiting for it to timeout..."); + loop {} + } + }; + + let mut button = PortInput::new(Input::new(p.P0_11, Pull::Up)); + + info!("Watchdog started, press button 1 to pet it or I'll reset in 3 seconds!"); + + loop { + button.wait_for_high().await; + button.wait_for_low().await; + info!("Button pressed, petting watchdog!"); + handle.pet(); + } +}