atsamd_hal/sercom/
pad.rs

1//! Type-level tools to configure SERCOM pads
2//!
3//! This module helps configure [`Pin`]s as SERCOM pads. It provides type-level
4//! tools to convert `Pin`s to the correct [`PinMode`] and to enforce type-level
5//! constraints at compile-time.
6//!
7//! # Overview
8//!
9//! A SERCOM pad is defined by two types, its corresponding [`Sercom`] instance
10//! and its [`PadNum`], from [`Pad0`] to [`Pad3`]. However, a given SERCOM pad
11//! can usually be mapped to several possible [`PinId`]s.
12//!
13//! There are two primary traits defined in this module:
14//! - The [`IsPad`] trait is implemented on `Pin` types that are properly
15//!   configured as SERCOM pads, with `PinMode` [`AlternateC`] or
16//!   [`AlternateD`]. It acts as both a [type class] for SERCOM pads and as a
17//!   [type-level function] to recover the corresponding [`Sercom`] and
18//!   [`PadNum`] types from the `Pin`.
19//! - The [`GetPad`] trait maps each [`PinId`] to its corresponding, pad-related
20//!   types. The [`PadMode`] alias uses `GetPad` to recover the corresponding
21//!   `PinMode` for a given SERCOM pad, while the [`Pad`] alias recovers the
22//!   configured [`Pin`] type.
23//!
24//! [`AlternateC`]: crate::gpio::AlternateC
25//! [`AlternateD`]: crate::gpio::AlternateD
26//! [type class]: crate::typelevel#type-classes
27//! [type-level function]: crate::typelevel#type-level-functions
28//! # IOSET (SAMD51/SAME5x only)
29//!
30//! SAMx5x chips do not allow arbitrary combinations of `PinId` for a given
31//! SERCOM. Instead, all `PinId`s must belong to the same IOSET. This module
32//! defines a [type-level enum], [`IoSet`], to enforce this restriction, and the
33//! [`InIoSet`] [type class] is responsible for labeling each `IsPad` type with
34//! its corresponding, valid `IoSet`\(s).
35
36use atsamd_hal_macros::{hal_cfg, hal_module};
37use paste::paste;
38use seq_macro::seq;
39
40use super::Sercom;
41#[hal_cfg(any("sercom0-d21", "sercom0-d5x"))]
42use crate::gpio::OptionalPinId;
43use crate::gpio::{AnyPin, OptionalPin, Pin, PinId, PinMode};
44use crate::typelevel::{NoneT, Sealed};
45
46#[hal_module(
47    any("sercom0-d11", "sercom0-d21") => "pad/impl_pad_thumbv6m.rs",
48    "sercom0-d5x" => "pad/impl_pad_thumbv7em.rs",
49)]
50mod impl_pad {}
51
52//==============================================================================
53// PadNum
54//==============================================================================
55
56/// Type-level enum representing a SERCOM pad number
57///
58/// It has variants [`Pad0`], [`Pad1`], [`Pad2`] & [`Pad3`]. See the [type-level
59/// enum] documentation for an explanation of the pattern.
60///
61/// [type-level enum]: crate::typelevel#type-level-enum
62pub trait PadNum: Sealed {}
63
64seq!(N in 0..=3 {
65    paste! {
66        #[doc = "Type-level variant of [`PadNum`] representing SERCOM pad " N]
67        ///
68        /// See the [type-level enum] documentation for an explanation of the
69        /// pattern.
70        ///
71        /// [type-level enum]: crate::typelevel#type-level-enum
72        pub enum Pad~N {}
73        impl Sealed for Pad~N {}
74        impl PadNum for Pad~N {}
75    }
76});
77
78//==============================================================================
79// OptionalPadNum
80//==============================================================================
81
82/// Type-level equivalent of `Option<PadNum>`
83///
84/// See the [`OptionalKind`] documentation for more details on the pattern.
85///
86/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern
87pub trait OptionalPadNum: Sealed {}
88
89impl OptionalPadNum for NoneT {}
90
91impl<N: PadNum> OptionalPadNum for N {}
92
93//==============================================================================
94// IsPad
95//==============================================================================
96
97/// Type class for [`Pin`]s configured as SERCOM pads
98///
99/// This trait serves as both a [type class] for `Pin`s configured as SERCOM
100/// pads and as a [type-level function] mapping each `Pin` type to its
101/// corresponding [`Sercom`] and [`PadNum`].
102///
103/// [type class]: crate::typelevel#type-classes
104/// [type-level function]: crate::typelevel#type-level-functions
105pub trait IsPad: AnyPin {
106    type Sercom: Sercom;
107    type PadNum: PadNum;
108}
109
110//==============================================================================
111// IsI2cPad
112//==============================================================================
113
114/// Type class for [`Pin`]s which can be used as I2C pads
115///
116/// This trait serves as a [type class] for `Pin`s configured as I2C pads.
117///
118/// [type class]: crate::typelevel#type-classes
119pub trait IsI2cPad: IsPad {}
120
121//==============================================================================
122// OptionalPad
123//==============================================================================
124
125/// Type-level equivalent of `Option<Pad>`
126///
127/// See the [`OptionalKind`] documentation for more details on the pattern.
128///
129/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern
130pub trait OptionalPad: OptionalPin {
131    type PadNum: OptionalPadNum;
132}
133
134impl OptionalPad for NoneT {
135    type PadNum = NoneT;
136}
137
138impl<P: IsPad> OptionalPad for P {
139    type PadNum = P::PadNum;
140}
141
142/// Type-level equivalent of `Some(Pad)`
143///
144/// See the [`OptionalKind`] documentation for more details on the pattern.
145///
146/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern
147pub trait SomePad: IsPad {}
148
149impl<P: IsPad> SomePad for P {}
150
151//==============================================================================
152// GetPad
153//==============================================================================
154
155/// Type-level function mapping [`PinId`]s to SERCOM-pad-related types
156///
157/// For SAMD21 and SAMx5x chips, a [`Sercom`] and a [`PinId`] is enough
158/// information to uniquely identify a pad, so this trait returns the
159/// corresponding [`PadNum`] and [`PinMode`].
160///
161/// For SAMD11 chips, on the other hand, some `PinId`s can serve as two
162/// different `PadNum`s for the *same* `Sercom`. For these chips, `GetPad`
163/// requires a second type parameter to specify the `PadNum` and only returns
164/// the `PinMode`.
165///
166/// See the documentation on [type-level functions] for more details.
167///
168/// [type-level functions]: crate::typelevel#type-level-functions
169#[hal_cfg("sercom0-d11")]
170pub trait GetPad<S, N>
171where
172    S: Sercom,
173    N: PadNum,
174    Self: PinId,
175{
176    type PinMode: PinMode;
177}
178
179/// Type-level function mapping [`PinId`]s to SERCOM-pad-related types
180///
181/// For SAMD21 and SAMx5x chips, a [`Sercom`] and a [`PinId`] is enough
182/// information to uniquely identify a pad, so this trait returns the
183/// corresponding [`PadNum`] and [`PinMode`].
184///
185/// For SAMD11 chips, on the other hand, some `PinId`s can serve as two
186/// different `PadNum`s for the *same* `Sercom`. For these chips, `GetPad`
187/// requires a second type parameter to specify the `PadNum` and only returns
188/// the `PinMode`.
189///
190/// See the documentation on [type-level functions] for more details.
191///
192/// [type-level functions]: crate::typelevel#type-level-functions
193#[hal_cfg(any("sercom0-d21", "sercom0-d5x"))]
194pub trait GetPad<S>
195where
196    S: Sercom,
197    Self: PinId,
198{
199    type PadNum: PadNum;
200    type PinMode: PinMode;
201}
202
203//==============================================================================
204// GetPad aliases
205//==============================================================================
206
207/// Type alias using [`GetPad`] to recover the [`PinMode`] for a given SERCOM
208/// pad
209#[hal_cfg("sercom0-d11")]
210pub type PadMode<S, N, I> = <I as GetPad<S, N>>::PinMode;
211
212/// Type alias using [`GetPad`] to recover the [`PinMode`] for a given SERCOM
213/// pad
214#[hal_cfg(any("sercom0-d21", "sercom0-d5x"))]
215pub type PadMode<S, I> = <I as GetPad<S>>::PinMode;
216
217/// Type alias to recover a [`Pin`] configured as a SERCOM pad in the correct
218/// [`PadMode`]
219#[hal_cfg("sercom0-d11")]
220pub type Pad<S, N, I> = Pin<I, PadMode<S, N, I>>;
221
222/// Type alias to recover a [`Pin`] configured as a SERCOM pad in the correct
223/// [`PadMode`]
224#[hal_cfg(any("sercom0-d21", "sercom0-d5x"))]
225pub type Pad<S, I> = Pin<I, PadMode<S, I>>;
226
227//==============================================================================
228// GetOptionalPad
229//==============================================================================
230
231/// Type-level function mapping [`OptionalPinId`]s to their corresponding
232/// [`OptionalPad`]s
233///
234/// This trait acts as a [type-level function] mapping `OptionalPinId`s to their
235/// corresponding `OptionalPad`. In pseudo-Rust, it is the type-level equivalent
236/// of starting with `Option<PinId>` and calling `.map(GetPad)` to recover an
237/// `Option<Pad>`.
238///
239/// [type-level functions]: crate::typelevel#type-level-functions
240#[hal_cfg(any("sercom0-d21", "sercom0-d5x"))]
241pub trait GetOptionalPad<S: Sercom>: OptionalPinId {
242    type PadNum: OptionalPadNum;
243    type Pad: OptionalPad;
244}
245
246#[hal_cfg(any("sercom0-d21", "sercom0-d5x"))]
247impl<S: Sercom> GetOptionalPad<S> for NoneT {
248    type PadNum = NoneT;
249    type Pad = NoneT;
250}
251
252#[hal_cfg(any("sercom0-d21", "sercom0-d5x"))]
253impl<S, I> GetOptionalPad<S> for I
254where
255    S: Sercom,
256    I: PinId + GetPad<S>,
257    Pad<S, I>: IsPad,
258{
259    type PadNum = I::PadNum;
260    type Pad = Pad<S, I>;
261}
262
263//==============================================================================
264// IoSet
265//==============================================================================
266
267#[hal_cfg("sercom0-d5x")]
268mod ioset {
269
270    use super::*;
271
272    /// Type-level enum representing a SERCOM IOSET
273    ///
274    /// See the [type-level enum] documentation for more details on the pattern.
275    ///
276    /// [type-level enum]: crate::typelevel#type-level-enum
277    pub trait IoSet: Sealed {}
278
279    seq!(N in 1..=6 {
280        paste! {
281            #[doc = "Type-level variant of [`IoSet`] representing SERCOM IOSET " N]
282            ///
283            /// See the [type-level enum] documentation for more details on the
284            /// pattern.
285            ///
286            /// [type-level enum]: crate::typelevel#type-level-enum
287            pub enum IoSet~N {}
288            impl Sealed for IoSet~N {}
289            impl IoSet for IoSet~N {}
290        }
291    });
292
293    /// Type-level variant of [`IoSet`] representing an undocumented SERCOM
294    /// IOSET
295    ///
296    /// After implementing `IoSet` type checking, it became clear that some
297    /// existing boards were using a combinations of pins that did not match any
298    /// IOSET in the datasheet. From that, we infer that there must be at least
299    /// two undocumented IOSETs, and we added these new `IoSet`s to account for
300    /// it.
301    ///
302    /// As of writing this documentation, only two undocumented IOSETs have been
303    /// discovered:
304    /// - [`UndocIoSet1`]: PA16, PA17, PB22 & PB23 configured for `Sercom1`.
305    ///   Both the pygamer & feather_m4 uses this combination.
306    /// - [`UndocIoSet2`]: PA00, PA01, PB22 & PB23 configured for `Sercom1`. The
307    ///   itsybitsy_m4 uses this combination.
308    ///
309    /// See the [type-level enum] documentation for more details on type-level
310    /// variants.
311    ///
312    /// [type-level enum]: crate::typelevel#type-level-enum
313    pub enum UndocIoSet1 {}
314    impl Sealed for UndocIoSet1 {}
315    impl IoSet for UndocIoSet1 {}
316
317    /// Type-level variant of [`IoSet`] representing an undocumented SERCOM
318    /// IOSET
319    ///
320    /// After implementing `IoSet` type checking, it became clear that some
321    /// existing boards were using a combinations of pins that did not match any
322    /// IOSET in the datasheet. From that, we infer that there must be at least
323    /// two undocumented IOSETs, and we added these new `IoSet`s to account for
324    /// it.
325    ///
326    /// As of writing this documentation, only two undocumented IOSETs have been
327    /// discovered:
328    /// - [`UndocIoSet1`]: PA16, PA17, PB22 & PB23 configured for `Sercom1`.
329    ///   Both the pygamer & feather_m4 uses this combination.
330    /// - [`UndocIoSet2`]: PA00, PA01, PB22 & PB23 configured for `Sercom1`. The
331    ///   itsybitsy_m4 uses this combination.
332    ///
333    /// See the [type-level enum] documentation for more details on type-level
334    /// variants.
335    ///
336    /// [type-level enum]: crate::typelevel#type-level-enum
337    pub enum UndocIoSet2 {}
338    impl Sealed for UndocIoSet2 {}
339    impl IoSet for UndocIoSet2 {}
340
341    /// Type class for SERCOM pads in a given [`IoSet`]
342    ///
343    /// This trait is used to label each [`Pin`] implementing [`IsPad`] with its
344    /// corresponding [`IoSet`]\(s). Downstream types can use this trait as a
345    /// [type class] to restrict [`Pin`]s to a given [`IoSet`]. See the [type
346    /// class] documentation for more details on the pattern.
347    ///
348    /// [type class]: crate::typelevel#type-classes
349    pub trait InIoSet<I>
350    where
351        Self: IsPad,
352        I: IoSet,
353    {
354    }
355}
356
357#[hal_cfg("sercom0-d5x")]
358pub use ioset::*;
359
360#[cfg(doc)]
361#[hal_cfg(not("sercom0-d5x"))]
362/// This trait is not present with the selected feature set, defined for
363/// documentation only
364pub trait IoSet {}
365
366#[cfg(doc)]
367#[hal_cfg(not("sercom0-d5x"))]
368/// This trait is not present with the selected feature set, defined for
369/// documentation only
370pub trait InIoSet {}
371
372#[cfg(doc)]
373#[hal_cfg(not("sercom0-d5x"))]
374/// This type is not present with the selected feature set, defined for
375/// documentation only
376pub enum UndocIoSet1 {}
377
378#[cfg(doc)]
379#[hal_cfg(not("sercom0-d5x"))]
380/// This type is not present with the selected feature set, defined for
381/// documentation only
382pub enum UndocIoSet2 {}