use core::{convert::Infallible, ops::RangeInclusive};
use fugit::HertzU32;
use nb::Error::WouldBlock;
use crate::{pac::XOSC, typelevel::Sealed};
pub trait State: Sealed {}
pub struct Disabled;
pub struct Unstable {
freq_hz: HertzU32,
}
pub struct Stable {
freq_hz: HertzU32,
}
impl State for Disabled {}
impl Sealed for Disabled {}
impl State for Unstable {}
impl Sealed for Unstable {}
impl State for Stable {}
impl Sealed for Stable {}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
FrequencyOutOfRange,
BadArgument,
}
pub fn setup_xosc_blocking(
xosc_dev: XOSC,
frequency: HertzU32,
) -> Result<CrystalOscillator<Stable>, Error> {
let initialized_xosc = CrystalOscillator::new(xosc_dev).initialize(frequency, 64)?;
let stable_xosc_token = nb::block!(initialized_xosc.await_stabilization()).unwrap();
Ok(initialized_xosc.get_stable(stable_xosc_token))
}
pub fn setup_xosc_blocking_custom_delay(
xosc_dev: XOSC,
frequency: HertzU32,
startup_delay_multiplier: u32,
) -> Result<CrystalOscillator<Stable>, Error> {
let initialized_xosc =
CrystalOscillator::new(xosc_dev).initialize(frequency, startup_delay_multiplier)?;
let stable_xosc_token = nb::block!(initialized_xosc.await_stabilization()).unwrap();
Ok(initialized_xosc.get_stable(stable_xosc_token))
}
pub struct CrystalOscillator<S: State> {
device: XOSC,
state: S,
}
impl<S: State> CrystalOscillator<S> {
fn transition<To: State>(self, state: To) -> CrystalOscillator<To> {
CrystalOscillator {
device: self.device,
state,
}
}
pub fn free(self) -> XOSC {
self.device
}
}
impl CrystalOscillator<Disabled> {
pub fn new(dev: XOSC) -> Self {
CrystalOscillator {
device: dev,
state: Disabled,
}
}
pub fn initialize(
self,
frequency: HertzU32,
startup_delay_multiplier: u32,
) -> Result<CrystalOscillator<Unstable>, Error> {
const ALLOWED_FREQUENCY_RANGE: RangeInclusive<HertzU32> =
HertzU32::MHz(1)..=HertzU32::MHz(15);
const STABLE_DELAY_AS_HZ: HertzU32 = HertzU32::Hz(1000);
const DIVIDER: u32 = 256;
if !ALLOWED_FREQUENCY_RANGE.contains(&frequency) {
return Err(Error::FrequencyOutOfRange);
}
if startup_delay_multiplier == 0 {
return Err(Error::BadArgument);
}
self.device.ctrl().write(|w| {
w.freq_range()._1_15mhz();
w
});
let startup_delay = frequency.to_Hz() / (STABLE_DELAY_AS_HZ.to_Hz() * DIVIDER);
let startup_delay = startup_delay.saturating_mul(startup_delay_multiplier);
let startup_delay: u16 = startup_delay.try_into().map_err(|_| Error::BadArgument)?;
self.device.startup().write(|w| unsafe {
w.delay().bits(startup_delay);
w
});
self.device.ctrl().write(|w| {
w.enable().enable();
w
});
Ok(self.transition(Unstable { freq_hz: frequency }))
}
}
pub struct StableOscillatorToken {
_private: (),
}
impl CrystalOscillator<Unstable> {
pub fn await_stabilization(&self) -> nb::Result<StableOscillatorToken, Infallible> {
if self.device.status().read().stable().bit_is_clear() {
return Err(WouldBlock);
}
Ok(StableOscillatorToken { _private: () })
}
pub fn get_stable(self, _token: StableOscillatorToken) -> CrystalOscillator<Stable> {
let freq_hz = self.state.freq_hz;
self.transition(Stable { freq_hz })
}
}
impl CrystalOscillator<Stable> {
pub fn operating_frequency(&self) -> HertzU32 {
self.state.freq_hz
}
pub fn disable(self) -> CrystalOscillator<Disabled> {
self.device.ctrl().modify(|_r, w| {
w.enable().disable();
w
});
self.transition(Disabled)
}
pub unsafe fn dormant(self) -> CrystalOscillator<Unstable> {
const XOSC_DORMANT_VALUE: u32 = 0x636f6d61;
self.device.dormant().write(|w| {
w.bits(XOSC_DORMANT_VALUE);
w
});
let freq_hz = self.state.freq_hz;
self.transition(Unstable { freq_hz })
}
}