aboutsummaryrefslogtreecommitdiff
path: root/embassy-hal-internal/src/peripheral.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-07-28 13:23:22 +0200
committerGitHub <[email protected]>2023-07-28 13:23:22 +0200
commit036e6ae30c9e772ef8ef20439f121e108b9106f0 (patch)
tree7c654de04304274a11d44733b51c5012aeec9e3c /embassy-hal-internal/src/peripheral.rs
parent0ced8400d00da30abe76438ef26224c7ed649708 (diff)
Rename embassy-hal-common to embassy-hal-internal, document it's for internal use only. (#1700)
Diffstat (limited to 'embassy-hal-internal/src/peripheral.rs')
-rw-r--r--embassy-hal-internal/src/peripheral.rs174
1 files changed, 174 insertions, 0 deletions
diff --git a/embassy-hal-internal/src/peripheral.rs b/embassy-hal-internal/src/peripheral.rs
new file mode 100644
index 000000000..38b4c452e
--- /dev/null
+++ b/embassy-hal-internal/src/peripheral.rs
@@ -0,0 +1,174 @@
1use core::marker::PhantomData;
2use core::ops::{Deref, DerefMut};
3
4/// An exclusive reference to a peripheral.
5///
6/// This is functionally the same as a `&'a mut T`. There's a few advantages in having
7/// a dedicated struct instead:
8///
9/// - Memory efficiency: Peripheral singletons are typically either zero-sized (for concrete
10/// peripherals like `PA9` or `SPI4`) or very small (for example `AnyPin`, which is 1 byte).
11/// However `&mut T` is always 4 bytes for 32-bit targets, even if T is zero-sized.
12/// PeripheralRef stores a copy of `T` instead, so it's the same size.
13/// - Code size efficiency. If the user uses the same driver with both `SPI4` and `&mut SPI4`,
14/// the driver code would be monomorphized two times. With PeripheralRef, the driver is generic
15/// over a lifetime only. `SPI4` becomes `PeripheralRef<'static, SPI4>`, and `&mut SPI4` becomes
16/// `PeripheralRef<'a, SPI4>`. Lifetimes don't cause monomorphization.
17pub struct PeripheralRef<'a, T> {
18 inner: T,
19 _lifetime: PhantomData<&'a mut T>,
20}
21
22impl<'a, T> PeripheralRef<'a, T> {
23 #[inline]
24 pub fn new(inner: T) -> Self {
25 Self {
26 inner,
27 _lifetime: PhantomData,
28 }
29 }
30
31 /// Unsafely clone (duplicate) a peripheral singleton.
32 ///
33 /// # Safety
34 ///
35 /// This returns an owned clone of the peripheral. You must manually ensure
36 /// only one copy of the peripheral is in use at a time. For example, don't
37 /// create two SPI drivers on `SPI1`, because they will "fight" each other.
38 ///
39 /// You should strongly prefer using `reborrow()` instead. It returns a
40 /// `PeripheralRef` that borrows `self`, which allows the borrow checker
41 /// to enforce this at compile time.
42 pub unsafe fn clone_unchecked(&self) -> PeripheralRef<'a, T>
43 where
44 T: Peripheral<P = T>,
45 {
46 PeripheralRef::new(self.inner.clone_unchecked())
47 }
48
49 /// Reborrow into a "child" PeripheralRef.
50 ///
51 /// `self` will stay borrowed until the child PeripheralRef is dropped.
52 pub fn reborrow(&mut self) -> PeripheralRef<'_, T>
53 where
54 T: Peripheral<P = T>,
55 {
56 // safety: we're returning the clone inside a new PeripheralRef that borrows
57 // self, so user code can't use both at the same time.
58 PeripheralRef::new(unsafe { self.inner.clone_unchecked() })
59 }
60
61 /// Map the inner peripheral using `Into`.
62 ///
63 /// This converts from `PeripheralRef<'a, T>` to `PeripheralRef<'a, U>`, using an
64 /// `Into` impl to convert from `T` to `U`.
65 ///
66 /// For example, this can be useful to degrade GPIO pins: converting from PeripheralRef<'a, PB11>` to `PeripheralRef<'a, AnyPin>`.
67 #[inline]
68 pub fn map_into<U>(self) -> PeripheralRef<'a, U>
69 where
70 T: Into<U>,
71 {
72 PeripheralRef {
73 inner: self.inner.into(),
74 _lifetime: PhantomData,
75 }
76 }
77}
78
79impl<'a, T> Deref for PeripheralRef<'a, T> {
80 type Target = T;
81
82 #[inline]
83 fn deref(&self) -> &Self::Target {
84 &self.inner
85 }
86}
87
88impl<'a, T> DerefMut for PeripheralRef<'a, T> {
89 #[inline]
90 fn deref_mut(&mut self) -> &mut Self::Target {
91 &mut self.inner
92 }
93}
94
95/// Trait for any type that can be used as a peripheral of type `P`.
96///
97/// This is used in driver constructors, to allow passing either owned peripherals (e.g. `TWISPI0`),
98/// or borrowed peripherals (e.g. `&mut TWISPI0`).
99///
100/// For example, if you have a driver with a constructor like this:
101///
102/// ```ignore
103/// impl<'d, T: Instance> Twim<'d, T> {
104/// pub fn new(
105/// twim: impl Peripheral<P = T> + 'd,
106/// irq: impl Peripheral<P = T::Interrupt> + 'd,
107/// sda: impl Peripheral<P = impl GpioPin> + 'd,
108/// scl: impl Peripheral<P = impl GpioPin> + 'd,
109/// config: Config,
110/// ) -> Self { .. }
111/// }
112/// ```
113///
114/// You may call it with owned peripherals, which yields an instance that can live forever (`'static`):
115///
116/// ```ignore
117/// let mut twi: Twim<'static, ...> = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config);
118/// ```
119///
120/// Or you may call it with borrowed peripherals, which yields an instance that can only live for as long
121/// as the borrows last:
122///
123/// ```ignore
124/// let mut twi: Twim<'_, ...> = Twim::new(&mut p.TWISPI0, &mut irq, &mut p.P0_03, &mut p.P0_04, config);
125/// ```
126///
127/// # Implementation details, for HAL authors
128///
129/// When writing a HAL, the intended way to use this trait is to take `impl Peripheral<P = ..>` in
130/// the HAL's public API (such as driver constructors), calling `.into_ref()` to obtain a `PeripheralRef`,
131/// and storing that in the driver struct.
132///
133/// `.into_ref()` on an owned `T` yields a `PeripheralRef<'static, T>`.
134/// `.into_ref()` on an `&'a mut T` yields a `PeripheralRef<'a, T>`.
135pub trait Peripheral: Sized {
136 /// Peripheral singleton type
137 type P;
138
139 /// Unsafely clone (duplicate) a peripheral singleton.
140 ///
141 /// # Safety
142 ///
143 /// This returns an owned clone of the peripheral. You must manually ensure
144 /// only one copy of the peripheral is in use at a time. For example, don't
145 /// create two SPI drivers on `SPI1`, because they will "fight" each other.
146 ///
147 /// You should strongly prefer using `into_ref()` instead. It returns a
148 /// `PeripheralRef`, which allows the borrow checker to enforce this at compile time.
149 unsafe fn clone_unchecked(&self) -> Self::P;
150
151 /// Convert a value into a `PeripheralRef`.
152 ///
153 /// When called on an owned `T`, yields a `PeripheralRef<'static, T>`.
154 /// When called on an `&'a mut T`, yields a `PeripheralRef<'a, T>`.
155 #[inline]
156 fn into_ref<'a>(self) -> PeripheralRef<'a, Self::P>
157 where
158 Self: 'a,
159 {
160 PeripheralRef::new(unsafe { self.clone_unchecked() })
161 }
162}
163
164impl<'b, T: DerefMut> Peripheral for T
165where
166 T::Target: Peripheral,
167{
168 type P = <T::Target as Peripheral>::P;
169
170 #[inline]
171 unsafe fn clone_unchecked(&self) -> Self::P {
172 self.deref().clone_unchecked()
173 }
174}