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