#![no_std]
#![no_main]
#[allow(unused_imports)]
use panic_halt as _;
use rp2040_hal as hal;
use core::{cell::RefCell, ops::DerefMut};
use critical_section::Mutex;
use embedded_hal::digital::StatefulOutputPin;
use fugit::RateExtU32;
use hal::{
clocks::{ClockError, ClocksManager, InitError, StoppableClock},
gpio,
gpio::{Interrupt::EdgeLow, Pins},
pac,
pac::{interrupt, CLOCKS, PLL_SYS, PLL_USB, RESETS, XOSC},
pll::{
common_configs::{PLL_SYS_125MHZ, PLL_USB_48MHZ},
setup_pll_blocking, start_pll_blocking, Disabled, Locked, PhaseLockedLoop,
},
rosc::RingOscillator,
sio::Sio,
watchdog::Watchdog,
xosc::{setup_xosc_blocking, CrystalOscillator, Stable, Unstable},
Clock,
};
use nb::block;
type ClocksAndPlls = (
ClocksManager,
CrystalOscillator<Stable>,
PhaseLockedLoop<Locked, PLL_SYS>,
PhaseLockedLoop<Locked, PLL_USB>,
);
type RestartedClockAndPlls = (
CrystalOscillator<Stable>,
PhaseLockedLoop<Locked, PLL_SYS>,
PhaseLockedLoop<Locked, PLL_USB>,
);
type ButtonPin = gpio::Pin<gpio::bank0::Gpio14, gpio::FunctionSioInput, gpio::PullUp>;
static GLOBAL_DEVICES: Mutex<RefCell<Option<ButtonPin>>> = Mutex::new(RefCell::new(None));
#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
#[rp2040_hal::entry]
fn main() -> ! {
let mut pac = pac::Peripherals::take().unwrap();
let mut watchdog = Watchdog::new(pac.WATCHDOG);
let sio = Sio::new(pac.SIO);
let (mut clocks, mut xosc, mut pll_sys, mut pll_usb) = init_clocks_and_plls(
XTAL_FREQ_HZ,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();
let rosc = RingOscillator::new(pac.ROSC).initialize();
rosc.disable();
let pins = Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
let mut led_pin = pins.gpio25.into_push_pull_output();
let button_pin = pins.gpio14.reconfigure();
button_pin.set_dormant_wake_enabled(EdgeLow, true);
button_pin.set_interrupt_enabled(EdgeLow, true);
critical_section::with(|cs| {
GLOBAL_DEVICES.borrow(cs).replace(Some(button_pin));
});
unsafe {
pac::NVIC::unmask(pac::Interrupt::IO_IRQ_BANK0);
}
let mut use_dormant = true;
loop {
if use_dormant {
pulse(&mut led_pin, 1);
let (disabled_pll_sys, disabled_pll_usb) =
prepare_clocks_and_plls_for_dormancy(&mut xosc, &mut clocks, pll_sys, pll_usb);
let unstable_xosc = unsafe { xosc.dormant() };
match restart_clocks_and_plls(
&mut clocks,
unstable_xosc,
disabled_pll_sys,
disabled_pll_usb,
&mut pac.RESETS,
) {
Ok((stable_xosc, stable_pll_sys, stable_pll_usb)) => {
xosc = stable_xosc;
pll_sys = stable_pll_sys;
pll_usb = stable_pll_usb;
}
Err(_) => {
panic!();
}
}
critical_section::with(|cs| {
let mut global_devices = GLOBAL_DEVICES.borrow(cs).borrow_mut();
if let Some(ref mut trigger_pin) = global_devices.deref_mut() {
trigger_pin.clear_interrupt(EdgeLow);
} else {
panic!();
};
});
} else {
pulse(&mut led_pin, 2);
cortex_m::asm::wfi();
}
use_dormant = !use_dormant;
}
}
fn pulse<P: StatefulOutputPin>(pin: &mut P, count: u32) {
const LED_PULSE_CYCLES: u32 = 2_000_000;
for i in 0..count * 2 {
let _ = pin.toggle();
cortex_m::asm::delay(LED_PULSE_CYCLES + (i % 2) * 9 * LED_PULSE_CYCLES);
}
}
fn init_clocks_and_plls(
xosc_crystal_freq: u32,
xosc_dev: XOSC,
clocks_dev: CLOCKS,
pll_sys_dev: PLL_SYS,
pll_usb_dev: PLL_USB,
resets: &mut RESETS,
watchdog: &mut Watchdog,
) -> Result<ClocksAndPlls, InitError> {
let xosc = setup_xosc_blocking(xosc_dev, xosc_crystal_freq.Hz()).unwrap();
watchdog.enable_tick_generation((xosc_crystal_freq / 1_000_000) as u8);
let mut clocks = ClocksManager::new(clocks_dev);
let pll_sys = setup_pll_blocking(
pll_sys_dev,
xosc.operating_frequency(),
PLL_SYS_125MHZ,
&mut clocks,
resets,
)
.map_err(InitError::PllError)?;
let pll_usb = setup_pll_blocking(
pll_usb_dev,
xosc.operating_frequency(),
PLL_USB_48MHZ,
&mut clocks,
resets,
)
.map_err(InitError::PllError)?;
clocks
.init_default(&xosc, &pll_sys, &pll_usb)
.map_err(InitError::ClockError)?;
Ok((clocks, xosc, pll_sys, pll_usb))
}
fn prepare_clocks_and_plls_for_dormancy(
xosc: &mut CrystalOscillator<Stable>,
clocks: &mut ClocksManager,
pll_sys: PhaseLockedLoop<Locked, PLL_SYS>,
pll_usb: PhaseLockedLoop<Locked, PLL_USB>,
) -> (
PhaseLockedLoop<Disabled, PLL_SYS>,
PhaseLockedLoop<Disabled, PLL_USB>,
) {
nb::block!(clocks.system_clock.reset_source_await()).unwrap();
clocks.usb_clock.disable();
clocks.adc_clock.disable();
clocks
.rtc_clock
.configure_clock(xosc, 46875u32.Hz())
.unwrap();
clocks
.peripheral_clock
.configure_clock(&clocks.system_clock, clocks.system_clock.freq())
.unwrap();
(pll_sys.disable(), pll_usb.disable())
}
fn restart_clocks_and_plls(
clocks: &mut ClocksManager,
unstable_xosc: CrystalOscillator<Unstable>,
disabled_pll_sys: PhaseLockedLoop<Disabled, PLL_SYS>,
disabled_pll_usb: PhaseLockedLoop<Disabled, PLL_USB>,
resets: &mut RESETS,
) -> Result<RestartedClockAndPlls, ClockError> {
let stable_xosc_token = block!(unstable_xosc.await_stabilization()).unwrap();
let xosc = unstable_xosc.get_stable(stable_xosc_token);
let pll_sys = start_pll_blocking(disabled_pll_sys, resets).unwrap();
let pll_usb = start_pll_blocking(disabled_pll_usb, resets).unwrap();
clocks
.init_default(&xosc, &pll_sys, &pll_usb)
.map(|_| (xosc, pll_sys, pll_usb))
}
#[interrupt]
fn IO_IRQ_BANK0() {
critical_section::with(|cs| {
let mut global_devices = GLOBAL_DEVICES.borrow(cs).borrow_mut();
if let Some(ref mut button_pin) = global_devices.deref_mut() {
if button_pin.interrupt_status(EdgeLow) {
button_pin.clear_interrupt(EdgeLow);
}
} else {
panic!();
};
});
}