atsamd_hal/
bsp_peripherals_macro.rs

1//==============================================================================
2//  bsp_peripherals
3//==============================================================================
4
5/// # Helper macro to give meaningful names to peripherals
6///
7/// The [`pac::Peripherals`](crate::pac::Peripherals) struct refers to each
8/// peripheral by its datasheet name. However, in the context of a BSP,
9/// peripherals can often be given more meaningful names. This macro gives BSP
10/// authors a convenient way to provide custom names for each peripheral.
11///
12/// ## Calling the macro
13///
14/// The `bsp_peripherals!` macro takes a series of `PERIPHERAL` blocks. Each
15/// block starts with a peripheral name from the `pac::Peripherals` struct and
16/// is delimited by curly brackets. The brackets should contain a
17/// comma-separated list of alternate names for the peripheral, in `PascalCase`.
18/// The example below defines `Uart` as an alias for the `SERCOM2` peripheral
19/// and `DisplaySpi` as an alias for the `SERCOM4` peripheral.
20///
21/// ```
22/// atsamd_hal::bsp_peripherals!(
23///     SERCOM2 { Uart }
24///     SERCOM4 { DisplaySpi }
25/// );
26/// ```
27///
28/// The macro defines a type alias for each name within curly brackets. The
29/// example above would exand to
30///
31/// ```
32/// pub type Uart = pac::SERCOM2;
33/// pub type DisplaySpi = pac::SERCOM4;
34/// ```
35///
36/// While these type aliases are useful, they do not completely separate the
37/// mapping of each peripheral from the corresponding code.
38///
39/// In Rust, each struct field can only have one name. Fields of the
40/// `pac::Peripherals` struct are named according to their datasheet identifier.
41/// Consequently, you must access the peripheral using that name. For example,
42///
43/// ```
44/// let mut peripherals = pac::Peripherals::take().unwrap();
45/// let uart = peripherals.SERCOM2;
46/// ```
47///
48/// To provide access to the same struct field using *different* names, the
49/// `bsp_peripherals!` macro defines another macro, `periph_alias!`. Based on
50/// the example above, we could use the `periph_alias!` macro to access the
51/// `SERCOM2` peripheral without ever referring to it directly.
52///
53/// ```
54/// let mut peripherals = pac::Peripherals::take().unwrap();
55/// let uart = periph_alias!(peripherals.uart);
56/// ```
57///
58/// Note that the `Uart` alias was translated to `snake_case` when accessing
59/// the `pac::Peripherals` field. The same is true for the `DisplaySpi` alias.
60///
61/// ```
62/// let mut peripherals = pac::Peripherals::take().unwrap();
63/// let display_spi = periph_alias!(peripherals.display_spi);
64/// ```
65///
66/// ## Attributes and documentation
67///
68/// BSP authors can also add attributes to various parts of the macro
69/// declaration. Attributes can be added to the entire `PERIPHERAL` block. These
70/// attributes will be propagated to every use of the corresponding
71/// `PERIPHERAL`. Attributes applied to each alias, on the other hand, will only
72/// be propagated to items specific to that alias, like the corresponding type
73/// alias.
74///
75/// ```
76/// atsamd_hal::bsp_peripherals!(
77///     SERCOM2 {
78///         #[cfg(feature = "uart")]
79///         Uart
80///     }
81///     #[cfg(feature = "display")]
82///     SERCOM4 { DisplaySpi }
83/// );
84/// ```
85#[macro_export]
86macro_rules! bsp_peripherals {
87    (
88        $(
89            $( #[$peripheral_cfg:meta] )*
90            $PERIPHERAL:ident {
91                $(
92                    $( #[$alias_cfg:meta] )*
93                    $Alias:ident $(,)?
94                )+
95            } $(,)?
96        )+
97    ) => {
98        $(
99            $( #[$peripheral_cfg] )*
100            $crate::__create_peripheral_aliases!(
101                $PERIPHERAL
102                $(
103                    $( #[$alias_cfg] )*
104                    $Alias
105                )+
106            );
107        )+
108
109        $crate::__define_periph_alias_macro!(
110            $(
111                $(
112                    $( #[$alias_cfg] )*
113                    ($PERIPHERAL, $Alias)
114                )+
115            )+
116        );
117    };
118}
119
120#[macro_export]
121#[doc(hidden)]
122macro_rules! __create_peripheral_aliases {
123    (
124        $PERIPHERAL:ident
125        $(
126            $( #[$attr:meta] )*
127            $Alias:ident
128        )+
129    ) => {
130        $crate::paste::paste! {
131            $(
132                $( #[$attr] )*
133                #[
134                    doc = "Alias for the "
135                    "[`" $PERIPHERAL "`](atsamd_hal::pac::" $PERIPHERAL ") "
136                    "peripheral"
137                ]
138                pub type $Alias = atsamd_hal::pac::$PERIPHERAL;
139            )+
140        }
141    };
142}
143
144#[macro_export]
145#[doc(hidden)]
146macro_rules! __define_periph_alias_macro {
147    (
148        $(
149            $( #[$attr:meta] )*
150            ($PERIPHERAL:ident, $Alias:ident)
151        )+
152    ) => {
153        $crate::paste::paste! {
154            /// Refer to fields of the [`Peripherals`](atsamd_hal::pac::Peripherals)
155            /// struct by alternate names
156            ///
157            /// This macro can be used to access fields of the `Peripherals`
158            /// struct by alternate names. The available aliases are:
159            ///
160            #[ doc =
161                $(
162                    "    - [`" $PERIPHERAL "`](atsamd_hal::pac::" $PERIPHERAL ") \
163                    can be refered to with the type alias [`" $Alias "`] and \
164                    accessed as the field name `" $Alias:snake "`\n"
165                )+
166            ]
167            ///
168            /// For example. suppose `display_spi` were an alternate name for
169            /// the `SERCOM4` peripheral. You could use the `periph_alias!`
170            /// macro to access it like this:
171            ///
172            /// ```
173            /// let mut peripherals = pac::Peripherals::take().unwrap();
174            /// // Replace this
175            /// let display_spi = peripherals.SERCOM4;
176            /// // With this
177            /// let display_spi = periph_alias!(peripherals.display_spi);
178            /// ```
179            #[macro_export]
180            macro_rules! periph_alias {
181                $(
182                    ( $peripherals:ident . [<$Alias:snake>] ) => {
183                        {
184                            $( #[$attr] )*
185                            macro_rules! [<peripheral_alias_ $Alias:snake>] {
186                                () => { $peripherals.[< $PERIPHERAL:snake:lower >] };
187                            }
188                            [<peripheral_alias_ $Alias:snake>]!()
189                        }
190                    };
191                )+
192            }
193        }
194    }
195}