Module atsamd_hal::sercom::spi
source · [−]Expand description
Use a SERCOM peripheral for SPI transactions
Using an SPI peripheral occurs in three steps. First, you must supply
gpio Pins to create a set of Pads. Next, you combine the
Pads with other pieces to form a Config struct. Finally, after
configuring the peripheral, you enable it to yield a functional
Spi struct. Transactions are performed using traits from the
embedded_hal crate, specifically those from the
spi, serial, and
blocking modules.
Crating a set of Pads
An SPI peripheral can use up to four Pins as Sercom pads. However,
only certain Pin combinations are acceptable. All Pins must be mapped to
the same Sercom, and for SAMx5x chips, they must also belong to the same
IoSet.
This HAL makes it impossible to use invalid Pin combinations, and the
Pads struct is responsible for enforcing these constraints.
A Pads type takes five or six type parameters, depending on the chip. The
first type always specifies the Sercom. On SAMx5x chips, the second type
specifies the IoSet. The remaining four type parameters, DI, DO, CK
and SS, represent the Data In, Data Out, Sclk and SS pads respectively.
Each of these type parameters is an OptionalPad and defaults to
NoneT. A Pad is just a Pin configured in the correct PinMode
that implements IsPad. The bsp_pins! macro can be
used to define convenient type aliases for Pad types.
use atsamd_hal::gpio::{PA08, PA09, AlternateC};
use atsamd_hal::sercom::{Sercom0, spi};
use atsamd_hal::typelevel::NoneT;
// SAMx5x-specific imports
use atsamd_hal::sercom::pad::IoSet1;
type Miso = Pin<PA08, AlternateC>;
type Sclk = Pin<PA09, AlternateC>;
// SAMD11/SAMD21 version
type Pads = spi::Pads<Sercom0, Miso, NoneT, Sclk>;
// SAMx5x version
type Pads = spi::Pads<Sercom0, IoSet1, Miso, NoneT, Sclk>;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. For SAMD21 and SAMx5x chips, the builder
methods automatically convert each pin to the correct PinMode. However,
due to inherent ambiguities, users must manually configure PinModes for
SAMD11 chips.
use atsamd_hal::target_device::Peripherals;
use atsamd_hal::gpio::Pins;
use atsamd_hal::sercom::{Sercom0, spi};
// SAMx5x-specific imports
use atsamd_hal::sercom::pad::IoSet1;
let mut peripherals = Peripherals::take().unwrap();
let pins = Pins::new(peripherals.PORT);
// SAMD21 version
let pads = spi::Pads::<Sercom0>::default()
.sclk(pins.pa09)
.data_in(pins.pa08)
.data_out(pins.pa11);
// SAMx5x version
let pads = spi::Pads::<Sercom0, IoSet1>::default()
.sclk(pins.pa09)
.data_in(pins.pa08)
.data_out(pins.pa11);To be accepted by the Config struct as a set of ValidPads, the
Pads must do two things:
- Specify
SomePadforCKand at least one ofDIorDO - Use a valid combination of
PadNums, so that thePadsimplementDipoDopo
Configuring the peripheral
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
Size type that varies by chip. Size essentially acts as a trait
alias. On SAMD11 and SAMD21 chips, it represents the
CharSize, which can either be EightBit or NineBit.
While on SAMx5x chips, it represents the transaction
Length
in bytes, using type-level numbers provided by the typenum crate. Valid
transaction lengths, from U1 to U255, are re-exported in the
lengths
sub-module.
use atsamd_hal::gpio::{PA08, PA09};
use atsamd_hal::sercom::{Sercom0, spi};
use atsamd_hal::sercom::spi::Master;
use atsamd_hal::typelevel::NoneT;
// SAMD11/SAMD21-specific imports
use atsamd_hal::sercom::spi::NineBit;
// SAMx5x-specific imports
use atsamd_hal::sercom::spi::lengths::U2;
use atsamd_hal::sercom::pad::IoSet1;
// SAMD11/SAMD21 version
type Pads = spi::PadsFromIds<Sercom0, PA08, NoneT, PA09>;
type Config = spi::Config<Pads, Master, NineBit>;
// SAMx5x version
type Pads = spi::PadsFromIds<Sercom0, IoSet1, PA08, NoneT, PA09>;
type Config = spi::Config<Pads, Master, U2>;For simplicity, this module ignores character size on SAMx5x chips. Instead,
the SPI peripheral is always configured to use 32-bit extension mode and the
hardware LENGTH counter. Note that, due to a hardware bug, ICSPACE must
be at least one when using the length counter. See the silicon errata for
more details.
Upon creation, the Config takes ownership of both the Pads and the
PAC Sercom struct. It takes a reference to the PM or 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;
// Not shown: configure GCLK for 10 MHz
// SAMD11/SAMD21 version
let pm = peripherals.PM;
let sercom = peripherals.SERCOM0;
let freq = 10.mhz();
let config = spi::Config::new(&pm, sercom, pads, freq);
// SAMx5x version
let mclk = peripherals.MCLK;
let sercom = peripherals.SERCOM0;
let freq = 10.mhz();
let config = spi::Config::new(&mclk, sercom, pads, freq);The Config uses two different APIs for configuration. For most
parameters, it provides get_ and set_ methods that take &self and
&mut self respectively, e.g. get_bit_order and
set_bit_order. However, because Config tracks
the OpMode and Size at compile-time, which requires changing the
corresponding type parameters, Config also provides a builder-pattern API,
where methods take and return self, e.g. bit_order.
Once configured, the enable method consumes the Config and returns an
enabled Spi struct that can be used for transactions. Because the
enable function takes the Config as self, the builder-pattern API is
usually the more ergonomic option.
use embedded_hal::spi::MODE_1;
// SAMD11/SAMD21 version
let spi = spi::Config::new(&pm, sercom, pads, freq)
.baud(1.mhz())
.char_size::<NineBit>()
.bit_order(BitOrder::LsbFirst)
.spi_mode(MODE_1)
.enable();
// SAMx5x version
let spi = spi::Config::new(&mclk, sercom, pads, freq)
.baud(1.mhz())
.length::<U2>()
.bit_order(BitOrder::LsbFirst)
.spi_mode(MODE_1)
.enable();To be accepted as a ValidConfig, the Config must have a set of
ValidPads that matches its OpMode. In particular, the SS pad must
be NoneT for Master mode, where the user is expected to handle it
manaully. But it must be SomePad in MasterHWSS and Slave modes,
where it is controlled by the hardware.
Using a functional Spi peripheral
An Spi struct has two type parameters. The first is the corresponding
Config, while the second represents its Capability, i.e. Rx,
Tx or Duplex. The enable function determines the Capability
automaically from the set of ValidPads.
use atsamd_hal::gpio::{PA08, PA09};
use atsamd_hal::sercom::{Sercom0, spi};
use atsamd_hal::sercom::spi::{Master, Rx};
use atsamd_hal::typelevel::NoneT;
// SAMD11/SAMD21-specific imports
use atsamd_hal::sercom::spi::NineBit;
// SAMx5x-specific imports
use atsamd_hal::sercom::spi::lengths::U2;
use atsamd_hal::sercom::pad::IoSet1;
// SAMD11/SAMD21 version
type Pads = spi::PadsFromIds<Sercom0, PA08, NoneT, PA09>;
type Config = spi::Config<Pads, Master, NineBit>;
type Spi = spi::Spi<Config, Rx>;
// SAMx5x version
type Pads = spi::PadsFromIds<Sercom0, IoSet1, PA08, NoneT, PA09>;
type Config = spi::Config<Pads, Master, U2>;
type Spi = spi::Spi<Config, Rx>;Only Spi structs can actually perform transactions. To do so, use the
various embedded HAL traits, like
spi::FullDuplex,
serial::Read or
serial::Write.
See the impl_ehal module documentation for more details about the
specific trait implementations, which vary based on Size and
Capability.
use nb::block;
use embedded_hal::spi::FullDuplex;
block!(spi.send(0xAA55));
let rcvd: u16 = block!(spi.read());Modules
embedded_hal traits for Spi structsStructs
Capability enum for duplex transactionsCapability enum for simplex, Receive-only
transactionsCapability enum for simplex, Transmit-only
transactionsEnums
enum for SPI transactionsOpMode variant for Master mode with hardware-controlled slave selectConstants
Traits
Sizes that can be completed in a single
read or write of the DATA registerOptionalPad types from a generic set
of PadsConfigurationsType Definitions
DATA registerSize type, which varies by chip