Expand description
Implement embedded_hal traits for Spi structs
As noted in the spi module documentation, the embedded-hal trait
implementations vary by both Size and Capability. Each
implementation is optimized to take advantage of all information known at
compile-time, so it is importatnt to carefully read the documentation in
this module.
Variations by Size
Remember that SAMx5x chips operate in 32-bit extension mode and use the
hardware LENGTH counter to set the number of bytes in each transaction.
The transaction Length is usually tracked at compile-time using
type-level integers from the typenum crate, but it can also be tracked
at run-time when using a DynLength.
The transaction Lengths can be sub-divided into three groups:
- Lengths of 1-4 bytes can be completed in a single read/write of the- DATAregister. These- Lengths are marked as- AtomicSizes.
- Lengths- GreaterThan4are known at compile-time but cannot be completed atomically.
- A DynLengthcan be any length, but the value is only known at run-time.
In general, transaction lengths with an AtomicSize implement embedded HAL
traits with the corresponding Word type. For example, Spi structs
using a transaction Length of 2 bytes implement FullDuplex<u16>. These
lengths implement both the blocking and non-blocking traits from embedded
HAL. The non-blocking traits are found in the spi and serial
modules, while the blocking traits are found in the blocking module.
Transaction lengths GreaterThan4 cannot be completed in a single read or
write of the DATA register, so these lengths do NOT implement the
non-blocking traits from the embedded HAL spi and serial modules.
Instead, they only implement traits from the blocking module. These traits
are implemented for u8 types, e.g. blocking::spi::Transfer<u8>, and
operate on [u8] slices. The length of the slice is checked to ensure it
matches the transaction Length.
Because a DynLength is not guaranteed to be an AtomicSize, the
corresponding Spi structs only implement the blocking traits as well.
For a non-blocking alternative that can be used to transfer arbitrary-length
slices, you could use either
DMA
or the spi_future module.
Variations by Capability
The implementations in this module also seek to optimize as much as possible
based on the Capability of the Spi struct. They follow a few general
rules:
- Txstructs can never receive data, so their corresponding trait implementations never read the- DATAregister and can never return an- Error::Overflow.
- Rxstructs in a- MasterModemust initiate all transactions, so their implementations of non-blocking traits must track the state of on-going transactions.
- Duplexstructs must always read as many bytes as they send, even when implementing- Write-only traits, to ensure they never introduce an- Error::Overflow.
Notes on individual embedded HAL traits
spi::FullDuplex
spi::FullDuplex is only implemented for structs with Duplex
Capability. Although the embedded HAL documentation assumes a
MasterMode, this module also implements it for the Slave OpMode.
serial::Read
serial::Read is only implemented for structs with Rx Capability. When
in a MasterMode, it initiates and tracks the state of the on-going
transactions. But this is not required when acting as a Slave.
serial::Write
serial::Write is only implemented for structs with Tx Capability.
These implementations never read the DATA register and ignore all
instances of Error::Overflow.
blocking::serial::Write
This trait uses the blocking::serial::write::Default implementation for
implementers of serial::Write.
blocking::spi traits
These traits are implemented following all of the rules outlined above for
the different Size and Capability options.