atsamd_hal/peripherals/clock/d5x/v2/
osculp32k.rs

1//! # Internal, ultra low power, 32 kHz oscillator
2//!
3//! ## Overview
4//!
5//! The `osculp32k` module provides access to the 32 kHz ultra low power
6//! internal oscillator (OSCULP32K) within the `OSC32KCTRL` peripheral.
7//!
8//! The `OSCULP32K` clock is unlike most other clocks. First, it is an internal
9//! clock that is always enabled and can't be disabled. And second, it has two
10//! separate outputs, one at 32 kHz and another divided down to 1 kHz. Moreover,
11//! none, either or both of these outputs can be enabled at any given time.
12//!
13//! We can see, then, that the `OSCULP32K` peripheral forms its own, miniature
14//! clock tree. There is a 1:N producer clock that is always enabled; and there
15//! are two possible consumer clocks that can be independently and optionally
16//! enabled. In fact, this structure is illustrated by the `OSCULP32K`
17//! register, which has no regular `ENABLE` bit and two different enable bits
18//! for clock output, `EN32K` and `EN1K`.
19//!
20//! To represent this structure in the type system, we divide the `OSCULP32K`
21//! peripheral into these three clocks. Users get access to the 1:N
22//! [`EnabledOscUlp32kBase`] clock [`Source`] at power-on reset, which can be
23//! consumed by both the [`OscUlp32k`] and [`OscUlp1k`] clocks. Note that
24//! `OscUlp32k` and `OscUlp1k` are themselves 1:N clocks as well.
25//!
26//! ## Write lock
27//!
28//! Rhe `OSCULP32K` register has a dedicated write lock bit that will freeze its
29//! configuration until the next power-on reset. We implement this by simply
30//! dropping the [`OscUlp32kBase`] clock, which prevents any further access to
31//! the `OSCULP32K` register.
32//!
33//! ## Example
34//!
35//! Creating and configuring the OSCULP32K clocks proceeds according to the
36//! principles outlined in the [`clock` module documentation]. It is best shown
37//! with an example.
38//!
39//! Let's start by using [`clock_system_at_reset`] to access the HAL clocking
40//! structs.
41//!
42//! ```no_run
43//! use atsamd_hal::{
44//!     clock::v2::{
45//!         clock_system_at_reset,
46//!         osculp32k::{OscUlp1k, OscUlp32k},
47//!     },
48//!     pac::Peripherals,
49//! };
50//! let mut pac = Peripherals::take().unwrap();
51//! let (buses, clocks, tokens) = clock_system_at_reset(
52//!     pac.oscctrl,
53//!     pac.osc32kctrl,
54//!     pac.gclk,
55//!     pac.mclk,
56//!     &mut pac.nvmctrl,
57//! );
58//! ```
59//!
60//! Next, we can extract the [`EnabledOscUlp32kBase`] clock from the [`Clocks`]
61//! struct and use it to enable the [`OscUlp1k`] and [`OscUlp32k`] clocks.
62//!
63//! ```no_run
64//! # use atsamd_hal::{
65//! #     clock::v2::{
66//! #         clock_system_at_reset,
67//! #         osculp32k::{OscUlp1k, OscUlp32k},
68//! #     },
69//! #     pac::Peripherals,
70//! # };
71//! # let mut pac = Peripherals::take().unwrap();
72//! # let (buses, clocks, tokens) = clock_system_at_reset(
73//! #     pac.oscctrl,
74//! #     pac.osc32kctrl,
75//! #     pac.gclk,
76//! #     pac.mclk,
77//! #     &mut pac.nvmctrl,
78//! # );
79//! let base = clocks.osculp32k_base;
80//! let (osculp1k, base) = OscUlp1k::enable(tokens.osculp32k.osculp1k, base);
81//! let (osculp32k, base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, base);
82//! ```
83//!
84//! We can then override the calibration value read from flash at start up.
85//!
86//! ```no_run
87//! # use atsamd_hal::{
88//! #     clock::v2::{
89//! #         clock_system_at_reset,
90//! #         osculp32k::{OscUlp1k, OscUlp32k},
91//! #     },
92//! #     pac::Peripherals,
93//! # };
94//! # let mut pac = Peripherals::take().unwrap();
95//! # let (buses, clocks, tokens) = clock_system_at_reset(
96//! #     pac.oscctrl,
97//! #     pac.osc32kctrl,
98//! #     pac.gclk,
99//! #     pac.mclk,
100//! #     &mut pac.nvmctrl,
101//! # );
102//! # let base = clocks.osculp32k_base;
103//! # let (osculp1k, base) = OscUlp1k::enable(tokens.osculp32k.osculp1k, base);
104//! # let (osculp32k, mut base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, base);
105//! base.set_calibration(128);
106//! ```
107//!
108//! And finally, we can set the write lock bit to freeze the configuation until
109//! the next power-on reset. Doing so also drops the `EnabledOscUlp32kBase`
110//! clock.
111//!
112//! ```no_run
113//! # use atsamd_hal::{
114//! #     clock::v2::{
115//! #         clock_system_at_reset,
116//! #         osculp32k::{OscUlp1k, OscUlp32k},
117//! #     },
118//! #     pac::Peripherals,
119//! # };
120//! # let mut pac = Peripherals::take().unwrap();
121//! # let (buses, clocks, tokens) = clock_system_at_reset(
122//! #     pac.oscctrl,
123//! #     pac.osc32kctrl,
124//! #     pac.gclk,
125//! #     pac.mclk,
126//! #     &mut pac.nvmctrl,
127//! # );
128//! # let base = clocks.osculp32k_base;
129//! # let (osculp1k, base) = OscUlp1k::enable(tokens.osculp32k.osculp1k, base);
130//! # let (osculp32k, mut base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, base);
131//! # base.set_calibration(128);
132//! base.write_lock();
133//! ```
134//!
135//! The complete example is shown below.
136//!
137//! ```no_run
138//! use atsamd_hal::{
139//!     clock::v2::{
140//!         clock_system_at_reset,
141//!         osculp32k::{OscUlp1k, OscUlp32k},
142//!     },
143//!     pac::Peripherals,
144//! };
145//! let mut pac = Peripherals::take().unwrap();
146//! let (buses, clocks, tokens) = clock_system_at_reset(
147//!     pac.oscctrl,
148//!     pac.osc32kctrl,
149//!     pac.gclk,
150//!     pac.mclk,
151//!     &mut pac.nvmctrl,
152//! );
153//! let base = clocks.osculp32k_base;
154//! let (osculp1k, base) = OscUlp1k::enable(tokens.osculp32k.osculp1k, base);
155//! let (osculp32k, mut base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, base);
156//! base.set_calibration(128);
157//! base.write_lock();
158//! ```
159//!
160//! [`clock` module documentation]: super
161//! [`clock_system_at_reset`]: super::clock_system_at_reset
162//! [`Clocks`]: super::Clocks
163
164use fugit::RateExtU32;
165use typenum::U0;
166
167use crate::pac::osc32kctrl::Osculp32k;
168
169use crate::time::Hertz;
170use crate::typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement, Sealed};
171
172use super::{Enabled, Source};
173
174//==============================================================================
175// Tokens
176//==============================================================================
177
178/// Singleton token for the [`OscUlp32kBase`] clock
179//
180// There should never be more than one instance of `OscUlp32kBaseToken`, because
181// it relies on that fact for memory safety.
182//
183// Users never see `OscUlp32kBaseToken`, because the OSCULP32K base oscillator
184// is always enabled. Internally, however, it is used as a register interface.
185// The token is zero-sized, so it can be carried by clock types without
186// introducing any memory bloat.
187//
188// As part of that register interface, the `OscUlp32kBaseToken` can access the
189// `OSCULP32K` register. That the token is a singleton guarantees the register
190// is written from only one location. This allows the token to be `Sync`, even
191// though the PAC `OSC32KCTRL` struct is not.
192struct OscUlp32kBaseToken(());
193
194/// Singleton token that can be exchanged for [`OscUlp1k`]
195///
196/// As explained in the [`clock` module documentation](super), instances of
197/// various `Token` types can be exchanged for actual clock types. They
198/// typically represent clocks that are disabled at power-on reset.
199///
200/// The [`OscUlp1k`] clock is disabled at power-on reset. To use it, you must
201/// first exchange the token for an actual clock with [`OscUlp1k::enable`].
202pub struct OscUlp1kToken(());
203
204/// Singleton token that can be exchanged for [`OscUlp32k`]
205///
206/// As explained in the [`clock` module documentation](super), instances of
207/// various `Token` types can be exchanged for actual clock types. They
208/// typically represent clocks that are disabled at power-on reset.
209///
210/// The [`OscUlp32k`] clock is disabled at power-on reset. To use it, you must
211/// first exchange the token for an actual clock with [`OscUlp32k::enable`].
212pub struct OscUlp32kToken(());
213
214/// Set of tokens representing the disabled OSCULP32K clocks power-on reset
215pub struct OscUlp32kTokens {
216    pub osculp1k: OscUlp1kToken,
217    pub osculp32k: OscUlp32kToken,
218}
219
220impl OscUlp32kTokens {
221    /// Create the set of tokens
222    ///
223    /// # Safety
224    ///
225    /// There must never be more than one instance of each token at any given
226    /// time. See the notes on `Token` types and memory safety in the root of
227    /// the `clock` module for more details.
228    pub(super) unsafe fn new() -> Self {
229        Self {
230            osculp1k: OscUlp1kToken(()),
231            osculp32k: OscUlp32kToken(()),
232        }
233    }
234}
235
236impl OscUlp32kBaseToken {
237    #[inline]
238    fn osculp32k(&self) -> &Osculp32k {
239        // Safety: The `OscUlp32kBaseToken` has exclusive access to the
240        // `OSCULP32K` register. See the notes on `Token` types and memory
241        // safety in the root of the `clock` module for more details.
242        unsafe { (*crate::pac::Osc32kctrl::PTR).osculp32k() }
243    }
244
245    /// Enable the 1 kHz output
246    #[inline]
247    fn enable_1k(&mut self) {
248        self.osculp32k().modify(|_, w| w.en1k().set_bit());
249    }
250
251    /// Disable the 1 kHz output
252    #[inline]
253    fn disable_1k(&mut self) {
254        self.osculp32k().modify(|_, w| w.en1k().clear_bit());
255    }
256
257    /// Enable the 32 kHz output
258    #[inline]
259    fn enable_32k(&mut self) {
260        self.osculp32k().modify(|_, w| w.en32k().set_bit());
261    }
262
263    /// Disable the 32 kHz output
264    #[inline]
265    fn disable_32k(&mut self) {
266        self.osculp32k().modify(|_, w| w.en32k().clear_bit());
267    }
268
269    /// Enable the write lock
270    #[inline]
271    fn write_lock(&mut self) {
272        self.osculp32k().modify(|_, w| w.wrtlock().set_bit());
273    }
274}
275
276//==============================================================================
277// OscUlpBase
278//==============================================================================
279
280/// OSC3ULP2K base clock, which feeds the [`OscUlp1k`] and [`OscUlp32k`] clocks
281///
282/// The OSCULP32K peripheral has two possible clock outputs, one at 32 kHz and
283/// another at 1 kHz. This structure is represented in the type system as a set
284/// of three clocks forming a small clock tree. The [`OscUlp32kBase`] clock
285/// represents the base oscillator that feeds the optional [`OscUlp1k`] and
286/// [`OscUlp32k`] output clocks. See the [module-level documentation](super) for
287/// details and examples.
288pub struct OscUlp32kBase {
289    token: OscUlp32kBaseToken,
290}
291
292/// The [`Enabled`] [`OscUlp32kBase`] clock
293///
294/// As described in the [`clock` module documentation](super), the [`Enabled`]
295/// wrapper implements compile-time clock tree safety by tracking the number of
296/// clocks consuming the [`OscUlp32kBase`] clock and restricts access to the
297/// underlying type to prevent misuse.
298///
299/// **NOTE:** The `OscUlp32kBase` clock is internal and can never be disabled,
300/// so we do not provide a `disable` method.
301///
302/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified,
303/// the counter is assumed to be zero.
304pub type EnabledOscUlp32kBase<N = U0> = Enabled<OscUlp32kBase, N>;
305
306impl OscUlp32kBase {
307    /// Create the ultra-low power base oscillator
308    ///
309    /// # Safety
310    ///
311    /// Because an `OscUlp32kBase` contains an `OscUlp32kBaseToken`, there must
312    /// never be more than one instance of this struct at any given time. See
313    /// the notes on `Token` types and memory safety in the root of the `clock`
314    /// module for more details.
315    #[inline]
316    pub(super) unsafe fn new() -> EnabledOscUlp32kBase {
317        let token = OscUlp32kBaseToken(());
318        Enabled::new(Self { token })
319    }
320}
321
322impl<N> EnabledOscUlp32kBase<N> {
323    /// Freeze the OSCULP32K configuration until power-on reset
324    ///
325    /// This function sets the write-lock bit, which freezes the OSCULP32K
326    /// configuration at the hardware level until power-on reset. At the API
327    /// level, it also consumes and drops the [`OscUlp32kBase`] clock, which
328    /// prevents any further modifications.
329    #[inline]
330    pub fn write_lock(mut self) {
331        self.0.token.write_lock();
332    }
333}
334
335//==============================================================================
336// Ids
337//==============================================================================
338
339/// Type representing the identity of the [`OscUlp1k`] clock
340///
341/// See the discussion on [`Id` types](super#id-types) for more information.
342pub enum OscUlp1kId {}
343
344impl Sealed for OscUlp1kId {}
345
346/// Type representing the identity of the [`OscUlp32k`] clock
347///
348/// See the discussion on [`Id` types](super#id-types) for more information.
349pub enum OscUlp32kId {}
350
351impl Sealed for OscUlp32kId {}
352
353//==============================================================================
354// OscUlp1k
355//==============================================================================
356
357/// Clock representing the 1 kHz output of the [`OscUlp32kBase`] clock
358///
359/// The OSCULP32K peripheral has two possible clock outputs, one at 32 kHz and
360/// another at 1 kHz. This structure is represented in the type system as a set
361/// of three clocks forming a small clock tree. The [`OscUlp1k`] clock is
362/// derived from the [`OscUlp32kBase`] clock. See the
363/// [module-level documentation](super) for details and examples.
364pub struct OscUlp1k {
365    token: OscUlp1kToken,
366}
367
368/// The [`Enabled`] [`OscUlp1k`] clock
369///
370/// As described in the [`clock` module documentation](super), the [`Enabled`]
371/// wrapper implements compile-time clock tree safety by tracking the number of
372/// clocks consuming the [`OscUlp1k`] clock and restricts access to the
373/// underlying type to prevent misuse.
374///
375/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified,
376/// the counter is assumed to be zero.
377pub type EnabledOscUlp1k<N = U0> = Enabled<OscUlp1k, N>;
378
379impl OscUlp1k {
380    /// Enable 1 kHz output from the [`OscUlp32kBase`] clock
381    ///
382    /// This will [`Increment`] the [`EnabledOscUlp32kBase`] counter.
383    #[inline]
384    pub fn enable<N: Increment>(
385        token: OscUlp1kToken,
386        mut base: EnabledOscUlp32kBase<N>,
387    ) -> (EnabledOscUlp1k, EnabledOscUlp32kBase<N::Inc>) {
388        base.0.token.enable_1k();
389        (Enabled::new(Self { token }), base.inc())
390    }
391}
392
393impl EnabledOscUlp1k {
394    /// Disable 1 kHz output from the [`OscUlp32kBase`] clock
395    ///
396    /// This will [`Decrement`] the [`EnabledOscUlp32kBase`] counter.
397    #[inline]
398    pub fn disable<N: Decrement>(
399        self,
400        mut base: EnabledOscUlp32kBase<N>,
401    ) -> (OscUlp1kToken, EnabledOscUlp32kBase<N::Dec>) {
402        base.0.token.disable_1k();
403        (self.0.token, base.dec())
404    }
405}
406
407impl<N> Source for EnabledOscUlp1k<N> {
408    type Id = OscUlp1kId;
409
410    #[inline]
411    fn freq(&self) -> Hertz {
412        1024.Hz()
413    }
414}
415
416//==============================================================================
417// OscUlp32k
418//==============================================================================
419
420/// Clock representing the 32 kHz output of the [`OscUlp32kBase`] clock
421///
422/// The OSCULP32K peripheral has two possible clock outputs, one at 32 kHz and
423/// another at 1 kHz. This structure is represented in the type system as a set
424/// of three clocks forming a small clock tree. The [`OscUlp32k`] clock is
425/// derived from the [`OscUlp32kBase`] clock. See the
426/// [module-level documentation](super) for details and examples.
427pub struct OscUlp32k {
428    token: OscUlp32kToken,
429}
430
431/// The [`Enabled`] [`OscUlp32k`] clock
432///
433/// As described in the [`clock` module documentation](super), the [`Enabled`]
434/// wrapper implements compile-time clock tree safety by tracking the number of
435/// clocks consuming the [`OscUlp32k`] clock and restricts access to the
436/// underlying type to prevent misuse.
437///
438/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified,
439/// the counter is assumed to be zero.
440pub type EnabledOscUlp32k<N = U0> = Enabled<OscUlp32k, N>;
441
442impl OscUlp32k {
443    /// Enable 32 kHz output from the [`OscUlp32kBase`] clock
444    ///
445    /// This will [`Increment`] the [`EnabledOscUlp32kBase`] counter.
446    #[inline]
447    pub fn enable<N: Increment>(
448        token: OscUlp32kToken,
449        mut base: EnabledOscUlp32kBase<N>,
450    ) -> (EnabledOscUlp32k, EnabledOscUlp32kBase<N::Inc>) {
451        base.0.token.enable_32k();
452        (Enabled::new(Self { token }), base.inc())
453    }
454}
455
456impl EnabledOscUlp32k {
457    /// Disable 32 kHz output from the [`OscUlp32kBase`] clock
458    ///
459    /// This will [`Decrement`] the [`EnabledOscUlp32kBase`] counter.
460    #[inline]
461    pub fn disable<N: Decrement>(
462        self,
463        mut base: EnabledOscUlp32kBase<N>,
464    ) -> (OscUlp32kToken, EnabledOscUlp32kBase<N::Dec>) {
465        base.0.token.disable_32k();
466        (self.0.token, base.dec())
467    }
468}
469
470impl<N> Source for EnabledOscUlp32k<N> {
471    type Id = OscUlp32kId;
472
473    #[inline]
474    fn freq(&self) -> Hertz {
475        32_768.Hz()
476    }
477}