Skip to main content

esp_hal/
macros.rs

1//! Macros used by the HAL.
2//!
3//! Most of the macros in this module are hidden and intended for internal use
4//! only. For the list of public macros, see the [procmacros](https://docs.rs/esp-hal-procmacros/latest/esp_hal_procmacros/)
5//! documentation.
6
7#[doc(hidden)]
8/// Helper macro for checking doctest code snippets
9#[macro_export]
10macro_rules! before_snippet {
11    () => {
12        r#"
13# #![no_std]
14# use esp_hal::{interrupt::{self, InterruptConfigurable}, time::{Duration, Instant, Rate}};
15# macro_rules! println {
16#     ($($tt:tt)*) => { };
17# }
18# macro_rules! print {
19#     ($($tt:tt)*) => { };
20# }
21# #[panic_handler]
22# fn panic(_ : &core::panic::PanicInfo) -> ! {
23#     loop {}
24# }
25# fn main() {
26#   let _ = example();
27# }
28# struct ExampleError {}
29# impl <T> From<T> for ExampleError where T: core::fmt::Debug {
30#   fn from(_value: T) -> Self {
31#       Self{}
32#   }
33# }
34# async fn example() -> Result<(), ExampleError> {
35#   let mut peripherals = esp_hal::init(esp_hal::Config::default());
36"#
37    };
38}
39
40#[doc(hidden)]
41#[macro_export]
42macro_rules! after_snippet {
43    () => {
44        r#"
45# Ok(())
46# }
47"#
48    };
49}
50
51#[doc(hidden)]
52#[macro_export]
53macro_rules! trm_markdown_link {
54    () => {
55        concat!("[Technical Reference Manual](", property!("trm"), ")")
56    };
57    ($anchor:literal) => {
58        concat!(
59            "[Technical Reference Manual](",
60            property!("trm"),
61            "#",
62            $anchor,
63            ")"
64        )
65    };
66}
67
68#[doc(hidden)]
69/// Shorthand to define AnyPeripheral instances.
70///
71/// This macro generates the following:
72///
73/// - An `AnyPeripheral` struct, name provided by the macro call.
74/// - An `any::Degrade` trait which is supposed to be used as a supertrait of a relevant Instance.
75/// - An `any::Inner` enum, with the same variants as the original peripheral.
76/// - A `From` implementation for each peripheral variant.
77/// - A `degrade` method for each peripheral variant using the `any::Degrade` trait.
78#[macro_export]
79macro_rules! any_peripheral {
80    ($(#[$meta:meta])* $vis:vis peripheral $name:ident<'d> {
81        $(
82            $(#[cfg($variant_meta:meta)])*
83            $variant:ident($inner:ty)
84        ),* $(,)?
85    }) => {
86        #[doc = concat!("Utilities related to [`", stringify!($name), "`]")]
87        #[doc(hidden)]
88        #[instability::unstable]
89        pub mod any {
90            #[allow(unused_imports)]
91            use super::*;
92
93            macro_rules! delegate {
94                ($any:ident, $inner_ident:ident => $code:tt) => {
95                    match &$any.0 {
96                        $(
97                            $(#[cfg($variant_meta)])*
98                            any::Inner::$variant($inner_ident) => $code,
99                        )*
100                    }
101                }
102            }
103
104            pub(crate) use delegate;
105
106            $(#[$meta])*
107            #[derive(Debug)]
108            pub(crate) enum Inner<'d> {
109                $(
110                    $(#[cfg($variant_meta)])*
111                    $variant($inner),
112                )*
113            }
114
115            #[cfg(feature = "defmt")]
116            impl defmt::Format for Inner<'_> {
117                fn format(&self, fmt: defmt::Formatter<'_>) {
118                    match self {
119                        $(
120                            $(#[cfg($variant_meta)])*
121                            Self::$variant(inner) => inner.format(fmt),
122                        )*
123                    }
124                }
125            }
126
127            // Trick to make peripherals implement something Into-like, without
128            // requiring Instance traits to have lifetimes. Rustdoc will list
129            // this trait as a supertrait, but will not give its definition.
130            // Users are encouraged to use From to convert a singleton into its
131            // relevant AnyPeripheral counterpart.
132            #[allow(unused)]
133            pub trait Degrade: Sized + $crate::private::Sealed {
134                fn degrade<'a>(self) -> super::$name<'a>
135                where
136                    Self: 'a;
137            }
138        }
139
140        $(#[$meta])*
141        ///
142        /// This struct is a type-erased version of a peripheral singleton. It is useful
143        /// for creating arrays of peripherals, or avoiding generics. Peripheral singletons
144        /// can be type erased by using their `From` implementation.
145        ///
146        /// ```rust,ignore
147        #[doc = concat!("let any_peripheral = ", stringify!($name), "::from(peripheral);")]
148        /// ```
149        #[derive(Debug)]
150        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
151        $vis struct $name<'d>(any::Inner<'d>);
152
153        impl $name<'_> {
154            /// Unsafely clone this peripheral reference.
155            ///
156            /// # Safety
157            ///
158            /// You must ensure that you're only using one instance of this type at a time.
159            #[inline]
160            #[allow(unused)]
161            pub unsafe fn clone_unchecked(&self) -> Self { unsafe {
162                any::delegate!(self, inner => { Self::from(inner.clone_unchecked()) })
163            }}
164
165            /// Creates a new peripheral reference with a shorter lifetime.
166            ///
167            /// Use this method if you would like to keep working with the peripheral after
168            /// you dropped the driver that consumes this.
169            ///
170            /// See [Peripheral singleton] section for more information.
171            ///
172            /// [Peripheral singleton]: crate#peripheral-singletons
173            #[inline]
174            #[allow(unused)]
175            pub fn reborrow(&mut self) -> $name<'_> {
176                unsafe { self.clone_unchecked() }
177            }
178
179            #[procmacros::doc_replace]
180            /// Attempts to downcast the pin into the underlying peripheral instance.
181            #[cfg_attr(
182                // Feature-gated so that this doesn't prevent gradual device bringup. Any
183                // stable driver would serve the purpose here, so this block will be part
184                // of the released documentation.
185                uart_driver_supported,
186                doc = r#"
187## Example
188
189```rust,no_run
190# {before_snippet}
191#
192# use esp_hal::{
193#     uart::AnyUart as AnyPeripheral,
194#     peripherals::{UART0 as PERI0, UART1 as PERI1},
195# };
196#
197# let peri0 = peripherals.UART0;
198# let peri1 = peripherals.UART1;
199// let peri0 = peripherals.PERI0;
200// let peri1 = peripherals.PERI1;
201let any_peri0 = AnyPeripheral::from(peri0);
202let any_peri1 = AnyPeripheral::from(peri1);
203
204let uart0 = any_peri0
205    .downcast::<PERI0>()
206    .expect("This downcast succeeds because AnyPeripheral was created from Peri0");
207let uart0 = any_peri1
208    .downcast::<PERI0>()
209    .expect_err("This AnyPeripheral was created from Peri1, it cannot be downcast to Peri0");
210#
211# {after_snippet}
212```
213"#
214            )]
215            #[inline]
216            #[allow(unused)]
217            pub fn downcast<P>(self) -> Result<P, Self>
218            where
219                Self: TryInto<P, Error = Self>
220            {
221                self.try_into()
222            }
223        }
224
225        impl $crate::private::Sealed for $name<'_> {}
226
227        // AnyPeripheral converts into itself
228        impl<'d> any::Degrade for $name<'d> {
229            #[inline]
230            fn degrade<'a>(self) -> $name<'a>
231            where
232                Self: 'a,
233            {
234                self
235            }
236        }
237
238        $(
239            // Variants convert into AnyPeripheral
240            $(#[cfg($variant_meta)])*
241            impl<'d> any::Degrade for $inner {
242                #[inline]
243                fn degrade<'a>(self) -> $name<'a>
244                where
245                    Self: 'a,
246                {
247                    $name::from(self)
248                }
249            }
250
251            $(#[cfg($variant_meta)])*
252            impl<'d> From<$inner> for $name<'d> {
253                #[inline]
254                fn from(inner: $inner) -> Self {
255                    Self(any::Inner::$variant(inner))
256                }
257            }
258
259            $(#[cfg($variant_meta)])*
260            impl<'d> TryFrom<$name<'d>> for $inner {
261                type Error = $name<'d>;
262
263                #[inline]
264                fn try_from(any: $name<'d>) -> Result<Self, $name<'d>> {
265                    #[allow(irrefutable_let_patterns)]
266                    if let $name(any::Inner::$variant(inner)) = any {
267                        Ok(inner)
268                    } else {
269                        Err(any)
270                    }
271                }
272            }
273        )*
274    };
275}
276
277/// Macro to choose between two expressions. Useful for implementing "else" for
278/// `$()?` macro syntax.
279#[macro_export]
280#[doc(hidden)]
281macro_rules! if_set {
282    (, $not_set:expr) => {
283        $not_set
284    };
285    ($set:expr, $not_set:expr) => {
286        $set
287    };
288}
289
290#[cfg(feature = "unstable")]
291include!(concat!(env!("OUT_DIR"), "/version_macro.rs"));
292
293#[doc_replace]
294/// Selects code based on the `esp-hal` version.
295///
296/// Branches are considered from top to bottom. The first branch whose version
297/// is less than or equal to the current `esp-hal` version is
298/// expanded. The fallback branch is expanded if no version branch matches.
299///
300/// Version branches should be listed in descending order.
301///
302/// # Example
303///
304/// ```rust,no_run
305/// # {before_snippet}
306/// use esp_hal::at_least_version;
307///
308/// let description = at_least_version! {
309///     (2, 0, 0) => { "esp-hal 2.0.0 or newer" }
310///     (1, 1, 0) => { "esp-hal 1.1.0 or newer" }
311///     _ => { "an older esp-hal version" }
312/// };
313/// # {after_snippet}
314/// ```
315#[macro_export]
316#[cfg(feature = "unstable")]
317#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
318macro_rules! at_least_version {
319    ($($branch:tt)*) => {
320        $crate::__esp_hal_at_least_version! { $($branch)* }
321    };
322}
323
324/// Macro to ignore tokens.
325///
326/// This is useful when we need existence of a metavariable (to expand a
327/// repetition), but we don't need to use it.
328#[macro_export]
329#[doc(hidden)]
330macro_rules! ignore {
331    ($($item:tt)*) => {};
332}
333
334/// Define a piece of (Espressif-specific) metadata that external tools may
335/// parse.
336///
337/// The symbol name be formatted as `_ESP_METADATA_<category>_<name>`.
338///
339/// This metadata is zero cost, i.e. the value will not be flashed to the
340/// device.
341#[macro_export]
342#[doc(hidden)]
343macro_rules! metadata {
344    ($category:literal, $key:ident, $value:expr) => {
345        #[cfg(feature = "rt")]
346        #[unsafe(link_section = concat!(".espressif.metadata"))]
347        #[used]
348        #[unsafe(export_name = concat!($category, ".", stringify!($key)))]
349        static $key: [u8; $value.len()] = const {
350            let val_bytes = $value.as_bytes();
351            let mut val_bytes_array = [0; $value.len()];
352            let mut i = 0;
353            while i < val_bytes.len() {
354                val_bytes_array[i] = val_bytes[i];
355                i += 1;
356            }
357            val_bytes_array
358        };
359    };
360}
361
362#[procmacros::doc_replace]
363/// Extract fields from [`Peripherals`][crate::peripherals::Peripherals] into named groups.
364#[cfg_attr(
365    // Feature-gated so that this doesn't prevent gradual device bringup. Any
366    // stable driver would serve the purpose here, so this block will be part
367    // of the released documentation.
368    all(soc_has_spi2, soc_has_i2c0, gpio_driver_supported),
369    doc = r#"
370## Example
371
372```rust,no_run
373# {before_snippet}
374#
375use esp_hal::assign_resources;
376
377assign_resources! {
378    Resources<'d> {
379        display: DisplayResources<'d> {
380            spi:  SPI2,
381            sda:  GPIO5,
382            sclk: GPIO4,
383            cs:   GPIO3,
384            dc:   GPIO2,
385        },
386        axl: AccelerometerResources<'d> {
387            i2c: I2C0,
388            sda: GPIO0,
389            scl: GPIO1,
390        },
391    }
392}
393
394# struct Display<'d>(core::marker::PhantomData<&'d ()>);
395fn init_display<'d>(r: DisplayResources<'d>) -> Display<'d> {
396    // use `r.spi`, `r.sda`, `r.sclk`, `r.cs`, `r.dc`
397    todo!()
398}
399
400# struct Accelerometer<'d>(core::marker::PhantomData<&'d ()>);
401fn init_accelerometer<'d>(r: AccelerometerResources<'d>) -> Accelerometer<'d> {
402    // use `r.i2c`, `r.sda`, `r.scl`
403    todo!()
404}
405
406// let peripherals = esp_hal::init(...);
407let resources = split_resources!(peripherals);
408
409let display = init_display(resources.display);
410let axl = init_accelerometer(resources.axl);
411
412// Other fields (`peripherals.UART0`, ...) of the `peripherals` struct can still be accessed.
413# {after_snippet}
414```
415"#
416)]
417// Based on https://crates.io/crates/assign-resources
418#[macro_export]
419#[cfg(feature = "unstable")]
420#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
421macro_rules! assign_resources {
422    {
423        $(#[$struct_meta:meta])*
424        $vis:vis $struct_name:ident<$struct_lt:lifetime> {
425            $(
426                $(#[$group_meta:meta])*
427                $group_name:ident : $group_struct:ident<$group_lt:lifetime> {
428                    $(
429                        $(#[$resource_meta:meta])*
430                        $resource_name:ident : $resource_field:ident
431                    ),*
432                    $(,)?
433                }
434            ),+
435            $(,)?
436        }
437    } => {
438        // Group structs
439        $(
440            $(#[$group_meta])*
441            #[allow(missing_docs)]
442            $vis struct $group_struct<$group_lt> {
443                $(
444                    $(#[$resource_meta])*
445                    pub $resource_name: $crate::peripherals::$resource_field<$group_lt>,
446                )+
447            }
448
449            impl<$group_lt> $group_struct<$group_lt> {
450                /// Unsafely create an instance of the assigned peripherals out of thin air.
451                ///
452                /// # Safety
453                ///
454                /// You must ensure that you're only using one instance of the contained peripherals at a time.
455                pub unsafe fn steal() -> Self {
456                    unsafe {
457                        Self {
458                            $($resource_name: $crate::peripherals::$resource_field::steal()),*
459                        }
460                    }
461                }
462
463                /// Creates a new reference to the peripheral group with a shorter lifetime.
464                ///
465                /// Use this method if you would like to keep working with the peripherals after
466                /// you dropped the drivers that consume this.
467                pub fn reborrow(&mut self) -> $group_struct<'_> {
468                    $group_struct {
469                        $($resource_name: self.$resource_name.reborrow()),*
470                    }
471                }
472            }
473        )+
474
475        // Outer struct
476        $(#[$struct_meta])*
477        /// Assigned resources.
478        $vis struct $struct_name<$struct_lt> {
479            $( pub $group_name: $group_struct<$struct_lt>, )+
480        }
481
482        impl<$struct_lt> $struct_name<$struct_lt> {
483            /// Unsafely create an instance of the assigned peripherals out of thin air.
484            ///
485            /// # Safety
486            ///
487            /// You must ensure that you're only using one instance of the contained peripherals at a time.
488            pub unsafe fn steal() -> Self {
489                unsafe {
490                    Self {
491                        $($group_name: $group_struct::steal()),*
492                    }
493                }
494            }
495
496            /// Creates a new reference to the assigned peripherals with a shorter lifetime.
497            ///
498            /// Use this method if you would like to keep working with the peripherals after
499            /// you dropped the drivers that consume this.
500            pub fn reborrow(&mut self) -> $struct_name<'_> {
501                $struct_name {
502                    $($group_name: self.$group_name.reborrow()),*
503                }
504            }
505        }
506
507        /// Extracts resources from the `Peripherals` struct.
508        #[macro_export]
509        macro_rules! split_resources {
510            ($peris:ident) => {
511                $struct_name {
512                    $($group_name: $group_struct {
513                        $($resource_name: $peris.$resource_field),*
514                    }),*
515                }
516            }
517        }
518    };
519}
520
521/// Helper macro to implement the relevant DMA channel compatibility trait for peripheral
522/// instances and DMA channel types.
523///
524/// Expected uses:
525///
526/// Drivers that define an AnyPeripheral type:
527///
528/// ```rust, ignore
529/// with_spi_dma_engine! {
530///     ($engine:tt, $any_peri:ident) => {
531///         crate::impl_dma_channel_trait! {
532///             $engine,
533///             any_peri = AnySpi,
534///             peris = for_each_spi_master,
535///             ($peri:path, $ch:path) => {
536///                 impl<'d> SpiMasterDmaChannel<'d, $peri<'d>> for $ch<'d> {}
537///             }
538///         }
539///     }
540/// }
541/// ```
542///
543/// Drivers that do not:
544///
545/// ```rust, ignore
546/// with_aes_dma_engine! {
547///     ($engine:tt, $any_peri:ident) => {
548///         crate::impl_dma_channel_trait! {
549///             $engine,
550///             peri = AES, // no lifetime!
551///             ($peri:path, $ch:path) => {
552///                 impl<'d> AesDmaChannel<'d> for $ch<'d> {}
553///             }
554///         }
555///     }
556/// }
557/// ```
558#[doc(hidden)]
559#[rustfmt::skip]
560#[cfg(dma_driver_supported)]
561macro_rules! impl_dma_channel_trait {
562    // Single peripheral instance case
563    (
564        $dma_engine:tt,
565        peri = $peri:tt,
566        $pattern:tt => $body:tt
567    ) => {
568        macro_rules! impl_dma_channel_trait_inner {
569            ($pattern) => $body;
570        }
571
572        for_each_dma_channel_peri_pair! {
573            ($dma_engine, any_channel = $any_ch:ident, $peri) => {
574                impl_dma_channel_trait_inner! { ( $peri <'d>, $crate::dma::$any_ch<'d>) }
575            };
576            ($dma_engine, $ch:ident, $peri) => {
577                impl_dma_channel_trait_inner! { ( $peri <'d>, $crate::peripherals::$ch<'d>) }
578            };
579        }
580    };
581
582    // Multiple peripheral instances case - implies AnyPeripheral
583    (
584        $dma_engine:tt,
585        any_peri = $any_peri:ty,
586        peris = $for_each_peri_macro:ident,
587        $pattern:tt => $body:tt
588    ) => {
589        macro_rules! impl_dma_channel_trait_inner {
590            ($pattern) => $body;
591        }
592
593        for_each_dma_channel! {
594            ($dma_engine, any_channel = $any_ch:ident) => {
595                impl_dma_channel_trait_inner! { ($any_peri, $crate::dma::$any_ch<'d>) }
596            };
597            ($dma_engine, $ch:ident) => {
598                impl_dma_channel_trait_inner! { ($any_peri, $crate::peripherals::$ch<'d>) }
599            };
600        }
601
602        $for_each_peri_macro! {
603            ($peri:ident) => {
604                for_each_dma_channel_peri_pair! {
605                    ($dma_engine, any_channel = $any_ch:ident, $peri) => {
606                        impl_dma_channel_trait_inner! { ($crate::peripherals::$peri<'d>, $crate::dma::$any_ch<'d>) }
607                    };
608                    ($dma_engine, $ch:ident, $peri) => {
609                        impl_dma_channel_trait_inner! { ($crate::peripherals::$peri<'d>, $crate::peripherals::$ch<'d>) }
610                    };
611                }
612            };
613        }
614    };
615}
616#[cfg(dma_driver_supported)]
617pub(crate) use impl_dma_channel_trait;
618#[cfg(feature = "unstable")]
619use procmacros::doc_replace;
620
621/// Macro to allow using unstable HAL features conditionally. Other crates can
622/// use this to "detect" the esp-hal/unstable feature.
623#[macro_export]
624#[doc(hidden)]
625#[cfg(feature = "unstable")]
626macro_rules! if_unstable_hal {
627    ($($tt:tt)*) => {
628        $($tt)*
629    };
630}
631
632/// Macro to allow using unstable HAL features conditionally. Other crates can
633/// use this to "detect" the esp-hal/unstable feature.
634#[macro_export]
635#[doc(hidden)]
636#[cfg(not(feature = "unstable"))]
637macro_rules! if_unstable_hal {
638    ($($tt:tt)*) => {};
639}