esp_hal/
peripherals.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/// Macro to create a peripheral structure.
18macro_rules! create_peripheral {
19    ($(#[$attr:meta])? $name:ident <= virtual ($($interrupt:ident: { $bind:ident, $enable:ident, $disable:ident }),*)) => {
20        $(#[$attr])?
21        #[derive(Debug)]
22        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
23        #[non_exhaustive]
24        #[allow(non_camel_case_types, clippy::upper_case_acronyms)]
25        #[doc = concat!(stringify!($name), " peripheral singleton")]
26        pub struct $name<'a> {
27            _marker: core::marker::PhantomData<&'a mut ()>,
28        }
29
30        impl $name<'_> {
31            /// Unsafely create an instance of this peripheral out of thin air.
32            ///
33            /// # Safety
34            ///
35            /// You must ensure that you're only using one instance of this type at a time.
36            #[inline]
37            pub unsafe fn steal() -> Self {
38                Self {
39                    _marker: core::marker::PhantomData,
40                }
41            }
42
43            /// Unsafely clone this peripheral reference.
44            ///
45            /// # Safety
46            ///
47            /// You must ensure that you're only using one instance of this type at a time.
48            #[inline]
49            #[allow(dead_code)]
50            pub unsafe fn clone_unchecked(&self) -> Self {
51                unsafe { Self::steal() }
52            }
53
54            /// Creates a new peripheral reference with a shorter lifetime.
55            ///
56            /// Use this method if you would like to keep working with the peripheral after
57            /// you dropped the driver that consumes this.
58            #[inline]
59            #[allow(dead_code)]
60            pub fn reborrow(&mut self) -> $name<'_> {
61                unsafe { self.clone_unchecked() }
62            }
63
64            $(
65                /// Binds an interrupt handler to the corresponding interrupt for this peripheral.
66                #[instability::unstable]
67                pub fn $bind(&self, handler: unsafe extern "C" fn() -> ()) {
68                    unsafe { $crate::interrupt::bind_interrupt($crate::peripherals::Interrupt::$interrupt, handler); }
69                }
70
71                /// Disables the interrupt handler
72                #[instability::unstable]
73                pub fn $disable(&self) {
74                    for core in $crate::system::Cpu::other() {
75                        $crate::interrupt::disable(core, $crate::peripherals::Interrupt::$interrupt);
76                    }
77                }
78
79                /// Enables the interrupt handler on the given core
80                #[instability::unstable]
81                pub fn $enable(&self, priority: $crate::interrupt::Priority) {
82                    unwrap!($crate::interrupt::enable($crate::peripherals::Interrupt::$interrupt, priority));
83                }
84            )*
85        }
86
87        impl $crate::private::Sealed for $name<'_> {}
88    };
89
90    ($(#[$attr:meta])? $name:ident <= $base:ident $interrupts:tt) => {
91        create_peripheral!($(#[$attr])? $name <= virtual $interrupts);
92
93        impl $name<'_> {
94            #[doc = r"Pointer to the register block"]
95            #[instability::unstable]
96            pub const PTR: *const <pac::$base as core::ops::Deref>::Target = pac::$base::PTR;
97
98            #[doc = r"Return the pointer to the register block"]
99            #[inline(always)]
100            #[instability::unstable]
101            pub const fn ptr() -> *const <pac::$base as core::ops::Deref>::Target {
102                pac::$base::PTR
103            }
104
105            #[doc = r"Return a reference to the register block"]
106            #[inline(always)]
107            #[instability::unstable]
108            pub const fn regs<'a>() -> &'a <pac::$base as core::ops::Deref>::Target {
109                unsafe { &*Self::PTR }
110            }
111
112            #[doc = r"Return a reference to the register block"]
113            #[inline(always)]
114            #[instability::unstable]
115            pub fn register_block(&self) -> &<pac::$base as core::ops::Deref>::Target {
116                unsafe { &*Self::PTR }
117            }
118        }
119    };
120}
121
122for_each_peripheral! {
123    // Define stable peripheral singletons
124    ($name:ident <= $from_pac:tt $interrupts:tt) => {
125        create_peripheral!($name <= $from_pac $interrupts);
126    };
127
128    // Define unstable peripheral singletons
129    ($name:ident <= $from_pac:tt $interrupts:tt (unstable)) => {
130        create_peripheral!(#[instability::unstable] $name <= $from_pac $interrupts);
131    };
132
133    // Define the Peripherals struct
134    (all $( ($name:ident <= $from_pac:tt $interrupts:tt $(($unstable:ident))?) ),*) => {
135        // We need a way to ignore the "unstable" marker, but macros can't generate attributes or struct fields.
136        // The solution is printing an empty doc comment.
137        macro_rules! ignore { ($any:tt) => {""} }
138
139        /// The `Peripherals` struct provides access to all of the hardware peripherals on the chip.
140        #[allow(non_snake_case)]
141        pub struct Peripherals {
142            $(
143                // This is a bit hairy, but non-macro attributes are not allowed on struct fields. We work
144                // around this by excluding code with the `$()?` optional macro syntax and an "unstable" marker
145                // in the source data. The marker itself is passed through the `ignore` macro so that it doesn't
146                // appear in the generated code.
147                //
148                // The code can end up looking two ways:
149                //
150                // - Without `unstable` we just generate the field:
151                // ```
152                // #[attributes]
153                // pub PERI: PERI<'static>,
154                // ```
155                //
156                // - With `unstable` we're basically emulating what `instability::unstable` would do:
157                // ```
158                // #[attributes]
159                // #[cfg(feature = "unstable")]
160                // pub PERI: PERI<'static>,
161                //
162                // #[attributes]
163                // #[cfg(not(feature = "unstable"))]
164                // pub(crate) PERI: PERI<'static>,
165                // ```
166                #[doc = concat!("The ", stringify!($name), " peripheral.")]
167                $(
168                    #[doc = "**This API is marked as unstable** and is only available when the `unstable`
169                            crate feature is enabled. This comes with no stability guarantees, and could be changed
170                            or removed at any time."]
171                    #[doc = ignore!($unstable)]
172                    #[cfg(feature = "unstable")]
173                    #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
174                )?
175                pub $name: $name<'static>,
176
177                $(
178                    #[doc = concat!("The ", stringify!($unstable_name), " peripheral.")]
179                    #[doc = "**This API is marked as unstable** and is only available when the `unstable`
180                            crate feature is enabled. This comes with no stability guarantees, and could be changed
181                            or removed at any time."]
182                    #[doc = ignore!($unstable)]
183                    #[cfg(not(feature = "unstable"))]
184                    #[allow(unused)]
185                    pub(crate) $name: $name<'static>,
186                )?
187            )*
188        }
189
190        impl Peripherals {
191            /// Returns all the peripherals *once*
192            #[inline]
193            #[cfg_attr(not(feature = "rt"), expect(dead_code))]
194            pub(crate) fn take() -> Self {
195                #[unsafe(no_mangle)]
196                static mut _ESP_HAL_DEVICE_PERIPHERALS: bool = false;
197
198                critical_section::with(|_| unsafe {
199                    if _ESP_HAL_DEVICE_PERIPHERALS {
200                        panic!("init called more than once!")
201                    }
202                    _ESP_HAL_DEVICE_PERIPHERALS = true;
203                    Self::steal()
204                })
205            }
206
207            /// Unsafely create an instance of this peripheral out of thin air.
208            ///
209            /// # Safety
210            ///
211            /// You must ensure that you're only using one instance of this type at a time.
212            #[inline]
213            pub unsafe fn steal() -> Self {
214                unsafe {
215                    Self {
216                        $(
217                            $name: $name::steal(),
218                        )*
219                    }
220                }
221            }
222        }
223    };
224}