Skip to content

Commit

Permalink
WIP spi unify + device/bus traits.
Browse files Browse the repository at this point in the history
  • Loading branch information
Dirbaio committed Jan 20, 2022
1 parent 61285e4 commit 37831ac
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 84 deletions.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@
#![deny(missing_docs)]
#![no_std]
#![feature(generic_associated_types)]

pub mod fmt;
pub use nb;
Expand Down
182 changes: 98 additions & 84 deletions src/spi/blocking.rs
Original file line number Diff line number Diff line change
@@ -1,108 +1,122 @@
//! Blocking SPI API
use super::ErrorType;
/// SPI device traits.
pub mod device {
use super::bus;
use crate::spi::ErrorType;

/// Blocking transfer with separate buffers
pub trait Transfer<Word = u8>: ErrorType {
/// Writes and reads simultaneously. `write` is written to the slave on MOSI and
/// words received on MISO are stored in `read`.
/// SPI device base trait
///
/// It is allowed for `read` and `write` to have different lengths, even zero length.
/// The transfer runs for `max(read.len(), write.len())` words. If `read` is shorter,
/// incoming words after `read` has been filled will be discarded. If `write` is shorter,
/// the value of words sent in MOSI after all `write` has been sent is implementation-defined,
/// typically `0x00`, `0xFF`, or configurable.
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>;
}
/// If you're writing a driver, require [`SpiDevice`] (read-write), [`SpiReadonlyDevice`], [`SpiWriteonlyDevice`]
/// to specify the kind of access to the device.
pub trait SpiDeviceBase: ErrorType {
/// Transaction type for this device.
type Transaction<'a>: ErrorType<Error = Self::Error>
where
Self: 'a;

impl<T: Transfer<Word>, Word: Copy> Transfer<Word> for &mut T {
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
T::transfer(self, read, write)
/// Start a transaction against this device.
///
/// This locks the bus, maybe asserts CS if [`ManagedChipSelect`] is implemented, and returns a
/// [`Transaction`](SpiDeviceBase::Transaction) instance that you can then use to do transfers
/// against the device.
///
/// The transaction lasts as long as the returned [`Transaction`](SpiDeviceBase::Transaction). Dropping
/// it automatically ends the transaction, which deasserts CS (if [`ManagedChipSelect`]) and unlocks the bus.
fn transaction<'a>(&'a mut self) -> Result<Self::Transaction<'a>, Self::Error>;
}
}

