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}