esp_hal/peripheral.rs
1//! # Exclusive peripheral access
2
3use core::{
4 marker::PhantomData,
5 ops::{Deref, DerefMut},
6};
7
8/// An exclusive reference to a peripheral.
9///
10/// This is functionally the same as a `&'a mut T`. There's a few advantages in
11/// having a dedicated struct instead:
12///
13/// - Memory efficiency: Peripheral singletons are typically either zero-sized
14/// (for concrete peripherals like `GpioPin<5>` or `SPI2`) or very small (for
15/// example `AnyPin`, which is 1 byte). However `&mut T` is always 4 bytes for
16/// 32-bit targets, even if T is zero-sized. PeripheralRef stores a copy of
17/// `T` instead, so it's the same size.
18/// - Code size efficiency. If the user uses the same driver with both `SPI2`
19/// and `&mut SPI2`, the driver code would be monomorphized two times. With
20/// PeripheralRef, the driver is generic over a lifetime only. `SPI2` becomes
21/// `PeripheralRef<'static, SPI2>`, and `&mut SPI2` becomes `PeripheralRef<'a,
22/// SPI2>`. Lifetimes don't cause monomorphization.
23#[derive(Debug)]
24#[cfg_attr(feature = "defmt", derive(defmt::Format))]
25pub struct PeripheralRef<'a, T> {
26 inner: T,
27 _lifetime: PhantomData<&'a mut T>,
28}
29
30impl<'a, T> PeripheralRef<'a, T> {
31 /// Create a new exclusive reference to a peripheral
32 #[inline]
33 pub fn new(inner: T) -> Self {
34 Self {
35 inner,
36 _lifetime: PhantomData,
37 }
38 }
39
40 /// Unsafely clone (duplicate) a peripheral singleton.
41 ///
42 /// # Safety
43 ///
44 /// This returns an owned clone of the peripheral. You must manually ensure
45 /// only one copy of the peripheral is in use at a time. For example, don't
46 /// create two SPI drivers on `SPI1`, because they will "fight" each other.
47 ///
48 /// You should strongly prefer using `reborrow()` instead. It returns a
49 /// `PeripheralRef` that borrows `self`, which allows the borrow checker
50 /// to enforce this at compile time.
51 pub unsafe fn clone_unchecked(&self) -> PeripheralRef<'a, T>
52 where
53 T: Peripheral<P = T>,
54 {
55 PeripheralRef::new(self.inner.clone_unchecked())
56 }
57
58 /// Reborrow into a "child" PeripheralRef.
59 ///
60 /// `self` will stay borrowed until the child PeripheralRef is dropped.
61 pub fn reborrow(&mut self) -> PeripheralRef<'_, T>
62 where
63 T: Peripheral<P = T>,
64 {
65 // safety: we're returning the clone inside a new PeripheralRef that borrows
66 // self, so user code can't use both at the same time.
67 PeripheralRef::new(unsafe { self.inner.clone_unchecked() })
68 }
69
70 /// Transform the inner peripheral.
71 ///
72 /// This converts from `PeripheralRef<'a, T>` to `PeripheralRef<'a, U>`,
73 /// using a user-provided impl to convert from `T` to `U`.
74 #[inline]
75 pub fn map<U>(self, transform: impl FnOnce(T) -> U) -> PeripheralRef<'a, U> {
76 PeripheralRef {
77 inner: transform(self.inner),
78 _lifetime: PhantomData,
79 }
80 }
81
82 /// Map the inner peripheral using `Into`.
83 ///
84 /// This converts from `PeripheralRef<'a, T>` to `PeripheralRef<'a, U>`,
85 /// using an `Into` impl to convert from `T` to `U`.
86 ///
87 /// For example, this can be useful to degrade GPIO pins: converting from
88 /// `PeripheralRef<'a, GpioPin<11>>` to `PeripheralRef<'a, AnyPin>`.
89 #[inline]
90 pub fn map_into<U>(self) -> PeripheralRef<'a, U>
91 where
92 T: Into<U>,
93 {
94 self.map(Into::into)
95 }
96}
97
98impl<T> Deref for PeripheralRef<'_, T> {
99 type Target = T;
100
101 #[inline]
102 fn deref(&self) -> &Self::Target {
103 &self.inner
104 }
105}
106
107/// Trait for any type that can be used as a peripheral of type `P`.
108///
109/// This is used in driver constructors, to allow passing either owned
110/// peripherals (e.g. `UART0`), or borrowed peripherals (e.g. `&mut UART0`).
111///
112/// For example, if you have a driver with a constructor like this:
113///
114/// ```rust, ignore
115/// impl<'d, T> Uart<'d, T, Blocking> {
116/// pub fn new<TX: PeripheralOutput, RX: PeripheralInput>(
117/// uart: impl Peripheral<P = T> + 'd,
118/// rx: impl Peripheral<P = RX> + 'd,
119/// tx: impl Peripheral<P = TX> + 'd,
120/// ) -> Result<Self, Error> {
121/// Ok(Self { .. })
122/// }
123/// }
124/// ```
125///
126/// You may call it with owned peripherals, which yields an instance that can
127/// live forever (`'static`):
128///
129/// ```rust, ignore
130/// let mut uart: Uart<'static, ...> = Uart::new(p.UART0, p.GPIO0, p.GPIO1);
131/// ```
132///
133/// Or you may call it with borrowed peripherals, which yields an instance that
134/// can only live for as long as the borrows last:
135///
136/// ```rust, ignore
137/// let mut uart: Uart<'_, ...> = Uart::new(&mut p.UART0, &mut p.GPIO0, &mut p.GPIO1);
138/// ```
139///
140/// # Implementation details, for HAL authors
141///
142/// When writing a HAL, the intended way to use this trait is to take `impl
143/// Peripheral<P = ..>` in the HAL's public API (such as driver constructors),
144/// calling `.into_ref()` to obtain a `PeripheralRef`, and storing that in the
145/// driver struct.
146///
147/// `.into_ref()` on an owned `T` yields a `PeripheralRef<'static, T>`.
148/// `.into_ref()` on an `&'a mut T` yields a `PeripheralRef<'a, T>`.
149pub trait Peripheral: Sized {
150 /// Peripheral singleton type
151 type P;
152
153 /// Unsafely clone (duplicate) a peripheral singleton.
154 ///
155 /// # Safety
156 ///
157 /// This returns an owned clone of the peripheral. You must manually ensure
158 /// only one copy of the peripheral is in use at a time. For example, don't
159 /// create two SPI drivers on `SPI1`, because they will "fight" each other.
160 ///
161 /// You should strongly prefer using `into_ref()` instead. It returns a
162 /// `PeripheralRef`, which allows the borrow checker to enforce this at
163 /// compile time.
164 unsafe fn clone_unchecked(&self) -> Self::P;
165
166 /// Convert a value into a `PeripheralRef`.
167 ///
168 /// When called on an owned `T`, yields a `PeripheralRef<'static, T>`.
169 /// When called on an `&'a mut T`, yields a `PeripheralRef<'a, T>`.
170 #[inline]
171 fn into_ref<'a>(self) -> PeripheralRef<'a, Self::P>
172 where
173 Self: 'a,
174 {
175 PeripheralRef::new(unsafe { self.clone_unchecked() })
176 }
177
178 /// Map the peripheral using `Into`.
179 ///
180 /// This converts from `Peripheral<P = T>` to `Peripheral<P = U>`,
181 /// using an `Into` impl to convert from `T` to `U`.
182 #[inline]
183 fn map_into<U>(self) -> U
184 where
185 Self::P: Into<U>,
186 U: Peripheral<P = U>,
187 {
188 self.map(Into::into)
189 }
190
191 /// Map the peripheral using `Into`.
192 ///
193 /// This converts from `Peripheral<P = T>` to `Peripheral<P = U>`,
194 /// using an `Into` impl to convert from `T` to `U`.
195 #[inline]
196 fn map<U>(self, transform: impl FnOnce(Self::P) -> U) -> U
197 where
198 U: Peripheral<P = U>,
199 {
200 transform(unsafe { self.clone_unchecked() })
201 }
202}
203
204impl<T: DerefMut> Peripheral for T
205where
206 T::Target: Peripheral,
207{
208 type P = <T::Target as Peripheral>::P;
209
210 #[inline]
211 unsafe fn clone_unchecked(&self) -> Self::P {
212 T::Target::clone_unchecked(self)
213 }
214}
215
216impl<T: Peripheral> Peripheral for PeripheralRef<'_, T> {
217 type P = T::P;
218
219 #[inline]
220 unsafe fn clone_unchecked(&self) -> Self::P {
221 T::clone_unchecked(self)
222 }
223}
224
225mod peripheral_macros {
226 /// Creates a new `Peripherals` struct and its associated methods.
227 ///
228 /// The macro has a few fields doing different things, in the form of
229 /// `second <= third (fourth)`.
230 /// - The second field is the name of the peripheral, as it appears in the
231 /// `Peripherals` struct.
232 /// - The third field is the name of the peripheral as it appears in the
233 /// PAC. This may be `virtual` if the peripheral is not present in the
234 /// PAC.
235 /// - The fourth field is an optional list of interrupts that can be bound
236 /// to the peripheral.
237 #[doc(hidden)]
238 #[macro_export]
239 macro_rules! peripherals {
240 (
241 peripherals: [
242 $(
243 $name:ident <= $from_pac:tt $(($($interrupt:ident),*))?
244 ),* $(,)?
245 ],
246 unstable_peripherals: [
247 $(
248 $unstable_name:ident <= $unstable_from_pac:tt $(($($unstable_interrupt:ident),*))?
249 ),* $(,)?
250 ],
251 pins: [
252 $( ( $pin:literal, $($pin_tokens:tt)* ) )*
253 ],
254 dma_channels: [
255 $(
256 $channel_name:ident : $channel_ty:path
257 ),* $(,)?
258 ]
259 ) => {
260 $(
261 $crate::create_peripheral!($name <= $from_pac);
262 )*
263 $(
264 $crate::create_peripheral!(#[instability::unstable] $unstable_name <= $unstable_from_pac);
265 )*
266
267 pub(crate) mod gpio {
268 $crate::gpio! {
269 $( ($pin, $($pin_tokens)* ) )*
270 }
271 }
272
273 paste::paste! {
274 /// The `Peripherals` struct provides access to all of the hardware peripherals on the chip.
275 #[allow(non_snake_case)]
276 pub struct Peripherals {
277 $(
278 #[doc = concat!("The ", stringify!($name), " peripheral.")]
279 pub $name: $name,
280 )*
281 $(
282 #[doc = concat!("The ", stringify!($unstable_name), " peripheral.")]
283 #[doc = "**This API is marked as unstable** and is only available when the `unstable`
284 crate feature is enabled. This comes with no stability guarantees, and could be changed
285 or removed at any time."]
286 #[cfg(any(doc, feature = "unstable"))]
287 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
288 pub $unstable_name: $unstable_name,
289
290 #[doc = concat!("The ", stringify!($unstable_name), " peripheral.")]
291 #[doc = "**This API is marked as unstable** and is only available when the `unstable`
292 crate feature is enabled. This comes with no stability guarantees, and could be changed
293 or removed at any time."]
294 #[cfg(not(any(doc, feature = "unstable")))]
295 #[allow(unused)]
296 pub(crate) $unstable_name: $unstable_name,
297 )*
298
299 $(
300 #[doc = concat!("GPIO", stringify!($pin))]
301 pub [<GPIO $pin>]: $crate::gpio::GpioPin<$pin>,
302 )*
303
304 $(
305 #[doc = concat!(stringify!($channel_name), " DMA channel.")]
306 pub $channel_name: $crate::dma::$channel_ty,
307 )*
308 }
309
310 impl Peripherals {
311 /// Returns all the peripherals *once*
312 #[inline]
313 pub(crate) fn take() -> Self {
314 #[no_mangle]
315 static mut _ESP_HAL_DEVICE_PERIPHERALS: bool = false;
316
317 critical_section::with(|_| unsafe {
318 if _ESP_HAL_DEVICE_PERIPHERALS {
319 panic!("init called more than once!")
320 }
321 _ESP_HAL_DEVICE_PERIPHERALS = true;
322 Self::steal()
323 })
324 }
325
326 /// Unsafely create an instance of this peripheral out of thin air.
327 ///
328 /// # Safety
329 ///
330 /// You must ensure that you're only using one instance of this type at a time.
331 #[inline]
332 pub unsafe fn steal() -> Self {
333 Self {
334 $(
335 $name: $name::steal(),
336 )*
337 $(
338 $unstable_name: $unstable_name::steal(),
339 )*
340
341 $(
342 [<GPIO $pin>]: $crate::gpio::GpioPin::<$pin>::steal(),
343 )*
344
345 $(
346 $channel_name: $crate::dma::$channel_ty::steal(),
347 )*
348 }
349 }
350 }
351 }
352
353 $(
354 $(
355 impl $name {
356 $(
357 paste::paste!{
358 /// Binds an interrupt handler to the corresponding interrupt for this peripheral.
359 #[instability::unstable]
360 pub fn [<bind_ $interrupt:lower _interrupt >](&mut self, handler: unsafe extern "C" fn() -> ()) {
361 unsafe { $crate::interrupt::bind_interrupt($crate::peripherals::Interrupt::$interrupt, handler); }
362 }
363 }
364 )*
365 }
366 )*
367 )*
368
369 $(
370 $(
371 impl $unstable_name {
372 $(
373 paste::paste!{
374 /// Binds an interrupt handler to the corresponding interrupt for this peripheral.
375 #[instability::unstable]
376 pub fn [<bind_ $unstable_interrupt:lower _interrupt >](&mut self, handler: unsafe extern "C" fn() -> ()) {
377 unsafe { $crate::interrupt::bind_interrupt($crate::peripherals::Interrupt::$unstable_interrupt, handler); }
378 }
379 }
380 )*
381 }
382 )*
383 )*
384 };
385 }
386
387 #[doc(hidden)]
388 #[macro_export]
389 macro_rules! into_ref {
390 ($($name:ident),*) => {
391 $(
392 #[allow(unused_mut)]
393 let mut $name = $name.into_ref();
394 )*
395 }
396 }
397
398 #[doc(hidden)]
399 #[macro_export]
400 macro_rules! into_mapped_ref {
401 ($($name:ident),*) => {
402 $(
403 #[allow(unused_mut)]
404 let mut $name = $name.map_into().into_ref();
405 )*
406 }
407 }
408
409 #[doc(hidden)]
410 #[macro_export]
411 /// Macro to create a peripheral structure.
412 macro_rules! create_peripheral {
413 ($(#[$attr:meta])? $name:ident <= virtual) => {
414 $(#[$attr])?
415 #[derive(Debug)]
416 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
417 #[non_exhaustive]
418 #[allow(non_camel_case_types, clippy::upper_case_acronyms)]
419 #[doc = concat!(stringify!($name), " peripheral singleton")]
420 pub struct $name;
421
422 impl $name {
423 /// Unsafely create an instance of this peripheral out of thin air.
424 ///
425 /// # Safety
426 ///
427 /// You must ensure that you're only using one instance of this type at a time.
428 #[inline]
429 pub unsafe fn steal() -> Self {
430 Self
431 }
432 }
433
434 impl $crate::peripheral::Peripheral for $name {
435 type P = $name;
436
437 #[inline]
438 unsafe fn clone_unchecked(&self) -> Self::P {
439 Self::steal()
440 }
441 }
442
443 impl $crate::private::Sealed for $name {}
444 };
445
446 ($(#[$attr:meta])? $name:ident <= $base:ident) => {
447 $crate::create_peripheral!($(#[$attr])? $name <= virtual);
448
449 impl $name {
450 #[doc = r"Pointer to the register block"]
451 #[instability::unstable]
452 pub const PTR: *const <pac::$base as core::ops::Deref>::Target = pac::$base::PTR;
453
454 #[doc = r"Return the pointer to the register block"]
455 #[inline(always)]
456 #[instability::unstable]
457 pub const fn ptr() -> *const <pac::$base as core::ops::Deref>::Target {
458 pac::$base::PTR
459 }
460
461 #[doc = r"Return a reference to the register block"]
462 #[inline(always)]
463 #[instability::unstable]
464 pub const fn regs<'a>() -> &'a <pac::$base as core::ops::Deref>::Target {
465 unsafe { &*Self::PTR }
466 }
467
468 #[doc = r"Return a reference to the register block"]
469 #[inline(always)]
470 #[instability::unstable]
471 pub fn register_block(&self) -> &<pac::$base as core::ops::Deref>::Target {
472 unsafe { &*Self::PTR }
473 }
474 }
475 };
476 }
477}