embassy-rp: Add Watchdog

This commit is contained in:
kalkyl 2022-12-24 02:51:06 +01:00
parent 67a6e5accf
commit eaad0cc1dc
3 changed files with 162 additions and 0 deletions

View file

@ -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
View 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);
}
}

View 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;
}
}