embassy-rp: Add Watchdog
This commit is contained in:
parent
67a6e5accf
commit
eaad0cc1dc
3 changed files with 162 additions and 0 deletions
|
@ -36,6 +36,7 @@ pub mod clocks;
|
|||
pub mod flash;
|
||||
pub mod multicore;
|
||||
mod reset;
|
||||
pub mod watchdog;
|
||||
|
||||
// Reexports
|
||||
|
||||
|
@ -119,6 +120,8 @@ embassy_hal_common::peripherals! {
|
|||
|
||||
PIO0,
|
||||
PIO1,
|
||||
|
||||
WATCHDOG,
|
||||
}
|
||||
|
||||
#[link_section = ".boot2"]
|
||||
|
|
111
embassy-rp/src/watchdog.rs
Normal file
111
embassy-rp/src/watchdog.rs
Normal file
|
@ -0,0 +1,111 @@
|
|||
//! Watchdog
|
||||
//!
|
||||
//! The watchdog is a countdown timer that can restart parts of the chip if it reaches zero. This can be used to restart the
|
||||
//! processor if software gets stuck in an infinite loop. The programmer must periodically write a value to the watchdog to
|
||||
//! stop it from reaching zero.
|
||||
//!
|
||||
//! Credit: based on `rp-hal` implementation (also licensed Apache+MIT)
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_time::Duration;
|
||||
|
||||
use crate::pac;
|
||||
use crate::peripherals::WATCHDOG;
|
||||
|
||||
/// Watchdog peripheral
|
||||
pub struct Watchdog<'d> {
|
||||
phantom: PhantomData<&'d WATCHDOG>,
|
||||
load_value: u32, // decremented by 2 per tick (µs)
|
||||
}
|
||||
|
||||
impl<'d> Watchdog<'d> {
|
||||
/// Create a new `Watchdog`
|
||||
pub fn new(_watchdog: WATCHDOG) -> Self {
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
load_value: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Start tick generation on clk_tick which is driven from clk_ref.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `cycles` - Total number of tick cycles before the next tick is generated.
|
||||
/// It is expected to be the frequency in MHz of clk_ref.
|
||||
pub fn enable_tick_generation(&mut self, cycles: u8) {
|
||||
const WATCHDOG_TICK_ENABLE_BITS: u32 = 0x200;
|
||||
unsafe {
|
||||
let watchdog = pac::WATCHDOG;
|
||||
watchdog
|
||||
.tick()
|
||||
.write_value(pac::watchdog::regs::Tick(WATCHDOG_TICK_ENABLE_BITS | cycles as u32))
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines whether or not the watchdog timer should be paused when processor(s) are in debug mode
|
||||
/// or when JTAG is accessing bus fabric
|
||||
pub fn pause_on_debug(&mut self, pause: bool) {
|
||||
unsafe {
|
||||
let watchdog = pac::WATCHDOG;
|
||||
watchdog.ctrl().write(|w| {
|
||||
w.set_pause_dbg0(pause);
|
||||
w.set_pause_dbg1(pause);
|
||||
w.set_pause_jtag(pause);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn load_counter(&self, counter: u32) {
|
||||
unsafe {
|
||||
let watchdog = pac::WATCHDOG;
|
||||
watchdog.load().write_value(pac::watchdog::regs::Load(counter));
|
||||
}
|
||||
}
|
||||
|
||||
fn enable(&self, bit: bool) {
|
||||
unsafe {
|
||||
let watchdog = pac::WATCHDOG;
|
||||
watchdog.ctrl().write(|w| w.set_enable(bit))
|
||||
}
|
||||
}
|
||||
|
||||
// Configure which hardware will be reset by the watchdog
|
||||
// (everything except ROSC, XOSC)
|
||||
unsafe fn configure_wdog_reset_triggers(&self) {
|
||||
let psm = pac::PSM;
|
||||
psm.wdsel().write_value(pac::psm::regs::Wdsel(
|
||||
0x0001ffff & !(0x01 << 0usize) & !(0x01 << 1usize),
|
||||
));
|
||||
}
|
||||
|
||||
/// Feed the watchdog timer
|
||||
pub fn feed(&mut self) {
|
||||
self.load_counter(self.load_value)
|
||||
}
|
||||
|
||||
/// Start the watchdog timer
|
||||
pub fn start(&mut self, period: Duration) {
|
||||
const MAX_PERIOD: u32 = 0xFFFFFF;
|
||||
|
||||
let delay_us = period.as_micros() as u32;
|
||||
if delay_us > MAX_PERIOD / 2 {
|
||||
panic!(
|
||||
"Period cannot exceed maximum load value of {} ({} microseconds))",
|
||||
MAX_PERIOD,
|
||||
MAX_PERIOD / 2
|
||||
);
|
||||
}
|
||||
// Due to a logic error, the watchdog decrements by 2 and
|
||||
// the load value must be compensated; see RP2040-E1
|
||||
self.load_value = delay_us * 2;
|
||||
|
||||
self.enable(false);
|
||||
unsafe {
|
||||
self.configure_wdog_reset_triggers();
|
||||
}
|
||||
self.load_counter(self.load_value);
|
||||
self.enable(true);
|
||||
}
|
||||
}
|
48
examples/rp/src/bin/watchdog.rs
Normal file
48
examples/rp/src/bin/watchdog.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio;
|
||||
use embassy_rp::watchdog::*;
|
||||
use embassy_time::{Duration, Timer};
|
||||
use gpio::{Level, Output};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Hello world!");
|
||||
|
||||
let mut watchdog = Watchdog::new(p.WATCHDOG);
|
||||
let mut led = Output::new(p.PIN_25, Level::Low);
|
||||
|
||||
// Set the LED high for 2 seconds so we know when we're about to start the watchdog
|
||||
led.set_high();
|
||||
Timer::after(Duration::from_secs(2)).await;
|
||||
|
||||
// Set to watchdog to reset if it's not reloaded within 1.05 seconds, and start it
|
||||
watchdog.start(Duration::from_millis(1_050));
|
||||
info!("Started the watchdog timer");
|
||||
|
||||
// Blink once a second for 5 seconds, refreshing the watchdog timer once a second to avoid a reset
|
||||
for _ in 1..=5 {
|
||||
led.set_low();
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
led.set_high();
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
info!("Feeding watchdog");
|
||||
watchdog.feed();
|
||||
}
|
||||
|
||||
info!("Stopped feeding, device will reset in 1.05 seconds");
|
||||
// Blink 10 times per second, not feeding the watchdog.
|
||||
// The processor should reset in 1.05 seconds, or 5 blinks time
|
||||
loop {
|
||||
led.set_low();
|
||||
Timer::after(Duration::from_millis(100)).await;
|
||||
led.set_high();
|
||||
Timer::after(Duration::from_millis(100)).await;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue