atsamd_hal/sercom/spi/async_api/
mod.rs

1use crate::{
2    async_hal::interrupts::{Binding, Handler, InterruptSource},
3    sercom::{
4        spi::{
5            Capability, Config, DataWidth, Duplex, Error, Flags, MasterMode, OpMode, Rx, Size, Spi,
6            Tx, ValidConfig, ValidPads,
7        },
8        Sercom,
9    },
10    typelevel::NoneT,
11};
12use atsamd_hal_macros::hal_macro_helper;
13use core::{marker::PhantomData, task::Poll};
14use embedded_hal_async::spi::{ErrorType, SpiBus};
15use num_traits::{AsPrimitive, PrimInt};
16
17#[cfg(feature = "dma")]
18mod dma;
19#[cfg(feature = "dma")]
20pub use dma::*;
21
22use super::{Receive, Slave, Transmit};
23
24/// Interrupt handler for async SPI operarions
25pub struct InterruptHandler<S: Sercom> {
26    _private: (),
27    _sercom: PhantomData<S>,
28}
29
30impl<S: Sercom> crate::typelevel::Sealed for InterruptHandler<S> {}
31
32impl<S: Sercom> Handler<S::Interrupt> for InterruptHandler<S> {
33    #[inline]
34    #[hal_macro_helper]
35    unsafe fn on_interrupt() {
36        unsafe {
37            let mut peripherals = crate::pac::Peripherals::steal();
38
39            #[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
40            let spi = S::reg_block(&mut peripherals).spi();
41            #[hal_cfg("sercom0-d5x")]
42            let spi = S::reg_block(&mut peripherals).spim();
43
44            let flags_pending = Flags::from_bits_truncate(spi.intflag().read().bits());
45            let enabled_flags = Flags::from_bits_truncate(spi.intenset().read().bits());
46
47            // Disable interrupts, but don't clear the flags. The future will take care of
48            // clearing flags and re-enabling interrupts when woken.
49            if (Flags::RX & enabled_flags).intersects(flags_pending) {
50                spi.intenclr().write(|w| w.bits(flags_pending.bits()));
51                S::rx_waker().wake();
52            }
53
54            if (Flags::TX & enabled_flags).intersects(flags_pending) {
55                spi.intenclr().write(|w| w.bits(flags_pending.bits()));
56                S::tx_waker().wake();
57            }
58        }
59    }
60}
61
62impl<C, A, S> Spi<C, A>
63where
64    C: ValidConfig<Sercom = S>,
65    A: Capability,
66    S: Sercom,
67{
68    /// Turn an [`Spi`] into a [`SpiFuture`].
69    ///
70    /// In cases where the underlying [`Spi`] is [`Duplex`], reading words need
71    /// to be accompanied with sending a no-op word. By default it is set to
72    /// 0x00, but you can configure it using [`Config::set_nop_word`].
73    #[inline]
74    pub fn into_future<I>(self, _interrupts: I) -> SpiFuture<C, A>
75    where
76        C::Word: Copy,
77        u8: AsPrimitive<C::Word>,
78        I: Binding<S::Interrupt, InterruptHandler<S>>,
79    {
80        S::Interrupt::unpend();
81        unsafe { S::Interrupt::enable() };
82
83        SpiFuture { spi: self }
84    }
85}
86
87/// `async` version of [`Spi`].
88///
89/// Create this struct by calling [`Spi::into_future`](Spi::into_future).
90pub struct SpiFuture<C, A, R = NoneT, T = NoneT>
91where
92    C: ValidConfig,
93    A: Capability,
94{
95    spi: Spi<C, A, R, T>,
96}
97
98#[cfg(feature = "defmt")]
99impl<C, A, R, T> defmt::Format for SpiFuture<C, A, R, T>
100where
101    C: ValidConfig,
102    A: Capability,
103{
104    fn format(&self, f: defmt::Formatter) {
105        defmt::write!(f, "SpiFuture defmt shim\n");
106    }
107}
108
109/// Convenience type for a [`SpiFuture`] with RX and TX capabilities
110pub type SpiFutureDuplex<C> = SpiFuture<C, Duplex>;
111
112/// Convenience type for a [`SpiFuture`] with RX capabilities
113pub type SpiFutureRx<C> = SpiFuture<C, Rx>;
114
115/// Convenience type for a [`SpiFuture`] with TX capabilities
116pub type SpiFutureTx<C> = SpiFuture<C, Tx>;
117
118impl<C, A, S, R, T> SpiFuture<C, A, R, T>
119where
120    C: ValidConfig<Sercom = S>,
121    A: Capability,
122    S: Sercom,
123{
124    #[inline]
125    async fn wait_flags(&mut self, flags_to_wait: Flags) -> Result<(), Error> {
126        let flags_to_wait = flags_to_wait | Flags::ERROR;
127
128        core::future::poll_fn(|cx| {
129            // Scope maybe_pending so we don't forget to re-poll the register later down.
130            {
131                let maybe_pending = self.spi.config.as_ref().regs.read_flags();
132                if flags_to_wait.intersects(maybe_pending) {
133                    return Poll::Ready(self.spi.check_and_clear_error(maybe_pending));
134                }
135            }
136
137            self.spi.disable_interrupts(Flags::all());
138
139            if flags_to_wait.intersects(Flags::RX) {
140                S::rx_waker().register(cx.waker());
141            }
142            if flags_to_wait.intersects(Flags::TX) {
143                S::tx_waker().register(cx.waker());
144            }
145
146            self.spi.enable_interrupts(flags_to_wait);
147            let maybe_pending = self.spi.config.as_ref().regs.read_flags();
148
149            if !flags_to_wait.intersects(maybe_pending) {
150                Poll::Pending
151            } else {
152                Poll::Ready(self.spi.check_and_clear_error(maybe_pending))
153            }
154        })
155        .await?;
156
157        Ok(())
158    }
159}
160
161impl<C, D> SpiFuture<C, D, NoneT, NoneT>
162where
163    C: ValidConfig,
164    D: Capability,
165{
166    /// Return the underlying [`Spi`].
167    pub fn free(self) -> Spi<C, D> {
168        self.spi
169    }
170}
171
172impl<P, M, C, D, R, S> SpiFuture<Config<P, M, C>, D, R, NoneT>
173where
174    Config<P, M, C>: ValidConfig<Sercom = S>,
175    P: ValidPads,
176    M: OpMode,
177    C: Size + 'static,
178    C::Word: PrimInt + AsPrimitive<DataWidth>,
179    D: Transmit,
180    DataWidth: AsPrimitive<C::Word>,
181    S: Sercom,
182{
183    /// Write words from a buffer asynchronously, word by word
184    #[inline]
185    pub async fn write(&mut self, words: &[C::Word]) -> Result<(), Error> {
186        if words.is_empty() {
187            return Ok(());
188        }
189        // When in Duplex mode, read as many words as we write to avoid buffer overflows
190        for word in words {
191            let _ = self.transfer_word_in_place(*word).await?;
192        }
193
194        Ok(())
195    }
196}
197
198impl<P, M, C, D, T, S> SpiFuture<Config<P, M, C>, D, NoneT, T>
199where
200    Config<P, M, C>: ValidConfig<Sercom = S>,
201    P: ValidPads,
202    M: MasterMode,
203    C: Size + 'static,
204    C::Word: PrimInt + AsPrimitive<DataWidth>,
205    D: Receive,
206    DataWidth: AsPrimitive<C::Word>,
207    S: Sercom,
208{
209    /// Read words into a buffer asynchronously, word by word.
210    ///
211    /// Since we are using a [`Duplex`] [`SpiFuture`], we need to send a word
212    /// simultaneously while receiving one. This `no-op` word is configurable
213    /// via [`Config::set_nop_word`].
214    #[inline]
215    pub async fn read(&mut self, buffer: &mut [C::Word]) -> Result<(), Error> {
216        if buffer.is_empty() {
217            return Ok(());
218        }
219
220        let nop_word = self.spi.config.as_ref().nop_word.as_();
221        for byte in buffer.iter_mut() {
222            *byte = self.transfer_word_in_place(nop_word).await?;
223        }
224
225        Ok(())
226    }
227}
228
229impl<P, M, C, S, D, R, T> SpiFuture<Config<P, M, C>, D, R, T>
230where
231    Config<P, M, C>: ValidConfig<Sercom = S>,
232    P: ValidPads,
233    M: OpMode,
234    C: Size + 'static,
235    C::Word: PrimInt + AsPrimitive<DataWidth>,
236    DataWidth: AsPrimitive<C::Word>,
237    D: Capability,
238    S: Sercom,
239{
240    /// Read and write a single word to the bus simultaneously.
241    pub async fn transfer_word_in_place(&mut self, to_send: C::Word) -> Result<C::Word, Error> {
242        self.wait_flags(Flags::DRE).await?;
243        unsafe {
244            self.spi.write_data(to_send.as_());
245        }
246
247        self.flush_rx().await?;
248        let word = unsafe { self.spi.read_data().as_() };
249
250        Ok(word)
251    }
252
253    /// Perform a transfer, word by word.
254    ///
255    /// No-op words will be written if `read` is longer than `write`. Extra
256    /// words are ignored if `write` is longer than `read`.
257    async fn transfer_word_by_word(
258        &mut self,
259        read: &mut [C::Word],
260        write: &[C::Word],
261    ) -> Result<(), Error> {
262        let nop_word = self.spi.config.as_ref().nop_word.as_();
263        for (r, w) in read
264            .iter_mut()
265            .zip(write.iter().chain(core::iter::repeat(&nop_word)))
266        {
267            *r = self.transfer_word_in_place(*w).await?;
268        }
269
270        Ok(())
271    }
272
273    /// Wait on a TXC while ignoring buffer overflow errors.
274    #[inline]
275    async fn flush_tx(&mut self) {
276        // Ignore buffer overflow errors
277        let _ = self.wait_flags(Flags::TXC).await;
278    }
279
280    /// Wait on RXC flag
281    #[inline]
282    async fn flush_rx(&mut self) -> Result<(), Error> {
283        self.wait_flags(Flags::RXC).await
284    }
285}
286
287impl<C, A, R, T> AsRef<Spi<C, A, R, T>> for SpiFuture<C, A, R, T>
288where
289    C: ValidConfig,
290    A: Capability,
291{
292    #[inline]
293    fn as_ref(&self) -> &Spi<C, A, R, T> {
294        &self.spi
295    }
296}
297
298impl<C, A, R, T> AsMut<Spi<C, A, R, T>> for SpiFuture<C, A, R, T>
299where
300    C: ValidConfig,
301    A: Capability,
302{
303    #[inline]
304    fn as_mut(&mut self) -> &mut Spi<C, A, R, T> {
305        &mut self.spi
306    }
307}
308
309impl<C, A, R, T> ErrorType for SpiFuture<C, A, R, T>
310where
311    C: ValidConfig,
312    A: Capability,
313{
314    type Error = Error;
315}
316
317impl<C, A, R, T> embedded_io::ErrorType for SpiFuture<C, A, R, T>
318where
319    C: ValidConfig,
320    A: Capability,
321{
322    type Error = Error;
323}
324
325impl<P, M, C, S> SpiBus<C::Word> for SpiFuture<Config<P, M, C>, Duplex>
326where
327    Config<P, M, C>: ValidConfig<Sercom = S>,
328    P: ValidPads,
329    M: MasterMode,
330    C: Size + 'static,
331    C::Word: PrimInt + AsPrimitive<DataWidth>,
332    DataWidth: AsPrimitive<C::Word>,
333    S: Sercom,
334{
335    async fn read(&mut self, words: &mut [C::Word]) -> Result<(), Self::Error> {
336        self.read(words).await
337    }
338
339    async fn write(&mut self, words: &[C::Word]) -> Result<(), Self::Error> {
340        self.write(words).await
341    }
342
343    async fn transfer(
344        &mut self,
345        read: &mut [C::Word],
346        write: &[C::Word],
347    ) -> Result<(), Self::Error> {
348        self.transfer_word_by_word(read, write).await
349    }
350
351    async fn transfer_in_place(&mut self, words: &mut [C::Word]) -> Result<(), Self::Error> {
352        // Can only ever do word-by-word to avoid DMA race conditions
353        for word in words {
354            let read = self.transfer_word_in_place(*word).await?;
355            *word = read;
356        }
357
358        Ok(())
359    }
360
361    async fn flush(&mut self) -> Result<(), Self::Error> {
362        // Wait for all transactions to complete, ignoring buffer overflow errors.
363        self.flush_tx().await;
364        Ok(())
365    }
366}
367
368/// [`embedded_io::Write`] implementation for [`Transmit`] [`SpiFuture`]s in
369/// either [`Slave`] or [`MasterMode`].
370impl<P, M, Z, D, R, S> embedded_io_async::Write for SpiFuture<Config<P, M, Z>, D, R, NoneT>
371where
372    Config<P, M, Z>: ValidConfig<Sercom = S>,
373    P: ValidPads,
374    M: OpMode,
375    Z: Size<Word = u8> + 'static,
376    D: Transmit,
377    S: Sercom,
378{
379    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
380        self.write(buf).await?;
381        Ok(buf.len())
382    }
383
384    async fn flush(&mut self) -> Result<(), Self::Error> {
385        self.flush_tx().await;
386        Ok(())
387    }
388}
389
390/// [`embedded_io::Read`] implementation for [`Receive`] [`SpiFuture`]s in
391/// [`MasterMode`].
392impl<P, M, Z, D, T, S> embedded_io_async::Read for SpiFuture<Config<P, M, Z>, D, NoneT, T>
393where
394    P: ValidPads,
395    M: MasterMode,
396    Z: Size<Word = u8> + 'static,
397    Config<P, M, Z>: ValidConfig<Sercom = S>,
398    D: Receive,
399    S: Sercom,
400{
401    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
402        self.read(buf).await?;
403        Ok(buf.len())
404    }
405}
406
407/// [`embedded_io::Read`] implementation for [`Receive`] [`SpiFuture`]s in
408/// [`Slave`] mode.
409impl<P, Z, D, T, S> embedded_io_async::Read for SpiFuture<Config<P, Slave, Z>, D, NoneT, T>
410where
411    P: ValidPads,
412    Z: Size<Word = u8> + 'static,
413    Config<P, Slave, Z>: ValidConfig<Sercom = S>,
414    D: Receive,
415    S: Sercom,
416{
417    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
418        if buf.is_empty() {
419            return Ok(0);
420        }
421
422        for word in buf.iter_mut() {
423            self.flush_rx().await?;
424            *word = unsafe { self.spi.read_data().as_() };
425        }
426
427        Ok(buf.len())
428    }
429}