|
| 1 | +//! Watchdog example for nRF52840-DK |
| 2 | +//! |
| 3 | +//! The watchdog will be configured to time out after five seconds. |
| 4 | +//! There will be four handles, one tied to each button. To prevent a |
| 5 | +//! reset from occuring, the user must press all four buttons within |
| 6 | +//! a five second window. The LEDs represent the "is_pet" state of |
| 7 | +//! each watchdog handle. |
| 8 | +//! |
| 9 | +//! This is basically playing whack-a-mole (pet-a-dog?) with the buttons |
| 10 | +//! to prevent the watchdog from restarting the system. If you use the |
| 11 | +//! Reset button to reset the device before the watchdog kicks, you'll |
| 12 | +//! see the reset reason was not due to the watchdog. |
| 13 | +
|
| 14 | +#![no_std] |
| 15 | +#![no_main] |
| 16 | + |
| 17 | +use embedded_hal::{ |
| 18 | + digital::v2::{InputPin, OutputPin}, |
| 19 | + timer::CountDown, |
| 20 | +}; |
| 21 | +use { |
| 22 | + core::{ |
| 23 | + panic::PanicInfo, |
| 24 | + sync::atomic::{compiler_fence, Ordering}, |
| 25 | + }, |
| 26 | + hal::pac::TIMER0, |
| 27 | + hal::{ |
| 28 | + gpio::{Input, Level, Output, Pin, PullUp, PushPull}, |
| 29 | + timer::Timer, |
| 30 | + wdt::{count, handles::HdlN, Parts, Watchdog, WatchdogHandle}, |
| 31 | + }, |
| 32 | + nrf52840_hal as hal, |
| 33 | + rtt_target::{rprintln, rtt_init_print}, |
| 34 | +}; |
| 35 | + |
| 36 | +#[rtic::app(device = crate::hal::pac, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] |
| 37 | +const APP: () = { |
| 38 | + struct Resources { |
| 39 | + btn1: Pin<Input<PullUp>>, |
| 40 | + btn2: Pin<Input<PullUp>>, |
| 41 | + btn3: Pin<Input<PullUp>>, |
| 42 | + btn4: Pin<Input<PullUp>>, |
| 43 | + led1: Pin<Output<PushPull>>, |
| 44 | + led2: Pin<Output<PushPull>>, |
| 45 | + led3: Pin<Output<PushPull>>, |
| 46 | + led4: Pin<Output<PushPull>>, |
| 47 | + hdl0: WatchdogHandle<HdlN>, |
| 48 | + hdl1: WatchdogHandle<HdlN>, |
| 49 | + hdl2: WatchdogHandle<HdlN>, |
| 50 | + hdl3: WatchdogHandle<HdlN>, |
| 51 | + timer: Timer<TIMER0>, |
| 52 | + } |
| 53 | + |
| 54 | + #[init] |
| 55 | + fn init(mut ctx: init::Context) -> init::LateResources { |
| 56 | + let _clocks = hal::clocks::Clocks::new(ctx.device.CLOCK).enable_ext_hfosc(); |
| 57 | + rtt_init_print!(); |
| 58 | + let p0 = hal::gpio::p0::Parts::new(ctx.device.P0); |
| 59 | + |
| 60 | + let btn1 = p0.p0_11.into_pullup_input().degrade(); |
| 61 | + let btn2 = p0.p0_12.into_pullup_input().degrade(); |
| 62 | + let btn3 = p0.p0_24.into_pullup_input().degrade(); |
| 63 | + let btn4 = p0.p0_25.into_pullup_input().degrade(); |
| 64 | + |
| 65 | + let led1 = p0.p0_13.into_push_pull_output(Level::High).degrade(); |
| 66 | + let led2 = p0.p0_14.into_push_pull_output(Level::High).degrade(); |
| 67 | + let led3 = p0.p0_15.into_push_pull_output(Level::High).degrade(); |
| 68 | + let led4 = p0.p0_16.into_push_pull_output(Level::High).degrade(); |
| 69 | + |
| 70 | + let timer = Timer::new(ctx.device.TIMER0); |
| 71 | + |
| 72 | + // Create a new watchdog instance |
| 73 | + // |
| 74 | + // In case the watchdog is already running, just spin and let it expire, since |
| 75 | + // we can't configure it anyway. This usually happens when we first program |
| 76 | + // the device and the watchdog was previously active |
| 77 | + let (hdl0, hdl1, hdl2, hdl3) = match Watchdog::try_new(ctx.device.WDT) { |
| 78 | + Ok(mut watchdog) => { |
| 79 | + // Set the watchdog to timeout after 5 seconds (in 32.768kHz ticks) |
| 80 | + watchdog.set_lfosc_ticks(5 * 32768); |
| 81 | + |
| 82 | + // Activate the watchdog with four handles |
| 83 | + let Parts { |
| 84 | + watchdog: _watchdog, |
| 85 | + handles, |
| 86 | + } = watchdog.activate::<count::Four>(); |
| 87 | + |
| 88 | + handles |
| 89 | + } |
| 90 | + Err(wdt) => match Watchdog::try_recover::<count::Four>(wdt) { |
| 91 | + Ok(Parts { mut handles, .. }) => { |
| 92 | + rprintln!("Oops, watchdog already active, but recovering!"); |
| 93 | + |
| 94 | + // Pet all the dogs quickly to reset to default timeout |
| 95 | + handles.0.pet(); |
| 96 | + handles.1.pet(); |
| 97 | + handles.2.pet(); |
| 98 | + handles.3.pet(); |
| 99 | + |
| 100 | + handles |
| 101 | + } |
| 102 | + Err(_wdt) => { |
| 103 | + rprintln!("Oops, watchdog already active, resetting!"); |
| 104 | + loop { |
| 105 | + continue; |
| 106 | + } |
| 107 | + } |
| 108 | + }, |
| 109 | + }; |
| 110 | + |
| 111 | + // Enable the monotonic timer (CYCCNT) |
| 112 | + ctx.core.DCB.enable_trace(); |
| 113 | + ctx.core.DWT.enable_cycle_counter(); |
| 114 | + |
| 115 | + rprintln!("Starting!"); |
| 116 | + |
| 117 | + if ctx.device.POWER.resetreas.read().dog().is_detected() { |
| 118 | + ctx.device.POWER.resetreas.modify(|_r, w| { |
| 119 | + // Clear the watchdog reset reason bit |
| 120 | + w.dog().set_bit() |
| 121 | + }); |
| 122 | + rprintln!("Restarted by the dog!"); |
| 123 | + } else { |
| 124 | + rprintln!("Not restarted by the dog!"); |
| 125 | + } |
| 126 | + |
| 127 | + init::LateResources { |
| 128 | + btn1, |
| 129 | + btn2, |
| 130 | + btn3, |
| 131 | + btn4, |
| 132 | + led1, |
| 133 | + led2, |
| 134 | + led3, |
| 135 | + led4, |
| 136 | + hdl0: hdl0.degrade(), |
| 137 | + hdl1: hdl1.degrade(), |
| 138 | + hdl2: hdl2.degrade(), |
| 139 | + hdl3: hdl3.degrade(), |
| 140 | + timer, |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + #[idle(resources = [btn1, btn2, btn3, btn4, led1, led2, led3, led4, hdl0, hdl1, hdl2, hdl3, timer])] |
| 145 | + fn idle(mut ctx: idle::Context) -> ! { |
| 146 | + let buttons = [ |
| 147 | + &ctx.resources.btn1, |
| 148 | + &ctx.resources.btn2, |
| 149 | + &ctx.resources.btn3, |
| 150 | + &ctx.resources.btn4, |
| 151 | + ]; |
| 152 | + |
| 153 | + let leds = [ |
| 154 | + &mut ctx.resources.led1, |
| 155 | + &mut ctx.resources.led2, |
| 156 | + &mut ctx.resources.led3, |
| 157 | + &mut ctx.resources.led4, |
| 158 | + ]; |
| 159 | + |
| 160 | + let handles = [ |
| 161 | + &mut ctx.resources.hdl0, |
| 162 | + &mut ctx.resources.hdl1, |
| 163 | + &mut ctx.resources.hdl2, |
| 164 | + &mut ctx.resources.hdl3, |
| 165 | + ]; |
| 166 | + |
| 167 | + let timer = &mut ctx.resources.timer; |
| 168 | + |
| 169 | + let mut cumulative_ticks = 0; |
| 170 | + let mut any_pet = false; |
| 171 | + |
| 172 | + timer.start(1_000_000u32 * 6); |
| 173 | + |
| 174 | + loop { |
| 175 | + let mut petted = 0; |
| 176 | + |
| 177 | + // Loop through all handles/leds/buttons |
| 178 | + for i in 0..4 { |
| 179 | + if !handles[i].is_pet() && buttons[i].is_low().unwrap() { |
| 180 | + rprintln!("Petting {}", i); |
| 181 | + any_pet = true; |
| 182 | + handles[i].pet(); |
| 183 | + while buttons[i].is_low().unwrap() {} |
| 184 | + } |
| 185 | + |
| 186 | + if handles[i].is_pet() { |
| 187 | + petted += 1; |
| 188 | + leds[i].set_low().ok(); |
| 189 | + } else { |
| 190 | + leds[i].set_high().ok(); |
| 191 | + } |
| 192 | + } |
| 193 | + |
| 194 | + // We must have pet all handles, reset the timer |
| 195 | + if any_pet && petted == 0 { |
| 196 | + cumulative_ticks = 0; |
| 197 | + any_pet = false; |
| 198 | + timer.start(1_000_000u32 * 6); |
| 199 | + } |
| 200 | + |
| 201 | + // Check whether to update the counter time |
| 202 | + let rd = timer.read(); |
| 203 | + if (cumulative_ticks + 250_000) <= rd { |
| 204 | + cumulative_ticks = rd; |
| 205 | + rprintln!("Time left: {}ms", (5_000_000 - rd) / 1_000); |
| 206 | + } |
| 207 | + } |
| 208 | + } |
| 209 | +}; |
| 210 | + |
| 211 | +#[inline(never)] |
| 212 | +#[panic_handler] |
| 213 | +fn panic(info: &PanicInfo) -> ! { |
| 214 | + cortex_m::interrupt::disable(); |
| 215 | + rprintln!("{}", info); |
| 216 | + loop { |
| 217 | + compiler_fence(Ordering::SeqCst); |
| 218 | + } |
| 219 | +} |
0 commit comments