nrf: add WDT driver
This commit is contained in:
		@@ -8,6 +8,9 @@ embassy_hal_common::peripherals! {
 | 
			
		||||
    RTC0,
 | 
			
		||||
    RTC1,
 | 
			
		||||
 | 
			
		||||
    // WDT
 | 
			
		||||
    WDT,
 | 
			
		||||
 | 
			
		||||
    // RNG
 | 
			
		||||
    RNG,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,9 @@ embassy_hal_common::peripherals! {
 | 
			
		||||
    RTC0,
 | 
			
		||||
    RTC1,
 | 
			
		||||
 | 
			
		||||
    // WDT
 | 
			
		||||
    WDT,
 | 
			
		||||
 | 
			
		||||
    // RNG
 | 
			
		||||
    RNG,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,9 @@ embassy_hal_common::peripherals! {
 | 
			
		||||
    RTC0,
 | 
			
		||||
    RTC1,
 | 
			
		||||
 | 
			
		||||
    // WDT
 | 
			
		||||
    WDT,
 | 
			
		||||
 | 
			
		||||
    // RNG
 | 
			
		||||
    RNG,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,9 @@ embassy_hal_common::peripherals! {
 | 
			
		||||
    RTC0,
 | 
			
		||||
    RTC1,
 | 
			
		||||
 | 
			
		||||
    // WDT
 | 
			
		||||
    WDT,
 | 
			
		||||
 | 
			
		||||
    // RNG
 | 
			
		||||
    RNG,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,9 @@ embassy_hal_common::peripherals! {
 | 
			
		||||
    RTC1,
 | 
			
		||||
    RTC2,
 | 
			
		||||
 | 
			
		||||
    // WDT
 | 
			
		||||
    WDT,
 | 
			
		||||
 | 
			
		||||
    // RNG
 | 
			
		||||
    RNG,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,9 @@ embassy_hal_common::peripherals! {
 | 
			
		||||
    RTC1,
 | 
			
		||||
    RTC2,
 | 
			
		||||
 | 
			
		||||
    // WDT
 | 
			
		||||
    WDT,
 | 
			
		||||
 | 
			
		||||
    // RNG
 | 
			
		||||
    RNG,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,9 @@ embassy_hal_common::peripherals! {
 | 
			
		||||
    RTC1,
 | 
			
		||||
    RTC2,
 | 
			
		||||
 | 
			
		||||
    // WDT
 | 
			
		||||
    WDT,
 | 
			
		||||
 | 
			
		||||
    // RNG
 | 
			
		||||
    RNG,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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")]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										153
									
								
								embassy-nrf/src/wdt.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								embassy-nrf/src/wdt.rs
									
									
									
									
									
										Normal file
									
								
							@@ -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<const N: usize>(
 | 
			
		||||
        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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								examples/nrf/src/bin/wdt.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								examples/nrf/src/bin/wdt.rs
									
									
									
									
									
										Normal file
									
								
							@@ -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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user