Module grand_central_m4::sercom::v2::spi[][src]

Expand description

Use the SERCOM peripheral for SPI transactions

Configuring an SPI peripheral occurs in three steps. First, you must create a set of Pads for use by the peripheral. Next, you assemble pieces into a Config struct. After configuring the peripheral, you then enable it, yielding a functional Spi struct. Transactions are performed using the spi and serial traits from embedded HAL.

Pads

A Sercom can use up to four Pins as peripheral pads, but only certain Pin combinations are acceptable. In particular, all Pins must be mapped to the same Sercom and IoSet (see section 6.2.8.1 of the datasheet). This HAL makes it impossible to use invalid Pin combinations, and the Pads struct is responsible for enforcing these constraints.

A Pads type takes up to six type parameters. The first two specify the Sercom and IoSet, while the remaining four, DI, DO, CK and SS, represent the Data In, Data Out, Sclk and SS pads respectively. Each of the remaining type parameters is an OptionalPad and defaults to NoneT. Aliases defining the pad types can be provided by the bsp_pins! macro.

use atsamd_hal::gpio::v2::{PA08, PA09, AlternateC};
use atsamd_hal::sercom::v2::{Sercom0, spi};
use atsamd_hal::sercom::v2::pad::IoSet1;
use atsamd_hal::typelevel::NoneT;

type Miso = Pin<PA08, AlternateC>;
type Sclk = Pin<PA09, AlternateC>;
type Pads = spi::Pads<Sercom0, IoSet1, Miso, NoneT, Sclk>;

Alternatively, you can use the PadsFromIds alias to define a set of Pads in terms of PinIds instead of Pins. This is useful when you don’t have Pin aliases pre-defined.

use atsamd_hal::gpio::v2::{PA08, PA09};
use atsamd_hal::sercom::v2::{Sercom0, spi};
use atsamd_hal::sercom::v2::pad::IoSet1;
use atsamd_hal::typelevel::NoneT;

type Pads = spi::PadsFromIds<Sercom0, IoSet1, PA08, NoneT, PA09>;

Instances of Pads are created using the builder pattern. Start by creating an empty set of Pads using Default. Then pass each respective Pin using the corresponding methods. Both v1::Pin and v2::Pin types are accepted here. The builder methods automatically convert each pin to the correct PinMode.

Note that the CK Pin must map to Pad1, and if specified, the SS Pin must map to Pad2. The DI and DO Pins can vary in PadNum based on the Dipo and Dopo values.

use atsamd_hal::pac::Peripherals;
use atsamd_hal::gpio::v2::Pins;
use atsamd_hal::sercom::v2::{Sercom0, spi};
use atsamd_hal::sercom::v2::pad::IoSet1;

let mut peripherals = Peripherals::take().unwrap();
let pins = Pins::new(peripherals.PORT);
let pads = spi::Pads::<Sercom0, IoSet1>::default()
    .sclk(pins.pa09)
    .data_in(pins.pa08)
    .data_out(pins.pa11);

To be accepted as ValidPads, a set of Pads must do two things:

  • Specify a type for CK and at least one of DI or DO
  • Satisfy the Dipo and Dopo traits

Config

Next, create a Config struct, which represents the SPI peripheral in its disabled state. A Config is specified with three type parameters: the Pads type; an OpMode, which defaults to Master; and a transaction Length, in bytes, represented at the type level using the [typenum] crate. Valid transaction lengths are provided in the lengths sub-module. The default Length is U1.

use atsamd_hal::gpio::v2::{PA08, PA09};
use atsamd_hal::sercom::v2::{Sercom0, spi};
use atsamd_hal::sercom::v2::spi::{Master, lengths::U2};
use atsamd_hal::sercom::v2::pad::IoSet1;
use atsamd_hal::typelevel::NoneT;

type Pads = spi::PadsFromIds<Sercom0, IoSet1, PA08, NoneT, PA09>;
type Config = spi::Config<Pads, Master, U2>;

