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#")]
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}