atsamd_hal/sercom/spi/
pads_thumbv6m.rs

1//! Define a container for a set of SERCOM pads
2//!
3//! See the [spi module](super) documentation for more details on declaring and
4//! instantiating a [`Pads`] type.
5
6use atsamd_hal_macros::hal_cfg;
7
8use core::marker::PhantomData;
9
10#[hal_cfg("sercom0-d21")]
11use crate::gpio::AnyPin;
12use crate::sercom::*;
13use crate::typelevel::{NoneT, Sealed};
14
15use super::{Capability, Duplex, Rx, Tx};
16
17//=============================================================================
18// DipoDopo
19//=============================================================================
20
21/// Configure the `DIPO` and `DOPO` fields based on a set of [`Pads`]
22///
23/// According to the datasheet, the `DIPO` and `DOPO` values specify which
24/// SERCOM pads are used for various functions. Moreover, depending on which
25/// pads are actually in use, only certain combinations of these values make
26/// sense and are valid.
27///
28/// This trait is implemented for valid, four-tuple combinations of
29/// [`OptionalPadNum`]s. Those implementations are then lifted to the
30/// corresponding [`Pads`] types.
31///
32/// To satisfy this trait, the combination of `OptionalPadNum`s must specify a
33/// [`PadNum`] for `CK` and at least one of `DI` and `DO`. Furthermore, no two
34/// `PadNum`s can conflict.
35pub trait DipoDopo {
36    const DIPO_DOPO: (u8, u8);
37}
38
39/// Lift the implementations of [`DipoDopo`] from four-tuples of
40/// [`OptionalPadNum`]s to the corresponding [`Pads`] types.
41impl<S, DI, DO, CK, SS> DipoDopo for Pads<S, DI, DO, CK, SS>
42where
43    S: Sercom,
44    DI: OptionalPad,
45    DO: OptionalPad,
46    CK: OptionalPad,
47    SS: OptionalPad,
48    (DI::PadNum, DO::PadNum, CK::PadNum, SS::PadNum): DipoDopo,
49{
50    const DIPO_DOPO: (u8, u8) = <(DI::PadNum, DO::PadNum, CK::PadNum, SS::PadNum)>::DIPO_DOPO;
51}
52
53//=============================================================================
54// Implement DipoDopo
55//=============================================================================
56
57/// Filter [`PadNum`] permutations and implement [`DipoDopo`]
58#[rustfmt::skip]
59macro_rules! impl_dipodopo {
60    // This is the entry pattern. Start by checking CK and SS.
61    ($DI:ident, $DO:ident, $CK:ident, $SS:ident) => { impl_dipodopo!(@ck_ss, $DI, $DO, $CK, $SS); };
62
63    // Check whether CK and SS form a valid pair.
64    // CK must be present, while SS must be the correct pad or absent.
65    (@ck_ss, $DI:ident, $DO:ident, Pad1, NoneT) => { impl_dipodopo!(@dipo, $DI, $DO, Pad1, NoneT); };
66    (@ck_ss, $DI:ident, $DO:ident, Pad1, Pad2)  => { impl_dipodopo!(@dipo, $DI, $DO, Pad1, Pad2); };
67    (@ck_ss, $DI:ident, $DO:ident, Pad3, NoneT) => { impl_dipodopo!(@dipo, $DI, $DO, Pad3, NoneT); };
68    (@ck_ss, $DI:ident, $DO:ident, Pad3, Pad1)  => { impl_dipodopo!(@dipo, $DI, $DO, Pad3, Pad1); };
69
70    // If CK and SS are not valid, fall through to this pattern.
71    (@ck_ss, $DI:ident, $DO:ident, $CK:ident, $SS:ident) => { };
72
73    // Assign DIPO based on DI.
74    // Our options are exhaustive, so no fall through pattern is needed.
75    (@dipo, NoneT, $DO:ident, $CK:ident, $SS:ident) => { impl_dipodopo!(@dopo, NoneT, $DO, $CK, $SS, 0); };
76    (@dipo, Pad0,  $DO:ident, $CK:ident, $SS:ident) => { impl_dipodopo!(@dopo, Pad0,  $DO, $CK, $SS, 0); };
77    (@dipo, Pad1,  $DO:ident, $CK:ident, $SS:ident) => { impl_dipodopo!(@dopo, Pad1,  $DO, $CK, $SS, 1); };
78    (@dipo, Pad2,  $DO:ident, $CK:ident, $SS:ident) => { impl_dipodopo!(@dopo, Pad2,  $DO, $CK, $SS, 2); };
79    (@dipo, Pad3,  $DO:ident, $CK:ident, $SS:ident) => { impl_dipodopo!(@dopo, Pad3,  $DO, $CK, $SS, 3); };
80
81    // Assign DOPO based on DO and CK.
82    (@dopo, $DI:ident, NoneT, Pad1, $SS:ident, $DIPO:literal) => { impl_dipodopo!(@filter, $DI, NoneT, Pad1, $SS, $DIPO, 0); };
83    (@dopo, $DI:ident, NoneT, Pad3, $SS:ident, $DIPO:literal) => { impl_dipodopo!(@filter, $DI, NoneT, Pad3, $SS, $DIPO, 1); };
84    (@dopo, $DI:ident, Pad0,  Pad1, $SS:ident, $DIPO:literal) => { impl_dipodopo!(@filter, $DI, Pad0,  Pad1, $SS, $DIPO, 0); };
85    (@dopo, $DI:ident, Pad2,  Pad3, $SS:ident, $DIPO:literal) => { impl_dipodopo!(@filter, $DI, Pad2,  Pad3, $SS, $DIPO, 1); };
86    (@dopo, $DI:ident, Pad3,  Pad1, $SS:ident, $DIPO:literal) => { impl_dipodopo!(@filter, $DI, Pad3,  Pad1, $SS, $DIPO, 2); };
87    (@dopo, $DI:ident, Pad0,  Pad3, $SS:ident, $DIPO:literal) => { impl_dipodopo!(@filter, $DI, Pad0,  Pad3, $SS, $DIPO, 3); };
88
89    // If DO is not valid, fall through to this pattern.
90    (@dopo, $DI:ident, $DO:ident, $CK:ident, $SS:ident, $DIPO:literal) => { };
91
92    // Filter any remaining permutations that conflict.
93    (@filter, NoneT, NoneT, $CK:ident, $SS:ident, $DIPO:literal, $DOPO:literal) => { };
94    (@filter, Pad0, Pad0, $CK:ident, $SS:ident, $DIPO:literal, $DOPO:literal) => { };
95    (@filter, Pad1, $DO:ident, Pad1, $SS:ident, $DIPO:literal, $DOPO:literal) => { };
96    (@filter, Pad1, $DO:ident, $CK:ident, Pad1, $DIPO:literal, $DOPO:literal) => { };
97    (@filter, Pad2, Pad2, $CK:ident, $SS:ident, $DIPO:literal, $DOPO:literal) => { };
98    (@filter, Pad2, $DO:ident, $CK:ident, Pad2, $DIPO:literal, $DOPO:literal) => { };
99    (@filter, Pad3, Pad3, $CK:ident, $SS:ident, $DIPO:literal, $DOPO:literal) => { };
100    (@filter, Pad3, $DO:ident, Pad3, $SS:ident, $DIPO:literal, $DOPO:literal) => { };
101    (@filter, $DI:ident, Pad2, $CK:ident, Pad2, $DIPO:literal, $DOPO:literal) => { };
102
103    // If there are no conflicts, fall through to this pattern
104    (@filter, $DI:ident, $DO:ident, $CK:ident, $SS:ident, $DIPO:literal, $DOPO:literal) => { impl_dipodopo!(@revise, $DI, $DO, $CK, $SS, $DIPO, $DOPO); };
105
106    // Revise DIPO and DOPO conflicts created by NoneT
107    (@revise, NoneT, Pad0, Pad1, $SS:ident, $DIPO:literal, $DOPO:literal) => { impl_dipodopo!(@implement, NoneT, Pad0, Pad1, $SS, 3, $DOPO); };
108    (@revise, NoneT, Pad0, Pad3, $SS:ident, $DIPO:literal, $DOPO:literal) => { impl_dipodopo!(@implement, NoneT, Pad0, Pad3, $SS, 2, $DOPO); };
109    (@revise, Pad0, NoneT, Pad1, $SS:ident, $DIPO:literal, $DOPO:literal) => { impl_dipodopo!(@implement, Pad0, NoneT, Pad1, $SS, $DIPO, 2); };
110    (@revise, Pad2, NoneT, Pad3, $SS:ident, $DIPO:literal, $DOPO:literal) => { impl_dipodopo!(@implement, Pad2, NoneT, Pad3, $SS, $DIPO, 3); };
111
112    // If there are no revisions, fall through to this pattern
113    (@revise, $DI:ident, $DO:ident, $CK:ident, $SS:ident, $DIPO:literal, $DOPO:literal) => { impl_dipodopo!(@implement, $DI, $DO, $CK, $SS, $DIPO, $DOPO); };
114
115    // Implement DipoDopo
116    (@implement, $DI:ident, $DO:ident, $CK:ident, $SS:ident, $DIPO:literal, $DOPO:literal) => {
117        impl DipoDopo for ($DI, $DO, $CK, $SS) {
118            const DIPO_DOPO: (u8, u8) = ($DIPO, $DOPO);
119        }
120    };
121}
122
123/// Try to implement [`DipoDopo`] on all possible 4-tuple permutations of
124/// [`OptionalPadNum`]s.
125///
126/// The leading `()` token tree stores a growing permutation of [`PadNum`]s.
127/// When it reaches four [`PadNum`]s, try to implement [`DipoDopo`].
128///
129/// The next `[]` token tree is a list of possible [`PadNum`]s to append to the
130/// growing permutation. Loop through this list and append each option to the
131/// permutation.
132///
133/// The final, optional `[]` token tree exists to temporarily store the entire
134/// list before pushing it down for the next permutation element.
135macro_rules! padnum_permutations {
136    // If we have built up four [`PadNum`]s, try to implement [`DipoDopo`].
137    // Ignore the remaining list of [`PadNum`]s.
138    (
139        ( $DI:ident $DO:ident $CK:ident $SS:ident ) [ $( $Pads:ident )* ]
140    ) => {
141        impl_dipodopo!($DI, $DO, $CK, $SS);
142    };
143    // If we only have one list of [`PadNum`]s, duplicate it, to save it for the
144    // next permutation element.
145    (
146        ( $($Perm:ident)* ) [ $($Pads:ident)+ ]
147    ) => {
148        padnum_permutations!( ( $($Perm)* ) [ $($Pads)+ ] [ $($Pads)+ ] );
149    };
150    (
151        ( $($Perm:ident)* ) [ $Head:ident $($Tail:ident)* ] [ $($Pads:ident)+ ]
152    ) => {
153        // Append the first [`PadNum`] from the list, then push down to the next
154        // permutation element.
155        padnum_permutations!( ( $($Perm)* $Head ) [ $($Pads)+ ] );
156
157        // Loop through the remaining [`PadNum`]s to do the same thing for each.
158        padnum_permutations!( ( $($Perm)* ) [ $($Tail)* ] [ $($Pads)+ ] );
159    };
160    // Once the list of [`PadNum`]s is empty, we're done with this element.
161    ( ( $($Perm:ident)* ) [ ] [ $($Pads:ident)+ ] ) => { };
162}
163
164padnum_permutations!( () [NoneT Pad0 Pad1 Pad2 Pad3] );
165
166//=============================================================================
167// Pads
168//=============================================================================
169
170/// Container for a set of SERCOM pads
171///
172/// See the [module-level](super) documentation for more details on declaring
173/// and instantiating `Pads` types.
174pub struct Pads<S, DI = NoneT, DO = NoneT, CK = NoneT, SS = NoneT>
175where
176    S: Sercom,
177    DI: OptionalPad,
178    DO: OptionalPad,
179    CK: OptionalPad,
180    SS: OptionalPad,
181{
182    sercom: PhantomData<S>,
183    data_in: DI,
184    data_out: DO,
185    sclk: CK,
186    ss: SS,
187}
188
189impl<S: Sercom> Default for Pads<S> {
190    fn default() -> Self {
191        Self {
192            sercom: PhantomData,
193            data_in: NoneT,
194            data_out: NoneT,
195            sclk: NoneT,
196            ss: NoneT,
197        }
198    }
199}
200
201impl<S, DI, DO, CK, SS> Pads<S, DI, DO, CK, SS>
202where
203    S: Sercom,
204    DI: OptionalPad,
205    DO: OptionalPad,
206    CK: OptionalPad,
207    SS: OptionalPad,
208{
209    /// Consume the [`Pads`] and return each individual
210    /// [`Pin`](crate::gpio::Pin)
211    #[inline]
212    pub fn free(self) -> (DI, DO, CK, SS) {
213        (self.data_in, self.data_out, self.sclk, self.ss)
214    }
215}
216
217#[hal_cfg("sercom0-d11")]
218impl<S, DI, DO, CK, SS> Pads<S, DI, DO, CK, SS>
219where
220    S: Sercom,
221    DI: OptionalPad,
222    DO: OptionalPad,
223    CK: OptionalPad,
224    SS: OptionalPad,
225{
226    /// Set the `DI` pad
227    ///
228    /// In a [`MasterMode`], this is MISO. In [`Slave`] [`OpMode`], this is
229    /// MOSI.
230    ///
231    /// [`MasterMode`]: super::MasterMode
232    /// [`Slave`]: super::Slave
233    /// [`OpMode`]: super::OpMode
234    #[inline]
235    pub fn data_in<P: IsPad>(self, pin: P) -> Pads<S, P, DO, CK, SS> {
236        Pads {
237            sercom: self.sercom,
238            data_in: pin,
239            data_out: self.data_out,
240            sclk: self.sclk,
241            ss: self.ss,
242        }
243    }
244
245    /// Set the `DO` pad
246    ///
247    /// In a [`MasterMode`], this is MOSI. In [`Slave`] [`OpMode`], this is
248    /// MISO.
249    ///
250    /// [`MasterMode`]: super::MasterMode
251    /// [`Slave`]: super::Slave
252    /// [`OpMode`]: super::OpMode
253    #[inline]
254    pub fn data_out<P: IsPad>(self, pin: P) -> Pads<S, DI, P, CK, SS> {
255        Pads {
256            sercom: self.sercom,
257            data_in: self.data_in,
258            data_out: pin,
259            sclk: self.sclk,
260            ss: self.ss,
261        }
262    }
263
264    /// Set the `SCK` pad
265    #[inline]
266    pub fn sclk<P: IsPad>(self, pin: P) -> Pads<S, DI, DO, P, SS> {
267        Pads {
268            sercom: self.sercom,
269            data_in: self.data_in,
270            data_out: self.data_out,
271            sclk: pin,
272            ss: self.ss,
273        }
274    }
275
276    /// Set the `SS` pad
277    #[inline]
278    pub fn ss<P: IsPad>(self, pin: P) -> Pads<S, DI, DO, CK, P> {
279        Pads {
280            sercom: self.sercom,
281            data_in: self.data_in,
282            data_out: self.data_out,
283            sclk: self.sclk,
284            ss: pin,
285        }
286    }
287}
288
289#[hal_cfg("sercom0-d21")]
290impl<S, DI, DO, CK, SS> Pads<S, DI, DO, CK, SS>
291where
292    S: Sercom,
293    DI: OptionalPad,
294    DO: OptionalPad,
295    CK: OptionalPad,
296    SS: OptionalPad,
297{
298    /// Set the `DI` pad
299    ///
300    /// In a [`MasterMode`], this is MISO. In [`Slave`] [`OpMode`], this is
301    /// MOSI.
302    ///
303    /// [`MasterMode`]: super::MasterMode
304    /// [`Slave`]: super::Slave
305    /// [`OpMode`]: super::OpMode
306    #[inline]
307    pub fn data_in<I>(self, pin: impl AnyPin<Id = I>) -> Pads<S, Pad<S, I>, DO, CK, SS>
308    where
309        I: GetPad<S>,
310        Pad<S, I>: IsPad,
311    {
312        Pads {
313            sercom: self.sercom,
314            data_in: pin.into().into_mode(),
315            data_out: self.data_out,
316            sclk: self.sclk,
317            ss: self.ss,
318        }
319    }
320
321    /// Set the `DO` pad
322    ///
323    /// In a [`MasterMode`], this is MOSI. In [`Slave`] [`OpMode`], this is
324    /// MISO.
325    ///
326    /// [`MasterMode`]: super::MasterMode
327    /// [`Slave`]: super::Slave
328    /// [`OpMode`]: super::OpMode
329    #[inline]
330    pub fn data_out<I>(self, pin: impl AnyPin<Id = I>) -> Pads<S, DI, Pad<S, I>, CK, SS>
331    where
332        I: GetPad<S>,
333        Pad<S, I>: IsPad,
334    {
335        Pads {
336            sercom: self.sercom,
337            data_in: self.data_in,
338            data_out: pin.into().into_mode(),
339            sclk: self.sclk,
340            ss: self.ss,
341        }
342    }
343
344    /// Set the `SCK` pad
345    #[inline]
346    pub fn sclk<I>(self, pin: impl AnyPin<Id = I>) -> Pads<S, DI, DO, Pad<S, I>, SS>
347    where
348        I: GetPad<S>,
349        Pad<S, I>: IsPad,
350    {
351        Pads {
352            sercom: self.sercom,
353            data_in: self.data_in,
354            data_out: self.data_out,
355            sclk: pin.into().into_mode(),
356            ss: self.ss,
357        }
358    }
359
360    /// Set the `SS` pad
361    #[inline]
362    pub fn ss<I>(self, pin: impl AnyPin<Id = I>) -> Pads<S, DI, DO, CK, Pad<S, I>>
363    where
364        I: GetPad<S>,
365        Pad<S, I>: IsPad,
366    {
367        Pads {
368            sercom: self.sercom,
369            data_in: self.data_in,
370            data_out: self.data_out,
371            sclk: self.sclk,
372            ss: pin.into().into_mode(),
373        }
374    }
375}
376
377//=============================================================================
378// PadsFromIds
379//=============================================================================
380
381/// Define a set of [`Pads`] using [`PinId`]s instead of [`Pin`]s
382///
383/// In some cases, it is more convenient to specify a set of `Pads` using
384/// `PinId`s rather than `Pin`s. This alias makes it easier to do so.
385///
386/// The first type parameter is the [`Sercom`], while the remaining four are
387/// effectively [`OptionalPinId`]s representing the corresponding type
388/// parameters of [`Pads`], i.e. `DI`, `DO`, `CK` & `SS`. Each of the
389/// remaining type parameters defaults to [`NoneT`].
390///
391/// ```
392/// use atsamd_hal::pac::Peripherals;
393/// use atsamd_hal::gpio::{PA08, PA09, Pins};
394/// use atsamd_hal::sercom::{Sercom0, spi};
395/// use atsamd_hal::typelevel::NoneT;
396///
397/// pub type Pads = spi::PadsFromIds<Sercom0, PA08, NoneT, PA09>;
398///
399/// pub fn create_pads() -> Pads {
400///     let peripherals = Peripherals::take().unwrap();
401///     let pins = Pins::new(peripherals.PORT);
402///     spi::Pads::default().sclk(pins.pa09).data_in(pins.pa08)
403/// }
404/// ```
405///
406/// [`Pin`]: crate::gpio::Pin
407/// [`PinId`]: crate::gpio::PinId
408/// [`OptionalPinId`]: crate::gpio::OptionalPinId
409#[hal_cfg("sercom0-d21")]
410pub type PadsFromIds<S, DI = NoneT, DO = NoneT, CK = NoneT, SS = NoneT> = Pads<
411    S,
412    <DI as GetOptionalPad<S>>::Pad,
413    <DO as GetOptionalPad<S>>::Pad,
414    <CK as GetOptionalPad<S>>::Pad,
415    <SS as GetOptionalPad<S>>::Pad,
416>;
417
418//=============================================================================
419// PadSet
420//=============================================================================
421
422/// Type-level function to recover the [`OptionalPad`] types from a generic set
423/// of [`Pads`]
424///
425/// This trait is used as an interface between the [`Pads`] type and other
426/// types in this module. It acts as a [type-level function], returning the
427/// corresponding [`Sercom`] and [`OptionalPad`] types. It serves to cut down on
428/// the total number of type parameters needed in the [`Config`] struct. The
429/// `Config` struct doesn't need access to the [`Pin`]s directly.  Rather, it
430/// only needs to apply the [`SomePad`] trait bound when a `Pin` is required.
431/// The `PadSet` trait allows each `Config` struct to store an instance of
432/// `Pads` without itself being generic over all six type parameters of the
433/// `Pads` type.
434///
435/// This trait is a simplified version of the [`AnyKind`] trait pattern.
436///
437/// [`Pin`]: crate::gpio::Pin
438/// [`Config`]: super::Config
439/// [type-level function]: crate::typelevel#type-level-functions
440/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
441pub trait PadSet: Sealed {
442    type Sercom: Sercom;
443    type DataIn: OptionalPad;
444    type DataOut: OptionalPad;
445    type Sclk: OptionalPad;
446    type SS: OptionalPad;
447}
448
449impl<S, DI, DO, CK, SS> Sealed for Pads<S, DI, DO, CK, SS>
450where
451    S: Sercom,
452    DI: OptionalPad,
453    DO: OptionalPad,
454    CK: OptionalPad,
455    SS: OptionalPad,
456{
457}
458
459impl<S, DI, DO, CK, SS> PadSet for Pads<S, DI, DO, CK, SS>
460where
461    S: Sercom,
462    DI: OptionalPad,
463    DO: OptionalPad,
464    CK: OptionalPad,
465    SS: OptionalPad,
466{
467    type Sercom = S;
468    type DataIn = DI;
469    type DataOut = DO;
470    type Sclk = CK;
471    type SS = SS;
472}
473
474//=============================================================================
475// ValidPads
476//=============================================================================
477
478/// Marker trait for valid sets of [`Pads`]
479///
480/// This trait labels sets of `Pads` that:
481/// - Specify [`SomePad`] for `CK` and at least one of `DI` or `DO`
482/// - Use a valid combination of [`PadNum`]s, so that the `Pads` implement
483///   [`DipoDopo`]
484pub trait ValidPads: PadSet + DipoDopo {
485    type Capability: Capability;
486}
487
488impl<S, DI, CK, SS> ValidPads for Pads<S, DI, NoneT, CK, SS>
489where
490    S: Sercom,
491    DI: SomePad,
492    CK: SomePad,
493    SS: OptionalPad,
494    Pads<S, DI, NoneT, CK, SS>: DipoDopo,
495{
496    type Capability = Rx;
497}
498
499impl<S, DO, CK, SS> ValidPads for Pads<S, NoneT, DO, CK, SS>
500where
501    S: Sercom,
502    DO: SomePad,
503    CK: SomePad,
504    SS: OptionalPad,
505    Pads<S, NoneT, DO, CK, SS>: DipoDopo,
506{
507    type Capability = Tx;
508}
509
510impl<S, DI, DO, CK, SS> ValidPads for Pads<S, DI, DO, CK, SS>
511where
512    S: Sercom,
513    DI: SomePad,
514    DO: SomePad,
515    CK: SomePad,
516    SS: OptionalPad,
517    Pads<S, DI, DO, CK, SS>: DipoDopo,
518{
519    type Capability = Duplex;
520}