-
Notifications
You must be signed in to change notification settings - Fork 220
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
99 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -406,6 +406,7 @@ | |
#![deny(missing_docs)] | ||
#![no_std] | ||
#![feature(generic_associated_types)] | ||
|
||
pub mod fmt; | ||
pub use nb; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
} |