From d96d21e176891fa1c9b6054afcd99a199cca0774 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 21 Oct 2021 07:20:17 -0600 Subject: [PATCH] Watchdog timer (#38) * Allow generic time units in timer setup functions -This implements the ability for any embedded-time::duration::* or embedded-time::rate::* to be used where previously only Hertz or Milliseconds were accepted. * WDT skeleton implementing all embedded_hal traits * WDT code written, need example to test function * More functions to use generic time; misc. cleanup * Change delay LED to use timer CountDown features * Added more info about time units/casting in docs * Tried writing to WFAR/WSAR before setting; fails - I'm pretty sure the reason things aren't working is that the WFAR/WSAR access_key1 and access_key2 registers need to be sent their passwords in order to configure the WDT. This commit doesn't work any better than the last one, but it seems like it's worth recording the failure state, so it can be referenced in the future. * Watchdog can now be enabled, WTS bit functioning - The access keys when sent in the correct order do allow for writing registers. Both must be sent, sequentially, immediately before writing. They are currently implemented for all writes relating to the WDT. I still need to try removing them and seeing which registers actually require the codes, and which can be written without. - The watchdog is being triggered, and the WTS bit is being set, but the timing values are off. Will need to investigate. * Watchdog is functioning, at ~30x tick speed - Added example test code with lots of debug printing. - Maybe the 1kHz clock is not being divided from the 32kHz clock. if that's the case, we will want to disable it as a valid clock source for the WDT, since it works for the timers but not the WDT. * WDT working as expected without 1kHz clock source - Disabling the 1kHz clock has the other clocks working as expected. - Still need to find and remove unnecessary access key sending. - Still need to test interrupts. * Add getters, cleanup functions, comments, layout * WDT interrupts working; example now shows use * Fixed `const` error in watchdog.rs get_key fn * fix bjoernQ's suggestions from PR thread --- examples/led_timer_interrupt.rs | 28 ++- examples/watchdog_example.rs | 160 ++++++++++++++++ hal_defaults.x | 1 + src/interrupts.rs | 9 + src/lib.rs | 1 + src/timer.rs | 46 +++-- src/watchdog.rs | 320 ++++++++++++++++++++++++++++++++ 7 files changed, 541 insertions(+), 24 deletions(-) create mode 100644 examples/watchdog_example.rs create mode 100644 src/watchdog.rs diff --git a/examples/led_timer_interrupt.rs b/examples/led_timer_interrupt.rs index 45906b2..9187d36 100644 --- a/examples/led_timer_interrupt.rs +++ b/examples/led_timer_interrupt.rs @@ -5,7 +5,7 @@ // The programmed blinking pattern below results in: // -the interrupt-controlled blue LED being on for 1500ms, then off for 1500ms. // -the interrupt-controlled green LED toggling on for 500ms and then off for 1000ms. -// -the delay-loop controlled red LED toggling on for 3000ms and off for 3000ms. +// -the timer::CountDown delay-loop controlled red LED toggling every 3000ms. // Global access to variables inside the interrupt handler function is controlled by the // Mutex> library, as outlined in the rust embedded handbook chapter on concurrency. // See https://docs.rust-embedded.org/book/concurrency/index.html for more info. @@ -16,7 +16,7 @@ use bl602_hal as hal; use core::cell::RefCell; use core::ops::DerefMut; -use embedded_hal::delay::blocking::DelayMs; +use embedded_hal::timer::nb::CountDown; use embedded_hal::digital::blocking::{OutputPin, ToggleableOutputPin}; use embedded_time::{duration::*, rate::*}; use hal::{ @@ -62,12 +62,17 @@ fn main() -> ! { let mut g_led_pin = glb.pin14.into_pull_down_output(); let _ = g_led_pin.set_high(); - // Initialize TimerCh0 to increment its count at a rate of 1000Hz: + // Initialize TimerCh0 to increment its count at a rate of 160MHz: let timers = dp.TIMER.split(); let timer_ch0 = timers .channel0 .set_clock_source(ClockSource::Fclk(&clocks), 160_000_000_u32.Hz()); + // Initialize TimerCh1 at a rate of 1kHz to use for a blocking delay on the red LED: + let mut timer_ch1 = timers + .channel1 + .set_clock_source(ClockSource::Clock1Khz, 1000u32.Hz()); + // Set up Match0 which we will use to control the blue LED: // Note that you can use any embedded_time::duration as a time value in these set functions. timer_ch0.enable_match0_interrupt(); @@ -83,8 +88,9 @@ fn main() -> ! { timer_ch0.set_preload_value(0.microseconds()); timer_ch0.set_preload(hal::timer::Preload::PreloadMatchComparator0); - // Finally, remember to enable the timer channel so the interrupts will function: + // Finally, remember to enable the timer channels so they will function: timer_ch0.enable(); + timer_ch1.enable(); // Move the references to their UnsafeCells once initialized, and before interrupts are enabled: riscv::interrupt::free(|cs| G_INTERRUPT_LED_PIN_B.borrow(cs).replace(Some(b_led_pin))); @@ -97,12 +103,16 @@ fn main() -> ! { // and immediately re-triggering the interrupt function when it returns. enable_interrupt(Interrupt::TimerCh0); - // Create a blocking delay function based on the current cpu frequency for the red LED control: - let mut d = bl602_hal::delay::McycleDelay::new(clocks.sysclk().0); - + // The .start() and .wait() functions as specified in the embedded_hal allow us to use + // a timer without needing to configure any match values or handle any interrupts: loop { - // Toggle the red channel every 3000ms to show it is still running in the background: - let _ = d.delay_ms(3000); + // Start the timer's CountDown functionality. The countdown will last 3000ms. + timer_ch1.start(3_000u32.milliseconds()).ok(); + // The .wait() function returns an error until the timer has finished counting down. + while timer_ch1.wait().is_err() { + // Stay in the while loop until the timer is done. + } + // Once the timer is done counting down, toggle the LED: let _ = r_led_pin.toggle(); } } diff --git a/examples/watchdog_example.rs b/examples/watchdog_example.rs new file mode 100644 index 0000000..f8b1b5f --- /dev/null +++ b/examples/watchdog_example.rs @@ -0,0 +1,160 @@ +/* + + This example code is designed to always fail to `feed()` the watchdog so you can see that it is + working as intended. When the watchdog fails, it sets the WTS bit of the WSR register on the + bl602 to be 1. You can read this bit on initialization of the chip to see if the watchdog has + been triggered. + + The code below will read this bit as 0 the first time it is powered on, and then it will fail + to `feed()` the watchdog, resulting in a watchdog reset. The next time (and every subsequent + time) it resets via the watchdog the WTS bit value that is read should be 1. To clear the bit, + you either have to manually clear it via the watchdog.clear_wts() function, or reset the board + using either the reset pin or turning off the power to the board and bringing it back online. + + After the board has been reset by the watchdog timer once, the code will change the Watchdog's + mode to call the Watchdog interrupt function instead. This interrupt function will no + longer reset the board, but will instead toggle the red led channel of the RGB led every time + the watchdog is triggered. +*/ + +#![no_std] +#![no_main] + +use bl602_hal as hal; +use core::cell::RefCell; +use core::fmt::Write; +use core::ops::DerefMut; +use embedded_hal::delay::blocking::DelayMs; +use embedded_hal::digital::blocking::{OutputPin, ToggleableOutputPin}; +use embedded_hal::watchdog::blocking::{Enable, Watchdog}; +use embedded_time::{duration::*, rate::*}; +use hal::{ + clock::{Strict, SysclkFreq, UART_PLL_FREQ}, + gpio::{Output, PullDown}, + interrupts::*, + pac, + prelude::*, + serial::*, + timer::*, + watchdog::*, +}; +use panic_halt as _; +use riscv::interrupt::Mutex; + +// Setup custom types to make the code below easier to read: +type RedLedPin = hal::gpio::Pin17>; +type WatchdogTimer = hal::watchdog::ConfiguredWatchdog0; + +// Initialize global static containers for peripheral access within the interrupt function: +static G_INTERRUPT_LED_PIN_R: Mutex>> = Mutex::new(RefCell::new(None)); +static G_LED_TIMER: Mutex>> = Mutex::new(RefCell::new(None)); + +#[riscv_rt::entry] +fn main() -> ! { + //take control of the device peripherals: + let dp = pac::Peripherals::take().unwrap(); + let mut gpio_pins = dp.GLB.split(); + + // Set up all the clocks we need + let clocks = Strict::new() + .use_pll(40_000_000u32.Hz()) + .sys_clk(SysclkFreq::Pll160Mhz) + .uart_clk(UART_PLL_FREQ.Hz()) + .freeze(&mut gpio_pins.clk_cfg); + + // Set up uart output for debug printing. Since this microcontroller has a pin matrix, + // we need to set up both the pins and the muxs + let pin16 = gpio_pins.pin16.into_uart_sig0(); + let pin7 = gpio_pins.pin7.into_uart_sig7(); + let mux0 = gpio_pins.uart_mux0.into_uart0_tx(); + let mux7 = gpio_pins.uart_mux7.into_uart0_rx(); + + // Configure our UART to 2MBaud, and use the pins we configured above + let mut serial = Serial::uart0( + dp.UART, + Config::default().baudrate(2_000_000.Bd()), + ((pin16, mux0), (pin7, mux7)), + clocks, + ); + + // Initialize the led pin to their default state: + let mut r_led_pin = gpio_pins.pin17.into_pull_down_output(); + let _ = r_led_pin.set_low(); + + // Create a blocking delay function based on the current cpu frequency + let mut d = bl602_hal::delay::McycleDelay::new(clocks.sysclk().0); + + // Set up the watchdog timer to the slowest tick rate possible: + let timers = dp.TIMER.split(); + let watchdog = timers + .watchdog + .set_clock_source(WdtClockSource::Rc32Khz, 125.Hz()); + + // Before setting up and enabling the watchdog, + // retrieve and print to serial the state of the WTS bit: + let wts_bit_value = match watchdog.has_watchdog_reset_occurred() { + true => 1_u8, + false => 0_u8, + }; + + writeln!(serial, "The watchdog WTS bit reads as: {}\r", wts_bit_value).ok(); + + // On a clean boot, the watchdog will trigger a board reset if not fed in time. + // If the watchdog has previously reset the board, switch to interrupt mode. + match wts_bit_value { + 0_u8 => watchdog.set_mode(WatchdogMode::Reset), + 1_u8 => { + watchdog.set_mode(WatchdogMode::Interrupt); + enable_interrupt(Interrupt::Watchdog); + } + _ => unreachable!(), + } + + // The watchdog timer doesn't begin counting ticks until it is started. We don't need to handle + // the error state, since the watchdog start function will never actually return an Err(). + let watchdog = watchdog.start(10_u32.seconds()).unwrap(); + + // Move the references to their UnsafeCells once initialized, and before interrupts are enabled: + riscv::interrupt::free(|cs| G_INTERRUPT_LED_PIN_R.borrow(cs).replace(Some(r_led_pin))); + riscv::interrupt::free(|cs| G_LED_TIMER.borrow(cs).replace(Some(watchdog))); + + loop { + // Since we delay for more than 10 seconds, the watchdog is only fed the first time through + // the loop. If you want to prevent the watchdog from triggering, decrease the number of + // milliseconds in the delay to less than 10 seconds. + + // In order to use the watchdog once it has been moved into the RefCell, you must call free(): + riscv::interrupt::free(|cs| { + if let Some(watchdog) = G_LED_TIMER.borrow(cs).borrow_mut().deref_mut() { + watchdog.feed().ok(); + } + }); + d.delay_ms(20_000).ok(); + } +} + +// This is the interrupt handler for the watchdog. It currently toggles the red led channel of the +// RGB led on the board every time the watchdog is triggered after it has been reset at least once. +#[allow(non_snake_case)] +#[no_mangle] +fn Watchdog(_: &mut TrapFrame) { + disable_interrupt(Interrupt::Watchdog); + clear_interrupt(Interrupt::Watchdog); + + //Clear the WDT interrupt flag and feed the watchdog to reset its counter: + riscv::interrupt::free(|cs| { + if let Some(watchdog) = G_LED_TIMER.borrow(cs).borrow_mut().deref_mut() { + watchdog.clear_interrupt(); + watchdog.feed().ok(); + } + }); + + // Toggle the red led whenever the interrupt is triggered: + riscv::interrupt::free(|cs| { + if let Some(led_pin) = G_INTERRUPT_LED_PIN_R.borrow(cs).borrow_mut().deref_mut() { + led_pin.toggle().ok(); + } + }); + + enable_interrupt(Interrupt::Watchdog); +} diff --git a/hal_defaults.x b/hal_defaults.x index 79dfb8f..ff203f7 100644 --- a/hal_defaults.x +++ b/hal_defaults.x @@ -1,3 +1,4 @@ PROVIDE(Gpio = DefaultHandler); PROVIDE(TimerCh0 = DefaultHandler); PROVIDE(TimerCh1 = DefaultHandler); +PROVIDE(Watchdog = DefaultHandler); diff --git a/src/interrupts.rs b/src/interrupts.rs index d5f89eb..6f27463 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -20,6 +20,7 @@ fn Gpio(); fn TimerCh0(); fn TimerCh1(); + fn Watchdog(); ``` */ @@ -29,6 +30,7 @@ extern "C" { fn Gpio(trap_frame: &mut TrapFrame); fn TimerCh0(trap_frame: &mut TrapFrame); fn TimerCh1(trap_frame: &mut TrapFrame); + fn Watchdog(trap_frame: &mut TrapFrame); } // see components\bl602\bl602_std\bl602_std\RISCV\Core\Include\clic.h @@ -41,6 +43,7 @@ const CLIC_INTIP: u32 = 0x000; const GPIO_IRQ: u32 = IRQ_NUM_BASE + 44; const TIMER_CH0_IRQ: u32 = IRQ_NUM_BASE + 36; const TIMER_CH1_IRQ: u32 = IRQ_NUM_BASE + 37; +const WATCHDOG_IRQ: u32 = IRQ_NUM_BASE + 38; #[doc(hidden)] #[no_mangle] @@ -137,6 +140,7 @@ pub unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) { Interrupt::Gpio => Gpio(trap_frame.as_mut().unwrap()), Interrupt::TimerCh0 => TimerCh0(trap_frame.as_mut().unwrap()), Interrupt::TimerCh1 => TimerCh1(trap_frame.as_mut().unwrap()), + Interrupt::Watchdog => Watchdog(trap_frame.as_mut().unwrap()), }; } } @@ -152,6 +156,9 @@ pub enum Interrupt { TimerCh0, /// Timer Channel 1 Interrupt TimerCh1, + /// Watchdog Timer Interrupt + /// Used when WDT is configured in Interrupt mode using ConfiguredWatchdog0::set_mode() + Watchdog, } impl Interrupt { @@ -161,6 +168,7 @@ impl Interrupt { Interrupt::Gpio => GPIO_IRQ, Interrupt::TimerCh0 => TIMER_CH0_IRQ, Interrupt::TimerCh1 => TIMER_CH1_IRQ, + Interrupt::Watchdog => WATCHDOG_IRQ, } } @@ -169,6 +177,7 @@ impl Interrupt { GPIO_IRQ => Interrupt::Gpio, TIMER_CH0_IRQ => Interrupt::TimerCh0, TIMER_CH1_IRQ => Interrupt::TimerCh1, + WATCHDOG_IRQ => Interrupt::Watchdog, _ => Interrupt::Unknown, } } diff --git a/src/lib.rs b/src/lib.rs index eabb5fc..03e6386 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,7 @@ pub mod rtc; pub mod serial; pub mod spi; pub mod timer; +pub mod watchdog; /// HAL crate prelude pub mod prelude { diff --git a/src/timer.rs b/src/timer.rs index 2e676e2..78add93 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -1,9 +1,12 @@ /*! # Timer - The chip has two 32-bit counters, each of which can independently control and configure its parameters and clock frequency + The chip has two 32-bit counters, each of which can independently control and configure its parameters and clock frequency. ## Example ```rust + use bl602_hal::timer::ClockSource; + use embedded_time::{duration::*, rate::*}; + let timers = dp.TIMER.split(); let ch0 = timers @@ -21,15 +24,21 @@ ch0.enable(); // start timer ``` + # Units + This library uses embedded_time::{duration::*, rate::*} for time units. You can use any supported units as long as they can be cast into Nanoseconds:: for durations, or Hertz for cycles. Time can be cast into other units supported by embedded_time by explicitly typing a variable and calling .into() Note that this will round to the nearest integer in the cast units, potentially losing precision. + + ## Time Casting Example: + ```rust + use embedded_time::duration::*; + // gets the current time in Nanoseconds:: and casts it into milliseconds. + let time_in_milliseconds: Milliseconds = watchdog.current_time().into(); + ``` */ use crate::{clock::Clocks, pac}; use bl602_pac::TIMER; use core::cell::RefCell; -use embedded_time::{ - duration::*, - rate::*, -}; +use embedded_time::{duration::*, rate::*}; use paste::paste; /// Error for [CountDown](embedded_hal::timer::CountDown) @@ -101,10 +110,14 @@ pub struct TimerChannel0 {} /// Timer Channel 1 pub struct TimerChannel1 {} +/// Watchdog Timer +pub struct TimerWatchdog {} + /// Timers obtained from [TIMER.split](bl602_pac::Peripherals::TIMER) pub struct Timers { pub channel0: TimerChannel0, pub channel1: TimerChannel1, + pub watchdog: TimerWatchdog, } macro_rules! impl_timer_channel { @@ -113,8 +126,8 @@ macro_rules! impl_timer_channel { /// A configured timer channel ready to use. pub struct $conf_name { clock: Hertz, - count_down_target: Option, - last_count_down_value: Option, + count_down_target: Option>, + last_count_down_value: Option>, is_running: RefCell, } @@ -228,17 +241,17 @@ macro_rules! impl_timer_channel { timer.[].modify(|_r, w| unsafe { w.tmr().bits(time) }); } - // Current counter value in raw ticks. + /// Current counter value in raw ticks. pub fn current_ticks(&self) -> u32 { let timer = unsafe { &*pac::TIMER::ptr() }; timer.[].read().bits() } - // Current counter value in milliseconds. - pub fn current_time(&self) -> Milliseconds { + /// Current counter value in nanoseconds. + pub fn current_time(&self) -> Nanoseconds:: { let timer = unsafe { &*pac::TIMER::ptr() }; let ticks = timer.[].read().bits() as u64; - Milliseconds::new( (ticks as u64 * 1000u64 / self.clock.0 as u64) as u32) + Nanoseconds::::new( (ticks as u64 * 1_000_000_000_u64 / self.clock.0 as u64) ) } /// Will only become true if `enable_match0_interrupt` is active @@ -284,23 +297,25 @@ macro_rules! impl_timer_channel { impl embedded_hal::timer::nb::CountDown for $conf_name { type Error = CountDownError; - type Time = Milliseconds; + type Time = Nanoseconds::; fn start(&mut self, count: T) -> Result<(), Self::Error> where T: Into, { - self.count_down_target = Some(Milliseconds(self.current_time().0 + count.into().0)); + self.count_down_target = Some( + Nanoseconds::::new(self.current_time().0 + count.into().0) + ); self.last_count_down_value = None; Ok(()) } fn wait(&mut self) -> nb::Result<(), Self::Error> { match self.count_down_target { - Some(millis) => { + Some(nanos) => { let current_time = self.current_time(); - if current_time >= millis { + if current_time >= nanos { Ok(()) } else { match self.last_count_down_value { @@ -382,6 +397,7 @@ impl TimerExt for TIMER { Timers { channel0: TimerChannel0 {}, channel1: TimerChannel1 {}, + watchdog: TimerWatchdog {}, } } } diff --git a/src/watchdog.rs b/src/watchdog.rs new file mode 100644 index 0000000..70e36fc --- /dev/null +++ b/src/watchdog.rs @@ -0,0 +1,320 @@ +/*! + # Watchdog + The BL602 has a single watchdog timer. It can be configured to run from four different clock sources, which can be divided by 1-256. It has a single counter and comparator, which will determine when the watchdog is triggered. The trigger can either reset the chip or call an interrupt function. + + ## Watchdog Setup and Activation Example: + ```rust + + let dp = pac::Peripherals::take().unwrap(); + let timers = dp.TIMER.split(); + let mut wd: ConfiguredWatchdog0 = timers + .watchdog + .set_clock_source(WdtClockSource::Rc32Khz, 125.Hz()); + wd.set_mode(WatchdogMode::Interrupt); + wd.start(10.seconds()); + + // When using the watchdog in interrupt mode, you must also enable the IRQ interrupt + enable_interrupt(Interrupt::Watchdog); + loop{ + // Do other things in the loop, but make sure to feed the watchdog at least every 10 seconds + wd.feed(); + } + + // ... + + // If you fail to feed the watchdog, the interrupt function `Watchdog()` will be triggered. + // Make sure to clear the interrupt in your interrupt function or you'll never escape + #[no_mangle] + fn Watchdog(trap_frame: &mut TrapFrame){ + clear_interrupt(Interrupt::Watchdog); + } + + ``` + # Units + This library uses embedded_time::{duration::*, rate::*} for time units. You can use any supported units as long as they can be cast into Nanoseconds:: for durations, or Hertz for cycles. Time can be cast into other units supported by embedded_time by explicitly typing a variable and calling .into() Note that this will round to the nearest integer in the cast units, potentially losing precision. + + ## Time Casting Example: + ```rust + use embedded_time::duration::*; + // gets the current time in Nanoseconds:: and casts it into milliseconds. + let time_in_milliseconds: Milliseconds = watchdog.current_time().into(); + ``` +*/ + +use crate::{clock::Clocks, pac, timer::TimerWatchdog}; +use embedded_time::{duration::*, rate::*}; + +/// Clock sources for a Watchdog channel. +/// There are only three timer clock sources available. +pub enum WdtClockSource<'a> { + /// System master clock + Fclk(&'a Clocks), + /// 32K clock + Rc32Khz, + /// 32M clock + Pll32Mhz, +} + +impl<'a> WdtClockSource<'a> { + fn tccr_value(&self) -> u8 { + match self { + WdtClockSource::Fclk(_) => 0, + WdtClockSource::Rc32Khz => 1, + WdtClockSource::Pll32Mhz => 3, + } + } + + fn hertz(&self) -> Hertz { + match self { + WdtClockSource::Fclk(clocks) => clocks.sysclk(), + WdtClockSource::Rc32Khz => 32_000.Hz(), + WdtClockSource::Pll32Mhz => 32_000_000.Hz(), + } + } +} + +/// Error for [Watchdog](embedded_hal::watchdog::blocking::Watchdog) +#[derive(Debug)] +pub enum WatchdogError { + Infallible, +} + +pub enum WatchdogMode { + Interrupt, + Reset, +} + +pub enum WatchdogKeys { + Wfar, + Wsar, +} + +impl WatchdogKeys { + /// Returns the key access register values that must be written immediately before + /// writing any of the watchdog timer's critical register values + fn get_key(&self) -> u16 { + match self { + WatchdogKeys::Wfar => 0xBABA_u16, + WatchdogKeys::Wsar => 0xEB10_u16, + } + } +} + +/// A configured Watchdog timer ready to be enabled or `feed()` +pub struct ConfiguredWatchdog0 { + clock: Hertz, +} + +/// This sends the access codes so that we can write values to the WDT registers. +fn send_access_codes() { + let timer = unsafe { &*pac::TIMER::ptr() }; + timer + .wfar + .write(|w| unsafe { w.wfar().bits(WatchdogKeys::Wfar.get_key()) }); + timer + .wsar + .write(|w| unsafe { w.wsar().bits(WatchdogKeys::Wsar.get_key()) }); +} + +impl ConfiguredWatchdog0 { + /// Enable the watchdog counter + pub fn enable(&self) { + let timer = unsafe { &*pac::TIMER::ptr() }; + send_access_codes(); + timer.wcr.write(|w| w.wcr().set_bit()); + send_access_codes(); + timer.wmer.modify(|_r, w| w.we().set_bit()); + } + + /// Read the WMER register's WE bit to see if the WDT is enabled or disabled. + pub fn is_enabled(&self) -> WatchdogMode { + let timer = unsafe { &*pac::TIMER::ptr() }; + match timer.wmer.read().we().bit() { + true => WatchdogMode::Reset, + false => WatchdogMode::Interrupt, + } + } + + //noinspection RsSelfConvention + /// Set the time that the watchdog timer will be triggered unless `feed()` + pub fn set_timeout(&self, time: impl Into>) { + let time: Nanoseconds = time.into(); + let ticks = (self.clock.0 as u64 * time.integer() / 1_000_000_000_u64) as u16; + let timer = unsafe { &*pac::TIMER::ptr() }; + send_access_codes(); + timer.wmr.write(|w| unsafe { w.wmr().bits(ticks) }); + } + + //noinspection RsSelfConvention + /// Determine whether the watchdog will reset the board, or trigger an interrupt + pub fn set_mode(&self, mode: WatchdogMode) { + let timer = unsafe { &*pac::TIMER::ptr() }; + match mode { + WatchdogMode::Interrupt => { + send_access_codes(); + timer.wmer.write(|w| w.wrie().clear_bit()); + } + WatchdogMode::Reset => { + send_access_codes(); + timer.wmer.write(|w| w.wrie().set_bit()); + } + } + } + + /// Check the value of the watchdog reset register (WTS) to see if a reset has occurred + pub fn has_watchdog_reset_occurred(&self) -> bool { + let timer = unsafe { &*pac::TIMER::ptr() }; + timer.wsr.read().wts().bits() as bool + } + + /// Clear the watchdog reset register (WTS) + pub fn clear_wts(&self) { + let timer = unsafe { &*pac::TIMER::ptr() }; + send_access_codes(); + timer.wsr.write(|w| w.wts().set_bit()); + } + + /// clears the watchdog interrupt once it has been set by the WDT activating in Interrupt mode + pub fn clear_interrupt(&self) { + let timer = unsafe { &*pac::TIMER::ptr() }; + send_access_codes(); + timer.wicr.write(|w| w.wiclr().set_bit()); + } + + /// Gets the value in ticks the match register is currently set to + pub fn get_match_ticks(&self) -> u16 { + let timer = unsafe { &*pac::TIMER::ptr() }; + timer.wmr.read().wmr().bits() as u16 + } + + /// Get the current value of the watchdog timer in nanoseconds + pub fn get_match_time(&self) -> Nanoseconds { + let ticks = self.get_match_ticks() as u64; + // ticks * (1e9 nanoseconds/second) / (ticks / second) = nanoseconds + Nanoseconds::::new((ticks * 1_000_000_000_u64) / self.clock.integer() as u64) + } + + /// Get the current value in ticks of the watchdog timer + pub fn get_current_ticks(&self) -> u16 { + let timer = unsafe { &*pac::TIMER::ptr() }; + timer.wvr.read().wvr().bits() as u16 + } + + /// Get the current value of the watchdog timer in nanoseconds + pub fn get_current_time(&self) -> Nanoseconds { + let ticks = self.get_current_ticks() as u64; + // ticks * (1e9 nanoseconds/second) / (ticks / second) = nanoseconds + Nanoseconds::::new((ticks * 1_000_000_000_u64) / self.clock.integer() as u64) + } + + /// Read the TCCR register containing the CS_WDT bits that select the clock source + pub fn get_cs_wdt(&self) -> u8 { + let timer = unsafe { &*pac::TIMER::ptr() }; + timer.tccr.read().cs_wdt().bits() as u8 + } + + /// Read the WMER register's WRIE bit to see if the WDT is in Reset or Interrupt mode. + pub fn get_wrie(&self) -> WatchdogMode { + let timer = unsafe { &*pac::TIMER::ptr() }; + match timer.wmer.read().wrie().bit() { + true => WatchdogMode::Reset, + false => WatchdogMode::Interrupt, + } + } + + /// Read the TCDR register's WCDR bits to see the clock division value. + pub fn get_wcdr(&self) -> u8 { + let timer = unsafe { &*pac::TIMER::ptr() }; + timer.tcdr.read().wcdr().bits() + } +} + +impl embedded_hal::watchdog::blocking::Watchdog for ConfiguredWatchdog0 { + type Error = WatchdogError; + + /// This feeds the watchdog by resetting its counter value to 0. + /// WCR register is write-only, no need to preserve register contents + fn feed(&mut self) -> Result<(), Self::Error> { + let timer = unsafe { &*pac::TIMER::ptr() }; + send_access_codes(); + timer.wcr.write(|w| w.wcr().set_bit()); + Ok(()) + } +} + +impl embedded_hal::watchdog::blocking::Disable for ConfiguredWatchdog0 { + type Error = WatchdogError; + type Target = ConfiguredWatchdog0; + + fn disable(self) -> Result { + let timer = unsafe { &*pac::TIMER::ptr() }; + send_access_codes(); + timer.wmer.write(|w| w.we().clear_bit()); + Ok(self) + } +} + +impl embedded_hal::watchdog::blocking::Enable for ConfiguredWatchdog0 { + type Error = WatchdogError; + type Time = Nanoseconds; + type Target = ConfiguredWatchdog0; + + fn start(self, period: T) -> Result + where + T: Into, + { + self.set_timeout(period); + self.enable(); + Ok(self) + } +} + +impl TimerWatchdog { + //noinspection RsSelfConvention + /// This sets up the watchdog clock source and target clock speed, and returns a ConfiguredWatchdog0. + /// Note that when setting up the clock source, you will need to select a source and tick rate that + /// allows for your desired timeout time to be expressed as 65535 or less ticks of the WDT. + /// + /// # Examples: + /// + /// - Slowest possible tick rate: Clock Source of 32 kHz clock selected, with target clock of 125 Hz + /// + /// - This results in the max time of 65535 ticks / 125 Hz = 524.28 seconds + /// + /// - Fast 32kHz tick rate: Clock source of 32 kHz clock selected, with target clock of 32_000 Hz. + /// + /// - This results in the max time of 65535 ticks / 32_000 Hz = ~2.04 seconds + /// + /// - Fastest possible tick rate: Clock source of Fclk at 160 MHz clock selected, with target clock of 160_000_000 Hz. + /// + /// - This results in the max time of 65535 ticks / 160_000_000 Hz = ~4.09 milliseconds + /// + pub fn set_clock_source( + self, + source: WdtClockSource, + target_clock: impl Into, + ) -> ConfiguredWatchdog0 { + let target_clock = target_clock.into(); + let timer = unsafe { &*pac::TIMER::ptr() }; + timer + .tccr + .modify(|_r, w| unsafe { w.cs_wdt().bits(source.tccr_value()) }); + let divider = source.hertz().0 / target_clock.0; + + if !(1..=256).contains(÷r) { + panic!("unreachable target clock"); + } + + timer + .tcdr + .modify(|_r, w| unsafe { w.wcdr().bits((divider - 1) as u8) }); + + //clear interrupt bit when initializing: + send_access_codes(); + timer.wicr.write(|w| w.wiclr().clear_bit()); + + ConfiguredWatchdog0 { + clock: target_clock, + } + } +}