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 {}