Skip to main content

esp_hal/peripherals/
mod.rs

1//! # Peripheral Instances
2//!
3//! This module creates singleton instances for each of the various peripherals,
4//! and re-exports them to allow users to access and use them in their
5//! applications.
6//!
7//! Should be noted that that the module also re-exports the [Interrupt] enum
8//! from the PAC, allowing users to handle interrupts associated with these
9//! peripherals.
10
11// We need to export this for users to use
12#[doc(hidden)]
13pub use pac::Interrupt;
14
15pub(crate) use crate::soc::pac;
16
17#[cfg(esp32h2)]
18#[path = "overlay_h2.rs"]
19mod overlay;
20
21/// Macro to create a peripheral structure.
22macro_rules! create_peripheral {
23    ($(#[$attr:meta])* $name:ident <= virtual ($($interrupt:ident: { $bind:ident, $enable:ident, $disable:ident }),*)) => {
24        #[derive(Debug)]
25        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
26        #[non_exhaustive]
27        #[allow(non_camel_case_types, clippy::upper_case_acronyms)]
28        $(#[$attr])*
29        pub struct $name<'a> {
30            _marker: core::marker::PhantomData<&'a mut ()>,
31        }
32
33        impl $name<'_> {
34            /// Unsafely create an instance of this peripheral out of thin air.
35            ///
36            /// # Safety
37            ///
38            /// You must ensure that you're only using one instance of this type at a time.
39            #[inline]
40            pub unsafe fn steal() -> Self {
41                Self {
42                    _marker: core::marker::PhantomData,
43                }
44            }
45
46            /// Unsafely clone this peripheral reference.
47            ///
48            /// # Safety
49            ///
50            /// You must ensure that you're only using one instance of this type at a time.
51            #[inline]
52            #[allow(dead_code)]
53            pub unsafe fn clone_unchecked(&self) -> Self {
54                unsafe { Self::steal() }
55            }
56
57            /// Creates a new peripheral reference with a shorter lifetime.
58            ///
59            /// Use this method if you would like to keep working with the peripheral after
60            /// you dropped the driver that consumes this.
61            #[inline]
62            #[allow(dead_code)]
63            pub fn reborrow(&mut self) -> $name<'_> {
64                unsafe { self.clone_unchecked() }
65            }
66
67            $(
68                /// Binds an interrupt handler to the corresponding interrupt for this peripheral, and enables the interrupt.
69                ///
70                /// <section class="warning">
71                /// This function is a very low-level way to work with interrupts. Unless you're writing drivers, this is probably not the interrupt API you want to use.
72                /// </section>
73                ///
74                #[instability::unstable]
75                pub fn $bind(&self, handler: $crate::interrupt::InterruptHandler) {
76                    $crate::interrupt::bind_handler($crate::peripherals::Interrupt::$interrupt, handler);
77                }
78
79                #[procmacros::doc_replace]
80                #[doc = concat!("Enables the ", stringify!($interrupt), " peripheral interrupt on the given priority level.")]
81                ///
82                /// <section class="warning">
83                /// This function is a very low-level way to work with interrupts. Unless you're writing drivers, this is probably not the interrupt API you want to use.
84                /// </section>
85                #[cfg_attr(multi_core, doc = "The interrupt handler will be enabled on the core that calls this function.")]
86                ///
87                /// Note that a suitable interrupt handler needs to be set up before the first interrupt
88                /// is triggered, otherwise the default handler will panic.
89                #[cfg_attr(not(feature = "unstable"), doc = "To set up an interrupt handler, create a function that has the same (non-mangled) name as the interrupt you want to handle.")]
90                #[cfg_attr(feature = "unstable", doc = concat!("To set up an interrupt handler, use [`Self::", stringify!($bind), "`] or create a function that has the same (non-mangled) name as the interrupt you want to handle."))]
91                ///
92                /// ## Examples
93                ///
94                /// ```rust, no_run
95                /// # {before_snippet}
96                /// use esp_hal::interrupt::Priority;
97                ///
98                /// #[unsafe(no_mangle)]
99                #[doc = concat!(r#"unsafe extern "C" fn "#, stringify!($interrupt), "() {")]
100                ///     // do something
101                /// }
102                ///
103                #[doc = concat!("peripherals.", stringify!($name), ".", stringify!($enable), "(Priority::Priority1);")]
104                #[doc = concat!("peripherals.", stringify!($name), ".", stringify!($disable), "_on_all_cores();")]
105                /// # {after_snippet}
106                /// ```
107                #[allow(dead_code, reason = "Peripheral may be unstable")]
108                pub fn $enable(&self, priority: $crate::interrupt::Priority) {
109                    $crate::interrupt::enable($crate::peripherals::Interrupt::$interrupt, priority);
110                }
111
112                paste::paste! {
113                    #[procmacros::doc_replace]
114                    #[doc = concat!("Disables the ",  stringify!($interrupt), " peripheral interrupt handler on the current CPU core.")]
115                    ///
116                    /// <section class="warning">
117                    /// This function is a very low-level way to work with interrupts. Unless you're writing drivers, this is probably not the interrupt API you want to use.
118                    /// </section>
119                    #[instability::unstable]
120                    pub fn $disable(&self) {
121                        $crate::interrupt::disable($crate::system::Cpu::current(), $crate::peripherals::Interrupt::$interrupt);
122                    }
123
124                    #[procmacros::doc_replace]
125                    #[doc = concat!("Disables the ",  stringify!($interrupt), " peripheral interrupt handler on all cores.")]
126                    ///
127                    /// <section class="warning">
128                    /// This function is a very low-level way to work with interrupts. Unless you're writing drivers, this is probably not the interrupt API you want to use.
129                    /// </section>
130                    #[allow(dead_code, reason = "Peripheral may be unstable")]
131                    pub fn [<$disable _on_all_cores>](&self) {
132                        for core in $crate::system::Cpu::all() {
133                            $crate::interrupt::disable(core, $crate::peripherals::Interrupt::$interrupt);
134                        }
135                    }
136                }
137            )*
138        }
139
140        impl $crate::private::Sealed for $name<'_> {}
141    };
142
143    ($(#[$attr:meta])* $name:ident <= $base:ident $interrupts:tt) => {
144        create_peripheral!($(#[$attr])* $name <= virtual $interrupts);
145
146        impl $name<'_> {
147            #[doc = r"Pointer to the register block"]
148            #[instability::unstable]
149            pub const PTR: *const <pac::$base as core::ops::Deref>::Target = pac::$base::PTR;
150
151            #[doc = r"Return the pointer to the register block"]
152            #[inline(always)]
153            #[instability::unstable]
154            pub const fn ptr() -> *const <pac::$base as core::ops::Deref>::Target {
155                pac::$base::PTR
156            }
157
158            #[doc = r"Return a reference to the register block"]
159            #[inline(always)]
160            #[instability::unstable]
161            pub const fn regs<'a>() -> &'a <pac::$base as core::ops::Deref>::Target {
162                unsafe { &*Self::PTR }
163            }
164
165            #[doc = r"Return a reference to the register block"]
166            #[inline(always)]
167            #[instability::unstable]
168            pub fn register_block(&self) -> &<pac::$base as core::ops::Deref>::Target {
169                unsafe { &*Self::PTR }
170            }
171        }
172    };
173}
174
175for_each_peripheral! {
176    // Define stable peripheral singletons
177    (@peri_type $(#[$meta:meta])* $name:ident <= $from_pac:tt $interrupts:tt) => {
178        create_peripheral!( $(#[$meta])* $name <= $from_pac $interrupts);
179    };
180
181    // Define unstable peripheral singletons
182    (@peri_type $(#[$meta:meta])* $name:ident <= $from_pac:tt $interrupts:tt (unstable)) => {
183        create_peripheral!(#[instability::unstable] $(#[$meta])* $name <= $from_pac $interrupts);
184    };
185
186    // Define the Peripherals struct
187    (singletons $( ($name:ident $(($unstable:ident))?) ),*) => {
188        // We need a way to ignore the "unstable" marker, but macros can't generate attributes or struct fields.
189        // The solution is printing an empty doc comment.
190        macro_rules! ignore { ($any:tt) => {""} }
191
192        /// The `Peripherals` struct provides access to all of the hardware peripherals on the chip.
193        #[allow(non_snake_case)]
194        #[non_exhaustive]
195        pub struct Peripherals {
196            $(
197                // This is a bit hairy, but non-macro attributes are not allowed on struct fields. We work
198                // around this by excluding code with the `$()?` optional macro syntax and an "unstable" marker
199                // in the source data. The marker itself is passed through the `ignore` macro so that it doesn't
200                // appear in the generated code.
201                //
202                // The code can end up looking two ways:
203                //
204                // - Without `unstable` we just generate the field:
205                // ```
206                // #[attributes]
207                // pub PERI: PERI<'static>,
208                // ```
209                //
210                // - With `unstable` we're basically emulating what `instability::unstable` would do:
211                // ```
212                // #[attributes]
213                // #[cfg(feature = "unstable")]
214                // pub PERI: PERI<'static>,
215                //
216                // #[attributes]
217                // #[cfg(not(feature = "unstable"))]
218                // pub(crate) PERI: PERI<'static>,
219                // ```
220                #[doc = concat!("The ", stringify!($name), " peripheral.")]
221                $(
222                    #[doc = "**This API is marked as unstable** and is only available when the `unstable`
223                            crate feature is enabled. This comes with no stability guarantees, and could be changed
224                            or removed at any time."]
225                    #[doc = ignore!($unstable)]
226                    #[cfg(feature = "unstable")]
227                    #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
228                )?
229                pub $name: $name<'static>,
230
231                $(
232                    #[doc = concat!("The ", stringify!($unstable_name), " peripheral.")]
233                    #[doc = "**This API is marked as unstable** and is only available when the `unstable`
234                            crate feature is enabled. This comes with no stability guarantees, and could be changed
235                            or removed at any time."]
236                    #[doc = ignore!($unstable)]
237                    #[cfg(not(feature = "unstable"))]
238                    #[allow(unused)]
239                    pub(crate) $name: $name<'static>,
240                )?
241            )*
242        }
243
244        impl Peripherals {
245            /// Returns all the peripherals *once*.
246            #[inline]
247            #[cfg(feature = "rt")]
248            pub(crate) fn take() -> Self {
249                #[unsafe(no_mangle)]
250                static mut _ESP_HAL_DEVICE_PERIPHERALS: bool = false;
251
252                crate::ESP_HAL_LOCK.lock(|| unsafe {
253                    if _ESP_HAL_DEVICE_PERIPHERALS {
254                        panic!("init called more than once!")
255                    }
256                    _ESP_HAL_DEVICE_PERIPHERALS = true;
257                    Self::steal()
258                })
259            }
260
261            /// Unsafely create an instance of this peripheral out of thin air.
262            ///
263            /// # Safety
264            ///
265            /// You must ensure that you're only using one instance of this type at a time.
266            #[inline]
267            pub unsafe fn steal() -> Self {
268                unsafe {
269                    Self {
270                        $(
271                            $name: $name::steal(),
272                        )*
273                    }
274                }
275            }
276        }
277    };
278}