kea_hal/clocks/
osc.rs

1//! # External Oscillator Peripheral
2//!
3
4use crate::gpio::gpioa::{PTB6, PTB7};
5use crate::pac::OSC;
6use core::marker::PhantomData;
7
8/// Custom Error Types
9pub enum Error {
10    /// Value was not valid for the function it was passed into.
11    InvalidValue,
12
13    /// Pin has already been returned. Can't give it again
14    NoPin,
15}
16
17// Common state-types
18
19/// Peripheral doesn't care about this type.
20///
21/// It will be set to something useful later.
22pub struct DontCare;
23
24/// Clock feature is disabled
25pub struct Stopped;
26
27/// Clock feature is Enabled, but is Disabled on entry to Stop Mode.
28pub struct Running;
29
30/// Clock feature is always Enabled, even in Stop mode.
31pub struct Unstoppable;
32
33/// External Oscillator Peripheral
34///
35/// Oscillator is Stopped (Disabled) by default.
36pub struct Osc<Status, OscType, Range, Gain, ExtalPinState, XtalPinState> {
37    _status: PhantomData<Status>,
38    _osc_type: PhantomData<OscType>,
39    _range: PhantomData<Range>,
40    _gain: PhantomData<Gain>,
41    extal_pin: Option<PTB7<ExtalPinState>>,
42    xtal_pin: Option<PTB6<XtalPinState>>,
43}
44
45/// Grabs ownership of OSC from the PAC
46pub trait OSCExt {
47    /// This module's state struct
48    type Osc;
49
50    /// grab the peripheral from the PAC and return the state struct
51    fn split(self) -> Self::Osc;
52}
53
54impl OSCExt for OSC {
55    type Osc = Osc<Stopped, ExtClock, LowRange, VariableGain, DontCare, DontCare>;
56    fn split(self) -> Osc<Stopped, ExtClock, LowRange, VariableGain, DontCare, DontCare> {
57        Osc {
58            _status: PhantomData,
59            _osc_type: PhantomData, //ExtClock is default, but don't care.
60            _range: PhantomData,    // LowRange is default, but don't care.
61            _gain: PhantomData,     // Variable (low power) is default, but don't care.
62            extal_pin: None,
63            xtal_pin: None,
64        }
65    }
66}
67
68/// External Clock used.
69pub struct ExtClock;
70
71/// External Oscillator or Resonator used
72pub struct ExtOsc;
73
74/// External Oscillator set for low range (roughly 32kHZ).
75pub struct LowRange;
76
77/// External oscillator set for high range (4-20MHz).
78pub struct HighRange;
79
80/// Oscillator module uses Variable gain to save power
81pub struct VariableGain;
82
83/// Oscillator module uses High Gain to get rail-to-rail oscillations.
84pub struct HighGain;
85
86impl<OscType, Range, Gain, ExtalPinState, XtalPinState>
87    Osc<Stopped, OscType, Range, Gain, ExtalPinState, XtalPinState>
88{
89    /// Start-up the oscillator. Blocks until the oscillator is running.
90    pub fn into_running(
91        self,
92        extal_pin: PTB7<ExtalPinState>,
93    ) -> Osc<Running, OscType, Range, Gain, ExtalPinState, XtalPinState> {
94        let osc = unsafe { &(*OSC::ptr()) };
95
96        // start the oscillator
97        osc.cr.modify(|_, w| w.oscen().set_bit());
98
99        // block until it's stable
100        while osc.cr.read().oscinit().is_0() {
101            cortex_m::asm::nop();
102        }
103
104        Osc {
105            _status: PhantomData,
106            _osc_type: PhantomData,
107            _range: PhantomData,
108            _gain: PhantomData,
109            extal_pin: Some(extal_pin),
110            xtal_pin: self.xtal_pin,
111        }
112    }
113
114    /// Returns the pin used for the EXTAL input
115    ///
116    /// @TODO what is the state of the pin after decoupling from OSC
117    /// peripheral?
118    pub fn release_extal_pin(
119        self,
120    ) -> (
121        Result<PTB7<ExtalPinState>, Error>,
122        Osc<Stopped, OscType, Range, Gain, DontCare, XtalPinState>,
123    ) {
124        (
125            self.extal_pin.ok_or(Error::NoPin),
126            Osc {
127                _status: PhantomData,
128                _osc_type: PhantomData,
129                _range: PhantomData,
130                _gain: PhantomData,
131                extal_pin: None,
132                xtal_pin: self.xtal_pin,
133            },
134        )
135    }
136}
137
138impl<OscType, Range, Gain, ExtalPinState, XtalPinState>
139    Osc<Running, OscType, Range, Gain, ExtalPinState, XtalPinState>
140{
141    /// Allow the OSC peripheral to continue running when the system goes into
142    /// STOP mode.
143    pub fn into_unstoppable(
144        self,
145    ) -> Osc<Unstoppable, OscType, Range, Gain, ExtalPinState, XtalPinState> {
146        unsafe {
147            (*OSC::ptr()).cr.modify(|_, w| w.oscsten().set_bit());
148        }
149        Osc {
150            _status: PhantomData,
151            _osc_type: PhantomData,
152            _range: PhantomData,
153            _gain: PhantomData,
154            extal_pin: self.extal_pin,
155            xtal_pin: self.xtal_pin,
156        }
157    }
158
159    /// Stop (disable) the OSC peripheral
160    ///
161    /// @TODO think of ways to not ruin someones day if they are using OSC_OUT
162    /// as a reference clock.
163    pub fn into_stopped(self) -> Osc<Stopped, OscType, Range, Gain, ExtalPinState, XtalPinState> {
164        let osc = unsafe { &(*OSC::ptr()) };
165        osc.cr.modify(|_, w| w.oscen().clear_bit());
166        Osc {
167            _status: PhantomData,
168            _osc_type: PhantomData,
169            _range: PhantomData,
170            _gain: PhantomData,
171            extal_pin: self.extal_pin,
172            xtal_pin: self.xtal_pin,
173        }
174    }
175}
176
177impl<OscType, Range, Gain, ExtalPinState, XtalPinState>
178    Osc<Unstoppable, OscType, Range, Gain, ExtalPinState, XtalPinState>
179{
180    /// Allow the OSC peripheral to be stopped when the system goes into STOP
181    /// mode.
182    pub fn into_running(self) -> Osc<Running, OscType, Range, Gain, ExtalPinState, XtalPinState> {
183        unsafe {
184            (*OSC::ptr()).cr.modify(|_, w| w.oscsten().clear_bit());
185        }
186        Osc {
187            _status: PhantomData,
188            _osc_type: PhantomData,
189            _range: PhantomData,
190            _gain: PhantomData,
191            extal_pin: self.extal_pin,
192            xtal_pin: self.xtal_pin,
193        }
194    }
195
196    /// Stop (disable) the OSC peripheral
197    ///
198    /// @TODO think of ways to not ruin someones day if they are using OSC_OUT
199    /// as a reference clock.
200    pub fn into_stopped(self) -> Osc<Stopped, OscType, Range, Gain, ExtalPinState, XtalPinState> {
201        let osc = unsafe { &(*OSC::ptr()) };
202        osc.cr.modify(|_, w| w.oscen().clear_bit());
203        Osc {
204            _status: PhantomData,
205            _osc_type: PhantomData,
206            _range: PhantomData,
207            _gain: PhantomData,
208            extal_pin: self.extal_pin,
209            xtal_pin: self.xtal_pin,
210        }
211    }
212}
213
214impl<Status, Range, Gain, ExtalPinState, XtalPinState>
215    Osc<Status, ExtOsc, Range, Gain, ExtalPinState, XtalPinState>
216{
217    /// Change to External Clock mode (not resonator or crystal)
218    ///
219    /// Consumes Pin PTB
220    pub fn into_ext_clock(self) -> Osc<Status, ExtClock, Range, Gain, ExtalPinState, XtalPinState> {
221        unsafe {
222            (*OSC::ptr()).cr.modify(|_, w| w.oscos()._0());
223        }
224        Osc {
225            _status: PhantomData,
226            _osc_type: PhantomData,
227            _range: PhantomData,
228            _gain: PhantomData,
229            extal_pin: self.extal_pin,
230            xtal_pin: self.xtal_pin,
231        }
232    }
233}
234
235impl<Status, Range, Gain, ExtalPinState, XtalPinState>
236    Osc<Status, ExtClock, Range, Gain, ExtalPinState, XtalPinState>
237{
238    /// Change to External oscillator / resonator mode
239    ///
240    /// Consumes Pins PTB6:PTB7
241    pub fn into_ext_osc(self) -> Osc<Status, ExtOsc, Range, Gain, ExtalPinState, XtalPinState> {
242        unsafe { (*OSC::ptr()).cr.modify(|_, w| w.oscos()._1()) }
243        Osc {
244            _status: PhantomData,
245            _osc_type: PhantomData,
246            _range: PhantomData,
247            _gain: PhantomData,
248            extal_pin: self.extal_pin,
249            xtal_pin: self.xtal_pin,
250        }
251    }
252
253    /// Returns the pin used for the XTAL input.
254    ///
255    /// This pin is only needed for the external oscillator / resonator mode.
256    ///
257    /// @TODO what is the state of the pin after decoupling from OSC
258    /// peripheral?
259    pub fn release_xtal_pin(
260        self,
261    ) -> (
262        Result<PTB6<XtalPinState>, Error>,
263        Osc<Status, ExtClock, Range, Gain, ExtalPinState, DontCare>,
264    ) {
265        (
266            self.xtal_pin.ok_or(Error::NoPin),
267            Osc {
268                _status: PhantomData,
269                _osc_type: PhantomData,
270                _range: PhantomData,
271                _gain: PhantomData,
272                extal_pin: self.extal_pin,
273                xtal_pin: None,
274            },
275        )
276    }
277}
278
279// Osc Module is stopped
280impl<OscType, Gain, ExtalPinState, XtalPinState>
281    Osc<Stopped, OscType, LowRange, Gain, ExtalPinState, XtalPinState>
282{
283    /// Set to high range.
284    ///
285    /// 4-20MHz input. Range must be set while the Osc module is disabled.
286    pub fn into_high_range(
287        self,
288    ) -> Osc<Stopped, OscType, HighRange, Gain, ExtalPinState, XtalPinState> {
289        unsafe {
290            (*OSC::ptr()).cr.modify(|_, w| w.range().set_bit());
291        }
292        Osc {
293            _status: PhantomData,
294            _osc_type: PhantomData,
295            _range: PhantomData,
296            _gain: PhantomData,
297            extal_pin: self.extal_pin,
298            xtal_pin: self.xtal_pin,
299        }
300    }
301}
302
303// Osc Module is stopped
304impl<OscType, Gain, ExtalPinState, XtalPinState>
305    Osc<Stopped, OscType, HighRange, Gain, ExtalPinState, XtalPinState>
306{
307    /// Set to low range.
308    ///
309    /// Roughly 32kHZ. Range must be set while the Osc module is disabled.
310    pub fn into_low_range(
311        self,
312    ) -> Osc<Stopped, OscType, HighRange, Gain, ExtalPinState, XtalPinState> {
313        unsafe {
314            (*OSC::ptr()).cr.modify(|_, w| w.range().clear_bit());
315        }
316        Osc {
317            _status: PhantomData,
318            _osc_type: PhantomData,
319            _range: PhantomData,
320            _gain: PhantomData,
321            extal_pin: self.extal_pin,
322            xtal_pin: self.xtal_pin,
323        }
324    }
325}
326
327// I'm pretty sure that there's no harm in changing this in External clock
328// mode, if so then change generic OscType to ExtOsc
329impl<Status, OscType, Range, ExtalPinState, XtalPinState>
330    Osc<Status, OscType, Range, HighGain, ExtalPinState, XtalPinState>
331{
332    /// Set the Oscillator to use Variable Gain.
333    ///
334    /// This mode adjusts the oscillator gain to get an acceptable amplitude,
335    /// minimizing power used.
336    pub fn into_variable_gain(
337        self,
338    ) -> Osc<Status, OscType, Range, VariableGain, ExtalPinState, XtalPinState> {
339        unsafe {
340            (*OSC::ptr()).cr.modify(|_, w| w.hgo().clear_bit());
341        };
342        Osc {
343            _status: PhantomData,
344            _osc_type: PhantomData,
345            _range: PhantomData,
346            _gain: PhantomData,
347            extal_pin: self.extal_pin,
348            xtal_pin: self.xtal_pin,
349        }
350    }
351}
352
353impl<Status, OscType, Range, ExtalPinState, XtalPinState>
354    Osc<Status, OscType, Range, HighGain, ExtalPinState, XtalPinState>
355{
356    /// Set the Oscillator to use High Gain
357    ///
358    /// This mode attempts to get rail-to-rail output from the oscillator or
359    /// resonator.
360    pub fn into_high_gain(
361        self,
362    ) -> Osc<Status, OscType, Range, HighGain, ExtalPinState, XtalPinState> {
363        unsafe {
364            (*OSC::ptr()).cr.modify(|_, w| w.hgo().set_bit());
365        };
366        Osc {
367            _status: PhantomData,
368            _osc_type: PhantomData,
369            _range: PhantomData,
370            _gain: PhantomData,
371            extal_pin: self.extal_pin,
372            xtal_pin: self.xtal_pin,
373        }
374    }
375}