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