1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
//! 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};
use fugit::HertzU32;
use nb::Error::WouldBlock;
use crate::typelevel::Sealed;
/// State of the Crystal Oscillator (typestate trait)
pub trait State: Sealed {}
/// XOSC is disabled (typestate)
pub struct Disabled;
/// XOSC is initialized, ie we've given parameters (typestate)
pub struct Initialized {
freq_hz: HertzU32,
}
/// Stable state (typestate)
pub struct Stable {
freq_hz: HertzU32,
}
/// XOSC is in dormant mode (see Chapter 2, Section 16, §5)
pub struct Dormant;
impl State for Disabled {}
impl Sealed for Disabled {}
impl State for Initialized {}
impl Sealed for Initialized {}
impl State for Stable {}
impl Sealed for Stable {}
impl State for Dormant {}
impl Sealed for Dormant {}
/// Possible errors when initializing the CrystalOscillator
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
/// Frequency is out of the 1-15MHz range (see datasheet)
FrequencyOutOfRange,
/// Argument is bad : overflows, ...
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: HertzU32,
) -> Result<CrystalOscillator<Stable>, 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<S: State> {
device: rp2040_pac::XOSC,
state: S,
}
impl<S: State> CrystalOscillator<S> {
/// Transitions the oscillator to another state.
fn transition<To: State>(self, state: To) -> CrystalOscillator<To> {
CrystalOscillator {
device: self.device,
state,
}
}
/// Releases the underlying device.
pub fn free(self) -> rp2040_pac::XOSC {
self.device
}
}
impl CrystalOscillator<Disabled> {
/// 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: HertzU32) -> Result<CrystalOscillator<Initialized>, Error> {
const ALLOWED_FREQUENCY_RANGE: RangeInclusive<HertzU32> =
HertzU32::MHz(1)..=HertzU32::MHz(15);
//1 ms = 10e-3 sec and Freq = 1/T where T is in seconds so 1ms converts to 1000Hz
const STABLE_DELAY_AS_HZ: HertzU32 = HertzU32::Hz(1000);
const DIVIDER: u32 = 256;
if !ALLOWED_FREQUENCY_RANGE.contains(&frequency) {
return Err(Error::FrequencyOutOfRange);
}
self.device.ctrl.write(|w| {
w.freq_range()._1_15mhz();
w
});
//startup_delay = ((freq_hz * STABLE_DELAY) / 256) = ((freq_hz / delay_to_hz) / 256)
// = freq_hz / (delay_to_hz * 256)
//See Chapter 2, Section 16, §3)
//We do the calculation first.
let startup_delay = frequency.to_Hz() / (STABLE_DELAY_AS_HZ.to_Hz() * DIVIDER);
//Then we check if it fits into an u16.
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(Initialized { freq_hz: frequency }))
}
}
/// 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<Initialized> {
/// 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<StableOscillatorToken, Infallible> {
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<Stable> {
let freq_hz = self.state.freq_hz;
self.transition(Stable { freq_hz })
}
}
impl CrystalOscillator<Stable> {
/// Operating frequency of the XOSC in hertz
pub fn operating_frequency(&self) -> HertzU32 {
self.state.freq_hz
}
/// Disables the XOSC
pub fn disable(self) -> CrystalOscillator<Disabled> {
self.device.ctrl.modify(|_r, w| {
w.enable().disable();
w
});
self.transition(Disabled)
}
/// Put the XOSC in DORMANT state.
///
/// # Safety
/// 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<Dormant> {
//taken from the C SDK
const XOSC_DORMANT_VALUE: u32 = 0x636f6d61;
self.device.dormant.write(|w| {
w.bits(XOSC_DORMANT_VALUE);
w
});
self.transition(Dormant)
}
}