The SPI peripheral has two different ways to control the transaction length, the character size and the length counter. The character size can be set to 8-bit or 9-bit transactions. The length counter can be set to produce transactions of any length from 1-255 bytes. For simplicity, this module ignores character size. Instead, the SPI peripheral is always configured to use 32-bit extension mode and the length counter.

Upon creation, the Config takes ownership of both the Pads and the PAC Sercom struct. It takes a reference to the MCLK, so that it can enable the APB clock, and it takes a frequency to indicate the GCLK configuration. Users are responsible for correctly configuring the GCLK.

use atsamd_hal::time::U32Ext;

let mclk = peripherals.MCLK;
let sercom = peripherals.SERCOM0;
// Configure GCLK for 10 MHz
let freq = 10.mhz();
let config = spi::Config::new(&mclk, sercom, pads, freq);

The Config struct uses the builder pattern to configure the peripheral, ending with a call to enable, which consumes the Config and returns an enabled Spi peripheral.

use embedded_hal::spi::MODE_1;

let spi = spi::Config::new(&mclk, sercom, pads, freq)
    .baud(1.mhz())
    .length::<U2>()
    .msb_first(false)
    .spi_mode(MODE_1)
    .enable();

To be accepted as a ValidConfig, the Config must have all the necessary pads for its OpMode.

Spi

An Spi struct can only be created from a Config, and it has only one type parameter, the corresponding config.

use atsamd_hal::gpio::v2::{PA08, PA09};
use atsamd_hal::sercom::v2::{Sercom0, spi};
use atsamd_hal::sercom::v2::spi::{Master, lengths::U2};
use atsamd_hal::sercom::v2::pad::IoSet1;
use atsamd_hal::typelevel::NoneT;

type Pads = spi::PadsFromIds<Sercom0, IoSet1, PA08, NoneT, PA09>;
type Config = spi::Config<Pads, Master, U2>;
type Spi = spi::Spi<Config>;

Only Spis struct can actually perform transactions. To do so, use the embedded HAL traits, like spi::FullDuplex, serial::Read and serial::Write. See the Spi documentation for more information about the trait implementations, which vary based on the transaction Length and Pads. For instance, FullDuplex is only implemented if the Pads are both Tx and Rx and if the transaction Length is less than U4.

use nb::block;
use embedded_hal::spi::FullDuplex;

block!(spi.send(0xAA55));
let rcvd: u16 = block!(spi.read());

Modules

Re-export [typenum] constants for use as Length type parameters

Structs

A configurable, disabled SPI peripheral

Error bit flags for SPI transactions

Interrupt bit flags for SPI transactions

Container for a set of SERCOM pads

An enabled SPI peripheral that can perform transactions

Enums

Error enum for SPI transactions

OpMode variant for Master mode

OpMode variant for Master mode with hardware-controlled slave select

Clock phase

Clock polarity

OpMode variant for Slave mode

Constants

Helper for CPOL = 0, CPHA = 0

Helper for CPOL = 0, CPHA = 1

Helper for CPOL = 1, CPHA = 0

Helper for CPOL = 1, CPHA = 1

Traits

Type class for all possible Config types

Type class for all possible Spi types

Marker trait for transactions that are performed atomically

Control the DIPO field as a function of the PadNum type

Control the DOPO field as a function of the PadNum type

Marker trait for transaction Lengths greater than four

Type-level enum representing the SPI transaction length, in bytes

Marker trait for Master operating modes

Marker trait for a set of Pads that cannot receive

Marker trait for a set of Pads that cannot transmit

Type-level enum representing the SPI operating mode

Type-level function to recover the OptionalPad types from a generic set of Pads

Marker trait for a set of Pads that can receive

Marker trait for statically known transaction Lengths

Marker trait for a set of Pads that can transmit

Marker trait for a set of Pads that can transmit OR receive

Marker trait for valid SPI Configurations

Marker trait for valid sets of Pads

Type Definitions

Marker type for a run-time dynamic Length

Define a set of Pads using PinIds instead of Pins

Type alias to recover the specific Config type from an implementation of AnyConfig

Type alias to recover the specific Spi type from an implementation of AnySpi

Type alias to recover the Word type from an implementation of Length