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}