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}