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//! .
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}