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}