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