1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
//==============================================================================
//  bsp_peripherals
//==============================================================================

/// # Helper macro to give meaningful names to peripherals
///
/// The [`pac::Peripherals`](crate::pac::Peripherals) struct refers to each
/// peripheral by its datasheet name. However, in the context of a BSP,
/// peripherals can often be given more meaningful names. This macro gives BSP
/// authors a convenient way to provide custom names for each peripheral.
///
/// ## Calling the macro
///
/// The `bsp_peripherals!` macro takes a series of `PERIPHERAL` blocks. Each
/// block starts with a peripheral name from the `pac::Peripherals` struct and
/// is delimited by curly brackets. The brackets should contain a
/// comma-separated list of alternate names for the peripheral, in `PascalCase`.
/// The example below defines `Uart` as an alias for the `SERCOM2` peripheral
/// and `DisplaySpi` as an alias for the `SERCOM4` peripheral.
///
/// ```
/// atsamd_hal::bsp_peripherals!(
///     SERCOM2 { Uart }
///     SERCOM4 { DisplaySpi }
/// );
/// ```
///
/// The macro defines a type alias for each name within curly brackets. The
/// example above would exand to
///
/// ```
/// pub type Uart = pac::SERCOM2;
/// pub type DisplaySpi = pac::SERCOM4;
/// ```
///
/// While these type aliases are useful, they do not completely separate the
/// mapping of each peripheral from the corresponding code.
///
/// In Rust, each struct field can only have one name. Fields of the
/// `pac::Peripherals` struct are named according to their datasheet identifier.
/// Consequently, you must access the peripheral using that name. For example,
///
/// ```
/// let mut peripherals = pac::Peripherals::take().unwrap();
/// let uart = peripherals.SERCOM2;
/// ```
///
/// To provide access to the same struct field using *different* names, the
/// `bsp_peripherals!` macro defines another macro, `periph_alias!`. Based on
/// the example above, we could use the `periph_alias!` macro to access the
/// `SERCOM2` peripheral without ever referring to it directly.
///
/// ```
/// let mut peripherals = pac::Peripherals::take().unwrap();
/// let uart = periph_alias!(peripherals.uart);
/// ```
///
/// Note that the `Uart` alias was translated to `snake_case` when accessing
/// the `pac::Peripherals` field. The same is true for the `DisplaySpi` alias.
///
/// ```
/// let mut peripherals = pac::Peripherals::take().unwrap();
/// let display_spi = periph_alias!(peripherals.display_spi);
/// ```
///
/// ## Attributes and documentation
///
/// BSP authors can also add attributes to various parts of the macro
/// declaration. Attributes can be added to the entire `PERIPHERAL` block. These
/// attributes will be propagated to every use of the corresponding
/// `PERIPHERAL`. Attributes applied to each alias, on the other hand, will only
/// be propagated to items specific to that alias, like the corresponding type
/// alias.
///
/// ```
/// atsamd_hal::bsp_peripherals!(
///     SERCOM2 {
///         #[cfg(feature = "uart")]
///         Uart
///     }
///     #[cfg(feature = "display")]
///     SERCOM4 { DisplaySpi }
/// );
/// ```
#[macro_export]
macro_rules! bsp_peripherals {
    (
        $(
            $( #[$peripheral_cfg:meta] )*
            $PERIPHERAL:ident {
                $(
                    $( #[$alias_cfg:meta] )*
                    $Alias:ident $(,)?
                )+
            } $(,)?
        )+
    ) => {
        $(
            $( #[$peripheral_cfg] )*
            $crate::__create_peripheral_aliases!(
                $PERIPHERAL
                $(
                    $( #[$alias_cfg] )*
                    $Alias
                )+
            );
        )+

        $crate::__define_periph_alias_macro!(
            $(
                $(
                    $( #[$alias_cfg] )*
                    ($PERIPHERAL, $Alias)
                )+
            )+
        );
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! __create_peripheral_aliases {
    (
        $PERIPHERAL:ident
        $(
            $( #[$attr:meta] )*
            $Alias:ident
        )+
    ) => {
        $crate::paste::paste! {
            $(
                $( #[$attr] )*
                #[
                    doc = "Alias for the "
                    "[`" $PERIPHERAL "`](atsamd_hal::pac::" $PERIPHERAL ") "
                    "peripheral"
                ]
                pub type $Alias = atsamd_hal::pac::$PERIPHERAL;
            )+
        }
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! __define_periph_alias_macro {
    (
        $(
            $( #[$attr:meta] )*
            ($PERIPHERAL:ident, $Alias:ident)
        )+
    ) => {
        $crate::paste::paste! {
            /// Refer to fields of the [`Peripherals`](atsamd_hal::pac::Peripherals)
            /// struct by alternate names
            ///
            /// This macro can be used to access fields of the `Peripherals`
            /// struct by alternate names. The available aliases are:
            ///
            #[ doc =
                $(
                    "    - [`" $PERIPHERAL "`](atsamd_hal::pac::" $PERIPHERAL ") \
                    can be refered to with the type alias [`" $Alias "`] and \
                    accessed as the field name `" $Alias:snake "`\n"
                )+
            ]
            ///
            /// For example. suppose `display_spi` were an alternate name for
            /// the `SERCOM4` peripheral. You could use the `periph_alias!`
            /// macro to access it like this:
            ///
            /// ```
            /// let mut peripherals = pac::Peripherals::take().unwrap();
            /// // Replace this
            /// let display_spi = peripherals.SERCOM4;
            /// // With this
            /// let display_spi = periph_alias!(peripherals.display_spi);
            /// ```
            #[macro_export]
            macro_rules! periph_alias {
                $(
                    ( $peripherals:ident . [<$Alias:snake>] ) => {
                        {
                            $( #[$attr] )*
                            macro_rules! [<peripheral_alias_ $Alias:snake>] {
                                () => { $peripherals.$PERIPHERAL };
                            }
                            [<peripheral_alias_ $Alias:snake>]!()
                        }
                    };
                )+
            }
        }
    }
}