Files
Embedded-Hacking/drivers/0x0e_watchdog_rust/src/board.rs
T
2026-07-06 14:32:12 -04:00

305 lines
10 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Implementation module
//!
//! **File:** `board.rs`
//! **Author:** Kevin Thomas
//! **Date:** 2025
//!
//! MIT License
//!
//! Copyright (c) 2025 Kevin Thomas
//!
//! Permission is hereby granted, free of charge, to any person obtaining a copy
//! of this software and associated documentation files (the "Software"), to deal
//! in the Software without restriction, including without limitation the rights
//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//! copies of the Software, and to permit persons to whom the Software is
//! furnished to do so, subject to the following conditions:
//!
//! The above copyright notice and this permission notice shall be included in
//! all copies or substantial portions of the Software.
//!
//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//! SOFTWARE.
// Watchdog driver pure-logic functions and constants
use watchdog_lib::watchdog;
// Microsecond duration type for watchdog timeout
use fugit::ExtU32;
// Rate extension trait for .Hz() baud rate construction
use fugit::RateExtU32;
// Clock trait for accessing system clock frequency
use hal::Clock;
// GPIO pin types and function selectors
use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone};
// UART configuration and peripheral types
use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral};
// Alias our HAL crate
#[cfg(rp2350)]
// Import rp235x_hal as hal
use rp235x_hal as hal;
#[cfg(rp2040)]
// Import rp2040_hal as hal
use rp2040_hal as hal;
/// External crystal frequency in Hz (12 MHz).
pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32;
/// UART baud rate in bits per second.
pub(crate) const UART_BAUD: u32 = 115_200;
/// Type alias for the configured TX pin (GPIO 0, UART function, no pull).
pub(crate) type TxPin = Pin<hal::gpio::bank0::Gpio0, FunctionUart, PullNone>;
/// Type alias for the configured RX pin (GPIO 1, UART function, no pull).
pub(crate) type RxPin = Pin<hal::gpio::bank0::Gpio1, FunctionUart, PullNone>;
/// Type alias for the default TX pin state from `Pins::new()`.
pub(crate) type TxPinDefault = Pin<hal::gpio::bank0::Gpio0, FunctionNull, PullDown>;
/// Type alias for the default RX pin state from `Pins::new()`.
pub(crate) type RxPinDefault = Pin<hal::gpio::bank0::Gpio1, FunctionNull, PullDown>;
/// Type alias for the fully-enabled UART0 peripheral with TX/RX p.
pub(crate) type EnabledUart = UartPeripheral<Enabled, hal::pac::UART0, (TxPin, RxPin)>;
/// Initialise system clocks and PLLs from the external 12 MHz crystal.
///
/// # Arguments
///
/// * `xosc` - XOSC peripheral singleton.
/// * `clocks` - CLOCKS peripheral singleton.
/// * `pll_sys` - PLL_SYS peripheral singleton.
/// * `pll_usb` - PLL_USB peripheral singleton.
/// * `resets` - Mutable reference to the RESETS peripheral.
/// * `watchdog` - Mutable reference to the watchdog timer.
///
/// # Returns
///
/// Configured clocks manager.
///
/// # Panics
///
/// Panics if clock initialisation fails.
pub(crate) fn init_clocks(
xosc: hal::pac::XOSC, clocks: hal::pac::CLOCKS, pll_sys: hal::pac::PLL_SYS, pll_usb: hal::pac::PLL_USB,
resets: &mut hal::pac::RESETS, watchdog: &mut hal::Watchdog,
) -> hal::clocks::ClocksManager {
hal::clocks::init_clocks_and_plls(XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog).unwrap()
}
/// Unlock the GPIO bank and return the pin set.
///
/// # Arguments
///
/// * `io_bank0` - IO_BANK0 peripheral singleton.
/// * `pads_bank0` - PADS_BANK0 peripheral singleton.
/// * `sio` - SIO peripheral singleton.
/// * `resets` - Mutable reference to the RESETS peripheral.
///
/// # Returns
///
/// GPIO pin set for the entire bank.
pub(crate) fn init_pins(
io: hal::pac::IO_BANK0, pads: hal::pac::PADS_BANK0, sio: hal::pac::SIO, rst: &mut hal::pac::RESETS
) -> hal::gpio::Pins {
hal::gpio::Pins::new(io, pads, hal::Sio::new(sio).gpio_bank0, rst)
}
/// Initialise UART0 for serial output (stdio equivalent).
///
/// # Arguments
///
/// * `uart0` - PAC UART0 peripheral singleton.
/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0).
/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1).
/// * `resets` - Mutable reference to the RESETS peripheral.
/// * `clocks` - Reference to the initialised clock configuration.
///
/// # Returns
///
/// Enabled UART0 peripheral ready for blocking writes.
///
/// # Panics
///
/// Panics if the HAL cannot achieve the requested baud rate.
pub(crate) fn init_uart(
uart0: hal::pac::UART0, tx: TxPinDefault, rx: RxPinDefault, rst: &mut hal::pac::RESETS, c: &hal::clocks::ClocksManager
) -> EnabledUart {
let pins = (tx.reconfigure::<FunctionUart, PullNone>(), rx.reconfigure::<FunctionUart, PullNone>());
let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One);
UartPeripheral::new(uart0, pins, rst).enable(cfg, c.peripheral_clock.freq()).unwrap()
}
/// Create a blocking delay timer from the ARM SysTick peripheral.
///
/// # Arguments
///
/// * `clocks` - Reference to the initialised clock configuration.
///
/// # Returns
///
/// Blocking delay provider.
///
/// # Panics
///
/// Panics if the cortex-m core peripherals have already been taken.
///
/// # Arguments
///
/// * `clocks` - The `clocks` parameter.
///
/// # Returns
///
/// A value of type `cortex_m::delay::Delay`.
pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay {
let core = cortex_m::Peripherals::take().unwrap();
cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz())
}
/// Check whether the last reset was caused by the watchdog.
///
/// Reads the WATCHDOG REASON register directly from the PAC. Returns
/// `true` if either the timer or force bits are set, matching the
/// C SDK `watchdog_caused_reboot()` behaviour.
///
/// # Returns
///
/// `true` if the watchdog triggered the last reset.
///
/// # Returns
///
/// `true` if successful or set, `false` otherwise.
pub(crate) fn watchdog_caused_reboot() -> bool {
let r = unsafe { &*hal::pac::WATCHDOG::ptr() }.reason().read();
r.timer().bit_is_set() || r.force().bit_is_set()
}
/// Enable the hardware watchdog with the specified timeout.
///
/// Wraps `hal::Watchdog::start()` converting the timeout from
/// milliseconds to microseconds as required by the HAL.
///
/// # Arguments
///
/// * `watchdog` - Mutable reference to the HAL watchdog.
/// * `timeout_ms` - Timeout in milliseconds (18388).
///
/// # Arguments
///
/// * `watchdog` - The `watchdog` parameter.
/// * `timeout_ms` - The `timeout_ms` parameter.
pub(crate) fn watchdog_enable(watchdog: &mut hal::Watchdog, timeout_ms: u32) {
watchdog.start((timeout_ms * 1_000).micros());
}
/// Feed the hardware watchdog to prevent a reboot.
///
/// Wraps `hal::Watchdog::feed()`.
///
/// # Arguments
///
/// * `watchdog` - Reference to the HAL watchdog.
///
/// # Arguments
///
/// * `watchdog` - The `watchdog` parameter.
pub(crate) fn watchdog_feed(watchdog: &hal::Watchdog) {
watchdog.feed();
}
/// Run the watchdog feed-and-report loop.
///
/// Feeds the watchdog every 1 second and prints `"Watchdog fed\r\n"`
/// over UART, matching the C demo's `_feed_and_report()` function.
/// This function never returns.
///
/// # Arguments
///
/// * `uart` - Reference to the enabled UART peripheral for serial output.
/// * `watchdog` - Reference to the HAL watchdog.
/// * `delay` - Mutable reference to the blocking delay provider.
/// * `state` - Mutable reference to the watchdog driver state.
pub(crate) fn feed_loop(
uart: &EnabledUart, wd: &hal::Watchdog, delay: &mut cortex_m::delay::Delay, state: &mut watchdog::WatchdogDriverState
) -> ! {
loop {
watchdog_feed(wd); state.feed();
let mut buf = [0u8; 32]; let n = watchdog::format_fed(&mut buf);
uart.write_full_blocking(&buf[..n]); delay.delay_ms(watchdog::FEED_INTERVAL_MS);
}
}
/// Initialise all peripherals and run the watchdog feed demo.
///
/// # Arguments
///
/// * `pac` - PAC Peripherals singleton (consumed).
///
/// # Returns
///
/// A value of type `!`.
///
/// # Arguments
///
/// * `pac` - The `pac` parameter.
///
/// # Returns
///
/// A value of type `!`.
pub(crate) fn run(mut p: hal::pac::Peripherals) -> ! {
let mut wd = hal::Watchdog::new(p.WATCHDOG);
let c = init_clocks(p.XOSC, p.CLOCKS, p.PLL_SYS, p.PLL_USB, &mut p.RESETS, &mut wd);
let pins = init_pins(p.IO_BANK0, p.PADS_BANK0, p.SIO, &mut p.RESETS);
let (u, mut d) = (init_uart(p.UART0, pins.gpio0, pins.gpio1, &mut p.RESETS, &c), init_delay(&c));
report_reset_reason(&u);
let mut state = start_watchdog(&u, &mut wd);
feed_loop(&u, &wd, &mut d, &mut state)
}
/// Print whether the last reset was caused by the watchdog.
///
/// # Arguments
///
/// * `uart` - Reference to the enabled UART peripheral for serial output.
///
/// # Arguments
///
/// * `uart` - The `uart` parameter.
fn report_reset_reason(uart: &EnabledUart) {
let mut buf = [0u8; 64];
let n = watchdog::format_reset_reason(&mut buf, watchdog_caused_reboot());
uart.write_full_blocking(&buf[..n]);
}
/// Create the driver state, enable the hardware watchdog, and report.
///
/// # Arguments
///
/// * `uart` - Reference to the enabled UART peripheral for serial output.
/// * `wd` - Mutable reference to the HAL watchdog.
///
/// # Returns
///
/// Initialised watchdog driver state.
///
/// # Arguments
///
/// * `uart` - The `uart` parameter.
/// * `wd` - The `wd` parameter.
///
/// # Returns
///
/// A value of type `watchdog::WatchdogDriverState`.
fn start_watchdog(uart: &EnabledUart, wd: &mut hal::Watchdog) -> watchdog::WatchdogDriverState {
let mut state = watchdog::WatchdogDriverState::new(); state.enable(watchdog::DEFAULT_TIMEOUT_MS);
watchdog_enable(wd, watchdog::DEFAULT_TIMEOUT_MS);
let mut buf = [0u8; 64]; let n = watchdog::format_enabled(&mut buf, watchdog::DEFAULT_TIMEOUT_MS);
uart.write_full_blocking(&buf[..n]); state
}