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::{Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
431use crate::sercom::{pad::SomePad, ApbClkCtrl, Sercom};
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 /// Obtain a pointer to the `DATA` register. Necessary for DMA transfers.
930 #[inline]
931 #[cfg(feature = "dma")]
932 pub(super) fn data_ptr(&self) -> *mut Z::Word {
933 self.regs.data_ptr::<Z>()
934 }
935
936 /// Change the [`OpMode`]
937 #[inline]
938 pub fn op_mode<M2: OpMode>(mut self) -> Config<P, M2, Z> {
939 self.regs.set_op_mode(M2::MODE, M2::MSSEN);
940 self.change()
941 }
942
943 /// Change the [`CharSize`] using the builder pattern
944 #[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
945 #[inline]
946 pub fn char_size<C2: CharSize>(mut self) -> Config<P, M, C2> {
947 self.regs.set_char_size(C2::BITS);
948 self.change()
949 }
950
951 /// Change the transaction [`Length`] using the builder pattern
952 ///
953 /// To use a run-time dynamic length, set the [`Length`] type to
954 /// [`DynLength`] and then use the [`dyn_length`] method.
955 ///
956 /// [`dyn_length`]: Config::dyn_length
957 #[hal_cfg("sercom0-d5x")]
958 #[inline]
959 pub fn length<L2: Length>(mut self) -> Config<P, M, L2> {
960 self.regs.set_length(L2::U8);
961 self.change()
962 }
963
964 /// Get the clock polarity
965 #[inline]
966 pub fn get_cpol(&self) -> Polarity {
967 self.regs.get_cpol()
968 }
969
970 /// Set the clock polarity
971 #[inline]
972 pub fn set_cpol(&mut self, cpol: Polarity) {
973 self.regs.set_cpol(cpol);
974 }
975
976 /// Set the clock polarity using the builder pattern
977 #[inline]
978 pub fn cpol(mut self, cpol: Polarity) -> Self {
979 self.set_cpol(cpol);
980 self
981 }
982
983 /// Get the clock phase
984 #[inline]
985 pub fn get_cpha(&self) -> Phase {
986 self.regs.get_cpha()
987 }
988
989 /// Set the clock phase
990 #[inline]
991 pub fn set_cpha(&mut self, cpha: Phase) {
992 self.regs.set_cpha(cpha)
993 }
994
995 /// Set the clock phase using the builder pattern
996 #[inline]
997 pub fn cpha(mut self, cpha: Phase) -> Self {
998 self.set_cpha(cpha);
999 self
1000 }
1001
1002 /// Get the SPI mode (clock polarity & phase)
1003 #[inline]
1004 pub fn get_spi_mode(&self) -> ehal::spi::Mode {
1005 self.regs.get_spi_mode()
1006 }
1007
1008 /// Set the SPI mode (clock polarity & phase)
1009 #[inline]
1010 pub fn set_spi_mode(&mut self, mode: ehal::spi::Mode) {
1011 self.regs.set_spi_mode(mode);
1012 }
1013
1014 /// Set the SPI mode (clock polarity & phase) using the builder pattern
1015 #[inline]
1016 pub fn spi_mode(mut self, mode: ehal::spi::Mode) -> Self {
1017 self.set_spi_mode(mode);
1018 self
1019 }
1020
1021 /// Get the bit order of transmission (MSB/LSB first)
1022 ///
1023 /// This only affects the order of bits within each byte. Bytes are always
1024 /// transferred in little endian order from the 32-bit DATA register.
1025 #[inline]
1026 pub fn get_bit_order(&self) -> BitOrder {
1027 self.regs.get_bit_order()
1028 }
1029
1030 /// Set the bit order of transmission (MSB/LSB first) using the builder
1031 /// pattern
1032 ///
1033 /// This only affects the order of bits within each byte. Bytes are always
1034 /// transferred in little endian order from the 32-bit DATA register.
1035 #[inline]
1036 pub fn set_bit_order(&mut self, order: BitOrder) {
1037 self.regs.set_bit_order(order);
1038 }
1039
1040 /// Set the bit order of transmission (MSB/LSB first) using the builder
1041 /// pattern
1042 ///
1043 /// This only affects the order of bits within each byte. Bytes are always
1044 /// transferred in little endian order from the 32-bit DATA register.
1045 #[inline]
1046 pub fn bit_order(mut self, order: BitOrder) -> Self {
1047 self.set_bit_order(order);
1048 self
1049 }
1050
1051 /// Get the NOP word
1052 ///
1053 /// This word is used when reading in Duplex mode, since an equal number of
1054 /// words must be sent in order to avoid overflow errors.
1055 pub fn get_nop_word(&self) -> DataWidth {
1056 self.nop_word
1057 }
1058
1059 /// Set the NOP word
1060 ///
1061 /// This word is used when reading in Duplex mode, since an equal number of
1062 /// words must be sent in order to avoid overflow errors.
1063 pub fn set_nop_word(&mut self, nop_word: DataWidth) {
1064 self.nop_word = nop_word;
1065 }
1066
1067 /// Set the NOP word using the builder pattern
1068 ///
1069 /// This word is used when reading in Duplex mode, since an equal number of
1070 /// words must be sent in order to avoid overflow errors.
1071 pub fn nop_word(mut self, nop_word: DataWidth) -> Self {
1072 self.nop_word = nop_word;
1073 self
1074 }
1075
1076 /// Get the baud rate
1077 ///
1078 /// The returned baud rate may not exactly match what was set.
1079 #[inline]
1080 pub fn get_baud(&mut self) -> Hertz {
1081 self.regs.get_baud(self.freq)
1082 }
1083
1084 /// Set the baud rate
1085 ///
1086 /// This function will calculate the best BAUD register setting based on the
1087 /// stored GCLK frequency and desired baud rate. The maximum baud rate is
1088 /// half the GCLK frequency. The minimum baud rate is the GCLK frequency /
1089 /// 512. Values outside this range will saturate at the extremes.
1090 #[inline]
1091 pub fn set_baud(&mut self, baud: Hertz) {
1092 self.regs.set_baud(self.freq, baud);
1093 }
1094
1095 /// Set the baud rate using the builder API
1096 ///
1097 /// This function will calculate the best BAUD register setting based on the
1098 /// stored GCLK frequency and desired baud rate. The maximum baud rate is
1099 /// half the GCLK frequency. The minimum baud rate is the GCLK frequency /
1100 /// 512. Values outside this range will saturate at the extremes.
1101 #[inline]
1102 pub fn baud(mut self, baud: Hertz) -> Self {
1103 self.set_baud(baud);
1104 self
1105 }
1106
1107 /// Read the enabled state of the immediate buffer overflow notification
1108 ///
1109 /// If set to true, an [`Error::Overflow`] will be issued as soon as an
1110 /// overflow occurs. Otherwise, it will not be issued until its place within
1111 /// the data stream.
1112 #[inline]
1113 pub fn get_ibon(&self) -> bool {
1114 self.regs.get_ibon()
1115 }
1116
1117 /// Enable or disable the immediate buffer overflow notification
1118 ///
1119 /// If set to true, an [`Error::Overflow`] will be issued as soon as an
1120 /// overflow occurs. Otherwise, it will not be issued until its place within
1121 /// the data stream.
1122 #[inline]
1123 pub fn set_ibon(&mut self, enabled: bool) {
1124 self.regs.set_ibon(enabled);
1125 }
1126
1127 /// Enable or disable the immediate buffer overflow notification using the
1128 /// builder API
1129 ///
1130 /// If set to true, an [`Error::Overflow`] will be issued as soon as an
1131 /// overflow occurs. Otherwise, it will not be issued until its place within
1132 /// the data stream.
1133 #[inline]
1134 pub fn ibon(mut self, enabled: bool) -> Self {
1135 self.set_ibon(enabled);
1136 self
1137 }
1138
1139 /// Read the enable state of run in standby mode
1140 #[inline]
1141 pub fn get_run_in_standby(&self) -> bool {
1142 self.regs.get_run_in_standby()
1143 }
1144
1145 /// Enable or disable run in standby mode
1146 #[inline]
1147 pub fn set_run_in_standby(&mut self, enabled: bool) {
1148 self.regs.set_run_in_standby(enabled);
1149 }
1150
1151 /// Enable or disable run in standby mode using the builder API
1152 #[inline]
1153 pub fn run_in_standby(mut self, enabled: bool) -> Self {
1154 self.set_run_in_standby(enabled);
1155 self
1156 }
1157
1158 /// Enable the SPI peripheral
1159 ///
1160 /// SPI transactions are not possible until the peripheral is enabled.
1161 /// This function is limited to [`ValidConfig`]s.
1162 #[inline]
1163 pub fn enable(mut self) -> Spi<Self, P::Capability>
1164 where
1165 Self: ValidConfig,
1166 {
1167 if P::Capability::RX_ENABLE {
1168 self.regs.rx_enable();
1169 }
1170 self.regs.enable();
1171 Spi {
1172 config: self,
1173 capability: P::Capability::default(),
1174 _rx_channel: NoneT,
1175 _tx_channel: NoneT,
1176 }
1177 }
1178}
1179
1180#[hal_cfg("sercom0-d5x")]
1181impl<P, M> Config<P, M, DynLength>
1182where
1183 P: ValidPads,
1184 M: OpMode,
1185{
1186 /// Get the transaction length
1187 #[inline]
1188 pub fn get_dyn_length(&self) -> u8 {
1189 self.regs.get_length()
1190 }
1191
1192 /// Set the transaction length
1193 ///
1194 /// Write the LENGTH register to set the transaction length. If the length
1195 /// is zero, it will be set to 1.
1196 #[inline]
1197 pub fn set_dyn_length(&mut self, length: u8) {
1198 self.regs.set_length(length);
1199 }
1200
1201 /// Set the transaction length using the builder API
1202 ///
1203 /// Write the LENGTH register to set the transaction length. If the length
1204 /// is zero, it will be set to 1.
1205 #[inline]
1206 pub fn dyn_length(mut self, length: u8) -> Self {
1207 self.set_dyn_length(length);
1208 self
1209 }
1210}
1211
1212//=============================================================================
1213// AnyConfig
1214//=============================================================================
1215
1216/// Type class for all possible [`Config`] types
1217///
1218/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for
1219/// [`Config`] types. See the `AnyKind` documentation for more details on the
1220/// pattern.
1221///
1222/// In addition to the normal, `AnyKind` associated types. This trait also
1223/// copies the [`Sercom`], [`Capability`] and [`Word`] types, to make it easier
1224/// to apply bounds to these types at the next level of abstraction.
1225///
1226/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
1227/// [type class]: crate::typelevel#type-classes
1228pub trait AnyConfig: Is<Type = SpecificConfig<Self>> {
1229 type Sercom: Sercom;
1230 type Pads: ValidPads<Sercom = Self::Sercom>;
1231 type Capability: Capability;
1232 type OpMode: OpMode;
1233 type Size: Size;
1234 type Word: 'static;
1235}
1236
1237/// Type alias to recover the specific [`Config`] type from an implementation of
1238/// [`AnyConfig`]
1239pub type SpecificConfig<C> =
1240 Config<<C as AnyConfig>::Pads, <C as AnyConfig>::OpMode, <C as AnyConfig>::Size>;
1241
1242impl<P, M, Z> Sealed for Config<P, M, Z>
1243where
1244 P: ValidPads,
1245 M: OpMode,
1246 Z: Size,
1247{
1248}
1249
1250impl<P, M, Z> AnyConfig for Config<P, M, Z>
1251where
1252 P: ValidPads,
1253 M: OpMode,
1254 Z: Size,
1255{
1256 type Sercom = P::Sercom;
1257 type Pads = P;
1258 type Capability = P::Capability;
1259 type OpMode = M;
1260 type Size = Z;
1261 type Word = Z::Word;
1262}
1263
1264impl<P, M, Z> AsRef<Self> for Config<P, M, Z>
1265where
1266 P: ValidPads,
1267 M: OpMode,
1268 Z: Size,
1269{
1270 #[inline]
1271 fn as_ref(&self) -> &Self {
1272 self
1273 }
1274}
1275
1276impl<P, M, Z> AsMut<Self> for Config<P, M, Z>
1277where
1278 P: ValidPads,
1279 M: OpMode,
1280 Z: Size,
1281{
1282 #[inline]
1283 fn as_mut(&mut self) -> &mut Self {
1284 self
1285 }
1286}
1287
1288//=============================================================================
1289// ValidConfig
1290//=============================================================================
1291
1292/// Marker trait for valid SPI [`Config`]urations
1293///
1294/// A functional SPI peripheral must have, at a minimum, an SCLK pad and
1295/// either a Data In or a Data Out pad. Dependeing on the [`OpMode`], an SS
1296/// pad may also be required.
1297///
1298/// The `ValidConfig` trait is implemented only for valid combinations of
1299/// [`Pads`] and [`OpMode`]. No [`Config`] is valid if the SCK pad is [`NoneT`]
1300/// or if both the Data In and Data Out pads are `NoneT`. When in [`Master`]
1301/// `OpMode`, the `SS` pad must be `NoneT`, while in [`MasterHWSS`] or
1302/// [`Slave`] [`OpMode`], the SS pad must be [`SomePad`].
1303pub trait ValidConfig: AnyConfig {}
1304
1305impl<P, Z> ValidConfig for Config<P, Master, Z>
1306where
1307 P: ValidPads<SS = NoneT>,
1308 Z: Size,
1309{
1310}
1311
1312impl<P, Z> ValidConfig for Config<P, MasterHWSS, Z>
1313where
1314 P: ValidPads,
1315 Z: Size,
1316 P::SS: SomePad,
1317{
1318}
1319
1320impl<P, Z> ValidConfig for Config<P, Slave, Z>
1321where
1322 P: ValidPads,
1323 Z: Size,
1324 P::SS: SomePad,
1325{
1326}
1327
1328//=============================================================================
1329// Spi
1330//=============================================================================
1331
1332/// An enabled SPI peripheral that can perform transactions
1333///
1334/// See the [`impl_ehal`] documentation for details on the implementations of
1335/// the embedded HAL traits, which vary based on [`Size`] and [`Capability`].
1336pub struct Spi<C, A, RxDma = NoneT, TxDma = NoneT>
1337where
1338 C: ValidConfig,
1339 A: Capability,
1340{
1341 config: C,
1342 capability: A,
1343 _rx_channel: RxDma,
1344 _tx_channel: TxDma,
1345}
1346
1347/// Get a shared reference to the underlying [`Config`] struct
1348///
1349/// This can be used to call the various `get_*` functions on `Config`
1350impl<C, A> AsRef<SpecificConfig<C>> for Spi<C, A>
1351where
1352 C: ValidConfig,
1353 A: Capability,
1354{
1355 #[inline]
1356 fn as_ref(&self) -> &SpecificConfig<C> {
1357 self.config.as_ref()
1358 }
1359}
1360
1361impl<C, A, RxDma, TxDma> Spi<C, A, RxDma, TxDma>
1362where
1363 C: ValidConfig,
1364 A: Capability,
1365{
1366 /// Obtain a pointer to the `DATA` register. Necessary for DMA transfers.
1367 #[inline]
1368 #[cfg(feature = "dma")]
1369 pub(super) fn data_ptr(&self) -> *mut C::Word
1370 where
1371 C::Size: Size<Word = C::Word>,
1372 {
1373 self.config.as_ref().data_ptr()
1374 }
1375
1376 /// Change the transaction [`Length`]
1377 ///
1378 /// Changing the transaction [`Length`] while is enabled is permissible but
1379 /// dangerous. If you have sent or received *any* bytes at the current
1380 /// [`Length`], you **must** wait for a TXC flag before changing to a new
1381 /// [`Length`].
1382 #[inline]
1383 #[allow(clippy::type_complexity)]
1384 #[hal_cfg("sercom0-d5x")]
1385 pub fn length<L: Length>(self) -> Spi<Config<C::Pads, C::OpMode, L>, A, RxDma, TxDma>
1386 where
1387 Config<C::Pads, C::OpMode, L>: ValidConfig,
1388 {
1389 Spi {
1390 config: self.config.into().length(),
1391 capability: self.capability,
1392 _rx_channel: self._rx_channel,
1393 _tx_channel: self._tx_channel,
1394 }
1395 }
1396
1397 /// Update the SPI configuration.
1398 ///
1399 /// Calling this method will temporarily disable the SERCOM peripheral, as
1400 /// some registers are enable-protected. This may interrupt any ongoing
1401 /// transactions.
1402 #[inline]
1403 pub fn reconfigure(&mut self, update: impl FnOnce(&mut SpecificConfig<C>)) {
1404 self.config.as_mut().regs.disable();
1405 update(self.config.as_mut());
1406 self.config.as_mut().regs.enable();
1407 }
1408
1409 /// Enable interrupts for the specified flags
1410 #[inline]
1411 pub fn enable_interrupts(&mut self, flags: Flags) {
1412 self.config.as_mut().regs.enable_interrupts(flags)
1413 }
1414
1415 /// Disable interrupts for the specified flags
1416 #[inline]
1417 pub fn disable_interrupts(&mut self, flags: Flags) {
1418 self.config.as_mut().regs.disable_interrupts(flags);
1419 }
1420
1421 /// Read the interrupt flags
1422 #[inline]
1423 pub fn read_flags(&self) -> Flags {
1424 self.config.as_ref().regs.read_flags()
1425 }
1426
1427 /// Clear the corresponding interrupt flags
1428 ///
1429 /// Only the ERROR, SSL and TXC flags can be cleared.
1430 ///
1431 /// **Note:** Implementations of `flush` methods (eg [`SpiBus::flush`]) wait
1432 /// on and clear the `TXC` flag. Manually clearing this flag could cause
1433 /// them to hang indefinitely.
1434 ///
1435 /// [`SpiBus::flush`]: ehal::spi::SpiBus::flush
1436 #[inline]
1437 pub fn clear_flags(&mut self, flags: Flags) {
1438 self.config.as_mut().regs.clear_flags(flags);
1439 }
1440
1441 /// Read the error status flags
1442 #[inline]
1443 pub fn read_status(&self) -> Status {
1444 self.config.as_ref().regs.read_status()
1445 }
1446
1447 /// Clear the corresponding error status flags
1448 #[inline]
1449 pub fn clear_status(&mut self, status: Status) {
1450 self.config.as_mut().regs.clear_status(status);
1451 }
1452
1453 /// Try to read the interrupt flags, but first check the error status flags.
1454 #[inline]
1455 pub fn read_flags_errors(&self) -> Result<Flags, Error> {
1456 self.config.as_ref().regs.read_flags_errors()
1457 }
1458
1459 /// Read from the DATA register
1460 ///
1461 /// # Safety
1462 ///
1463 /// Reading from the data register directly is `unsafe`, because it will
1464 /// clear the RXC flag, which could break assumptions made elsewhere in
1465 /// this module.
1466 #[inline]
1467 pub unsafe fn read_data(&mut self) -> DataWidth {
1468 self.config.as_mut().regs.read_data()
1469 }
1470
1471 /// Write to the DATA register
1472 ///
1473 /// # Safety
1474 ///
1475 /// Writing to the data register directly is `unsafe`, because it will clear
1476 /// the DRE flag, which could break assumptions made elsewhere in this
1477 /// module.
1478 #[inline]
1479 pub unsafe fn write_data(&mut self, data: DataWidth) {
1480 self.config.as_mut().regs.write_data(data);
1481 }
1482
1483 /// Disable the SPI peripheral and return the [`Config`] struct
1484 #[inline]
1485 pub fn disable(mut self) -> C {
1486 self.config.as_mut().regs.rx_disable();
1487 self.config.as_mut().regs.disable();
1488 self.config
1489 }
1490
1491 /// Block until at least one of the flags specified in `flags`, or `ERROR`,
1492 /// is set.
1493 ///
1494 /// Returns `Err(Error)` if an error is detected; also clears the ERROR
1495 /// interrupt flag and the affected STATUS flags.
1496 fn block_on_flags(&mut self, flags: Flags) -> Result<(), Error> {
1497 while !self.read_flags().intersects(flags | Flags::ERROR) {
1498 core::hint::spin_loop();
1499 }
1500 let flags = self.read_flags();
1501 self.check_and_clear_error(flags)
1502 }
1503
1504 #[inline]
1505 fn check_and_clear_error(&mut self, flags: Flags) -> Result<(), Error> {
1506 if flags.contains(Flags::ERROR) {
1507 let errors = self.read_status();
1508 // Clear all status flags at once; BUFOVF has priority, and will mask LENERR if
1509 // both show up at the same time.
1510 self.clear_status(errors);
1511 self.clear_flags(Flags::ERROR);
1512 return errors.check_bus_error();
1513 }
1514
1515 Ok(())
1516 }
1517}
1518
1519impl<C, D> Spi<C, D>
1520where
1521 C: ValidConfig,
1522 D: Receive,
1523 C::OpMode: MasterMode,
1524{
1525 /// Attach RX and TX DMA channels to this [`Spi`]. Its
1526 /// [`SpiBus`](crate::ehal::spi::SpiBus) implementation will use DMA to
1527 /// carry out its transactions. In Master mode, since even read SPI
1528 /// transaction necessarily involve a write, [`Rx`]-only must take two
1529 /// DMA channels, just the same as if it were [`Duplex`].
1530 #[cfg(feature = "dma")]
1531 pub fn with_dma_channels<R, T>(self, rx: R, tx: T) -> Spi<C, D, R, T>
1532 where
1533 R: crate::dmac::AnyChannel<Status = crate::dmac::Ready>,
1534 T: crate::dmac::AnyChannel<Status = crate::dmac::Ready>,
1535 {
1536 Spi {
1537 capability: self.capability,
1538 config: self.config,
1539 _rx_channel: rx,
1540 _tx_channel: tx,
1541 }
1542 }
1543}
1544
1545#[cfg(feature = "dma")]
1546impl<C, D, RxDma, TxDma, S> Spi<C, D, RxDma, TxDma>
1547where
1548 C: ValidConfig,
1549 D: Capability,
1550 RxDma: crate::dmac::AnyChannel<Status = S>,
1551 TxDma: crate::dmac::AnyChannel<Status = S>,
1552 S: crate::dmac::ReadyChannel,
1553{
1554 /// Reclaim the DMA channels. Any subsequent SPI transaction will not use
1555 /// DMA.
1556 pub fn take_dma_channels(self) -> (Spi<C, D, NoneT, NoneT>, RxDma, TxDma) {
1557 (
1558 Spi {
1559 capability: self.capability,
1560 config: self.config,
1561 _rx_channel: NoneT,
1562 _tx_channel: NoneT,
1563 },
1564 self._rx_channel,
1565 self._tx_channel,
1566 )
1567 }
1568}
1569
1570#[cfg(feature = "dma")]
1571impl<C> Spi<C, Duplex>
1572where
1573 C: ValidConfig<OpMode = Slave>,
1574{
1575 /// Attach a DMA channel to this [`Spi`]. Its
1576 /// [`SpiBus`](crate::ehal::spi::SpiBus) implementation will use DMA to
1577 /// carry out its transactions. In Slave mode, a [`Duplex`] [`Spi`] needs
1578 /// two DMA channels.
1579 #[cfg(feature = "dma")]
1580 pub fn with_dma_channels_slave<R, T>(self, rx: R, tx: T) -> Spi<C, Duplex, R, T>
1581 where
1582 R: crate::dmac::AnyChannel<Status = crate::dmac::Ready>,
1583 T: crate::dmac::AnyChannel<Status = crate::dmac::Ready>,
1584 {
1585 Spi {
1586 capability: self.capability,
1587 config: self.config,
1588 _rx_channel: rx,
1589 _tx_channel: tx,
1590 }
1591 }
1592}
1593
1594#[cfg(feature = "dma")]
1595impl<C> Spi<C, Rx>
1596where
1597 C: ValidConfig<OpMode = Slave>,
1598{
1599 /// Attach a DMA channel to this [`Spi`]. Its
1600 /// [`SpiBus`](crate::ehal::spi::SpiBus) implementation will use DMA to
1601 /// carry out its transactions. In Slave mode, a [`Rx`] [`Spi`] only needs a
1602 /// single DMA channel.
1603 #[cfg(feature = "dma")]
1604 pub fn with_rx_channel<R>(self, rx: R) -> Spi<C, Rx, R, NoneT>
1605 where
1606 R: crate::dmac::AnyChannel<Status = crate::dmac::Ready>,
1607 {
1608 Spi {
1609 capability: self.capability,
1610 config: self.config,
1611 _rx_channel: rx,
1612 _tx_channel: NoneT,
1613 }
1614 }
1615}
1616
1617#[cfg(feature = "dma")]
1618impl<C, D, R, T, S> Spi<C, D, R, T>
1619where
1620 C: ValidConfig,
1621 D: Receive,
1622 R: crate::dmac::AnyChannel<Status = S>,
1623 S: crate::dmac::ReadyChannel,
1624{
1625 /// Reclaim the Rx DMA channel. Any subsequent SPI transaction will not use
1626 /// DMA.
1627 #[cfg(feature = "dma")]
1628 pub fn take_rx_channel(self) -> (Spi<C, D, NoneT, T>, R) {
1629 (
1630 Spi {
1631 capability: self.capability,
1632 config: self.config,
1633 _tx_channel: self._tx_channel,
1634 _rx_channel: NoneT,
1635 },
1636 self._rx_channel,
1637 )
1638 }
1639}
1640
1641#[cfg(feature = "dma")]
1642impl<C> Spi<C, Tx>
1643where
1644 C: ValidConfig,
1645{
1646 /// Attach a DMA channel to this [`Spi`]. Its
1647 /// [`SpiBus`](crate::ehal::spi::SpiBus) implementation will use DMA to
1648 /// carry out its transactions. For [`Tx`] [`Spi`]s, only a single DMA
1649 /// channel is necessary.
1650 #[cfg(feature = "dma")]
1651 pub fn with_tx_channel<T>(self, tx: T) -> Spi<C, Tx, NoneT, T>
1652 where
1653 T: crate::dmac::AnyChannel<Status = crate::dmac::Ready>,
1654 {
1655 Spi {
1656 capability: self.capability,
1657 config: self.config,
1658 _rx_channel: NoneT,
1659 _tx_channel: tx,
1660 }
1661 }
1662}
1663
1664#[cfg(feature = "dma")]
1665impl<C, D, R, T, S> Spi<C, D, R, T>
1666where
1667 C: ValidConfig,
1668 D: Capability,
1669 T: crate::dmac::AnyChannel<Status = S>,
1670 S: crate::dmac::ReadyChannel,
1671{
1672 /// Reclaim the DMA channel. Any subsequent SPI transaction will not use
1673 /// DMA.
1674 pub fn take_tx_channel(self) -> (Spi<C, D, R, NoneT>, T) {
1675 (
1676 Spi {
1677 capability: self.capability,
1678 config: self.config,
1679 _rx_channel: self._rx_channel,
1680 _tx_channel: NoneT,
1681 },
1682 self._tx_channel,
1683 )
1684 }
1685}
1686
1687/// Wrapper type around a [`Spi`] that allows using
1688/// [`embedded_hal::spi::SpiBus`] even though it only has RX capability. Will
1689/// panic if any write-adjacent method is used (ie, `write`, `transfer`,
1690/// `transfer_in_place`, and `flush`).
1691///
1692/// Also implements `Into<Spi>`, `AsRef<Spi>` and `AsMut<Spi>` if you need to
1693/// use `Spi` methods.
1694///
1695/// [`embedded_hal::spi::SpiBus`]: crate::ehal::spi::SpiBus
1696pub struct PanicOnWrite<T: crate::ehal::spi::ErrorType>(T);
1697
1698impl<C: ValidConfig, R, T> From<PanicOnWrite<Spi<C, Rx, R, T>>> for Spi<C, Rx, R, T> {
1699 fn from(value: PanicOnWrite<Spi<C, Rx, R, T>>) -> Self {
1700 value.0
1701 }
1702}
1703
1704impl<C: ValidConfig, R, T> AsRef<Spi<C, Rx, R, T>> for PanicOnWrite<Spi<C, Rx, R, T>> {
1705 fn as_ref(&self) -> &Spi<C, Rx, R, T> {
1706 &self.0
1707 }
1708}
1709impl<C: ValidConfig, R, T> AsMut<Spi<C, Rx, R, T>> for PanicOnWrite<Spi<C, Rx, R, T>> {
1710 fn as_mut(&mut self) -> &mut Spi<C, Rx, R, T> {
1711 &mut self.0
1712 }
1713}
1714
1715impl<C: ValidConfig, R, T> Spi<C, Tx, R, T> {
1716 /// Turn a [`Tx`] [`Spi`] into a [`PanicOnWrite`]
1717 pub fn into_panic_on_write(self) -> PanicOnWrite<Self> {
1718 PanicOnWrite(self)
1719 }
1720}
1721
1722/// Wrapper type around a [`Spi`] that allows using
1723/// [`embedded_hal::spi::SpiBus`] even though it only has TX capability. Will
1724/// panic if any write-adjacent method is used (ie, `read`, `transfer`, and
1725/// `transfer_in_place`).
1726///
1727/// Also implements `Into<Spi>`, `AsRef<Spi>` and `AsMut<Spi>` if you need to
1728/// use `Spi` methods.
1729///
1730/// [`embedded_hal::spi::SpiBus`]: crate::ehal::spi::SpiBus
1731pub struct PanicOnRead<T: crate::ehal::spi::ErrorType>(T);
1732
1733impl<C: ValidConfig, R, T> From<PanicOnRead<Spi<C, Tx, R, T>>> for Spi<C, Tx, R, T> {
1734 fn from(value: PanicOnRead<Spi<C, Tx, R, T>>) -> Self {
1735 value.0
1736 }
1737}
1738
1739impl<C: ValidConfig, R, T> AsRef<Spi<C, Tx, R, T>> for PanicOnRead<Spi<C, Tx, R, T>> {
1740 fn as_ref(&self) -> &Spi<C, Tx, R, T> {
1741 &self.0
1742 }
1743}
1744
1745impl<C: ValidConfig, R, T> AsMut<Spi<C, Tx, R, T>> for PanicOnRead<Spi<C, Tx, R, T>> {
1746 fn as_mut(&mut self) -> &mut Spi<C, Tx, R, T> {
1747 &mut self.0
1748 }
1749}
1750
1751impl<C: ValidConfig, R, T> Spi<C, Tx, R, T> {
1752 /// Turn a [`Rx`] [`Spi`] into a [`PanicOnRead`]
1753 pub fn into_panic_on_read(self) -> PanicOnRead<Self> {
1754 PanicOnRead(self)
1755 }
1756}
1757
1758#[hal_cfg("sercom0-d5x")]
1759impl<P, M, A> Spi<Config<P, M, DynLength>, A>
1760where
1761 P: ValidPads,
1762 M: OpMode,
1763 Config<P, M, DynLength>: ValidConfig,
1764 A: Capability,
1765{
1766 /// Return the current transaction length
1767 ///
1768 /// Read the LENGTH register to determine the current transaction length
1769 #[inline]
1770 pub fn get_dyn_length(&self) -> u8 {
1771 self.config.get_dyn_length()
1772 }
1773
1774 /// Set the transaction length
1775 ///
1776 /// Write the LENGTH register to set the transaction length. Panics if the
1777 /// length is zero.
1778 ///
1779 /// Changing the transaction `LENGTH` while is enabled is permissible but
1780 /// dangerous. If you have sent or received *any* bytes at the current
1781 /// `LENGTH`, you **must** wait for a TXC flag before changing to a new
1782 /// `LENGTH`.
1783 #[inline]
1784 pub fn set_dyn_length(&mut self, length: u8) {
1785 self.config.set_dyn_length(length);
1786 }
1787}
1788
1789#[cfg(doc)]
1790#[hal_cfg(not("sercom0-d5x"))]
1791impl<C: ValidConfig, R, T> Spi<C, Tx, R, T> {
1792 /// This method is not present with the selected feature set, defined for
1793 /// documentation only
1794 pub fn get_dyn_length(&self) -> u8 {
1795 unimplemented!()
1796 }
1797}
1798
1799//=============================================================================
1800// AnySpi
1801//=============================================================================
1802
1803/// Type class for all possible [`Spi`] types
1804///
1805/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for
1806/// [`Spi`] types. See the `AnyKind` documentation for more details on the
1807/// pattern.
1808///
1809/// In addition to the normal, `AnyKind` associated types. This trait also
1810/// copies the [`Sercom`], [`Pads`], [`Capability`], [`OpMode`], [`Size`] and
1811/// [`Word`] types, to make it easier to apply bounds to these types at the next
1812/// level of abstraction.
1813///
1814/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
1815/// [type class]: crate::typelevel#type-classes
1816pub trait AnySpi: Is<Type = SpecificSpi<Self>> {
1817 type Sercom: Sercom;
1818 type Pads: ValidPads;
1819 type Capability: Capability;
1820 type OpMode: OpMode;
1821 type Size: Size;
1822 type Word: 'static;
1823 type Config: ValidConfig<Sercom = Self::Sercom>;
1824}
1825
1826/// Type alias to recover the specific [`Spi`] type from an implementation of
1827/// [`AnySpi`]
1828pub type SpecificSpi<S> = Spi<<S as AnySpi>::Config, <S as AnySpi>::Capability>;
1829
1830impl<C, A> AsRef<Self> for Spi<C, A>
1831where
1832 C: ValidConfig,
1833 A: Capability,
1834{
1835 #[inline]
1836 fn as_ref(&self) -> &Self {
1837 self
1838 }
1839}
1840
1841impl<C, A> AsMut<Self> for Spi<C, A>
1842where
1843 C: ValidConfig,
1844 A: Capability,
1845{
1846 #[inline]
1847 fn as_mut(&mut self) -> &mut Self {
1848 self
1849 }
1850}
1851
1852impl<C, A> Sealed for Spi<C, A>
1853where
1854 C: ValidConfig,
1855 A: Capability,
1856{
1857}
1858
1859impl<C, A> AnySpi for Spi<C, A>
1860where
1861 C: ValidConfig,
1862 A: Capability,
1863{
1864 type Sercom = C::Sercom;
1865 type Pads = C::Pads;
1866 type Capability = A;
1867 type OpMode = C::OpMode;
1868 type Size = C::Size;
1869 type Word = C::Word;
1870 type Config = C;
1871}