diff --git a/.gitignore b/.gitignore index f8d7c8b49..7f487cce0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ **/*.rs.bk .#* -/target/ +target Cargo.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index ad4fb44ed..8ac881cb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - `Error` traits for Can, SPI, I2C and Serial are implemented for Infallible +### Changed +- `spi`: traits now enforce all impls on the same struct (eg `Transfer` and `Write`) have the same `Error` type. +- `spi/blocking`: unified traits into `Read`, `Write`, `ReadWrite`. +- `spi/blocking`: renamed Transactional `exec` to `batch`. +- `spi/blocking`: Added `read_batch`, `write_batch` methods. + ## [v1.0.0-alpha.6] - 2021-11-19 *** This is (also) an alpha release with breaking changes (sorry) *** diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml new file mode 100644 index 000000000..6226f7e50 --- /dev/null +++ b/embedded-hal-async/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "embedded-hal-async" +version = "0.1.0" +edition = "2021" +categories = ["asynchronous", "embedded", "hardware-support", "no-std"] +description = " A Hardware Abstraction Layer (HAL) for embedded systems, async version" +documentation = "https://docs.rs/embedded-hal-async" +keywords = ["hal", "IO", "async"] +license = "MIT OR Apache-2.0" +readme = "README.md" +repository = "https://github.com/rust-embedded/embedded-hal" + +[dependencies] +nb = "1" +embedded-hal = { version = "1.0.0-alpha.6", path = ".." } \ No newline at end of file diff --git a/embedded-hal-async/src/delay.rs b/embedded-hal-async/src/delay.rs new file mode 100644 index 000000000..a7b8f6313 --- /dev/null +++ b/embedded-hal-async/src/delay.rs @@ -0,0 +1,52 @@ +//! Delays + +use core::future::Future; + +/// Microsecond delay +pub trait DelayUs { + /// Enumeration of errors + type Error: core::fmt::Debug; + + /// The future returned by the `delay_us` function. + type DelayUsFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Pauses execution for at minimum `us` microseconds. Pause can be longer + /// if the implementation requires it due to precision/timing issues. + fn delay_us(&mut self, us: u32) -> Self::DelayUsFuture<'_>; + + /// The future returned by the `delay_ms` function. + type DelayMsFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Pauses execution for at minimum `ms` milliseconds. Pause can be longer + /// if the implementation requires it due to precision/timing issues. + fn delay_ms(&mut self, ms: u32) -> Self::DelayMsFuture<'_>; +} + +impl DelayUs for &mut T +where + T: DelayUs, +{ + type Error = T::Error; + + type DelayUsFuture<'a> + where + Self: 'a, + = T::DelayUsFuture<'a>; + + fn delay_us(&mut self, us: u32) -> Self::DelayUsFuture<'_> { + T::delay_us(self, us) + } + + type DelayMsFuture<'a> + where + Self: 'a, + = T::DelayMsFuture<'a>; + + fn delay_ms(&mut self, ms: u32) -> Self::DelayMsFuture<'_> { + T::delay_ms(self, ms) + } +} diff --git a/embedded-hal-async/src/digital.rs b/embedded-hal-async/src/digital.rs new file mode 100644 index 000000000..ae1d6c543 --- /dev/null +++ b/embedded-hal-async/src/digital.rs @@ -0,0 +1,100 @@ +//! Asynchronous digital I/O +//! +//! # Example +//! +//! ```rust +//! # use embedded_hal_async::digital::WaitForHigh; +//! /// Asynchronously wait until the `ready_pin` becomes high. +//! async fn wait_until_ready

(ready_pin: &mut P) +//! where +//! P: WaitForHigh, +//! { +//! ready_pin +//! .wait_for_high() +//! .await +//! .expect("failed to await input pin") +//! } +//! ``` + +use core::future::Future; + +/// Asynchronously wait for a pin to be high. +pub trait WaitForHigh { + /// Enumeration of errors. + type Error: core::fmt::Debug; + + /// The future returned by the `wait_for_high` function. + type WaitForHighFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Returns a future that resolves when this pin _is_ high. If the pin + /// is already high, the future resolves immediately. + /// + /// # Note for implementers + /// The pin may have switched back to low before the task was run after + /// being woken. The future should still resolve in that case. + fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a>; +} + +/// Asynchronously wait for a pin to be low. +pub trait WaitForLow { + /// Enumeration of errors. + type Error: core::fmt::Debug; + + /// The future returned by `wait_for_low`. + type WaitForLowFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Returns a future that resolves when this pin _is_ low. If the pin + /// is already low, the future resolves immediately. + /// + /// # Note for implementers + /// The pin may have switched back to high before the task was run after + /// being woken. The future should still resolve in that case. + fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a>; +} + +/// Wait for a rising edge (transition from low to high). +pub trait WaitForRisingEdge { + /// Enumeration of errors. + type Error: core::fmt::Debug; + + /// The future returned from `wait_for_rising_edge`. + type WaitForRisingEdgeFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Returns a future that resolves when this pin transitions from low to high. + fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a>; +} + +/// Wait for a falling edge (transition from high to low). +pub trait WaitForFallingEdge { + /// Enumeration of errors. + type Error: core::fmt::Debug; + + /// The future returned from `wait_for_falling_edge`. + type WaitForFallingEdgeFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Returns a future that resolves when this pin transitions from high to low. + fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a>; +} + +/// Wait for any edge (transition from low to high OR high to low). +pub trait WaitForAnyEdge { + /// Enumeration of errors. + type Error: core::fmt::Debug; + + /// The future returned from `wait_for_any_edge`. + type WaitForAnyEdgeFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Returns a future that resolves when this pin undergoes any transition, e.g. + /// low to high OR high to low. + fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a>; +} diff --git a/embedded-hal-async/src/i2c.rs b/embedded-hal-async/src/i2c.rs new file mode 100644 index 000000000..ef0718757 --- /dev/null +++ b/embedded-hal-async/src/i2c.rs @@ -0,0 +1,163 @@ +//! Async I2C API +//! +//! This API supports 7-bit and 10-bit addresses. Traits feature an `AddressMode` +//! marker type parameter. Two implementation of the `AddressMode` exist: +//! `SevenBitAddress` and `TenBitAddress`. +//! +//! Through this marker types it is possible to implement each address mode for +//! the traits independently in `embedded-hal` implementations and device drivers +//! can depend only on the mode that they support. +//! +//! Additionally, the I2C 10-bit address mode has been developed to be fully +//! backwards compatible with the 7-bit address mode. This allows for a +//! software-emulated 10-bit addressing implementation if the address mode +//! is not supported by the hardware. +//! +//! Since 7-bit addressing is the mode of the majority of I2C devices, +//! `SevenBitAddress` has been set as default mode and thus can be omitted if desired. + +use core::future::Future; +pub use embedded_hal::i2c::{ + AddressMode, Error, ErrorKind, NoAcknowledgeSource, SevenBitAddress, TenBitAddress, +}; + +/// Async read +pub trait Read { + /// Error type + type Error: Error; + /// The future associated with the `read` method. + type ReadFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Reads enough bytes from slave with `address` to fill `buffer` + /// + /// # I2C Events (contract) + /// + /// ``` text + /// Master: ST SAD+R MAK MAK ... NMAK SP + /// Slave: SAK B0 B1 ... BN + /// ``` + /// + /// Where + /// + /// - `ST` = start condition + /// - `SAD+R` = slave address followed by bit 1 to indicate reading + /// - `SAK` = slave acknowledge + /// - `Bi` = ith byte of data + /// - `MAK` = master acknowledge + /// - `NMAK` = master no acknowledge + /// - `SP` = stop condition + fn read<'a>(&'a mut self, address: A, read: &'a mut [u8]) -> Self::ReadFuture<'a>; +} + +impl> Read for &mut T { + type Error = T::Error; + + type ReadFuture<'a> + where + Self: 'a, + = T::ReadFuture<'a>; + + fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { + T::read(self, address, buffer) + } +} + +/// Async write +pub trait Write { + /// Error type + type Error: Error; + /// The future associated with the `write` method. + type WriteFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Writes bytes to slave with address `address` + /// + /// # I2C Events (contract) + /// + /// ``` text + /// Master: ST SAD+W B0 B1 ... BN SP + /// Slave: SAK SAK SAK ... SAK + /// ``` + /// + /// Where + /// + /// - `ST` = start condition + /// - `SAD+W` = slave address followed by bit 0 to indicate writing + /// - `SAK` = slave acknowledge + /// - `Bi` = ith byte of data + /// - `SP` = stop condition + fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a>; +} + +impl> Write for &mut T { + type Error = T::Error; + + type WriteFuture<'a> + where + Self: 'a, + = T::WriteFuture<'a>; + + fn write<'a>(&'a mut self, address: A, bytes: &'a [u8]) -> Self::WriteFuture<'a> { + T::write(self, address, bytes) + } +} + +/// Async write + read +pub trait WriteRead { + /// Error type + type Error: Error; + /// The future associated with the `write_read` method. + type WriteReadFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Writes bytes to slave with address `address` and then reads enough bytes to fill `read` *in a + /// single transaction*. + /// + /// # I2C Events (contract) + /// + /// ``` text + /// Master: ST SAD+W O0 O1 ... OM SR SAD+R MAK MAK ... NMAK SP + /// Slave: SAK SAK SAK ... SAK SAK I0 I1 ... IN + /// ``` + /// + /// Where + /// + /// - `ST` = start condition + /// - `SAD+W` = slave address followed by bit 0 to indicate writing + /// - `SAK` = slave acknowledge + /// - `Oi` = ith outgoing byte of data + /// - `SR` = repeated start condition + /// - `SAD+R` = slave address followed by bit 1 to indicate reading + /// - `Ii` = ith incoming byte of data + /// - `MAK` = master acknowledge + /// - `NMAK` = master no acknowledge + /// - `SP` = stop condition + fn write_read<'a>( + &'a mut self, + address: A, + write: &'a [u8], + read: &'a mut [u8], + ) -> Self::WriteReadFuture<'a>; +} + +impl> WriteRead for &mut T { + type Error = T::Error; + + type WriteReadFuture<'a> + where + Self: 'a, + = T::WriteReadFuture<'a>; + + fn write_read<'a>( + &'a mut self, + address: A, + bytes: &'a [u8], + buffer: &'a mut [u8], + ) -> Self::WriteReadFuture<'a> { + T::write_read(self, address, bytes, buffer) + } +} diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs new file mode 100644 index 000000000..1026b5fc5 --- /dev/null +++ b/embedded-hal-async/src/lib.rs @@ -0,0 +1,13 @@ +#![feature(generic_associated_types)] +#![no_std] +#![deny(missing_docs)] + +//! Asynchronous APIs +//! +//! This traits use `core::future::Future` and generic associated types. + +pub mod delay; +pub mod digital; +pub mod i2c; +pub mod serial; +pub mod spi; diff --git a/embedded-hal-async/src/serial.rs b/embedded-hal-async/src/serial.rs new file mode 100644 index 000000000..95bc2cea3 --- /dev/null +++ b/embedded-hal-async/src/serial.rs @@ -0,0 +1,74 @@ +//! Serial interface + +use core::future::Future; +pub use embedded_hal::serial::{Error, ErrorKind}; + +/// Read half of a serial interface +/// +/// Some serial interfaces support different data sizes (8 bits, 9 bits, etc.); +/// This can be encoded in this trait via the `Word` type parameter. +pub trait Read { + /// Read error + type Error: Error; + + /// The future associated with the `read` method. + type ReadFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Reads words from the serial interface into the supplied slice. + fn read<'a>(&'a mut self, read: &'a mut [Word]) -> Self::ReadFuture<'a>; +} + +impl, Word: 'static> Read for &mut T { + type Error = T::Error; + type ReadFuture<'a> + where + Self: 'a, + = T::ReadFuture<'a>; + + fn read<'a>(&'a mut self, read: &'a mut [Word]) -> Self::ReadFuture<'a> { + T::read(self, read) + } +} +/// Write half of a serial interface +pub trait Write { + /// Write error + type Error: Error; + + /// The future associated with the `write` method. + type WriteFuture<'a>: Future> + 'a + where + Self: 'a; + + /// The future associated with the `flush` method. + type FlushFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Writes a single word to the serial interface + fn write<'a>(&'a mut self, words: &'a [Word]) -> Self::WriteFuture<'a>; + + /// Ensures that none of the previously written words are still buffered + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a>; +} + +impl, Word: 'static> Write for &mut T { + type Error = T::Error; + type WriteFuture<'a> + where + Self: 'a, + = T::WriteFuture<'a>; + type FlushFuture<'a> + where + Self: 'a, + = T::FlushFuture<'a>; + + fn write<'a>(&'a mut self, words: &'a [Word]) -> Self::WriteFuture<'a> { + T::write(self, words) + } + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + T::flush(self) + } +} diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs new file mode 100644 index 000000000..2a6d79697 --- /dev/null +++ b/embedded-hal-async/src/spi.rs @@ -0,0 +1,157 @@ +//! Serial Peripheral Interface + +use core::future::Future; + +pub use embedded_hal::spi::blocking::Operation; +pub use embedded_hal::spi::{ + Error, ErrorKind, ErrorType, Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, +}; + +/// Read-only SPI +pub trait Read: ErrorType { + /// Associated future for the `read` method. + type ReadFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Reads `words` from the slave. + /// + /// The word value sent on MOSI during reading is implementation-defined, + /// typically `0x00`, `0xFF`, or configurable. + fn read<'a>(&'a mut self, words: &'a mut [W]) -> Self::ReadFuture<'a>; + + /// Associated future for the `read_batch` method. + type ReadBatchFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Reads all slices in `words` from the slave as part of a single SPI transaction. + /// + /// The word value sent on MOSI during reading is implementation-defined, + /// typically `0x00`, `0xFF`, or configurable. + fn read_batch<'a>(&'a mut self, words: &'a mut [&'a mut [W]]) -> Self::ReadBatchFuture<'a>; +} + +impl, W: 'static> Read for &mut T { + type ReadFuture<'a> + where + Self: 'a, + = T::ReadFuture<'a>; + + fn read<'a>(&'a mut self, words: &'a mut [W]) -> Self::ReadFuture<'a> { + T::read(self, words) + } + + type ReadBatchFuture<'a> + where + Self: 'a, + = T::ReadBatchFuture<'a>; + + fn read_batch<'a>(&'a mut self, words: &'a mut [&'a mut [W]]) -> Self::ReadBatchFuture<'a> { + T::read_batch(self, words) + } +} + +/// Write-only SPI +pub trait Write: ErrorType { + /// Associated future for the `write` method. + type WriteFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Writes `words` to the slave, ignoring all the incoming words + fn write<'a>(&'a mut self, words: &'a [W]) -> Self::WriteFuture<'a>; + + /// Associated future for the `write_batch` method. + type WriteBatchFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Writes all slices in `words` to the slave as part of a single SPI transaction, ignoring all the incoming words + fn write_batch<'a>(&'a mut self, words: &'a [&'a [W]]) -> Self::WriteBatchFuture<'a>; +} + +impl, W: 'static> Write for &mut T { + type WriteFuture<'a> + where + Self: 'a, + = T::WriteFuture<'a>; + + fn write<'a>(&'a mut self, words: &'a [W]) -> Self::WriteFuture<'a> { + T::write(self, words) + } + + type WriteBatchFuture<'a> + where + Self: 'a, + = T::WriteBatchFuture<'a>; + + fn write_batch<'a>(&'a mut self, words: &'a [&'a [W]]) -> Self::WriteBatchFuture<'a> { + T::write_batch(self, words) + } +} + +/// Read-write SPI +pub trait ReadWrite: Read + Write { + /// Associated future for the `transfer` method. + type TransferFuture<'a>: Future> + 'a + where + Self: 'a; + + /// 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<'a>(&'a mut self, read: &'a mut [W], write: &'a [W]) -> Self::TransferFuture<'a>; + + /// Associated future for the `transfer_in_place` method. + type TransferInPlaceFuture<'a>: Future> + 'a + where + Self: 'a; + + /// 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<'a>(&'a mut self, words: &'a mut [W]) -> Self::TransferInPlaceFuture<'a>; + + /// Associated future for the `batch` method. + type BatchFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Execute multiple actions as part of a single SPI transaction + fn batch<'a>(&'a mut self, operations: &'a mut [Operation<'a, W>]) -> Self::BatchFuture<'a>; +} + +impl, W: 'static> ReadWrite for &mut T { + type TransferFuture<'a> + where + Self: 'a, + = T::TransferFuture<'a>; + + fn transfer<'a>(&'a mut self, read: &'a mut [W], write: &'a [W]) -> Self::TransferFuture<'a> { + T::transfer(self, read, write) + } + + type TransferInPlaceFuture<'a> + where + Self: 'a, + = T::TransferInPlaceFuture<'a>; + + fn transfer_in_place<'a>(&'a mut self, words: &'a mut [W]) -> Self::TransferInPlaceFuture<'a> { + T::transfer_in_place(self, words) + } + + type BatchFuture<'a> + where + Self: 'a, + = T::BatchFuture<'a>; + + fn batch<'a>(&'a mut self, operations: &'a mut [Operation<'a, W>]) -> Self::BatchFuture<'a> { + T::batch(self, operations) + } +} diff --git a/src/spi/blocking.rs b/src/spi/blocking.rs index cde2baae9..0fb169e41 100644 --- a/src/spi/blocking.rs +++ b/src/spi/blocking.rs @@ -1,89 +1,39 @@ //! Blocking SPI API -/// Blocking transfer with separate buffers -pub trait Transfer { - /// Error type - type Error: crate::spi::Error; +use super::ErrorType; - /// Writes and reads simultaneously. `write` is written to the slave on MOSI and - /// words received on MISO are stored in `read`. +/// Blocking read-only SPI +pub trait Read: ErrorType { + /// Reads `words` from the slave. /// - /// 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, + /// The word value sent on MOSI during reading is implementation-defined, /// typically `0x00`, `0xFF`, or configurable. - fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error>; -} - -impl, W> Transfer for &mut T { - type Error = T::Error; - - fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> { - T::transfer(self, read, write) - } -} - -/// Blocking transfer with single buffer (in-place) -pub trait TransferInplace { - /// Error type - type Error: crate::spi::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_inplace(&mut self, words: &mut [W]) -> Result<(), Self::Error>; -} - -impl, W> TransferInplace for &mut T { - type Error = T::Error; - - fn transfer_inplace(&mut self, words: &mut [W]) -> Result<(), Self::Error> { - T::transfer_inplace(self, words) - } -} - -/// Blocking read -pub trait Read { - /// Error type - type Error: crate::spi::Error; + fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error>; - /// Reads `words` from the slave. + /// Reads all slices in `words` from the slave as part of a single SPI transaction. /// /// The word value sent on MOSI during reading is implementation-defined, /// typically `0x00`, `0xFF`, or configurable. - fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error>; + fn read_batch(&mut self, words: &mut [&mut [W]]) -> Result<(), Self::Error>; } impl, W> Read for &mut T { - type Error = T::Error; - fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> { T::read(self, words) } -} -/// Blocking write -pub trait Write { - /// Error type - type Error: crate::spi::Error; + fn read_batch(&mut self, words: &mut [&mut [W]]) -> Result<(), Self::Error> { + T::read_batch(self, words) + } +} +/// Blocking write-only SPI +pub trait Write: ErrorType { /// Writes `words` to the slave, ignoring all the incoming words fn write(&mut self, words: &[W]) -> Result<(), Self::Error>; -} - -impl, W> Write for &mut T { - type Error = T::Error; - - fn write(&mut self, words: &[W]) -> Result<(), Self::Error> { - T::write(self, words) - } -} -/// Blocking write (iterator version) -pub trait WriteIter { - /// Error type - type Error: crate::spi::Error; + /// Writes all slices in `words` to the slave as part of a single SPI transaction, ignoring all the incoming words + fn write_batch(&mut self, words: &[&[W]]) -> Result<(), Self::Error>; /// Writes `words` to the slave, ignoring all the incoming words fn write_iter(&mut self, words: WI) -> Result<(), Self::Error> @@ -91,8 +41,14 @@ pub trait WriteIter { WI: IntoIterator; } -impl, W> WriteIter for &mut T { - type Error = T::Error; +impl, W> Write for &mut T { + fn write(&mut self, words: &[W]) -> Result<(), Self::Error> { + T::write(self, words) + } + + fn write_batch(&mut self, words: &[&[W]]) -> Result<(), Self::Error> { + T::write_batch(self, words) + } fn write_iter(&mut self, words: WI) -> Result<(), Self::Error> where @@ -102,7 +58,7 @@ impl, W> WriteIter for &mut T { } } -/// Operation for transactional SPI trait +/// Operation for ReadWrite::batch /// /// This allows composition of SPI operations into a single bus transaction #[derive(Debug, PartialEq)] @@ -114,23 +70,40 @@ pub enum Operation<'a, W: 'static = u8> { /// Write data out while reading data into the provided buffer Transfer(&'a mut [W], &'a [W]), /// Write data out while reading data into the provided buffer - TransferInplace(&'a mut [W]), + TransferInPlace(&'a mut [W]), } -/// Transactional trait allows multiple actions to be executed -/// as part of a single SPI transaction -pub trait Transactional { - /// Associated error type - type Error: crate::spi::Error; +/// Blocking read-write SPI +pub trait ReadWrite: Read + Write { + /// 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 [W], write: &[W]) -> 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 [W]) -> Result<(), Self::Error>; - /// Execute the provided transactions - fn exec<'a>(&mut self, operations: &mut [Operation<'a, W>]) -> Result<(), Self::Error>; + /// Execute multiple actions as part of a single SPI transaction + fn batch<'a>(&mut self, operations: &mut [Operation<'a, W>]) -> Result<(), Self::Error>; } -impl, W: 'static> Transactional for &mut T { - type Error = T::Error; +impl, W> ReadWrite for &mut T { + fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> { + T::transfer(self, read, write) + } + + fn transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Self::Error> { + T::transfer_in_place(self, words) + } - fn exec<'a>(&mut self, operations: &mut [Operation<'a, W>]) -> Result<(), Self::Error> { - T::exec(self, operations) + fn batch<'a>(&mut self, operations: &mut [Operation<'a, W>]) -> Result<(), Self::Error> { + T::batch(self, operations) } } diff --git a/src/spi/mod.rs b/src/spi/mod.rs index 048de49a4..a1924251c 100644 --- a/src/spi/mod.rs +++ b/src/spi/mod.rs @@ -113,3 +113,15 @@ impl core::fmt::Display for ErrorKind { } } } + +/// SPI error type trait +/// +/// This just defines the error type, to be used by the other SPI traits. +pub trait ErrorType { + /// Error type + type Error: Error; +} + +impl ErrorType for &mut T { + type Error = T::Error; +} diff --git a/src/spi/nb.rs b/src/spi/nb.rs index 6d540a762..2b9bbe5db 100644 --- a/src/spi/nb.rs +++ b/src/spi/nb.rs @@ -1,5 +1,7 @@ //! Serial Peripheral Interface +use super::ErrorType; + /// Full duplex (master mode) /// /// # Notes @@ -16,10 +18,7 @@ /// /// - Some SPIs can work with 8-bit *and* 16-bit words. You can overload this trait with different /// `Word` types to allow operation in both modes. -pub trait FullDuplex { - /// An enumeration of SPI errors - type Error: crate::spi::Error; - +pub trait FullDuplex: ErrorType { /// Reads the word stored in the shift register /// /// **NOTE** A word must be sent to the slave before attempting to call this @@ -31,8 +30,6 @@ pub trait FullDuplex { } impl, Word> FullDuplex for &mut T { - type Error = T::Error; - fn read(&mut self) -> nb::Result { T::read(self) }