1use alloc::boxed::Box;
11use core::future::Future;
12use core::pin::Pin;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]
16pub enum SpiError {
17 Overrun,
18 ModeFault,
19 Crc,
20 InvalidFrequency,
21 Other,
22}
23
24impl embedded_hal::spi::Error for SpiError {
25 fn kind(&self) -> embedded_hal::spi::ErrorKind {
26 match self {
27 SpiError::Overrun => embedded_hal::spi::ErrorKind::Overrun,
28 SpiError::ModeFault => embedded_hal::spi::ErrorKind::ModeFault,
29 SpiError::Crc => embedded_hal::spi::ErrorKind::FrameFormat,
30 SpiError::InvalidFrequency => embedded_hal::spi::ErrorKind::Other,
31 SpiError::Other => embedded_hal::spi::ErrorKind::Other,
32 }
33 }
34}
35
36impl From<embassy_rp::spi::Error> for SpiError {
37 fn from(_: embassy_rp::spi::Error) -> Self {
38 SpiError::Other
39 }
40}
41
42impl From<embassy_rp::pio_programs::spi::Error> for SpiError {
43 fn from(_: embassy_rp::pio_programs::spi::Error) -> Self {
44 SpiError::Other
45 }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]
50pub enum SpiBusVersion {
51 Hardware,
53 Pio,
55 BitBang,
57}
58
59pub trait DynSpiBus {
61 fn flush<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), SpiError>> + 'a>>;
62
63 fn write<'a>(
64 &'a mut self,
65 data: &'a [u8],
66 ) -> Pin<Box<dyn Future<Output = Result<(), SpiError>> + 'a>>;
67
68 fn read<'a>(
69 &'a mut self,
70 data: &'a mut [u8],
71 ) -> Pin<Box<dyn Future<Output = Result<(), SpiError>> + 'a>>;
72
73 fn transfer<'a>(
74 &'a mut self,
75 read: &'a mut [u8],
76 write: &'a [u8],
77 ) -> Pin<Box<dyn Future<Output = Result<(), SpiError>> + 'a>>;
78
79 fn transfer_in_place<'a>(
80 &'a mut self,
81 words: &'a mut [u8],
82 ) -> Pin<Box<dyn Future<Output = Result<(), SpiError>> + 'a>>;
83}
84
85pub trait DynSpiBusBlocking {
87 fn flush_blocking(&mut self) -> Result<(), SpiError>;
88 fn write_blocking(&mut self, data: &[u8]) -> Result<(), SpiError>;
89 fn read_blocking(&mut self, data: &mut [u8]) -> Result<(), SpiError>;
90 fn transfer_blocking(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), SpiError>;
91 fn transfer_in_place_blocking(&mut self, words: &mut [u8]) -> Result<(), SpiError>;
92}
93
94pub trait DynSpiBusCombined: DynSpiBus + DynSpiBusBlocking + Send {}
96impl<T: DynSpiBus + DynSpiBusBlocking + Send> DynSpiBusCombined for T {}
97
98#[must_use = "dropping a bus handle does not return its startup resources"]
113pub struct SpiBusHandle {
114 inner: Box<dyn DynSpiBusCombined>,
115 version: SpiBusVersion,
116}
117
118impl SpiBusHandle {
119 pub fn new(inner: Box<dyn DynSpiBusCombined>, version: SpiBusVersion) -> Self {
121 Self { inner, version }
122 }
123
124 pub fn version(&self) -> SpiBusVersion {
126 self.version
127 }
128
129 pub async fn flush(&mut self) -> Result<(), SpiError> {
131 self.inner.flush().await
132 }
133
134 pub async fn write(&mut self, data: &[u8]) -> Result<(), SpiError> {
136 self.inner.write(data).await
137 }
138
139 pub async fn read(&mut self, data: &mut [u8]) -> Result<(), SpiError> {
141 self.inner.read(data).await
142 }
143
144 pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), SpiError> {
146 self.inner.transfer(read, write).await
147 }
148
149 pub async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), SpiError> {
151 self.inner.transfer_in_place(words).await
152 }
153
154 pub fn flush_blocking(&mut self) -> Result<(), SpiError> {
156 self.inner.flush_blocking()
157 }
158
159 pub fn write_blocking(&mut self, data: &[u8]) -> Result<(), SpiError> {
161 self.inner.write_blocking(data)
162 }
163
164 pub fn read_blocking(&mut self, data: &mut [u8]) -> Result<(), SpiError> {
166 self.inner.read_blocking(data)
167 }
168
169 pub fn transfer_blocking(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), SpiError> {
171 self.inner.transfer_blocking(read, write)
172 }
173
174 pub fn transfer_in_place_blocking(&mut self, words: &mut [u8]) -> Result<(), SpiError> {
176 self.inner.transfer_in_place_blocking(words)
177 }
178}
179
180impl embedded_hal::spi::ErrorType for SpiBusHandle {
183 type Error = SpiError;
184}
185
186impl embedded_hal::spi::SpiBus<u8> for SpiBusHandle {
187 fn flush(&mut self) -> Result<(), SpiError> {
188 self.flush_blocking()
189 }
190
191 fn read(&mut self, words: &mut [u8]) -> Result<(), SpiError> {
192 self.read_blocking(words)
193 }
194
195 fn write(&mut self, words: &[u8]) -> Result<(), SpiError> {
196 self.write_blocking(words)
197 }
198
199 fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), SpiError> {
200 self.transfer_blocking(read, write)
201 }
202
203 fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), SpiError> {
204 self.transfer_in_place_blocking(words)
205 }
206}
207
208impl embedded_hal_async::spi::SpiBus<u8> for SpiBusHandle {
209 async fn flush(&mut self) -> Result<(), SpiError> {
210 self.flush().await
211 }
212
213 async fn read(&mut self, words: &mut [u8]) -> Result<(), SpiError> {
214 self.read(words).await
215 }
216
217 async fn write(&mut self, words: &[u8]) -> Result<(), SpiError> {
218 self.write(words).await
219 }
220
221 async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), SpiError> {
222 self.transfer(read, write).await
223 }
224
225 async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), SpiError> {
226 self.inner.transfer_in_place(words).await
227 }
228}
229
230impl<T> DynSpiBusBlocking for T
233where
234 T: embedded_hal::spi::SpiBus<u8>,
235 T::Error: Into<SpiError>,
236 T: Send,
237{
238 fn flush_blocking(&mut self) -> Result<(), SpiError> {
239 self.flush().map_err(|e| e.into())
240 }
241
242 fn write_blocking(&mut self, data: &[u8]) -> Result<(), SpiError> {
243 self.write(data).map_err(|e| e.into())
244 }
245
246 fn read_blocking(&mut self, data: &mut [u8]) -> Result<(), SpiError> {
247 self.read(data).map_err(|e| e.into())
248 }
249
250 fn transfer_blocking(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), SpiError> {
251 self.transfer(read, write).map_err(|e| e.into())
252 }
253
254 fn transfer_in_place_blocking(&mut self, words: &mut [u8]) -> Result<(), SpiError> {
255 self.transfer_in_place(words).map_err(|e| e.into())
256 }
257}
258
259impl<T> DynSpiBus for T
260where
261 T: embedded_hal_async::spi::SpiBus<u8>,
262 T::Error: Into<SpiError>,
263 T: Send,
264{
265 fn flush<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), SpiError>> + 'a>> {
266 Box::pin(async move {
267 embedded_hal_async::spi::SpiBus::flush(self)
268 .await
269 .map_err(|e| e.into())
270 })
271 }
272
273 fn write<'a>(
274 &'a mut self,
275 data: &'a [u8],
276 ) -> Pin<Box<dyn Future<Output = Result<(), SpiError>> + 'a>> {
277 Box::pin(async move {
278 embedded_hal_async::spi::SpiBus::write(self, data)
279 .await
280 .map_err(|e| e.into())
281 })
282 }
283
284 fn read<'a>(
285 &'a mut self,
286 data: &'a mut [u8],
287 ) -> Pin<Box<dyn Future<Output = Result<(), SpiError>> + 'a>> {
288 Box::pin(async move {
289 embedded_hal_async::spi::SpiBus::read(self, data)
290 .await
291 .map_err(|e| e.into())
292 })
293 }
294
295 fn transfer<'a>(
296 &'a mut self,
297 read: &'a mut [u8],
298 write: &'a [u8],
299 ) -> Pin<Box<dyn Future<Output = Result<(), SpiError>> + 'a>> {
300 Box::pin(async move {
301 embedded_hal_async::spi::SpiBus::transfer(self, read, write)
302 .await
303 .map_err(|e| e.into())
304 })
305 }
306
307 fn transfer_in_place<'a>(
308 &'a mut self,
309 words: &'a mut [u8],
310 ) -> Pin<Box<dyn Future<Output = Result<(), SpiError>> + 'a>> {
311 Box::pin(async move {
312 embedded_hal_async::spi::SpiBus::transfer_in_place(self, words)
313 .await
314 .map_err(|e| e.into())
315 })
316 }
317}