From d0d9291cdee12bcdf9c294cacba94dc6e6da9eb6 Mon Sep 17 00:00:00 2001 From: Nic0w Date: Sat, 24 Apr 2021 13:38:17 +0200 Subject: [PATCH 1/7] Working HAL for the XOSC --- rp2040-hal/Cargo.toml | 1 + rp2040-hal/src/lib.rs | 3 + rp2040-hal/src/xosc.rs | 192 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+) create mode 100644 rp2040-hal/src/xosc.rs diff --git a/rp2040-hal/Cargo.toml b/rp2040-hal/Cargo.toml index 782adb1..b0c0238 100644 --- a/rp2040-hal/Cargo.toml +++ b/rp2040-hal/Cargo.toml @@ -14,3 +14,4 @@ cortex-m = "0.7.1" embedded-hal = "0.2.4" nb = "1.0.0" rp2040-pac = "0.1.1" +embedded-time = "0.10.1" diff --git a/rp2040-hal/src/lib.rs b/rp2040-hal/src/lib.rs index e8d96e2..8fb2aba 100644 --- a/rp2040-hal/src/lib.rs +++ b/rp2040-hal/src/lib.rs @@ -10,6 +10,8 @@ extern crate cortex_m; extern crate embedded_hal as hal; extern crate nb; +extern crate embedded_time; + pub extern crate rp2040_pac as pac; pub mod adc; @@ -24,3 +26,4 @@ pub mod timer; pub mod uart; pub mod usb; pub mod watchdog; +pub mod xosc; diff --git a/rp2040-hal/src/xosc.rs b/rp2040-hal/src/xosc.rs new file mode 100644 index 0000000..2f07acf --- /dev/null +++ b/rp2040-hal/src/xosc.rs @@ -0,0 +1,192 @@ +//! Crystal Oscillator (XOSC) +// See [Chapter 2 Section 16](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details + +use core::{ + convert::Infallible, + ops::RangeInclusive +}; + +use embedded_time::{ + fraction::Fraction, + fixed_point::FixedPoint, + rate::{ + Hertz, + Megahertz, + }, + duration::{ + Seconds, + Milliseconds, + Duration + } +}; + +use nb::Error::WouldBlock; + +/// State of the Crystal Oscillator (typestate trait) +pub trait State {} + +/// XOSC is disabled (typestate) +pub struct Disabled; + +/// XOSC is initialized, ie we've given parameters (typestate) +pub struct Initialized { + freq_hz: Hertz +} + +/// Stable state (typestate) +pub struct Stable{ + freq_hz: Hertz +} + +/// XOSC is disabling (typestate) +pub struct Disabling; + +/// XOSC is in dormant mode (see Chapter 2, Section 16, §5) +pub struct Dormant; + +impl State for Disabled {} +impl State for Initialized {} +impl State for Stable {} +impl State for Disabling {} +impl State for Dormant {} + +/// Possible errors when initializing the CrystalOscillator +pub enum Error { + /// Frequency is out of the 1-15MHz range (see datasheet) + FrequencyOutOfRange, + + /// Argument is bad : overflows, ... + BadArgument +} + +/// A Crystal Oscillator. +pub struct CrystalOscillator { + device: rp2040_pac::XOSC, + state: S +} + +impl CrystalOscillator { + /// Transitions the oscillator to another state. + fn transition(self, state: To) -> CrystalOscillator { + CrystalOscillator { + device: self.device, + state: state + } + } + + /// Releases the underlying device. + pub fn free(self) -> rp2040_pac::XOSC { + self.device + } +} + +impl CrystalOscillator { + + /// Creates a new CrystalOscillator from the underlying device. + pub fn new(dev: rp2040_pac::XOSC) -> Self { + CrystalOscillator { + device: dev, + state: Disabled + } + } + + /// Initializes the XOSC : frequency range is set, startup delay is calculated and set. + pub fn initialize(self, frequency: Megahertz) -> Result, Error> { + + const ALLOWED_FREQUENCY_RANGE: RangeInclusive> = Megahertz(1)..=Megahertz(15); + const STABLE_DELAY: Milliseconds = Milliseconds(1_u64); + const DIVIDER: Fraction = Fraction::new(1, 256); + + if !ALLOWED_FREQUENCY_RANGE.contains(&frequency) { + return Err(Error::FrequencyOutOfRange) + } + + self.device.ctrl.write(|w| { + w.freq_range()._1_15mhz(); + w + }); + + let freq_hz: Hertz = frequency.into(); + let delay_sec: Seconds = STABLE_DELAY.into(); + + //let startup_delay = ((freq_hz / 1000) + 128) / 256; + let startup_delay = delay_sec. + checked_mul(freq_hz.integer()).and_then(|r| + r.to_generic::(DIVIDER).ok() + ). + ok_or(Error::BadArgument)?; + + self.device.startup.write(|w| unsafe { + w.delay().bits(*startup_delay.integer() as u16); + w + }); + + self.device.ctrl.write(|w| { + w.enable().enable(); + w + }); + + Ok(self.transition(Initialized { + freq_hz: freq_hz + })) + } +} + +/// A token that's given when the oscillator is stablilzed, and can be exchanged to proceed to the next stage. +pub struct StableOscillatorToken { + _private : () +} + +impl CrystalOscillator { + + /// One has to wait for the startup delay before using the oscillator, ie awaiting stablilzation of the XOSC + pub fn await_stabilization(&self) -> nb::Result { + + if self.device.status.read().stable().bit_is_clear() { + return Err(WouldBlock) + } + + Ok(StableOscillatorToken { + _private: () + }) + } + + /// Returns the stablilzed oscillator + pub fn get_stable(self, _token: StableOscillatorToken) -> CrystalOscillator { + let freq_hz = self.state.freq_hz; + self.transition(Stable { + freq_hz:freq_hz + }) + } +} + +impl CrystalOscillator { + + /// Operating frequency of the XOSC in hertz + pub fn operating_frequency(&self) -> Hertz { + self.state.freq_hz + } + + /// Disables the XOSC + pub fn disable(self) -> CrystalOscillator { + self.device.ctrl.modify(|_r,w| { + w.enable().disable(); + w + }); + + self.transition(Disabling) + } + + /// Put the XOSC in dormant state + pub fn dormant(self) -> CrystalOscillator { + //taken from the C SDK + const XOSC_DORMANT_VALUE: u32 = 0x636f6d61; + + self.device.dormant.write(|w| unsafe { + w.bits(XOSC_DORMANT_VALUE); + w + }); + + self.transition(Dormant) + } +} From ed1e847618cccc54780963e664ffb1ef043319e3 Mon Sep 17 00:00:00 2001 From: Nic0w Date: Sat, 24 Apr 2021 22:26:26 +0200 Subject: [PATCH 2/7] Remove line as it builds fine without it. --- rp2040-hal/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rp2040-hal/src/lib.rs b/rp2040-hal/src/lib.rs index 8fb2aba..477db61 100644 --- a/rp2040-hal/src/lib.rs +++ b/rp2040-hal/src/lib.rs @@ -10,7 +10,6 @@ extern crate cortex_m; extern crate embedded_hal as hal; extern crate nb; -extern crate embedded_time; pub extern crate rp2040_pac as pac; From 568cafe2d109ac3263c530b5adad708cf6efc063 Mon Sep 17 00:00:00 2001 From: Nic0w Date: Sat, 24 Apr 2021 23:38:49 +0200 Subject: [PATCH 3/7] Multiple changes related to @tdittr 's comments --- rp2040-hal/src/xosc.rs | 47 +++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/rp2040-hal/src/xosc.rs b/rp2040-hal/src/xosc.rs index 2f07acf..54a5147 100644 --- a/rp2040-hal/src/xosc.rs +++ b/rp2040-hal/src/xosc.rs @@ -1,6 +1,7 @@ //! Crystal Oscillator (XOSC) // See [Chapter 2 Section 16](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details +use core::convert::TryInto; use core::{ convert::Infallible, ops::RangeInclusive @@ -30,24 +31,20 @@ pub struct Disabled; /// XOSC is initialized, ie we've given parameters (typestate) pub struct Initialized { - freq_hz: Hertz + freq_hz: Hertz } /// Stable state (typestate) pub struct Stable{ - freq_hz: Hertz + freq_hz: Hertz } -/// XOSC is disabling (typestate) -pub struct Disabling; - /// XOSC is in dormant mode (see Chapter 2, Section 16, §5) pub struct Dormant; impl State for Disabled {} impl State for Initialized {} impl State for Stable {} -impl State for Disabling {} impl State for Dormant {} /// Possible errors when initializing the CrystalOscillator @@ -91,10 +88,10 @@ impl CrystalOscillator { } /// Initializes the XOSC : frequency range is set, startup delay is calculated and set. - pub fn initialize(self, frequency: Megahertz) -> Result, Error> { + pub fn initialize(self, frequency: Hertz) -> Result, Error> { const ALLOWED_FREQUENCY_RANGE: RangeInclusive> = Megahertz(1)..=Megahertz(15); - const STABLE_DELAY: Milliseconds = Milliseconds(1_u64); + const STABLE_DELAY: Milliseconds = Milliseconds(1_u32); const DIVIDER: Fraction = Fraction::new(1, 256); if !ALLOWED_FREQUENCY_RANGE.contains(&frequency) { @@ -106,18 +103,22 @@ impl CrystalOscillator { w }); - let freq_hz: Hertz = frequency.into(); - let delay_sec: Seconds = STABLE_DELAY.into(); + let delay_sec: Seconds = STABLE_DELAY.into(); - //let startup_delay = ((freq_hz / 1000) + 128) / 256; + //startup_delay = ((freq_hz * 10e-3) / 256; See Chapter 2, Section 16, §3) + //We do the calculation first. let startup_delay = delay_sec. - checked_mul(freq_hz.integer()).and_then(|r| - r.to_generic::(DIVIDER).ok() + checked_mul(frequency.integer()).and_then(|r| + r.to_generic::(DIVIDER).ok() ). ok_or(Error::BadArgument)?; + //Then we check if it fits into an u16. + let startup_delay: u16 = (*startup_delay.integer()).try_into(). + map_err(|_|Error::BadArgument)?; + self.device.startup.write(|w| unsafe { - w.delay().bits(*startup_delay.integer() as u16); + w.delay().bits(startup_delay); w }); @@ -127,7 +128,7 @@ impl CrystalOscillator { }); Ok(self.transition(Initialized { - freq_hz: freq_hz + freq_hz: frequency })) } } @@ -155,7 +156,7 @@ impl CrystalOscillator { pub fn get_stable(self, _token: StableOscillatorToken) -> CrystalOscillator { let freq_hz = self.state.freq_hz; self.transition(Stable { - freq_hz:freq_hz + freq_hz }) } } @@ -163,22 +164,26 @@ impl CrystalOscillator { impl CrystalOscillator { /// Operating frequency of the XOSC in hertz - pub fn operating_frequency(&self) -> Hertz { + pub fn operating_frequency(&self) -> Hertz { self.state.freq_hz } /// Disables the XOSC - pub fn disable(self) -> CrystalOscillator { + pub fn disable(self) -> CrystalOscillator { self.device.ctrl.modify(|_r,w| { w.enable().disable(); w }); - self.transition(Disabling) + self.transition(Disabled) } - /// Put the XOSC in dormant state - pub fn dormant(self) -> CrystalOscillator { + /// Put the XOSC in DORMANT state. + /// This method is marked unsafe because prior to switch the XOSC into DORMANT state, + /// PLLs must be stopped and IRQs have to be properly configured. + /// This method does not do any of that, it merely switches the XOSC to DORMANT state. + /// See Chapter 2, Section 16, §5) for details. + pub unsafe fn dormant(self) -> CrystalOscillator { //taken from the C SDK const XOSC_DORMANT_VALUE: u32 = 0x636f6d61; From d5cbd44adeb9137df7394b7daf887aafe5f2703c Mon Sep 17 00:00:00 2001 From: Nic0w Date: Sun, 25 Apr 2021 00:36:46 +0200 Subject: [PATCH 4/7] Fixing calculation bug pointed out by @tdittr --- rp2040-hal/src/xosc.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/rp2040-hal/src/xosc.rs b/rp2040-hal/src/xosc.rs index 54a5147..124cde6 100644 --- a/rp2040-hal/src/xosc.rs +++ b/rp2040-hal/src/xosc.rs @@ -92,7 +92,7 @@ impl CrystalOscillator { const ALLOWED_FREQUENCY_RANGE: RangeInclusive> = Megahertz(1)..=Megahertz(15); const STABLE_DELAY: Milliseconds = Milliseconds(1_u32); - const DIVIDER: Fraction = Fraction::new(1, 256); + const DIVIDER: Fraction = Fraction::new(256, 1); if !ALLOWED_FREQUENCY_RANGE.contains(&frequency) { return Err(Error::FrequencyOutOfRange) @@ -103,12 +103,14 @@ impl CrystalOscillator { w }); - let delay_sec: Seconds = STABLE_DELAY.into(); + //1 ms = 10e-3 sec and Freq = 1/T where T is in seconds so 1ms converts to 1000Hz + let delay_to_hz: Hertz = STABLE_DELAY.to_rate(); - //startup_delay = ((freq_hz * 10e-3) / 256; See Chapter 2, Section 16, §3) + //startup_delay = ((freq_hz * 10e-3) / 256) = ((freq_hz / 1000) / 256) + //See Chapter 2, Section 16, §3) //We do the calculation first. - let startup_delay = delay_sec. - checked_mul(frequency.integer()).and_then(|r| + let startup_delay = frequency. + checked_div(delay_to_hz.integer()).and_then(|r| r.to_generic::(DIVIDER).ok() ). ok_or(Error::BadArgument)?; @@ -118,7 +120,7 @@ impl CrystalOscillator { map_err(|_|Error::BadArgument)?; self.device.startup.write(|w| unsafe { - w.delay().bits(startup_delay); + w.delay().bits(startup_delay+1); w }); From 72694a07b5bff9dd0531e4042c08c5fb04e6824c Mon Sep 17 00:00:00 2001 From: Nic0w Date: Sun, 25 Apr 2021 08:58:43 +0200 Subject: [PATCH 5/7] Fix frequency range check. --- rp2040-hal/src/xosc.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rp2040-hal/src/xosc.rs b/rp2040-hal/src/xosc.rs index 124cde6..b97724d 100644 --- a/rp2040-hal/src/xosc.rs +++ b/rp2040-hal/src/xosc.rs @@ -94,7 +94,9 @@ impl CrystalOscillator { const STABLE_DELAY: Milliseconds = Milliseconds(1_u32); const DIVIDER: Fraction = Fraction::new(256, 1); - if !ALLOWED_FREQUENCY_RANGE.contains(&frequency) { + let freq_mhz: Megahertz = frequency.into(); + + if !ALLOWED_FREQUENCY_RANGE.contains(&freq_mhz) { return Err(Error::FrequencyOutOfRange) } @@ -120,7 +122,7 @@ impl CrystalOscillator { map_err(|_|Error::BadArgument)?; self.device.startup.write(|w| unsafe { - w.delay().bits(startup_delay+1); + w.delay().bits(startup_delay); w }); From 8f6aea6a31b04b43851b4755cd463b3e11d84b97 Mon Sep 17 00:00:00 2001 From: Nic0w Date: Sun, 25 Apr 2021 09:03:20 +0200 Subject: [PATCH 6/7] Fix compilation issues. --- rp2040-hal/src/xosc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rp2040-hal/src/xosc.rs b/rp2040-hal/src/xosc.rs index b97724d..0d3a1f5 100644 --- a/rp2040-hal/src/xosc.rs +++ b/rp2040-hal/src/xosc.rs @@ -13,9 +13,9 @@ use embedded_time::{ rate::{ Hertz, Megahertz, + Rate }, duration::{ - Seconds, Milliseconds, Duration } @@ -106,7 +106,7 @@ impl CrystalOscillator { }); //1 ms = 10e-3 sec and Freq = 1/T where T is in seconds so 1ms converts to 1000Hz - let delay_to_hz: Hertz = STABLE_DELAY.to_rate(); + let delay_to_hz: Hertz = STABLE_DELAY.to_rate().map_err(|_|Error::BadArgument)?; //startup_delay = ((freq_hz * 10e-3) / 256) = ((freq_hz / 1000) / 256) //See Chapter 2, Section 16, §3) From 2a704a73f0fa0178e0775c2aec64183061deee4c Mon Sep 17 00:00:00 2001 From: Nic0w Date: Sun, 25 Apr 2021 16:34:48 +0200 Subject: [PATCH 7/7] Adding blocking helper method to setup the XOSC easily. --- rp2040-hal/src/xosc.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rp2040-hal/src/xosc.rs b/rp2040-hal/src/xosc.rs index 0d3a1f5..cf5d9ed 100644 --- a/rp2040-hal/src/xosc.rs +++ b/rp2040-hal/src/xosc.rs @@ -56,6 +56,18 @@ pub enum Error { BadArgument } + +/// Blocking helper method to setup the XOSC without going through all the steps. +pub fn setup_xosc_blocking(xosc_dev: rp2040_pac::XOSC, frequency: Hertz) -> Result, Error> { + + let initialized_xosc = CrystalOscillator::new(xosc_dev).initialize(frequency)?; + + let stable_xosc_token = nb::block!(initialized_xosc.await_stabilization()).unwrap(); + + Ok(initialized_xosc.get_stable(stable_xosc_token)) +} + + /// A Crystal Oscillator. pub struct CrystalOscillator { device: rp2040_pac::XOSC,