atsamd_hal/sercom/spi/impl_ehal/
thumbv7em.rs

1//! Implement Embedded HAL ([v0.2](ehal_02) and [nb](ehal_nb)) traits for
2//! [`Spi`] structs
3//!
4//! As noted in the [spi module](super) documentation, the embedded-hal trait
5//! implementations vary by both [`Size`] and [`Capability`]. Each
6//! implementation is optimized to take advantage of all information known at
7//! compile-time, so it is importatnt to carefully read the documentation in
8//! this module.
9//!
10//! # Variations by [`Size`]
11//!
12//! Remember that SAMx5x chips operate in 32-bit extension mode and use the
13//! hardware `LENGTH` counter to set the number of bytes in each transaction.
14//! The transaction [`Length`] is usually tracked at compile-time using
15//! type-level integers from the [`typenum`] crate, but it can also be tracked
16//! at run-time when using a [`DynLength`].
17//!
18//! The transaction `Length`s can be sub-divided into three groups:
19//! - `Length`s of 1-4 bytes can be completed in a single read/write of the
20//!   `DATA` register. These `Length`s are marked as [`AtomicSize`]s.
21//! - `Length`s [`GreaterThan4`] are known at compile-time but cannot be
22//!   completed atomically.
23//! - A `DynLength` can be any length, but the value is only known at run-time.
24//!
25//! In general, transaction lengths with an `AtomicSize` implement embedded HAL
26//! traits with the corresponding [`Word`] type. For example, [`Spi`] structs
27//! using a transaction `Length` of 2 bytes implement `FullDuplex<u16>`. These
28//! lengths implement both the blocking and non-blocking traits from embedded
29//! HAL. The non-blocking traits are found in the [`spi`](`ehal_nb::spi`) and
30//! [`serial`](`ehal_nb::serial`) modules, while the blocking traits are found
31//! in the embedded HAL v0.2 [`blocking`](ehal_02::blocking) module.
32//!
33//! Transaction lengths `GreaterThan4` cannot be completed in a single read or
34//! write of the `DATA` register, so these lengths do **NOT** implement the
35//! non-blocking traits from the embedded HAL `spi` and `serial` modules.
36//! Instead, they only implement traits from the `blocking` module. These traits
37//! are implemented for `u8` types, e.g. `blocking::spi::Transfer<u8>`, and
38//! operate on `[u8]` slices. The length of the slice is checked to ensure it
39//! matches the transaction `Length`.
40//!
41//! Because a `DynLength` is not guaranteed to be an `AtomicSize`, the
42//! corresponding `Spi` structs only implement the `blocking` traits as well.
43//!
44//! For a non-blocking alternative that can be used to transfer arbitrary-length
45//! slices, you could use either
46#![cfg_attr(feature = "dma", doc = "[`DMA`](crate::dmac)")]
47#![cfg_attr(not(feature = "dma"), doc = "`DMA`")]
48//! or the [`spi_future`] module.
49//!
50//! # Variations by [`Capability`]
51//!
52//! The implementations in this module also seek to optimize as much as possible
53//! based on the `Capability` of the `Spi` struct. They follow a few general
54//! rules:
55//! - [`Tx`] structs can never receive data, so their corresponding trait
56//!   implementations never read the `DATA` register and can never return an
57//!   [`Error::Overflow`].
58//! - [`Rx`] structs in a [`MasterMode`](super::MasterMode) must initiate all
59//!   transactions, so their implementations of non-blocking traits must track
60//!   the state of on-going transactions.
61//! - [`Duplex`] structs must always read as many bytes as they send, even when
62//!   implementing `Write`-only traits, to ensure they never introduce an
63//!   [`Error::Overflow`].
64//!
65//! # Notes on individual embedded HAL traits
66//!
67//! ## `spi::FullDuplex`
68//!
69//! `spi::FullDuplex` is only implemented for structs with `Duplex`
70//! `Capability`. Although the embedded HAL documentation assumes a
71//! `MasterMode`, this module also implements it for the [`Slave`] [`OpMode`].
72//!
73//! ## `serial::Read`
74//!
75//! `serial::Read` is only implemented for structs with `Rx` `Capability`. When
76//! in a `MasterMode`, it initiates and tracks the state of the on-going
77//! transactions. But this is not required when acting as a `Slave`.
78//!
79//! ## `serial::Write`
80//!
81//! `serial::Write` is only implemented for structs with `Tx` `Capability`.
82//! These implementations never read the `DATA` register and ignore all
83//! instances of [`Error::Overflow`].
84//!
85//! ## `blocking::serial::Write`
86//!
87//! This trait uses the `blocking::serial::write::Default` implementation for
88//! implementers of `serial::Write`.
89//!
90//! ## `blocking::spi` traits
91//!
92//! These traits are implemented following all of the rules outlined above for
93//! the different [`Size`] and [`Capability`] options.
94//!
95//! [`Size`]: super::Size
96//! [`spi_future`]: crate::sercom::spi_future
97
98use crate::ehal_02;
99use crate::ehal_nb;
100use crate::sercom::spi::{
101    AtomicSize, Config, DataWidth, Duplex, DynLength, Error, Flags, GreaterThan4, Length,
102    MasterMode, OpMode, Receive, Rx, Slave, Spi, Status, Tx, ValidConfig, ValidPads, Word,
103};
104#[cfg(doc)]
105use crate::sercom::spi::Capability;
106use nb::Error::WouldBlock;
107use num_traits::{AsPrimitive, PrimInt};
108use typenum::{U1, U2, U3, U4};
109
110use crate::pac::sercom0::RegisterBlock;
111
112//=============================================================================
113// serial::Read
114//=============================================================================
115
116/// Implement [`ehal_nb::serial::Read`] for [`Rx`] [`Spi`] structs in a
117/// [`MasterMode`]
118///
119/// `serial::Read` is only implemented for `Spi` structs with `Rx`
120/// [`Capability`]. In a `MasterMode`, `Read` has to initiate transactions, so
121/// it keeps track of the transaction state. If a transaction is in progress, it
122/// will wait on `RXC`. If not, it will wait on `DRE`, and then send `0`.
123impl<P, M, L> ehal_nb::serial::Read<L::Word> for Spi<Config<P, M, L>, Rx>
124where
125    Config<P, M, L>: ValidConfig,
126    P: ValidPads,
127    M: MasterMode,
128    L: Length,
129    L::Word: PrimInt,
130    DataWidth: AsPrimitive<L::Word>,
131{
132    fn read(&mut self) -> nb::Result<L::Word, Self::Error> {
133        let in_progress = self.capability.in_progress;
134        let flags = self.read_flags_errors()?;
135        if !in_progress && flags.contains(Flags::DRE) {
136            unsafe { self.write_data(0) };
137            self.capability.in_progress = true;
138            Err(WouldBlock)
139        } else if in_progress && flags.contains(Flags::RXC) {
140            self.capability.in_progress = false;
141            unsafe { Ok(self.read_data().as_()) }
142        } else {
143            Err(WouldBlock)
144        }
145    }
146}
147
148/// Implement embedded-hal 0.2 [`serial::Read`] for [`Rx`] [`Spi`] structs in a
149/// [`MasterMode`]
150///
151/// Refer to the [`serial::Read`] implementation of [`Spi`] for more details.
152///
153/// [`serial::Read`]: ehal_02::serial::Read
154impl<P, M, L> ehal_02::serial::Read<L::Word> for Spi<Config<P, M, L>, Rx>
155where
156    Config<P, M, L>: ValidConfig,
157    P: ValidPads,
158    M: MasterMode,
159    L: Length,
160    L::Word: PrimInt,
161    DataWidth: AsPrimitive<L::Word>,
162{
163    type Error = Error;
164
165    #[inline]
166    fn read(&mut self) -> nb::Result<L::Word, Error> {
167        <Self as ehal_nb::serial::Read<L::Word>>::read(self)
168    }
169}
170
171/// Implement [`serial::Read`] for [`Rx`] [`Spi`] structs in [`Slave`]
172/// [`OpMode`]
173///
174/// `serial::Read` is only implemented for `Spi` structs with `Rx`
175/// [`Capability`]. In `Slave` `OpMode`, `Read` does not have to initiate
176/// transactions, so it does not have to store any internal state. It only has
177/// to wait on `RXC`.
178///
179/// [`serial::Read`]: ehal_nb::serial::Read
180impl<P, L> ehal_nb::serial::Read<L::Word> for Spi<Config<P, Slave, L>, Rx>
181where
182    Config<P, Slave, L>: ValidConfig,
183    P: ValidPads,
184    L: Length,
185    L::Word: PrimInt,
186    DataWidth: AsPrimitive<L::Word>,
187{
188    #[inline]
189    fn read(&mut self) -> nb::Result<L::Word, Error> {
190        let flags = self.read_flags_errors()?;
191        if flags.contains(Flags::RXC) {
192            unsafe { Ok(self.read_data().as_()) }
193        } else {
194            Err(WouldBlock)
195        }
196    }
197}
198
199/// Implement embedded-hal 0.2 [`serial::Read`] for [`Rx`] [`Spi`] structs in
200/// [`Slave`] [`OpMode`]
201///
202/// Refer to the [`serial::Read`] implementation of [`Spi`] for more details.
203///
204/// [`serial::Read`]: ehal_02::serial::Read
205impl<P, L> ehal_02::serial::Read<L::Word> for Spi<Config<P, Slave, L>, Rx>
206where
207    Config<P, Slave, L>: ValidConfig,
208    P: ValidPads,
209    L: Length,
210    L::Word: PrimInt,
211    DataWidth: AsPrimitive<L::Word>,
212{
213    type Error = Error;
214
215    #[inline]
216    fn read(&mut self) -> nb::Result<L::Word, Error> {
217        <Self as ehal_nb::serial::Read<L::Word>>::read(self)
218    }
219}
220
221//=============================================================================
222// serial::Write
223//=============================================================================
224
225/// Implement [`ehal_nb::serial::Write`] for [`Tx`] [`Spi`] structs
226///
227/// `serial::Write` is only implemented for `Spi` structs with `Tx`
228/// [`Capability`]. Because the `Capability` is `Tx`, this implementation never
229/// reads the DATA register and ignores all buffer overflow errors.
230impl<C> ehal_nb::serial::Write<C::Word> for Spi<C, Tx>
231where
232    C: ValidConfig,
233    C::Size: AtomicSize,
234    C::Word: PrimInt + AsPrimitive<DataWidth>,
235{
236    #[inline]
237    fn write(&mut self, word: C::Word) -> nb::Result<(), Error> {
238        // Ignore buffer overflow errors
239        if self.read_status().contains(Status::LENERR) {
240            Err(Error::LengthError.into())
241        } else if self.read_flags().contains(Flags::DRE) {
242            self.config.as_mut().regs.write_data(word.as_());
243            Ok(())
244        } else {
245            Err(WouldBlock)
246        }
247    }
248
249    #[inline]
250    fn flush(&mut self) -> nb::Result<(), Error> {
251        // Ignore buffer overflow errors
252        if self.read_status().contains(Status::LENERR) {
253            Err(Error::LengthError.into())
254        } else if self.read_flags().contains(Flags::TXC) {
255            Ok(())
256        } else {
257            Err(WouldBlock)
258        }
259    }
260}
261
262/// Implement embedded-hal 0.2 [`serial::Write`] for [`Tx`] [`Spi`] structs
263///
264/// [`serial::Write`]: ehal_02::serial::Write
265impl<C> ehal_02::serial::Write<C::Word> for Spi<C, Tx>
266where
267    C: ValidConfig,
268    C::Size: AtomicSize,
269    C::Word: PrimInt + AsPrimitive<DataWidth>,
270{
271    type Error = Error;
272
273    #[inline]
274    fn write(&mut self, word: C::Word) -> nb::Result<(), Error> {
275        <Self as ehal_nb::serial::Write<C::Word>>::write(self, word)
276    }
277
278    #[inline]
279    fn flush(&mut self) -> nb::Result<(), Error> {
280        <Self as ehal_nb::serial::Write<C::Word>>::flush(self)
281    }
282}
283
284//=============================================================================
285// blocking::serial::Write
286//=============================================================================
287
288impl<C> ehal_02::blocking::serial::write::Default<C::Word> for Spi<C, Tx>
289where
290    C: ValidConfig,
291    Spi<C, Tx>: ehal_02::serial::Write<C::Word>,
292{
293}
294
295//=============================================================================
296// spi::FullDuplex
297//=============================================================================
298
299// Implement [`spi::FullDuplex`] for [`Spi`] structs with [`AtomicSize`]
300///
301/// `spi::FullDuplex` is only implemented when the `Spi` struct has [`Duplex`]
302/// [`Capability`] and the transaction [`Length`] is `<= 4` bytes. When the
303/// [`Length`] is `<= 4`, the [`Word`] is a primitive integer, with a size that
304/// depends on the [`Length`] (`u8`, `u16` or `u32`).
305impl<C> ehal_nb::spi::FullDuplex<C::Word> for Spi<C, Duplex>
306where
307    C: ValidConfig,
308    C::Size: AtomicSize,
309    C::Word: PrimInt + AsPrimitive<DataWidth>,
310    DataWidth: AsPrimitive<C::Word>,
311{
312    #[inline]
313    fn read(&mut self) -> nb::Result<C::Word, Error> {
314        let flags = self.read_flags_errors()?;
315        if flags.contains(Flags::RXC) {
316            Ok(self.config.as_mut().regs.read_data().as_())
317        } else {
318            Err(WouldBlock)
319        }
320    }
321
322    #[inline]
323    fn write(&mut self, word: C::Word) -> nb::Result<(), Error> {
324        let flags = self.read_flags_errors()?;
325        if flags.contains(Flags::DRE) {
326            self.config.as_mut().regs.write_data(word.as_());
327            Ok(())
328        } else {
329            Err(WouldBlock)
330        }
331    }
332}
333
334/// Implement embedded-hal 0.2 [`spi::FullDuplex`] for [`Spi`] structs with [`AtomicSize`]
335///
336/// `spi::FullDuplex` is only implemented when the `Spi` struct has [`Duplex`]
337/// [`Capability`] and the transaction [`Length`] is `<= 4` bytes. When the
338/// [`Length`] is `<= 4`, the [`Word`] is a primitive integer, with a size that
339/// depends on the [`Length`] (`u8`, `u16` or `u32`).
340///
341/// [`spi::FullDuplex`]: ehal_02::spi::FullDuplex
342impl<C> ehal_02::spi::FullDuplex<C::Word> for Spi<C, Duplex>
343where
344    C: ValidConfig,
345    C::Size: AtomicSize,
346    C::Word: PrimInt + AsPrimitive<DataWidth>,
347    DataWidth: AsPrimitive<C::Word>,
348{
349    type Error = Error;
350
351    #[inline]
352    fn read(&mut self) -> nb::Result<C::Word, Error> {
353        let flags = self.read_flags_errors()?;
354        if flags.contains(Flags::RXC) {
355            Ok(self.config.as_mut().regs.read_data().as_())
356        } else {
357            Err(WouldBlock)
358        }
359    }
360
361    #[inline]
362    fn send(&mut self, word: C::Word) -> nb::Result<(), Error> {
363        let flags = self.read_flags_errors()?;
364        if flags.contains(Flags::DRE) {
365            self.config.as_mut().regs.write_data(word.as_());
366            Ok(())
367        } else {
368            Err(WouldBlock)
369        }
370    }
371}
372
373//=============================================================================
374// blocking::spi::Transfer
375//=============================================================================
376
377macro_rules! impl_blocking_spi_transfer {
378    ( $($Length:ident),+ ) => {
379        $(
380
381            /// Implement embedded_hal 0.2 [`Transfer`] for [`Spi`] structs that
382            /// can [`Receive`] and have an [`AtomicSize`]
383            ///
384            /// The transaction [`Length`] must be `<= 4`. The transfer accepts
385            /// a slice of primitive integers, depending on the `Length` (`u8`,
386            /// `u16` or `u32`).
387            ///
388            /// [`Transfer`]: ehal_02::blocking::spi::Transfer
389            impl<P, M, A> $crate::ehal_02::blocking::spi::Transfer<Word<$Length>> for Spi<Config<P, M, $Length>, A>
390            where
391                Config<P, M, $Length>: ValidConfig,
392                P: ValidPads,
393                M: OpMode,
394                A: Receive,
395            {
396                type Error = Error;
397
398                #[inline]
399                fn transfer<'w>(&mut self, words: &'w mut [Word<$Length>]) -> Result<&'w [Word<$Length>], Error> {
400                    let cells = core::cell::Cell::from_mut(words).as_slice_of_cells();
401                    let mut to_send = cells.iter();
402                    let mut to_recv = cells.iter();
403                    while to_recv.len() > 0 {
404                        let flags = self.read_flags_errors()?;
405                        if to_send.len() > 0 && flags.contains(Flags::DRE) {
406                            let word = match to_send.next() {
407                                Some(cell) => cell.get(),
408                                None => unreachable!(),
409                            };
410                            self.config.as_mut().regs.write_data(word as DataWidth);
411                        }
412                        if to_recv.len() > to_send.len() && flags.contains(Flags::RXC) {
413                            let word = self.config.as_mut().regs.read_data() as Word<$Length>;
414                            match to_recv.next() {
415                                Some(cell) => cell.set(word),
416                                None => unreachable!(),
417                            }
418                        }
419                    }
420                    Ok(words)
421                }
422            }
423        )+
424    }
425}
426
427impl_blocking_spi_transfer!(U1, U2, U3, U4);
428
429/// Implement embedded-hal 0.2 [`Transfer`] for [`Spi`] structs that can
430/// [`Receive`] and have long transaction [`Length`]s
431///
432/// The transaction [`Length`] must be `> 4`. The transfer accepts a slice of
433/// `u8` with a length equal to the transaction [`Length`]. If the slice length
434/// is incorrect, it will panic.
435///
436/// [`Transfer`]: ehal_02::blocking::spi::Transfer
437impl<P, M, L, A> ehal_02::blocking::spi::Transfer<u8> for Spi<Config<P, M, L>, A>
438where
439    Config<P, M, L>: ValidConfig,
440    P: ValidPads,
441    M: OpMode,
442    L: GreaterThan4,
443    A: Receive,
444{
445    type Error = Error;
446
447    #[inline]
448    fn transfer<'w>(&mut self, buf: &'w mut [u8]) -> Result<&'w [u8], Error> {
449        assert_eq!(buf.len(), L::USIZE);
450        let sercom = unsafe { self.config.as_ref().sercom() };
451        transfer_slice(sercom, buf)
452    }
453}
454
455/// Implement embedded-hal 0.2 [`Transfer`] for [`Spi`] structs that can
456/// [`Receive`] and have [`DynLength`]
457///
458/// The transfer accepts a slice of `u8` with a length equal to the run-time
459/// dynamic transaction length. If the slice length does not match the result of
460/// [`Spi::get_dyn_length`], it will panic.
461///
462/// [`Transfer`]: ehal_02::blocking::spi::Transfer
463impl<P, M, A> ehal_02::blocking::spi::Transfer<u8> for Spi<Config<P, M, DynLength>, A>
464where
465    Config<P, M, DynLength>: ValidConfig,
466    P: ValidPads,
467    M: OpMode,
468    A: Receive,
469{
470    type Error = Error;
471
472    #[inline]
473    fn transfer<'w>(&mut self, buf: &'w mut [u8]) -> Result<&'w [u8], Error> {
474        assert_eq!(buf.len(), self.get_dyn_length() as usize);
475        let sercom = unsafe { self.config.as_ref().sercom() };
476        transfer_slice(sercom, buf)
477    }
478}
479
480//=============================================================================
481// blocking::spi::Write
482//=============================================================================
483
484macro_rules! impl_blocking_spi_write {
485    ( $($Length:ident),+ ) => {
486        $(
487
488            /// Implement embedded-hal 0.2 [`Write`] for [`Spi`] structs with
489            /// [`Duplex`] [`Capability`] and an [`AtomicSize`]
490            ///
491            /// The transaction `Length` must be `<= 4`. The transfer accepts a
492            /// slice of primitive integers, depending on the `Length` (`u8`,
493            /// `u16` or `u32`).
494            ///
495            /// [`Write`]: ehal_02::blocking::spi::Write
496            impl<P, M> $crate::ehal_02::blocking::spi::Write<Word<$Length>> for Spi<Config<P, M, $Length>, Duplex>
497            where
498                Config<P, M, $Length>: ValidConfig,
499                P: ValidPads,
500                M: OpMode,
501            {
502                type Error = Error;
503
504                #[inline]
505                fn write(&mut self, words: &[Word<$Length>]) -> Result<(), Error> {
506                    // We are `Duplex`, so we must receive as many words as we send,
507                    // otherwise we could trigger an overflow
508                    let mut to_send = words.iter();
509                    let mut to_recv = to_send.len();
510                    while to_recv > 0 {
511                        let flags = self.read_flags_errors()?;
512                        if to_send.len() > 0 && flags.contains(Flags::DRE) {
513                            let word = match to_send.next() {
514                                Some(word) => *word,
515                                None => unreachable!(),
516                            };
517                            self.config.as_mut().regs.write_data(word as DataWidth);
518                        }
519                        if to_recv > to_send.len() && flags.contains(Flags::RXC) {
520                            self.config.as_mut().regs.read_data() as Word<$Length>;
521                            to_recv -= 1;
522                        }
523                    }
524                    Ok(())
525                }
526            }
527
528            /// Implement embedded-hal 0.2 [`Write`] for [`Spi`] structs with
529            /// [`Tx`] [`Capability`] and an [`AtomicSize`]
530            ///
531            /// The transaction `Length` must be `<= 4`. The transfer accepts a
532            /// slice of primitive integers, depending on the `Length` (`u8`,
533            /// `u16` or `u32`).
534            ///
535            /// Because the `Capability` is `Tx`, this implementation never
536            /// reads the DATA register and ignores all buffer overflow errors.
537            ///
538            /// [`Write`]: ehal_02::blocking::spi::Write
539            impl<P, M> $crate::ehal_02::blocking::spi::Write<Word<$Length>> for Spi<Config<P, M, $Length>, Tx>
540            where
541                Config<P, M, $Length>: ValidConfig,
542                P: ValidPads,
543                M: OpMode,
544            {
545                type Error = Error;
546
547                #[inline]
548                fn write(&mut self, words: &[Word<$Length>]) -> Result<(), Error> {
549                    // We are `Tx`, so we don't have to consider reading at all, ever.
550                    for word in words {
551                        loop {
552                            // Ignore buffer overflow errors
553                            if self.read_status().contains(Status::LENERR) {
554                                return Err(Error::LengthError)
555                            } else if self.read_flags().contains(Flags::DRE) {
556                                self.config.as_mut().regs.write_data(*word as DataWidth);
557                                break
558                            }
559                        }
560                    }
561                    // Wait until all data is shifted out
562                    while !self.read_flags().contains(Flags::TXC) {}
563                    Ok(())
564                }
565            }
566        )+
567    }
568}
569
570impl_blocking_spi_write!(U1, U2, U3, U4);
571
572/// Implement embedded-hal 0.2 [`Write`] for [`Spi`] structs with [`Duplex`]
573/// [`Capability`] and long transaction [`Length`]s
574///
575/// The transaction [`Length`] must be `> 4`. The transfer accepts a `[u8]` with
576/// a length equal to the transfer [`Length`]. If the slice length is incorrect,
577/// it will panic.
578///
579/// [`Write`]: ehal_02::blocking::spi::Write
580impl<P, M, L> ehal_02::blocking::spi::Write<u8> for Spi<Config<P, M, L>, Duplex>
581where
582    Config<P, M, L>: ValidConfig,
583    P: ValidPads,
584    M: OpMode,
585    L: GreaterThan4,
586{
587    type Error = Error;
588
589    #[inline]
590    fn write(&mut self, buf: &[u8]) -> Result<(), Error> {
591        if buf.len() != L::USIZE {
592            panic!("Slice length does not equal SPI transfer length");
593        }
594        let sercom = unsafe { self.config.as_ref().sercom() };
595        write_slice(sercom, buf, true)
596    }
597}
598
599/// Implement embedded-hal 0.2 [`Write`] for [`Spi`] structs with [`Tx`]
600/// [`Capability`] and long transaction [`Length`]s
601///
602/// The transaction [`Length`] must be `> 4`. The transfer accepts a `[u8]` with
603/// a length equal to the transfer [`Length`]. If the slice length is incorrect,
604/// it will panic.
605///
606/// Because the `Capability` is `Tx`, this implementation never reads the DATA
607/// register and ignores all buffer overflow errors.
608///
609/// [`Write`]: ehal_02::blocking::spi::Write
610impl<P, M, L> ehal_02::blocking::spi::Write<u8> for Spi<Config<P, M, L>, Tx>
611where
612    Config<P, M, L>: ValidConfig,
613    P: ValidPads,
614    M: OpMode,
615    L: GreaterThan4,
616{
617    type Error = Error;
618
619    #[inline]
620    fn write(&mut self, buf: &[u8]) -> Result<(), Error> {
621        if buf.len() != L::USIZE {
622            panic!("Slice length does not equal SPI transfer length");
623        }
624        let sercom = unsafe { self.config.as_ref().sercom() };
625        write_slice(sercom, buf, false)?;
626        // Wait until all data is shifted out
627        while !self.read_flags().contains(Flags::TXC) {}
628        Ok(())
629    }
630}
631
632/// Implement embedded-hal 0.2 [`Write`] for [`Spi`] structs with [`Duplex`]
633/// [`Capability`] and [`DynLength`]
634///
635/// The transfer accepts a `[u8]` with a length equal to the run-time dynamic
636/// transaction length. If the slice length does not match the result of
637/// [`Spi::get_dyn_length`], it will panic.
638///
639/// [`Write`]: ehal_02::blocking::spi::Write
640impl<P, M> ehal_02::blocking::spi::Write<u8> for Spi<Config<P, M, DynLength>, Duplex>
641where
642    Config<P, M, DynLength>: ValidConfig,
643    P: ValidPads,
644    M: OpMode,
645{
646    type Error = Error;
647
648    #[inline]
649    fn write(&mut self, buf: &[u8]) -> Result<(), Error> {
650        if buf.len() != self.get_dyn_length() as usize {
651            panic!("Slice length does not equal SPI transfer length");
652        }
653        let sercom = unsafe { self.config.as_ref().sercom() };
654        write_slice(sercom, buf, true)
655    }
656}
657
658/// Implement embedded-hal 0.2 [`Write`] for [`Spi`] structs with [`Tx`]
659/// [`Capability`] and [`DynLength`]
660///
661/// The transfer accepts a `[u8]` with a length equal to the run-time dynamic
662/// transaction length. If the slice length does not match the result of
663/// `Spi::get_dyn_length`], it will panic.
664///
665/// Because the `Capability` is `Tx`, this implementation never reads the DATA
666/// register and ignores all buffer overflow errors.
667///
668/// [`Write`]: ehal_02::blocking::spi::Write
669impl<P, M> ehal_02::blocking::spi::Write<u8> for Spi<Config<P, M, DynLength>, Tx>
670where
671    Config<P, M, DynLength>: ValidConfig,
672    P: ValidPads,
673    M: OpMode,
674{
675    type Error = Error;
676
677    #[inline]
678    fn write(&mut self, buf: &[u8]) -> Result<(), Error> {
679        if buf.len() != self.get_dyn_length() as usize {
680            panic!("Slice length does not equal SPI transfer length");
681        }
682        let sercom = unsafe { self.config.as_ref().sercom() };
683        write_slice(sercom, buf, false)?;
684        // Wait until all data is shifted out
685        while !self.read_flags().contains(Flags::TXC) {}
686        Ok(())
687    }
688}
689
690//=============================================================================
691// blocking::spi::WriteIter
692//=============================================================================
693
694macro_rules! impl_blocking_spi_write_iter {
695    ( $($Length:ident),+ ) => {
696        $(
697
698            /// Implement embedded-hal 0.2 [`WriteIter`] for [`Spi`] structs
699            /// with [`Duplex`] [`Capability`] and an [`AtomicSize`]
700            ///
701            /// The transaction `Length` must be `<= 4`. The transfer accepts a
702            /// slice of primitive integers, depending on the `Length` (`u8`,
703            /// `u16` or `u32`).
704            ///
705            /// [`WriteIter`]: ehal_02::blocking::spi::WriteIter
706            impl<P, M> $crate::ehal_02::blocking::spi::WriteIter<Word<$Length>> for Spi<Config<P, M, $Length>, Duplex>
707            where
708                Config<P, M, $Length>: ValidConfig,
709                P: ValidPads,
710                M: OpMode,
711            {
712                type Error = Error;
713
714                #[inline]
715                fn write_iter<WI>(&mut self, words: WI) -> Result<(), Error>
716                where
717                    WI: IntoIterator<Item = Word<$Length>>,
718                {
719                    // We are `Duplex`, so we must receive as many words as we send,
720                    // otherwise we could trigger an overflow. However, we don't know
721                    // how many words there are to start with, so we have to send and
722                    // receive them one at a time.
723                    for word in words.into_iter() {
724                        loop {
725                            let flags = self.read_flags_errors()?;
726                            if flags.contains(Flags::DRE) {
727                                unsafe { self.write_data(word as DataWidth) };
728                                break
729                            }
730                        }
731                        loop {
732                            let flags = self.read_flags_errors()?;
733                            if flags.contains(Flags::RXC) {
734                                self.config.as_mut().regs.read_data() as Word<$Length>;
735                                break
736                            }
737                        }
738                    }
739                    Ok(())
740                }
741            }
742            /// Implement embedded-hal 0.2 [`WriteIter`] for [`Spi`] structs
743            /// with [`Tx`] [`Capability`] and an [`AtomicSize`]
744            ///
745            /// The transaction `Length` must be `<= 4`. The transfer accepts a
746            /// slice of primitive integers, depending on the `Length` (`u8`,
747            /// `u16` or `u32`).
748            ///
749            /// Because the `Capability` is `Tx`, this implementation never
750            /// reads the DATA register and ignores all buffer overflow errors.
751            ///
752            /// [`WriteIter`]: embedded_hal_02::blocking::spi::WriteIter
753            impl<P, M> $crate::ehal_02::blocking::spi::WriteIter<Word<$Length>> for Spi<Config<P, M, $Length>, Tx>
754            where
755                Config<P, M, $Length>: ValidConfig,
756                P: ValidPads,
757                M: OpMode,
758            {
759                type Error = Error;
760
761                #[inline]
762                fn write_iter<WI>(&mut self, words: WI) -> Result<(), Error>
763                where
764                    WI: IntoIterator<Item = Word<$Length>>,
765                {
766                    // We are `Tx`, so we don't have to consider reading at all, ever.
767                    for word in words.into_iter() {
768                        loop {
769                            // Ignore buffer overflow errors
770                            if self.read_status().contains(Status::LENERR) {
771                                return Err(Error::LengthError)
772                            } else if self.read_flags().contains(Flags::DRE) {
773                                unsafe { self.write_data(word as DataWidth) };
774                                break
775                            }
776                        }
777                    }
778                    // Wait until all data is shifted out
779                    while !self.read_flags().contains(Flags::TXC) {}
780                    Ok(())
781                }
782            }
783        )+
784    };
785}
786
787impl_blocking_spi_write_iter!(U1, U2, U3, U4);
788
789//=============================================================================
790// Helper functions
791//=============================================================================
792
793/// Transfer a `[u8]` slice four bytes at a time
794///
795/// This function exists to avoid both code duplication and monomorphization
796/// bloat. It will take a `[u8]` and transfer it four bytes at a time.
797fn transfer_slice<'w>(sercom: &RegisterBlock, buf: &'w mut [u8]) -> Result<&'w [u8], Error> {
798    let cells = core::cell::Cell::from_mut(buf).as_slice_of_cells();
799    let mut to_send = cells.iter();
800    let mut to_recv = cells.iter();
801    while to_recv.len() > 0 {
802        let errors = sercom.spim().status().read();
803        if errors.bufovf().bit_is_set() {
804            return Err(Error::Overflow);
805        }
806        if errors.lenerr().bit_is_set() {
807            return Err(Error::LengthError);
808        }
809        let flags = sercom.spim().intflag().read();
810        if to_send.len() > 0 && flags.dre().bit_is_set() {
811            let mut bytes = [0; 4];
812            for byte in &mut bytes {
813                match to_send.next() {
814                    Some(cell) => *byte = cell.get(),
815                    None => break,
816                }
817            }
818            let word = u32::from_le_bytes(bytes);
819            sercom
820                .spim()
821                .data()
822                .write(|w| unsafe { w.data().bits(word) });
823        }
824        if to_recv.len() > to_send.len() && flags.rxc().bit_is_set() {
825            let word = sercom.spim().data().read().data().bits();
826            let bytes = word.to_le_bytes();
827            for byte in bytes.iter() {
828                match to_recv.next() {
829                    Some(cell) => cell.set(*byte),
830                    None => break,
831                }
832            }
833        }
834    }
835    Ok(buf)
836}
837
838/// Write a `[u8]` four bytes at a time
839///
840/// This function exists to avoid both code duplication and monomorphization
841/// bloat. It will take a `[u8]` and write four bytes at a time to the SPI on
842/// every DRE flag. If the `duplex` argument is true, it will read as many times
843/// as it writes. Otherwise, it will skip reading the `DATA` register entirely.
844/// If `duplex` is false, buffer overflow errors are ignored
845fn write_slice(sercom: &RegisterBlock, buf: &[u8], duplex: bool) -> Result<(), Error> {
846    let mut to_send = buf.iter();
847    let mut to_recv: usize = to_send.len();
848    while to_recv > 0 {
849        let errors = sercom.spim().status().read();
850        if duplex && errors.bufovf().bit_is_set() {
851            return Err(Error::Overflow);
852        }
853        if errors.lenerr().bit_is_set() {
854            return Err(Error::LengthError);
855        }
856        let flags = sercom.spim().intflag().read();
857        // Send the word
858        if to_send.len() > 0 && flags.dre().bit_is_set() {
859            let mut bytes = [0; 4];
860            for byte in &mut bytes {
861                match to_send.next() {
862                    Some(d) => *byte = *d,
863                    None => break,
864                }
865            }
866            let word = u32::from_le_bytes(bytes);
867            sercom
868                .spim()
869                .data()
870                .write(|w| unsafe { w.data().bits(word) });
871        }
872        if duplex && to_recv > to_send.len() && flags.rxc().bit_is_set() {
873            sercom.spim().data().read().data().bits();
874            let diff = to_recv - to_send.len();
875            to_recv -= if diff < 4 { diff } else { 4 };
876        }
877    }
878    Ok(())
879}