atsamd_hal/sercom/spi.rs
1//! Use a SERCOM peripheral for SPI transactions
2//!
3//! Using an SPI peripheral occurs in three steps. First, you must supply
4//! [`gpio`] [`Pin`]s to create a set of [`Pads`]. Next, you combine the `Pads`
5//! with other pieces to form a [`Config`] struct. Finally, after configuring
6//! the peripheral, you [`enable`] it to yield a functional [`Spi`] struct.
7//! Transactions are performed using traits from the Embedded HAL crates,
8//! specifically those from the [`spi`](ehal::spi),
9//! [`serial`](embedded_hal_02::serial), and
10//! [`blocking`](embedded_hal_02::blocking) modules.
11//!
12//! # Crating a set of [`Pads`]
13//!
14//! An SPI peripheral can use up to four [`Pin`]s as [`Sercom`] pads. However,
15//! only certain `Pin` combinations are acceptable. All `Pin`s must be mapped to
16//! the same `Sercom`, and for SAMx5x chips they must also belong to the same
17//! [`IoSet`]. This HAL makes it impossible to use invalid `Pin` combinations,
18//! and the [`Pads`] struct is responsible for enforcing these constraints.
19//!
20//! A `Pads` type takes five parameters: the first specifies the `Sercom`, and
21//! the remaining four type parameters, `DI`, `DO`, `CK` and `SS`, represent the
22//! Data In, Data Out, Sclk and SS pads respectively. Each of these type
23//! parameters is an [`OptionalPad`] and defaults to [`NoneT`]. A `Pad` is just
24//! a `Pin` configured in the correct [`PinMode`] that implements [`IsPad`]. The
25//! [`bsp_pins!`](crate::bsp_pins) macro can be used to define convenient type
26//! aliases for `Pad` types.
27//!
28//! ```
29//! use atsamd_hal::gpio::{PA08, PA09, AlternateC};
30//! use atsamd_hal::sercom::{Sercom0, spi};
31//! use atsamd_hal::typelevel::NoneT;
32//!
33//! type Miso = Pin<PA08, AlternateC>;
34//! type Sclk = Pin<PA09, AlternateC>;
35//!
36//! type Pads = spi::Pads<Sercom0, Miso, NoneT, Sclk>;
37//! ```
38//!
39//! [`enable`]: Config::enable
40//! [`gpio`]: crate::gpio
41//! [`IoSet`]: crate::sercom::pad::IoSet
42//! [`Pin`]: crate::gpio::pin::Pin
43//! [`PinId`]: crate::gpio::pin::PinId
44//! [`PinMode`]: crate::gpio::pin::PinMode
45//!
46//!
47//! Alternatively, you can use the `PadsFromIds` alias to define a set of `Pads`
48//! in terms of [`PinId`]s instead of [`Pin`]s. This is useful when you don't
49//! have [`Pin`] aliases pre-defined.
50//!
51//! ```
52//! use atsamd_hal::gpio::{PA08, PA09};
53//! use atsamd_hal::sercom::{Sercom0, spi};
54//! use atsamd_hal::typelevel::NoneT;
55//!
56//! type Pads = spi::PadsFromIds<Sercom0, PA08, NoneT, PA09>;
57//! ```
58//!
59//! Instances of `Pads` are created using the builder pattern. Start by creating
60//! an empty set of `Pads` using [`Default`]. Then pass each respective `Pin`
61//! using the corresponding methods. For SAMD21 and SAMx5x chips, the builder
62//! methods automatically convert each pin to the correct [`PinMode`]. However,
63//! due to inherent ambiguities, users must manually configure `PinMode`s for
64//! SAMD11 chips.
65//!
66//! ```
67//! use atsamd_hal::target_device::Peripherals;
68//! use atsamd_hal::gpio::Pins;
69//! use atsamd_hal::sercom::{Sercom0, spi};
70//!
71//! let mut peripherals = Peripherals::take().unwrap();
72//! let pins = Pins::new(peripherals.PORT);
73//!
74//! let pads = spi::Pads::<Sercom0>::default()
75//! .sclk(pins.pa09)
76//! .data_in(pins.pa08)
77//! .data_out(pins.pa11);
78//! ```
79//!
80//! To be accepted by the [`Config`] struct as a set of [`ValidPads`], the
81//! `Pads` must do two things:
82//! - Specify [`SomePad`] for `CK` and at least one of `DI` or `DO`
83//! - Use a valid combination of [`PadNum`]s, so that the `Pads` implement
84//! [`DipoDopo`]
85//!
86//! # `Config`uring the peripheral
87//!
88//! Next, create a [`Config`] struct, which represents the SPI peripheral in its
89//! disabled state. A `Config` is specified with three type parameters: the
90//! [`Pads`] type; an [`OpMode`], which defaults to [`Master`]; and a [`Size`]
91//! type that varies by chip. [`Size`] essentially acts as a trait alias. On
92//! SAMD11 and SAMD21 chips, it represents the `CharSize`, which can either be
93//! `EightBit` or `NineBit`. While on SAMx5x chips, it represents the
94//! transaction `Length` in bytes, using type-level numbers provided by the
95//! [`typenum`] crate. Valid transaction lengths, from `U1` to `U255`, are
96//! re-exported in the `lengths` sub-module.
97//!
98//! ```
99//! use atsamd_hal::gpio::{PA08, PA09};
100//! use atsamd_hal::sercom::{Sercom0, spi};
101//! use atsamd_hal::sercom::spi::Master;
102//! use atsamd_hal::typelevel::NoneT;
103//!
104//! // SAMD11/SAMD21-specific imports
105//! use atsamd_hal::sercom::spi::NineBit;
106//!
107//! // SAMx5x-specific imports
108//! use atsamd_hal::sercom::spi::lengths::U2;
109//!
110//! type Pads = spi::PadsFromIds<Sercom0, PA08, NoneT, PA09>;
111//!
112//! // SAMD11/SAMD21 version
113//! type Config = spi::Config<Pads, Master, NineBit>;
114//!
115//! // SAMx5x version
116//! type Config = spi::Config<Pads, Master, U2>;
117//! ```
118//!
119//! For simplicity, this module ignores character size on SAMx5x chips. Instead,
120//! the SPI peripheral is always configured to use 32-bit extension mode and the
121//! hardware `LENGTH` counter. Note that, due to a hardware bug, `ICSPACE` must
122//! be at least one when using the length counter. See the silicon errata for
123//! more details.
124//!
125//! Upon creation, the [`Config`] takes ownership of both the [`Pads`] and the
126//! PAC [`Sercom`] struct. It takes a reference to the `PM` or `MCLK`, so that
127//! it can enable the APB clock, and it takes a frequency to indicate the GCLK
128//! configuration. Users are responsible for correctly configuring the GCLK.
129//!
130//! ```
131//! use atsamd_hal::time::U32Ext;
132//!
133//! // Not shown: configure GCLK for 10 MHz
134//!
135//! // SAMD11/SAMD21 version
136//! let pm = peripherals.PM;
137//! let sercom = peripherals.SERCOM0;
138//! let freq = 10.mhz();
139//! let config = spi::Config::new(&pm, sercom, pads, freq);
140//!
141//! // SAMx5x version
142//! let mclk = peripherals.MCLK;
143//! let sercom = peripherals.SERCOM0;
144//! let freq = 10.mhz();
145//! let config = spi::Config::new(&mclk, sercom, pads, freq);
146//! ```
147//!
148//! The [`Config`] uses two different APIs for configuration. For most
149//! parameters, it provides `get_` and `set_` methods that take `&self` and
150//! `&mut self` respectively, e.g. [`get_bit_order`](Config::get_bit_order) and
151//! [`set_bit_order`](Config::set_bit_order). However, because `Config` tracks
152//! the [`OpMode`] and [`Size`] at compile-time, which requires changing the
153//! corresponding type parameters, `Config` also provides a builder-pattern API,
154//! where methods take and return `self`, e.g. [`bit_order`](Config::bit_order).
155//!
156//! Once configured, the [`enable`] method consumes the `Config` and returns an
157//! enabled [`Spi`] struct that can be used for transactions. Because the
158//! `enable` function takes the `Config` as `self`, the builder-pattern API is
159//! usually the more ergonomic option.
160//!
161//! ```
162//! use embedded_hal::spi::MODE_1;
163//!
164//! // SAMD11/SAMD21 version
165//! let spi = spi::Config::new(&pm, sercom, pads, freq)
166//! .baud(1.mhz())
167//! .char_size::<NineBit>()
168//! .bit_order(BitOrder::LsbFirst)
169//! .spi_mode(MODE_1)
170//! .enable();
171//!
172//! // SAMx5x version
173//! let spi = spi::Config::new(&mclk, sercom, pads, freq)
174//! .baud(1.mhz())
175//! .length::<U2>()
176//! .bit_order(BitOrder::LsbFirst)
177//! .spi_mode(MODE_1)
178//! .enable();
179//! ```
180//!
181//! To be accepted as a [`ValidConfig`], the `Config` must have a set of
182//! [`ValidPads`] that matches its [`OpMode`]. In particular, the `SS` pad must
183//! be [`NoneT`] for [`Master`] mode, where the user is expected to handle it
184//! manaully. But it must be [`SomePad`] in [`MasterHWSS`] and [`Slave`] modes,
185//! where it is controlled by the hardware.
186//!
187//! # Using a functional `Spi` peripheral
188//!
189//! An [`Spi`] struct has two type parameters. The first is the corresponding
190//! `Config`, while the second represents its [`Capability`], i.e. [`Rx`],
191//! [`Tx`] or [`Duplex`]. The [`enable`] function determines the `Capability`
192//! automaically from the set of [`ValidPads`].
193//!
194//! ```
195//! use atsamd_hal::gpio::{PA08, PA09};
196//! use atsamd_hal::sercom::{Sercom0, spi};
197//! use atsamd_hal::sercom::spi::{Master, Rx};
198//! use atsamd_hal::typelevel::NoneT;
199//!
200//! // SAMD11/SAMD21-specific imports
201//! use atsamd_hal::sercom::spi::NineBit;
202//!
203//! // SAMx5x-specific imports
204//! use atsamd_hal::sercom::spi::lengths::U2;
205//!
206//! type Pads = spi::PadsFromIds<Sercom0, PA08, NoneT, PA09>;
207//!
208//! // SAMD11/SAMD21 version
209//! type Config = spi::Config<Pads, Master, NineBit>;
210//!
211//! // SAMx5x version
212//! type Config = spi::Config<Pads, Master, U2>;
213//!
214//! type Spi = spi::Spi<Config, Rx>;
215//! ```
216//!
217//! Only [`Spi`] structs can actually perform transactions. To do so, use the
218//! various embedded HAL traits, like [`spi::SpiBus`](crate::ehal::spi::SpiBus),
219//! [`embedded_io::Read`], [`embedded_io::Write`],
220//! [`embedded_hal_nb::serial::Read`](crate::ehal_nb::serial::Read), or
221//! [`embedded_hal_nb::serial::Write`](crate::ehal_nb::serial::Write). See the
222//! [`impl_ehal`] module documentation for more details about the specific trait
223//! implementations, which vary based on [`Size`] and [`Capability`].
224//!
225//! ```
226//! use nb::block;
227//! use crate::ehal_02::spi::FullDuplex;
228//!
229//! block!(spi.send(0xAA55));
230//! let rcvd: u16 = block!(spi.read());
231//! ```
232//!
233//! ## Flushing the bus
234//!
235//! The [`SpiBus`](crate::ehal::spi::SpiBus) methods do not flush the bus when a
236//! transaction is complete. This is in part to increase performance and allow
237//! for pipelining SPI transactions. This is true for both sync and async
238//! operation. As such, you should ensure you manually call
239//! [`flush`](crate::ehal::spi::SpiBus::flush) when:
240//! * You must synchronize SPI activity and GPIO activity, for example before
241//! deasserting a CS pin.
242//! * Before deinitializing the SPI peripheral.
243//!
244//! Take note that the [`SpiDevice`](crate::ehal::spi::SpiDevice)
245//! implementations automatically take care of flushing, so no further flushing
246//! is needed.
247//!
248//! [See the embedded-hal
249//! spec](https://docs.rs/embedded-hal/latest/embedded_hal/spi/index.html#flushing)
250//! for more information.
251//!
252//! # [`PanicOnRead`] and [`PanicOnWrite`]
253//!
254//! Some driver libraries take a type implementing [`embedded_hal::spi::SpiBus`]
255//! or [`embedded_hal::spi::SpiDevice`], even when they only need to receive or
256//! send data, but not both. A good example is WS2812 addressable LEDs
257//! (neopixels), which only take a data input. Therefore, their protocol can be
258//! implemented with a [`Tx`] [`Spi`] that only has a MOSI pin. In another
259//! example, often LCD screens only have a MOSI and SCK pins. In order to
260//! unnecessarily tying up pins in the [`Spi`] struct, and provide an escape
261//! hatch for situations where constructing the [`Spi`] struct would otherwise
262//! be impossible, we provide the [`PanicOnRead`] and [`PanicOnWrite`] wrapper
263//! types, which implement [`embedded_hal::spi::SpiBus`].
264//!
265//! As the names imply, they panic if an incompatible method is called. See
266//! [`Spi::into_panic_on_write`] and [`Spi::into_panic_on_read`].
267//!
268//! [`PanicOnRead`] and [`PanicOnWrite`] are compatible with DMA.
269//!
270//! # Using SPI with DMA <span class="stab portability" title="Available on crate feature `dma` only"><code>dma</code></span>
271//!
272//! This HAL includes support for DMA-enabled SPI transfers. Use
273//! [`Spi::with_dma_channels`] ([`Duplex`] and [`Rx`]), and
274//! [`Spi::with_tx_channel`] ([`Tx`]-only) to attach DMA channels to the [`Spi`]
275//! struct. A DMA-enabled [`Spi`] implements the blocking
276//! [`embedded_hal::spi::SpiBus`], [`embedded_io::Write`] and/or
277//! [`embedded_io::Read`] traits, which can be used to perform SPI transactions
278//! which are fast, continuous and low jitter, even if they are preemped by a
279//! higher priority interrupt.
280//!
281//! ```
282//! // Assume channel0 and channel1 are configured `dmac::Channel`, and spi a
283//! // fully-configured `Spi`
284//!
285//! // Create data to send
286//! let buffer: [u8; 50] = [0xff; 50];
287//!
288//! // Attach DMA channels
289//! let spi = spi.with_dma_channels(channel0, channel1);
290//!
291//! // Perform the transfer
292//! spi.write(&mut buffer)?;
293//! ```
294//!
295//! # `async` operation <span class="stab portability" title="Available on crate feature `async` only"><code>async</code></span>
296//!
297//! An [`Spi`] can be used for `async` operations. Configuring a [`Spi`] in
298//! async mode is relatively simple:
299//!
300//! * Bind the corresponding `SERCOM` interrupt source to the SPI
301//! [`InterruptHandler`] (refer to the module-level [`async_hal`]
302//! documentation for more information).
303//! * Turn a previously configured [`Spi`] into a [`SpiFuture`] by calling
304//! [`Spi::into_future`]
305//! * Optionally, add DMA channels to RX, TX or both using
306//! [`SpiFuture::with_rx_dma_channel`] and [`SpiFuture::with_tx_dma_channel`].
307//! The API is exactly the same whether DMA channels are used or not.
308//! * Use the provided async methods for reading or writing to the SPI
309//! peripheral. [`SpiFuture`] implements [`embedded_hal_async::spi::SpiBus`].
310//!
311//! `SpiFuture` implements `AsRef<Spi>` and `AsMut<Spi>` so that it can be
312//! reconfigured using the regular [`Spi`] methods.
313//!
314//! ## Considerations when using `async` [`Spi`] with DMA <span class="stab portability" title="Available on crate feature `async` only"><code>async</code></span> <span class="stab portability" title="Available on crate feature `dma` only"><code>dma</code></span>
315//!
316//! * An [`Spi`] struct must be turned into an [`SpiFuture`] by calling
317//! [`Spi::into_future`] before calling `with_dma_channel`. The DMA channel
318//! itself must also be configured in async mode by using
319//! [`DmaController::into_future`](crate::dmac::DmaController::into_future).
320//! If a DMA channel is added to the [`Spi`] struct before it is turned into
321//! an [`SpiFuture`], it will not be able to use DMA in async mode.
322//!
323//! ```
324//! // This will work
325//! let spi = spi.into_future().with_dma_channels(rx_channel, tx_channel);
326//!
327//! // This won't
328//! let spi = spi.with_dma_channels(rx_channel, tx_channel).into_future();
329//! ```
330//!
331//! ### Safety considerations
332//!
333//! In `async` mode, an SPI+DMA transfer does not require `'static` source and
334//! destination buffers. This, in theory, makes its use `unsafe`. However it is
335//! marked as safe for better ergonomics, and to enable the implementation of
336//! the [`embedded_hal_async::spi::SpiBus`] trait.
337//!
338//! This means that, as an user, you **must** ensure that the [`Future`]s
339//! returned by the [`embedded_hal_async::spi::SpiBus`] methods may never be
340//! forgotten through [`forget`] or by wrapping them with a [`ManuallyDrop`].
341//!
342//! The returned futures implement [`Drop`] and will automatically stop any
343//! ongoing transfers; this guarantees that the memory occupied by the
344//! now-dropped buffers may not be corrupted by running transfers.
345//!
346//! This means that using functions like [`futures::select_biased`] to implement
347//! timeouts is safe; transfers will be safely cancelled if the timeout expires.
348//!
349//! This also means that should you [`forget`] this [`Future`] after its first
350//! [`poll`] call, the transfer will keep running, ruining the now-reclaimed
351//! memory, as well as the rest of your day.
352//!
353//! * `await`ing is fine: the [`Future`] will run to completion.
354//! * Dropping an incomplete transfer is also fine. Dropping can happen, for
355//! example, if the transfer doesn't complete before a timeout expires.
356//! * Dropping an incomplete transfer *without running its destructor* is
357//! **unsound** and will trigger undefined behavior.
358//!
359//! ```ignore
360//! async fn always_ready() {}
361//!
362//! let mut buffer = [0x00; 10];
363//!
364//! // This is completely safe
365//! spi.read(&mut buffer).await?;
366//!
367//! // This is also safe: we launch a transfer, which is then immediately cancelled
368//! futures::select_biased! {
369//! _ = spi.read(&mut buffer)?,
370//! _ = always_ready(),
371//! }
372//!
373//! // This, while contrived, is also safe.
374//! {
375//! use core::future::Future;
376//!
377//! let future = spi.read(&mut buffer);
378//! futures::pin_mut!(future);
379//! // Assume ctx is a `core::task::Context` given out by the executor.
380//! // The future is polled, therefore starting the transfer
381//! future.as_mut().poll(ctx);
382//!
383//! // Future is dropped here - transfer is cancelled.
384//! }
385//!
386//! // DANGER: This is an example of undefined behavior
387//! {
388//! use core::future::Future;
389//! use core::ops::DerefMut;
390//!
391//! let future = core::mem::ManuallyDrop::new(spi.read(&mut buffer));
392//! futures::pin_mut!(future);
393//! // To actually make this example compile, we would need to wrap the returned
394//! // future from `i2c.read()` in a newtype that implements Future, because we
395//! // can't actually call as_mut() without being able to name the type we want
396//! // to deref to.
397//! let future_ref: &mut SomeNewTypeFuture = &mut future.as_mut();
398//! future.as_mut().poll(ctx);
399//!
400//! // Future is NOT dropped here - transfer is not cancelled, resulting un UB.
401//! }
402//! ```
403//!
404//! As you can see, unsoundness is relatively hard to come by - however, caution
405//! should still be exercised.
406//!
407//! [`enable`]: Config::enable
408//! [`gpio`]: crate::gpio
409//! [`IsPad`]: super::pad::IsPad
410//! [`OptionalPad`]: super::pad::OptionalPad
411//! [`PadNum`]: super::pad::PadNum
412//! [`Pin`]: crate::gpio::pin::Pin
413//! [`PinId`]: crate::gpio::pin::PinId
414//! [`PinMode`]: crate::gpio::pin::PinMode
415//! [`embedded_hal::spi::SpiBus`]: crate::ehal::spi::SpiBus
416//! [`embedded_hal::spi::SpiDevice`]: crate::ehal::spi::SpiDevice
417//! [`async_hal`]: crate::async_hal
418//! [`forget`]: core::mem::forget
419//! [`ManuallyDrop`]: core::mem::ManuallyDrop
420//! [`Future`]: core::future::Future
421//! [`poll`]: core::future::Future::poll
422
423use core::marker::PhantomData;
424
425use atsamd_hal_macros::{hal_cfg, hal_docs, hal_macro_helper, hal_module};
426use bitflags::bitflags;
427use num_traits::AsPrimitive;
428
429use crate::ehal;
430pub use crate::ehal::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Phase, Polarity};
431use crate::sercom::{ApbClkCtrl, Sercom, pad::SomePad};
432use crate::time::Hertz;
433use crate::typelevel::{Is, NoneT, Sealed};
434
435mod reg;
436use reg::Registers;
437
438//=============================================================================
439// Chip-specific imports
440//=============================================================================
441
442#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
443use crate::pac::sercom0::spi::ctrla::Modeselect;
444#[hal_cfg("sercom0-d5x")]
445use crate::pac::sercom0::spim::ctrla::Modeselect;
446
447#[hal_module(
448 any("sercom0-d11", "sercom0-d21") => "spi/pads_thumbv6m.rs",
449 "sercom0-d5x" => "spi/pads_thumbv7em.rs",
450)]
451pub mod pads {}
452
453pub use pads::*;
454
455#[hal_module(
456 any("sercom0-d11", "sercom0-d21") => "spi/char_size.rs",
457 "sercom0-d5x" => "spi/length.rs",
458)]
459pub mod size {}
460
461#[cfg(doc)]
462#[hal_cfg(not(any("sercom0-d11", "sercom0-d21")))]
463/// This type is not present with the selected feature set, defined for
464/// documentation only
465pub enum NineBit {}
466
467#[cfg(doc)]
468#[hal_cfg(not(any("sercom0-d11", "sercom0-d21")))]
469/// This type is not present with the selected feature set, defined for
470/// documentation only
471pub enum EightBit {}
472
473#[cfg(doc)]
474#[hal_cfg(not(any("sercom0-d11", "sercom0-d21")))]
475/// This trait is not present with the selected feature set, defined for
476/// documentation only
477pub trait CharSize {
478 /// This type is not present with the selected feature set, defined for
479 /// documentation only
480 type Word;
481}
482
483#[cfg(doc)]
484#[hal_cfg(not("sercom0-d5x"))]
485/// This trait is not present with the selected feature set, defined for
486/// documentation only
487pub trait Length {}
488
489pub use size::*;
490
491/// Valid transaction [`Length`]s from the [`typenum`] crate
492#[hal_cfg("sercom0-d5x")]
493pub mod lengths {
494 seq_macro::seq!(N in 1..=255 {
495 pub use typenum::U~N;
496 });
497}
498
499pub mod impl_ehal;
500
501#[cfg(feature = "async")]
502mod async_api;
503#[cfg(feature = "async")]
504pub use async_api::*;
505
506//=============================================================================
507// BitOrder
508//=============================================================================
509
510/// Define the bit order of transactions
511#[repr(u8)]
512#[derive(Copy, Clone, PartialEq, Eq)]
513#[cfg_attr(feature = "defmt", derive(defmt::Format))]
514pub enum BitOrder {
515 LsbFirst,
516 MsbFirst,
517}
518
519//=============================================================================
520// Flags
521//=============================================================================
522
523const DRE: u8 = 0x01;
524const TXC: u8 = 0x02;
525const RXC: u8 = 0x04;
526const SSL: u8 = 0x08;
527const ERROR: u8 = 0x80;
528
529pub const RX_FLAG_MASK: u8 = RXC | ERROR;
530pub const TX_FLAG_MASK: u8 = DRE | TXC;
531
532bitflags! {
533 /// Interrupt bit flags for SPI transactions
534 ///
535 /// The available interrupt flags are `DRE`, `RXC`, `TXC`, `SSL` and
536 /// `ERROR`. The binary format of the underlying bits exactly matches the
537 /// `INTFLAG` register.
538 #[derive(Clone, Copy)]
539 pub struct Flags: u8 {
540 const DRE = DRE;
541 const TXC = TXC;
542 const RXC = RXC;
543 const SSL = SSL;
544 const ERROR = ERROR;
545 }
546}
547
548#[allow(dead_code)]
549impl Flags {
550 pub(super) const RX: Self = Self::from_bits_retain(RX_FLAG_MASK);
551 pub(super) const TX: Self = Self::from_bits_retain(TX_FLAG_MASK);
552}
553
554//=============================================================================
555// Status
556//=============================================================================
557
558bitflags! {
559 /// Status bit flags for SPI transactions
560 ///
561 /// The available status flags are `BUFOVF` and `LENERR`. The binary format
562 /// of the underlying bits exactly matches the `STATUS` register.
563 #[derive(Clone, Copy)]
564 pub struct Status: u16 {
565 const BUFOVF = 0x0004;
566 const LENERR = 0x0800;
567 }
568}
569
570impl Status {
571 /// Check whether [`Self`] originates from an error.
572 ///
573 /// # Errors
574 ///
575 /// Returns an error if `STATUS` contains `BUFOVF` or `LENERR`
576 pub fn check_bus_error(self) -> Result<(), Error> {
577 // Buffer overflow has priority
578 if self.contains(Status::BUFOVF) {
579 Err(Error::Overflow)
580 } else if self.contains(Status::LENERR) {
581 Err(Error::LengthError)
582 } else {
583 Ok(())
584 }
585 }
586}
587
588//=============================================================================
589// Error
590//=============================================================================
591
592/// Error `enum` for SPI transactions
593///
594/// The SPI peripheral only has two error types, buffer overflow and transaction
595/// length error.
596#[derive(Clone, Copy, Debug, Eq, PartialEq)]
597#[cfg_attr(feature = "defmt", derive(defmt::Format))]
598pub enum Error {
599 Overflow,
600 LengthError,
601 #[cfg(feature = "dma")]
602 Dma(crate::dmac::Error),
603}
604
605//=============================================================================
606// Operating mode
607//=============================================================================
608
609/// Type-level enum representing the SPI operating mode
610///
611/// See the documentation on [type-level enums] for a discussion of the pattern.
612///
613/// The available operating modes are [`Master`], [`MasterHWSS`] and [`Slave`].
614/// In [`Master`] mode, the `SS` signal must be handled by the user, so `SS`
615/// must be [`NoneT`]. In [`MasterHWSS`] mode, the hardware drives the `SS`
616/// line, so [`SomePad`] is required. In [`Slave`] mode, the `SS` pad is
617/// required as well, to indicate when data is valid.
618///
619/// [type-level enums]: crate::typelevel#type-level-enums
620pub trait OpMode: Sealed {
621 /// Corresponding variant from the PAC enum
622 const MODE: Modeselect;
623 /// Bit indicating whether hardware `SS` control is enabled
624 const MSSEN: bool;
625}
626
627/// [`OpMode`] variant for Master mode
628pub enum Master {}
629
630/// [`OpMode`] variant for Master mode with hardware-controlled slave select
631pub enum MasterHWSS {}
632
633/// [`OpMode`] variant for Slave mode
634pub enum Slave {}
635
636impl Sealed for Master {}
637impl Sealed for MasterHWSS {}
638impl Sealed for Slave {}
639
640impl OpMode for Master {
641 const MODE: Modeselect = Modeselect::SpiMaster;
642 const MSSEN: bool = false;
643}
644
645impl OpMode for MasterHWSS {
646 const MODE: Modeselect = Modeselect::SpiMaster;
647 const MSSEN: bool = true;
648}
649
650impl OpMode for Slave {
651 const MODE: Modeselect = Modeselect::SpiSlave;
652 const MSSEN: bool = false;
653}
654
655/// Marker trait for Master operating modes
656///
657/// This trait is implemented for [`Master`] and [`MasterHWSS`] but not for
658/// [`Slave`].
659pub trait MasterMode: OpMode {}
660
661impl MasterMode for Master {}
662impl MasterMode for MasterHWSS {}
663
664//=============================================================================
665// Size
666//=============================================================================
667
668/// Type alias for the width of the `DATA` register
669#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
670pub type DataWidth = u16;
671
672/// Type alias for the width of the `DATA` register
673#[hal_cfg("sercom0-d5x")]
674pub type DataWidth = u32;
675
676/// Trait alias whose definition varies by chip
677///
678/// On SAMD11 and SAMD21 chips, this represents the [`CharSize`].
679#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
680pub trait Size: CharSize {}
681
682#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
683impl<C: CharSize> Size for C {}
684
685/// Type alias for the default [`Size`] type, which varies by chip
686#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
687pub type DefaultSize = EightBit;
688
689/// Trait alias whose definition varies by chip
690///
691/// On SAMx5x chips, this represents the transaction [`Length`].
692#[hal_cfg("sercom0-d5x")]
693pub trait Size: Length {}
694
695#[hal_cfg("sercom0-d5x")]
696impl<L: Length> Size for L {}
697
698/// Type alias for the default [`Size`] type, which varies by chip
699#[hal_cfg("sercom0-d5x")]
700pub type DefaultSize = typenum::U1;
701
702//==============================================================================
703// AtomicSize
704//==============================================================================
705
706/// Marker trait for transaction [`Size`]s that can be completed in a single
707/// read or write of the `DATA` register
708pub trait AtomicSize: Size {}
709
710#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
711impl<C: CharSize> AtomicSize for C {}
712
713#[hal_cfg("sercom0-d5x")]
714seq_macro::seq!(N in 1..=4 {
715 impl AtomicSize for lengths::U~N {}
716});
717
718//==============================================================================
719// Capability
720//==============================================================================
721
722/// Type-level enum representing the simplex or duplex transaction capability
723///
724/// The available, type-level variants are [`Rx`], [`Tx`] and [`Duplex`]. See
725/// the [type-level enum] documentation for more details.
726///
727/// [type-level enum]: crate::typelevel#type-level-enums
728pub trait Capability: Sealed + Default {
729 const RX_ENABLE: bool;
730}
731
732/// Sub-set of [`Capability`] variants that can receive data, i.e. [`Rx`] and
733/// [`Duplex`]
734pub trait Receive: Capability {}
735
736/// Sub-set of [`Capability`] variants that can transmit dat, i.e. [`Tx`] and
737/// [`Duplex`]
738pub trait Transmit: Capability {}
739
740/// Type-level variant of the [`Capability`] enum for simplex, [`Receive`]-only
741/// transactions
742///
743/// [`Spi`] structs are `Rx` when the `DO` (Data Out) type is [`NoneT`] in the
744/// corresponding [`Pads`] struct.
745///
746/// While the [`Tx`] and [`Duplex`] structs are zero-sized, this struct is not.
747/// Because an SPI master must initiate all transactions, using it in a simplex,
748/// [`Receive`]-only context is slightly complicated. In that case, the [`Spi`]
749/// struct must track whether a transaction needs to be started or is already in
750/// progress. This struct contains a `bool` to track that progress.
751#[derive(Default)]
752pub struct Rx {
753 pub(super) in_progress: bool,
754}
755
756impl Sealed for Rx {}
757impl Capability for Rx {
758 const RX_ENABLE: bool = true;
759}
760impl Receive for Rx {}
761
762/// Type-level variant of the [`Capability`] enum for simplex, [`Transmit`]-only
763/// transactions
764///
765/// [`Spi`] structs are `Tx` when the `DI` (Data In) type is [`NoneT`] in the
766/// corresponding [`Pads`] struct.
767#[derive(Default)]
768pub struct Tx;
769
770impl Sealed for Tx {}
771impl Capability for Tx {
772 const RX_ENABLE: bool = false;
773}
774impl Transmit for Tx {}
775
776/// Type-level variant of the [`Capability`] enum for duplex transactions
777///
778/// [`Spi`] structs are `Duplex` when both the `DI` and `DO` [`Pads`] are
779/// [`SomePad`].
780/// corresponding [`Pads`] struct.
781#[derive(Default)]
782pub struct Duplex;
783
784impl Sealed for Duplex {}
785impl Capability for Duplex {
786 const RX_ENABLE: bool = true;
787}
788impl Receive for Duplex {}
789impl Transmit for Duplex {}
790
791//=============================================================================
792// Config
793//=============================================================================
794
795/// A configurable SPI peripheral in its disabled state
796///
797/// See the [module-level](super) documentation for more details on declaring
798/// and instantiating `Pads` types.
799pub struct Config<P, M = Master, Z = DefaultSize>
800where
801 P: ValidPads,
802 M: OpMode,
803 Z: Size,
804{
805 regs: Registers<P::Sercom>,
806 pads: P,
807 mode: PhantomData<M>,
808 size: PhantomData<Z>,
809 freq: Hertz,
810 nop_word: DataWidth,
811}
812
813impl<P: ValidPads> Config<P> {
814 /// Create a new [`Config`] in the default configuration.
815 #[inline]
816 #[hal_macro_helper]
817 fn default(sercom: P::Sercom, pads: P, freq: impl Into<Hertz>) -> Self {
818 let mut regs = Registers { sercom };
819 regs.reset();
820 regs.set_op_mode(Master::MODE, Master::MSSEN);
821 regs.set_dipo_dopo(P::DIPO_DOPO);
822 #[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
823 regs.set_char_size(EightBit::BITS);
824 #[hal_cfg("sercom0-d5x")]
825 regs.set_length(1);
826 Self {
827 regs,
828 pads,
829 mode: PhantomData,
830 size: PhantomData,
831 freq: freq.into(),
832 nop_word: 0x00.as_(),
833 }
834 }
835
836 #[hal_docs(
837 {
838 /// Create a new [`Config`] in the default configuration
839 ///
840 /// This function will enable the corresponding APB clock, reset the
841 /// [`Sercom`] peripheral, and return a [`Config`] in the default
842 /// configuration. The default [`OpMode`] is [`Master`], while the default
843 /// [`Size`] is an
844 }
845 any("sercom0-d11", "sercom0-d21") => {
846 /// [`EightBit`] [`CharSize`]
847 }
848 "sercom0-d5x" => {
849 /// `EightBit` `CharSize`
850 }
851 {
852 /// for SAMD11 and SAMD21 chips or a
853 }
854 any("sercom0-d11", "sercom0-d21") => {
855 /// `Length` of `U1`
856 }
857 "sercom0-d5x" => {
858 /// [`Length`] of `U1`
859 }
860 {
861 /// for SAMx5x chips. Note that [`Config`] takes ownership of both the
862 /// PAC [`Sercom`] struct as well as the [`Pads`].
863 ///
864 /// Users must configure GCLK manually. The `freq` parameter represents the
865 /// GCLK frequency for this [`Sercom`] instance.
866 }
867 )]
868 #[inline]
869 pub fn new(
870 apb_clk_ctrl: &ApbClkCtrl,
871 mut sercom: P::Sercom,
872 pads: P,
873 freq: impl Into<Hertz>,
874 ) -> Self {
875 sercom.enable_apb_clock(apb_clk_ctrl);
876 Self::default(sercom, pads, freq)
877 }
878}
879
880impl<P, M, Z> Config<P, M, Z>
881where
882 P: ValidPads,
883 M: OpMode,
884 Z: Size,
885{
886 /// Change the [`OpMode`] or [`Size`]
887 #[inline]
888 fn change<M2, Z2>(self) -> Config<P, M2, Z2>
889 where
890 M2: OpMode,
891 Z2: Size,
892 {
893 Config {
894 regs: self.regs,
895 pads: self.pads,
896 mode: PhantomData,
897 size: PhantomData,
898 freq: self.freq,
899 nop_word: self.nop_word,
900 }
901 }
902
903 /// Obtain a reference to the PAC `SERCOM` struct
904 ///
905 /// # Safety
906 ///
907 /// Directly accessing the `SERCOM` could break the invariants of the
908 /// type-level tracking in this module, so it is unsafe.
909 #[inline]
910 pub unsafe fn sercom(&self) -> &P::Sercom {
911 &self.regs.sercom
912 }
913
914 /// Trigger the [`Sercom`]'s SWRST and return a [`Config`] in the
915 /// default configuration.
916 #[inline]
917 pub fn reset(self) -> Config<P> {
918 Config::default(self.regs.sercom, self.pads, self.freq)
919 }
920
921 /// Consume the [`Config`], reset the peripheral, and return the [`Sercom`]
922 /// and [`Pads`]
923 #[inline]
924 pub fn free(mut self) -> (P::Sercom, P) {
925 self.regs.reset();
926 (self.regs.sercom, self.pads)
927 }
928
929 /// Change the [`OpMode`]
930 #[inline]
931 pub fn op_mode<M2: OpMode>(mut self) -> Config<P, M2, Z> {
932 self.regs.set_op_mode(M2::MODE, M2::MSSEN);
933 self.change()
934 }
935
936 /// Change the [`CharSize`] using the builder pattern
937 #[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
938 #[inline]
939 pub fn char_size<C2: CharSize>(mut self) -> Config<P, M, C2> {
940 self.regs.set_char_size(C2::BITS);
941 self.change()
942 }
943
944 /// Change the transaction [`Length`] using the builder pattern
945 ///
946 /// To use a run-time dynamic length, set the [`Length`] type to
947 /// [`DynLength`] and then use the [`dyn_length`] method.
948 ///
949 /// [`dyn_length`]: Config::dyn_length
950 #[hal_cfg("sercom0-d5x")]
951 #[inline]
952 pub fn length<L2: Length>(mut self) -> Config<P, M, L2> {
953 self.regs.set_length(L2::U8);
954 self.change()
955 }
956
957 /// Get the clock polarity
958 #[inline]
959 pub fn get_cpol(&self) -> Polarity {
960 self.regs.get_cpol()
961 }
962
963 /// Set the clock polarity
964 #[inline]
965 pub fn set_cpol(&mut self, cpol: Polarity) {
966 self.regs.set_cpol(cpol);
967 }
968
969 /// Set the clock polarity using the builder pattern
970 #[inline]
971 pub fn cpol(mut self, cpol: Polarity) -> Self {
972 self.set_cpol(cpol);
973 self
974 }
975
976 /// Get the clock phase
977 #[inline]
978 pub fn get_cpha(&self) -> Phase {
979 self.regs.get_cpha()
980 }
981
982 /// Set the clock phase
983 #[inline]
984 pub fn set_cpha(&mut self, cpha: Phase) {
985 self.regs.set_cpha(cpha)
986 }
987
988 /// Set the clock phase using the builder pattern
989 #[inline]
990 pub fn cpha(mut self, cpha: Phase) -> Self {
991 self.set_cpha(cpha);
992 self
993 }
994
995 /// Get the SPI mode (clock polarity & phase)
996 #[inline]
997 pub fn get_spi_mode(&self) -> ehal::spi::Mode {
998 self.regs.get_spi_mode()
999 }
1000
1001 /// Set the SPI mode (clock polarity & phase)
1002 #[inline]
1003 pub fn set_spi_mode(&mut self, mode: ehal::spi::Mode) {
1004 self.regs.set_spi_mode(mode);
1005 }
1006
1007 /// Set the SPI mode (clock polarity & phase) using the builder pattern
1008 #[inline]
1009 pub fn spi_mode(mut self, mode: ehal::spi::Mode) -> Self {
1010 self.set_spi_mode(mode);
1011 self
1012 }
1013
1014 /// Get the bit order of transmission (MSB/LSB first)
1015 ///
1016 /// This only affects the order of bits within each byte. Bytes are always
1017 /// transferred in little endian order from the 32-bit DATA register.
1018 #[inline]
1019 pub fn get_bit_order(&self) -> BitOrder {
1020 self.regs.get_bit_order()
1021 }
1022
1023 /// Set the bit order of transmission (MSB/LSB first) using the builder
1024 /// pattern
1025 ///
1026 /// This only affects the order of bits within each byte. Bytes are always
1027 /// transferred in little endian order from the 32-bit DATA register.
1028 #[inline]
1029 pub fn set_bit_order(&mut self, order: BitOrder) {
1030 self.regs.set_bit_order(order);
1031 }
1032
1033 /// Set the bit order of transmission (MSB/LSB first) using the builder
1034 /// pattern
1035 ///
1036 /// This only affects the order of bits within each byte. Bytes are always
1037 /// transferred in little endian order from the 32-bit DATA register.
1038 #[inline]
1039 pub fn bit_order(mut self, order: BitOrder) -> Self {
1040 self.set_bit_order(order);
1041 self
1042 }
1043
1044 /// Get the NOP word
1045 ///
1046 /// This word is used when reading in Duplex mode, since an equal number of
1047 /// words must be sent in order to avoid overflow errors.
1048 pub fn get_nop_word(&self) -> DataWidth {
1049 self.nop_word
1050 }
1051
1052 /// Set the NOP word
1053 ///
1054 /// This word is used when reading in Duplex mode, since an equal number of
1055 /// words must be sent in order to avoid overflow errors.
1056 pub fn set_nop_word(&mut self, nop_word: DataWidth) {
1057 self.nop_word = nop_word;
1058 }
1059
1060 /// Set the NOP word using the builder pattern
1061 ///
1062 /// This word is used when reading in Duplex mode, since an equal number of
1063 /// words must be sent in order to avoid overflow errors.
1064 pub fn nop_word(mut self, nop_word: DataWidth) -> Self {
1065 self.nop_word = nop_word;
1066 self
1067 }
1068
1069 /// Get the baud rate
1070 ///
1071 /// The returned baud rate may not exactly match what was set.
1072 #[inline]
1073 pub fn get_baud(&mut self) -> Hertz {
1074 self.regs.get_baud(self.freq)
1075 }
1076
1077 /// Set the baud rate
1078 ///
1079 /// This function will calculate the best BAUD register setting based on the
1080 /// stored GCLK frequency and desired baud rate. The maximum baud rate is
1081 /// half the GCLK frequency. The minimum baud rate is the GCLK frequency /
1082 /// 512. Values outside this range will saturate at the extremes.
1083 #[inline]
1084 pub fn set_baud(&mut self, baud: Hertz) {
1085 self.regs.set_baud(self.freq, baud);
1086 }
1087
1088 /// Set the baud rate using the builder API
1089 ///
1090 /// This function will calculate the best BAUD register setting based on the
1091 /// stored GCLK frequency and desired baud rate. The maximum baud rate is
1092 /// half the GCLK frequency. The minimum baud rate is the GCLK frequency /
1093 /// 512. Values outside this range will saturate at the extremes.
1094 #[inline]
1095 pub fn baud(mut self, baud: Hertz) -> Self {
1096 self.set_baud(baud);
1097 self
1098 }
1099
1100 /// Read the enabled state of the immediate buffer overflow notification
1101 ///
1102 /// If set to true, an [`Error::Overflow`] will be issued as soon as an
1103 /// overflow occurs. Otherwise, it will not be issued until its place within
1104 /// the data stream.
1105 #[inline]
1106 pub fn get_ibon(&self) -> bool {
1107 self.regs.get_ibon()
1108 }
1109
1110 /// Enable or disable the immediate buffer overflow notification
1111 ///
1112 /// If set to true, an [`Error::Overflow`] will be issued as soon as an
1113 /// overflow occurs. Otherwise, it will not be issued until its place within
1114 /// the data stream.
1115 #[inline]
1116 pub fn set_ibon(&mut self, enabled: bool) {
1117 self.regs.set_ibon(enabled);
1118 }
1119
1120 /// Enable or disable the immediate buffer overflow notification using the
1121 /// builder API
1122 ///
1123 /// If set to true, an [`Error::Overflow`] will be issued as soon as an
1124 /// overflow occurs. Otherwise, it will not be issued until its place within
1125 /// the data stream.
1126 #[inline]
1127 pub fn ibon(mut self, enabled: bool) -> Self {
1128 self.set_ibon(enabled);
1129 self
1130 }
1131
1132 /// Read the enable state of run in standby mode
1133 #[inline]
1134 pub fn get_run_in_standby(&self) -> bool {
1135 self.regs.get_run_in_standby()
1136 }
1137
1138 /// Enable or disable run in standby mode
1139 #[inline]
1140 pub fn set_run_in_standby(&mut self, enabled: bool) {
1141 self.regs.set_run_in_standby(enabled);
1142 }
1143
1144 /// Enable or disable run in standby mode using the builder API
1145 #[inline]
1146 pub fn run_in_standby(mut self, enabled: bool) -> Self {
1147 self.set_run_in_standby(enabled);
1148 self
1149 }
1150
1151 /// Enable the SPI peripheral
1152 ///
1153 /// SPI transactions are not possible until the peripheral is enabled.
1154 /// This function is limited to [`ValidConfig`]s.
1155 #[inline]
1156 pub fn enable(mut self) -> Spi<Self, P::Capability>
1157 where
1158 Self: ValidConfig,
1159 {
1160 if P::Capability::RX_ENABLE {
1161 self.regs.rx_enable();
1162 }
1163 self.regs.enable();
1164 Spi {
1165 config: self,
1166 capability: P::Capability::default(),
1167 _rx_channel: NoneT,
1168 _tx_channel: NoneT,
1169 }
1170 }
1171}
1172
1173#[hal_cfg("sercom0-d5x")]
1174impl<P, M> Config<P, M, DynLength>
1175where
1176 P: ValidPads,
1177 M: OpMode,
1178{
1179 /// Get the transaction length
1180 #[inline]
1181 pub fn get_dyn_length(&self) -> u8 {
1182 self.regs.get_length()
1183 }
1184
1185 /// Set the transaction length
1186 ///
1187 /// Write the LENGTH register to set the transaction length. If the length
1188 /// is zero, it will be set to 1.
1189 #[inline]
1190 pub fn set_dyn_length(&mut self, length: u8) {
1191 self.regs.set_length(length);
1192 }
1193
1194 /// Set the transaction length using the builder API
1195 ///
1196 /// Write the LENGTH register to set the transaction length. If the length
1197 /// is zero, it will be set to 1.
1198 #[inline]
1199 pub fn dyn_length(mut self, length: u8) -> Self {
1200 self.set_dyn_length(length);
1201 self
1202 }
1203}
1204
1205//=============================================================================
1206// AnyConfig
1207//=============================================================================
1208
1209/// Type class for all possible [`Config`] types
1210///
1211/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for
1212/// [`Config`] types. See the `AnyKind` documentation for more details on the
1213/// pattern.
1214///
1215/// In addition to the normal, `AnyKind` associated types. This trait also
1216/// copies the [`Sercom`], [`Capability`] and [`Word`] types, to make it easier
1217/// to apply bounds to these types at the next level of abstraction.
1218///
1219/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
1220/// [type class]: crate::typelevel#type-classes
1221pub trait AnyConfig: Is<Type = SpecificConfig<Self>> {
1222 type Sercom: Sercom;
1223 type Pads: ValidPads<Sercom = Self::Sercom>;
1224 type Capability: Capability;
1225 type OpMode: OpMode;
1226 type Size: Size;
1227 type Word: 'static;
1228}
1229
1230/// Type alias to recover the specific [`Config`] type from an implementation of
1231/// [`AnyConfig`]
1232pub type SpecificConfig<C> =
1233 Config<<C as AnyConfig>::Pads, <C as AnyConfig>::OpMode, <C as AnyConfig>::Size>;
1234
1235impl<P, M, Z> Sealed for Config<P, M, Z>
1236where
1237 P: ValidPads,
1238 M: OpMode,
1239 Z: Size,
1240{
1241}
1242
1243impl<P, M, Z> AnyConfig for Config<P, M, Z>
1244where
1245 P: ValidPads,
1246 M: OpMode,
1247 Z: Size,
1248{
1249 type Sercom = P::Sercom;
1250 type Pads = P;
1251 type Capability = P::Capability;
1252 type OpMode = M;
1253 type Size = Z;
1254 type Word = Z::Word;
1255}
1256
1257impl<P, M, Z> AsRef<Self> for Config<P, M, Z>
1258where
1259 P: ValidPads,
1260 M: OpMode,
1261 Z: Size,
1262{
1263 #[inline]
1264 fn as_ref(&self) -> &Self {
1265 self
1266 }
1267}
1268
1269impl<P, M, Z> AsMut<Self> for Config<P, M, Z>
1270where
1271 P: ValidPads,
1272 M: OpMode,
1273 Z: Size,
1274{
1275 #[inline]
1276 fn as_mut(&mut self) -> &mut Self {
1277 self
1278 }
1279}
1280
1281//=============================================================================
1282// ValidConfig
1283//=============================================================================
1284
1285/// Marker trait for valid SPI [`Config`]urations
1286///
1287/// A functional SPI peripheral must have, at a minimum, an SCLK pad and
1288/// either a Data In or a Data Out pad. Dependeing on the [`OpMode`], an SS
1289/// pad may also be required.
1290///
1291/// The `ValidConfig` trait is implemented only for valid combinations of
1292/// [`Pads`] and [`OpMode`]. No [`Config`] is valid if the SCK pad is [`NoneT`]
1293/// or if both the Data In and Data Out pads are `NoneT`. When in [`Master`]
1294/// `OpMode`, the `SS` pad must be `NoneT`, while in [`MasterHWSS`] or
1295/// [`Slave`] [`OpMode`], the SS pad must be [`SomePad`].
1296pub trait ValidConfig: AnyConfig {}
1297
1298impl<P, Z> ValidConfig for Config<P, Master, Z>
1299where
1300 P: ValidPads<SS = NoneT>,
1301 Z: Size,
1302{
1303}
1304
1305impl<P, Z> ValidConfig for Config<P, MasterHWSS, Z>
1306where
1307 P: ValidPads,
1308 Z: Size,
1309 P::SS: SomePad,
1310{
1311}
1312
1313impl<P, Z> ValidConfig for Config<P, Slave, Z>
1314where
1315 P: ValidPads,
1316 Z: Size,
1317 P::SS: SomePad,
1318{
1319}
1320
1321//=============================================================================
1322// Spi
1323//=============================================================================
1324
1325/// An enabled SPI peripheral that can perform transactions
1326///
1327/// See the [`impl_ehal`] documentation for details on the implementations of
1328/// the embedded HAL traits, which vary based on [`Size`] and [`Capability`].
1329pub struct Spi<C, A, RxDma = NoneT, TxDma = NoneT>
1330where
1331 C: ValidConfig,
1332 A: Capability,
1333{
1334 config: C,
1335 capability: A,
1336 _rx_channel: RxDma,
1337 _tx_channel: TxDma,
1338}
1339
1340/// Get a shared reference to the underlying [`Config`] struct
1341///
1342/// This can be used to call the various `get_*` functions on `Config`
1343impl<C, A> AsRef<SpecificConfig<C>> for Spi<C, A>
1344where
1345 C: ValidConfig,
1346 A: Capability,
1347{
1348 #[inline]
1349 fn as_ref(&self) -> &SpecificConfig<C> {
1350 self.config.as_ref()
1351 }
1352}
1353
1354impl<C, A, RxDma, TxDma> Spi<C, A, RxDma, TxDma>
1355where
1356 C: ValidConfig,
1357 A: Capability,
1358{
1359 /// Change the transaction [`Length`]
1360 ///
1361 /// Changing the transaction [`Length`] while is enabled is permissible but
1362 /// dangerous. If you have sent or received *any* bytes at the current
1363 /// [`Length`], you **must** wait for a TXC flag before changing to a new
1364 /// [`Length`].
1365 #[inline]
1366 #[allow(clippy::type_complexity)]
1367 #[hal_cfg("sercom0-d5x")]
1368 pub fn length<L: Length>(self) -> Spi<Config<C::Pads, C::OpMode, L>, A, RxDma, TxDma>
1369 where
1370 Config<C::Pads, C::OpMode, L>: ValidConfig,
1371 {
1372 Spi {
1373 config: self.config.into().length(),
1374 capability: self.capability,
1375 _rx_channel: self._rx_channel,
1376 _tx_channel: self._tx_channel,
1377 }
1378 }
1379
1380 /// Update the SPI configuration.
1381 ///
1382 /// Calling this method will temporarily disable the SERCOM peripheral, as
1383 /// some registers are enable-protected. This may interrupt any ongoing
1384 /// transactions.
1385 #[inline]
1386 pub fn reconfigure(&mut self, update: impl FnOnce(&mut SpecificConfig<C>)) {
1387 self.config.as_mut().regs.disable();
1388 update(self.config.as_mut());
1389 self.config.as_mut().regs.enable();
1390 }
1391
1392 /// Enable interrupts for the specified flags
1393 #[inline]
1394 pub fn enable_interrupts(&mut self, flags: Flags) {
1395 self.config.as_mut().regs.enable_interrupts(flags)
1396 }
1397
1398 /// Disable interrupts for the specified flags
1399 #[inline]
1400 pub fn disable_interrupts(&mut self, flags: Flags) {
1401 self.config.as_mut().regs.disable_interrupts(flags);
1402 }
1403
1404 /// Read the interrupt flags
1405 #[inline]
1406 pub fn read_flags(&self) -> Flags {
1407 self.config.as_ref().regs.read_flags()
1408 }
1409
1410 /// Clear the corresponding interrupt flags
1411 ///
1412 /// Only the ERROR, SSL and TXC flags can be cleared.
1413 ///
1414 /// **Note:** Implementations of `flush` methods (eg [`SpiBus::flush`]) wait
1415 /// on and clear the `TXC` flag. Manually clearing this flag could cause
1416 /// them to hang indefinitely.
1417 ///
1418 /// [`SpiBus::flush`]: ehal::spi::SpiBus::flush
1419 #[inline]
1420 pub fn clear_flags(&mut self, flags: Flags) {
1421 self.config.as_mut().regs.clear_flags(flags);
1422 }
1423
1424 /// Read the error status flags
1425 #[inline]
1426 pub fn read_status(&self) -> Status {
1427 self.config.as_ref().regs.read_status()
1428 }
1429
1430 /// Clear the corresponding error status flags
1431 #[inline]
1432 pub fn clear_status(&mut self, status: Status) {
1433 self.config.as_mut().regs.clear_status(status);
1434 }
1435
1436 /// Try to read the interrupt flags, but first check the error status flags.
1437 #[inline]
1438 pub fn read_flags_errors(&self) -> Result<Flags, Error> {
1439 self.config.as_ref().regs.read_flags_errors()
1440 }
1441
1442 /// Read from the DATA register
1443 ///
1444 /// # Safety
1445 ///
1446 /// Reading from the data register directly is `unsafe`, because it will
1447 /// clear the RXC flag, which could break assumptions made elsewhere in
1448 /// this module.
1449 #[inline]
1450 pub unsafe fn read_data(&mut self) -> DataWidth {
1451 self.config.as_mut().regs.read_data()
1452 }
1453
1454 /// Write to the DATA register
1455 ///
1456 /// # Safety
1457 ///
1458 /// Writing to the data register directly is `unsafe`, because it will clear
1459 /// the DRE flag, which could break assumptions made elsewhere in this
1460 /// module.
1461 #[inline]
1462 pub unsafe fn write_data(&mut self, data: DataWidth) {
1463 self.config.as_mut().regs.write_data(data);
1464 }
1465
1466 /// Disable the SPI peripheral and return the [`Config`] struct
1467 #[inline]
1468 pub fn disable(mut self) -> C {
1469 self.config.as_mut().regs.rx_disable();
1470 self.config.as_mut().regs.disable();
1471 self.config
1472 }
1473
1474 /// Block until at least one of the flags specified in `flags`, or `ERROR`,
1475 /// is set.
1476 ///
1477 /// Returns `Err(Error)` if an error is detected; also clears the ERROR
1478 /// interrupt flag and the affected STATUS flags.
1479 fn block_on_flags(&mut self, flags: Flags) -> Result<(), Error> {
1480 while !self.read_flags().intersects(flags | Flags::ERROR) {
1481 core::hint::spin_loop();
1482 }
1483 let flags = self.read_flags();
1484 self.check_and_clear_error(flags)
1485 }
1486
1487 #[inline]
1488 fn check_and_clear_error(&mut self, flags: Flags) -> Result<(), Error> {
1489 if flags.contains(Flags::ERROR) {
1490 let errors = self.read_status();
1491 // Clear all status flags at once; BUFOVF has priority, and will mask LENERR if
1492 // both show up at the same time.
1493 self.clear_status(errors);
1494 self.clear_flags(Flags::ERROR);
1495 return errors.check_bus_error();
1496 }
1497
1498 Ok(())
1499 }
1500}
1501
1502impl<C, D> Spi<C, D>
1503where
1504 C: ValidConfig,
1505 D: Receive,
1506 C::OpMode: MasterMode,
1507{
1508 /// Attach RX and TX DMA channels to this [`Spi`]. Its
1509 /// [`SpiBus`](crate::ehal::spi::SpiBus) implementation will use DMA to
1510 /// carry out its transactions. In Master mode, since even read SPI
1511 /// transaction necessarily involve a write to shift data in, [`Rx`]-only
1512 /// must take two DMA channels, just the same as if it were [`Duplex`].
1513 #[cfg(feature = "dma")]
1514 pub fn with_dma_channels<R, T>(self, rx: R, tx: T) -> Spi<C, D, R, T>
1515 where
1516 R: crate::dmac::AnyChannel<Status = crate::dmac::Ready>,
1517 T: crate::dmac::AnyChannel<Status = crate::dmac::Ready>,
1518 {
1519 Spi {
1520 capability: self.capability,
1521 config: self.config,
1522 _rx_channel: rx,
1523 _tx_channel: tx,
1524 }
1525 }
1526}
1527
1528impl<C> Spi<C, Duplex>
1529where
1530 C: ValidConfig<OpMode = Slave>,
1531{
1532 /// Attach a DMA channel to this [`Spi`]. Its
1533 /// [`SpiBus`](crate::ehal::spi::SpiBus) implementation will use DMA to
1534 /// carry out its transactions. In Slave mode, a [`Duplex`] [`Spi`] needs
1535 /// two DMA channels.
1536 #[cfg(feature = "dma")]
1537 pub fn with_dma_channels_slave<R, T>(self, rx: R, tx: T) -> Spi<C, Duplex, R, T>
1538 where
1539 R: crate::dmac::AnyChannel<Status = crate::dmac::Ready>,
1540 T: crate::dmac::AnyChannel<Status = crate::dmac::Ready>,
1541 {
1542 Spi {
1543 capability: self.capability,
1544 config: self.config,
1545 _rx_channel: rx,
1546 _tx_channel: tx,
1547 }
1548 }
1549}
1550
1551#[cfg(feature = "dma")]
1552impl<C, T> Spi<C, Rx, NoneT, T>
1553where
1554 C: ValidConfig<OpMode = Slave>,
1555{
1556 /// Attach a DMA channel to this [`Spi`]. Its
1557 /// [`SpiBus`](crate::ehal::spi::SpiBus) implementation will use DMA to
1558 /// carry out its transactions. In Slave mode, a [`Rx`] [`Spi`] only needs a
1559 /// single DMA channel.
1560 #[cfg(feature = "dma")]
1561 pub fn with_rx_channel<R>(self, rx: R) -> Spi<C, Rx, R, T>
1562 where
1563 R: crate::dmac::AnyChannel<Status = crate::dmac::Ready>,
1564 {
1565 Spi {
1566 capability: self.capability,
1567 config: self.config,
1568 _rx_channel: rx,
1569 _tx_channel: self._tx_channel,
1570 }
1571 }
1572}
1573
1574#[cfg(feature = "dma")]
1575impl<C, R> Spi<C, Tx, R, NoneT>
1576where
1577 C: ValidConfig,
1578{
1579 /// Attach a DMA channel to this [`Spi`]. Its
1580 /// [`SpiBus`](crate::ehal::spi::SpiBus) implementation will use DMA to
1581 /// carry out its transactions. For [`Tx`] [`Spi`]s, only a single DMA
1582 /// channel is necessary.
1583 #[cfg(feature = "dma")]
1584 pub fn with_tx_channel<T>(self, tx: T) -> Spi<C, Tx, R, T>
1585 where
1586 T: crate::dmac::AnyChannel<Status = crate::dmac::Ready>,
1587 {
1588 Spi {
1589 capability: self.capability,
1590 config: self.config,
1591 _rx_channel: self._rx_channel,
1592 _tx_channel: tx,
1593 }
1594 }
1595}
1596
1597#[cfg(feature = "dma")]
1598impl<C, D, R, T> Spi<C, D, R, T>
1599where
1600 C: ValidConfig,
1601 D: Capability,
1602{
1603 /// Reclaim both RX and TX DMA channels. Any subsequent SPI transaction will
1604 /// not use DMA.
1605 pub fn take_dma_channels(self) -> (Spi<C, D, NoneT, NoneT>, R, T)
1606 where
1607 R: crate::dmac::AnyChannel<Status: crate::dmac::ReadyChannel>,
1608 T: crate::dmac::AnyChannel<Status: crate::dmac::ReadyChannel>,
1609 {
1610 (
1611 Spi {
1612 capability: self.capability,
1613 config: self.config,
1614 _rx_channel: NoneT,
1615 _tx_channel: NoneT,
1616 },
1617 self._rx_channel,
1618 self._tx_channel,
1619 )
1620 }
1621
1622 /// Reclaim the RX DMA channel. Any subsequent SPI RX transaction will not
1623 /// use DMA.
1624 pub fn take_rx_channel(self) -> (Spi<C, D, NoneT, T>, R)
1625 where
1626 R: crate::dmac::AnyChannel<Status: crate::dmac::ReadyChannel>,
1627 {
1628 (
1629 Spi {
1630 capability: self.capability,
1631 config: self.config,
1632 _tx_channel: self._tx_channel,
1633 _rx_channel: NoneT,
1634 },
1635 self._rx_channel,
1636 )
1637 }
1638
1639 /// Reclaim the TX DMA channel. Any subsequent SPI TX transaction will not
1640 /// use DMA.
1641 pub fn take_tx_channel(self) -> (Spi<C, D, R, NoneT>, T)
1642 where
1643 T: crate::dmac::AnyChannel<Status: crate::dmac::ReadyChannel>,
1644 {
1645 (
1646 Spi {
1647 capability: self.capability,
1648 config: self.config,
1649 _rx_channel: self._rx_channel,
1650 _tx_channel: NoneT,
1651 },
1652 self._tx_channel,
1653 )
1654 }
1655}
1656
1657/// Wrapper type around a [`Spi`] that allows using
1658/// [`embedded_hal::spi::SpiBus`] even though it only has RX capability. Will
1659/// panic if any write-adjacent method is used (ie, `write`, `transfer`,
1660/// `transfer_in_place`, and `flush`).
1661///
1662/// Also implements `Into<Spi>`, `AsRef<Spi>` and `AsMut<Spi>` if you need to
1663/// use `Spi` methods.
1664///
1665/// [`embedded_hal::spi::SpiBus`]: crate::ehal::spi::SpiBus
1666pub struct PanicOnWrite<T: crate::ehal::spi::ErrorType>(T);
1667
1668impl<C: ValidConfig, R, T> From<PanicOnWrite<Spi<C, Rx, R, T>>> for Spi<C, Rx, R, T> {
1669 fn from(value: PanicOnWrite<Spi<C, Rx, R, T>>) -> Self {
1670 value.0
1671 }
1672}
1673
1674impl<C: ValidConfig, R, T> AsRef<Spi<C, Rx, R, T>> for PanicOnWrite<Spi<C, Rx, R, T>> {
1675 fn as_ref(&self) -> &Spi<C, Rx, R, T> {
1676 &self.0
1677 }
1678}
1679impl<C: ValidConfig, R, T> AsMut<Spi<C, Rx, R, T>> for PanicOnWrite<Spi<C, Rx, R, T>> {
1680 fn as_mut(&mut self) -> &mut Spi<C, Rx, R, T> {
1681 &mut self.0
1682 }
1683}
1684
1685impl<C: ValidConfig, R, T> Spi<C, Tx, R, T> {
1686 /// Turn a [`Tx`] [`Spi`] into a [`PanicOnWrite`]
1687 pub fn into_panic_on_write(self) -> PanicOnWrite<Self> {
1688 PanicOnWrite(self)
1689 }
1690}
1691
1692/// Wrapper type around a [`Spi`] that allows using
1693/// [`embedded_hal::spi::SpiBus`] even though it only has TX capability. Will
1694/// panic if any write-adjacent method is used (ie, `read`, `transfer`, and
1695/// `transfer_in_place`).
1696///
1697/// Also implements `Into<Spi>`, `AsRef<Spi>` and `AsMut<Spi>` if you need to
1698/// use `Spi` methods.
1699///
1700/// [`embedded_hal::spi::SpiBus`]: crate::ehal::spi::SpiBus
1701pub struct PanicOnRead<T: crate::ehal::spi::ErrorType>(T);
1702
1703impl<C: ValidConfig, R, T> From<PanicOnRead<Spi<C, Tx, R, T>>> for Spi<C, Tx, R, T> {
1704 fn from(value: PanicOnRead<Spi<C, Tx, R, T>>) -> Self {
1705 value.0
1706 }
1707}
1708
1709impl<C: ValidConfig, R, T> AsRef<Spi<C, Tx, R, T>> for PanicOnRead<Spi<C, Tx, R, T>> {
1710 fn as_ref(&self) -> &Spi<C, Tx, R, T> {
1711 &self.0
1712 }
1713}
1714
1715impl<C: ValidConfig, R, T> AsMut<Spi<C, Tx, R, T>> for PanicOnRead<Spi<C, Tx, R, T>> {
1716 fn as_mut(&mut self) -> &mut Spi<C, Tx, R, T> {
1717 &mut self.0
1718 }
1719}
1720
1721impl<C: ValidConfig, R, T> Spi<C, Tx, R, T> {
1722 /// Turn a [`Rx`] [`Spi`] into a [`PanicOnRead`]
1723 pub fn into_panic_on_read(self) -> PanicOnRead<Self> {
1724 PanicOnRead(self)
1725 }
1726}
1727
1728#[hal_cfg("sercom0-d5x")]
1729impl<P, M, A> Spi<Config<P, M, DynLength>, A>
1730where
1731 P: ValidPads,
1732 M: OpMode,
1733 Config<P, M, DynLength>: ValidConfig,
1734 A: Capability,
1735{
1736 /// Return the current transaction length
1737 ///
1738 /// Read the LENGTH register to determine the current transaction length
1739 #[inline]
1740 pub fn get_dyn_length(&self) -> u8 {
1741 self.config.get_dyn_length()
1742 }
1743
1744 /// Set the transaction length
1745 ///
1746 /// Write the LENGTH register to set the transaction length. Panics if the
1747 /// length is zero.
1748 ///
1749 /// Changing the transaction `LENGTH` while is enabled is permissible but
1750 /// dangerous. If you have sent or received *any* bytes at the current
1751 /// `LENGTH`, you **must** wait for a TXC flag before changing to a new
1752 /// `LENGTH`.
1753 #[inline]
1754 pub fn set_dyn_length(&mut self, length: u8) {
1755 self.config.set_dyn_length(length);
1756 }
1757}
1758
1759#[cfg(doc)]
1760#[hal_cfg(not("sercom0-d5x"))]
1761impl<C: ValidConfig, R, T> Spi<C, Tx, R, T> {
1762 /// This method is not present with the selected feature set, defined for
1763 /// documentation only
1764 pub fn get_dyn_length(&self) -> u8 {
1765 unimplemented!()
1766 }
1767}
1768
1769//=============================================================================
1770// AnySpi
1771//=============================================================================
1772
1773/// Type class for all possible [`Spi`] types
1774///
1775/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for
1776/// [`Spi`] types. See the `AnyKind` documentation for more details on the
1777/// pattern.
1778///
1779/// In addition to the normal, `AnyKind` associated types. This trait also
1780/// copies the [`Sercom`], [`Pads`], [`Capability`], [`OpMode`], [`Size`] and
1781/// [`Word`] types, to make it easier to apply bounds to these types at the next
1782/// level of abstraction.
1783///
1784/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
1785/// [type class]: crate::typelevel#type-classes
1786pub trait AnySpi: Is<Type = SpecificSpi<Self>> {
1787 type Sercom: Sercom;
1788 type Pads: ValidPads;
1789 type Capability: Capability;
1790 type OpMode: OpMode;
1791 type Size: Size;
1792 type Word: 'static;
1793 type Config: ValidConfig<Sercom = Self::Sercom>;
1794}
1795
1796/// Type alias to recover the specific [`Spi`] type from an implementation of
1797/// [`AnySpi`]
1798pub type SpecificSpi<S> = Spi<<S as AnySpi>::Config, <S as AnySpi>::Capability>;
1799
1800impl<C, A> AsRef<Self> for Spi<C, A>
1801where
1802 C: ValidConfig,
1803 A: Capability,
1804{
1805 #[inline]
1806 fn as_ref(&self) -> &Self {
1807 self
1808 }
1809}
1810
1811impl<C, A> AsMut<Self> for Spi<C, A>
1812where
1813 C: ValidConfig,
1814 A: Capability,
1815{
1816 #[inline]
1817 fn as_mut(&mut self) -> &mut Self {
1818 self
1819 }
1820}
1821
1822impl<C, A> Sealed for Spi<C, A>
1823where
1824 C: ValidConfig,
1825 A: Capability,
1826{
1827}
1828
1829impl<C, A> AnySpi for Spi<C, A>
1830where
1831 C: ValidConfig,
1832 A: Capability,
1833{
1834 type Sercom = C::Sercom;
1835 type Pads = C::Pads;
1836 type Capability = A;
1837 type OpMode = C::OpMode;
1838 type Size = C::Size;
1839 type Word = C::Word;
1840 type Config = C;
1841}