/// Blocking transfer with single buffer (in-place)
pub trait TransferInplace<Word: Copy = u8>: ErrorType {
/// Writes and reads simultaneously. The contents of `words` are
/// written to the slave, and the received words are stored into the same
/// `words` buffer, overwriting it.
fn transfer_inplace(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
}
/// Managed chip select marker trait.
///
/// If this trait is implemented for an SPI device, the chip select line
/// is automatically managed by the implementation. In particular:
///
/// - CS is asserted (driven low) when calling [`SpiDeviceBase::transaction()`]
/// - CS is deasserted (driven high) when dropping the returned [`SpiDeviceBase::Transaction`]
pub trait ManagedChipSelect: SpiDeviceBase {}

impl<T: TransferInplace<Word>, Word: Copy> TransferInplace<Word> for &mut T {
fn transfer_inplace(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
T::transfer_inplace(self, words)
/// SPI device
pub trait SpiDevice<Word: Copy = u8>: SpiDeviceBase
where
for<'a> Self::Transaction<'a>: bus::SpiBus<Word>,
{
}
}

/// Blocking read
pub trait Read<Word: Copy = u8>: ErrorType {
/// Reads `words` from the slave.
///
/// The word value sent on MOSI during reading is implementation-defined,
/// typically `0x00`, `0xFF`, or configurable.
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
}
/// SPI device with read-only access
pub trait SpiReadonlyDevice<Word: Copy = u8>: SpiDeviceBase
where
for<'a> Self::Transaction<'a>: bus::SpiReadonlyBus<Word>,
{
}

impl<T: Read<Word>, Word: Copy> Read<Word> for &mut T {
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
T::read(self, words)
/// SPI device with read-write access
pub trait SpiWriteonlyDevice<Word: Copy = u8>: SpiDeviceBase
where
for<'a> Self::Transaction<'a>: bus::SpiWriteonlyBus<Word>,
{
}
}

/// Blocking write
pub trait Write<Word: Copy = u8>: ErrorType {
/// Writes `words` to the slave, ignoring all the incoming words
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>;
// TODO: blanket impls for &mut
}

impl<T: Write<Word>, Word: Copy> Write<Word> for &mut T {
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
T::write(self, words)
/// SPI bus traits.
///
/// Owning an instance of them means owning the entire bus.
pub mod bus {
use crate::spi::ErrorType;

/// Blocking read-only SPI
pub trait SpiReadonlyBus<Word: Copy = u8>: ErrorType {
/// Reads `words` from the slave.
///
/// The word value sent on MOSI during reading is implementation-defined,
/// typically `0x00`, `0xFF`, or configurable.
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
}
}

/// Blocking write (iterator version)
pub trait WriteIter<Word: Copy = u8>: ErrorType {
/// Writes `words` to the slave, ignoring all the incoming words
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
where
WI: IntoIterator<Item = Word>;
}
impl<T: SpiReadonlyBus<Word>, Word: Copy> SpiReadonlyBus<Word> for &mut T {
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
T::read(self, words)
}
}

impl<T: WriteIter<Word>, Word: Copy> WriteIter<Word> for &mut T {
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
where
WI: IntoIterator<Item = Word>,
{
T::write_iter(self, words)
/// Blocking write-only SPI
pub trait SpiWriteonlyBus<Word: Copy = u8>: ErrorType {
/// Writes `words` to the slave, ignoring all the incoming words
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>;
}
}

/// Operation for transactional SPI trait
///
/// This allows composition of SPI operations into a single bus transaction
#[derive(Debug, PartialEq)]
pub enum Operation<'a, Word: 'static + Copy = u8> {
/// Read data into the provided buffer.
Read(&'a mut [Word]),
/// Write data from the provided buffer, discarding read data
Write(&'a [Word]),
/// Write data out while reading data into the provided buffer
Transfer(&'a mut [Word], &'a [Word]),
/// Write data out while reading data into the provided buffer
TransferInplace(&'a mut [Word]),
}
impl<T: SpiWriteonlyBus<Word>, Word: Copy> SpiWriteonlyBus<Word> for &mut T {
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
T::write(self, words)
}
}

/// Transactional trait allows multiple actions to be executed
/// as part of a single SPI transaction
pub trait Transactional<Word: 'static + Copy = u8>: ErrorType {
/// Execute the provided transactions
fn exec<'a>(&mut self, operations: &mut [Operation<'a, Word>]) -> Result<(), Self::Error>;
}
/// Blocking read-write SPI
pub trait SpiBus<Word: Copy = u8>: SpiReadonlyBus<Word> + SpiWriteonlyBus<Word> {
/// Writes and reads simultaneously. `write` is written to the slave on MOSI and
/// words received on MISO are stored in `read`.
///
/// It is allowed for `read` and `write` to have different lengths, even zero length.
/// The transfer runs for `max(read.len(), write.len())` words. If `read` is shorter,
/// incoming words after `read` has been filled will be discarded. If `write` is shorter,
/// the value of words sent in MOSI after all `write` has been sent is implementation-defined,
/// typically `0x00`, `0xFF`, or configurable.
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>;

/// Writes and reads simultaneously. The contents of `words` are
/// written to the slave, and the received words are stored into the same
/// `words` buffer, overwriting it.
fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
}

impl<T: SpiBus<Word>, Word: Copy> SpiBus<Word> for &mut T {
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
T::transfer(self, read, write)
}

impl<T: Transactional<Word>, Word: 'static + Copy> Transactional<Word> for &mut T {
fn exec<'a>(&mut self, operations: &mut [Operation<'a, Word>]) -> Result<(), Self::Error> {
T::exec(self, operations)
fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
T::transfer_in_place(self, words)
}
}
}

0 comments on commit 37831ac

Please sign in to comment.