diff --git a/CHANGELOG.md b/CHANGELOG.md index bbde76f71..02d231069 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added -- `Transactional` SPI interface for executing groups of SPI transactions +- `Transactional` SPI interface for executing groups of SPI transactions. +- `Transactional` I2C interface for executing groups of I2C transactions. ### Changed @@ -20,7 +21,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - 10-bit addressing mode for I2C traits. - `try_set_state` method for `OutputPin` using an input `PinState` value. -- `Transactional` interface for grouping SPI transactions ### Changed diff --git a/src/blocking/i2c.rs b/src/blocking/i2c.rs index dedc7facc..e0aa7ca62 100644 --- a/src/blocking/i2c.rs +++ b/src/blocking/i2c.rs @@ -232,3 +232,117 @@ pub trait WriteIterRead { where B: IntoIterator; } + +/// Transactional I2C operation. +/// +/// Several operations can be combined as part of a transaction. +#[derive(Debug, PartialEq)] +pub enum Operation<'a> { + /// Read data into the provided buffer + Read(&'a mut [u8]), + /// Write data from the provided buffer + Write(&'a [u8]), +} + +/// Transactional I2C interface. +/// +/// This allows combining operations within an I2C transaction. +pub trait Transactional { + /// Error type + type Error; + + /// Execute the provided operations on the I2C bus. + /// + /// Transaction contract: + /// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate. + /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. + /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. + /// - After executing the last operation an SP is sent automatically. + /// - If the last operation is a `Read` the master does not send an acknowledge for the last byte. + /// + /// - `ST` = start condition + /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing + /// - `SR` = repeated start condition + /// - `SP` = stop condition + fn try_exec<'a>( + &mut self, + address: u8, + operations: &mut [Operation<'a>], + ) -> Result<(), Self::Error>; +} + +/// Transactional I2C interface (iterator version). +/// +/// This allows combining operation within an I2C transaction. +pub trait TransactionalIter { + /// Error type + type Error; + + /// Execute the provided operations on the I2C bus (iterator version). + /// + /// Transaction contract: + /// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate. + /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. + /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. + /// - After executing the last operation an SP is sent automatically. + /// - If the last operation is a `Read` the master does not send an acknowledge for the last byte. + /// + /// - `ST` = start condition + /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing + /// - `SR` = repeated start condition + /// - `SP` = stop condition + fn try_exec_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error> + where + O: IntoIterator>; +} + +/// Default implementation of `blocking::i2c::Write`, `blocking::i2c::Read` and +/// `blocking::i2c::WriteRead` traits for `blocking::i2c::Transactional` implementers. +pub mod transactional { + use super::{Operation, Read, Transactional, Write, WriteRead}; + + /// Default implementation of `blocking::i2c::Write`, `blocking::i2c::Read` and + /// `blocking::i2c::WriteRead` traits for `blocking::i2c::Transactional` implementers. + pub trait Default {} + + impl Write for S + where + S: self::Default + Transactional, + { + type Error = E; + + fn try_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { + self.try_exec(address, &mut [Operation::Write(bytes)]) + } + } + + impl Read for S + where + S: self::Default + Transactional, + { + type Error = E; + + fn try_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.try_exec(address, &mut [Operation::Read(buffer)]) + } + } + + impl WriteRead for S + where + S: self::Default + Transactional, + { + type Error = E; + + fn try_write_read( + &mut self, + address: u8, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Self::Error> { + self.try_exec( + address, + &mut [Operation::Write(bytes), Operation::Read(buffer)], + ) + } + } +} diff --git a/src/prelude.rs b/src/prelude.rs index 315acd9c7..9ec6fb52d 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -8,8 +8,9 @@ pub use crate::adc::OneShot as _embedded_hal_adc_OneShot; pub use crate::blocking::delay::DelayMs as _embedded_hal_blocking_delay_DelayMs; pub use crate::blocking::delay::DelayUs as _embedded_hal_blocking_delay_DelayUs; pub use crate::blocking::i2c::{ - Read as _embedded_hal_blocking_i2c_Read, Write as _embedded_hal_blocking_i2c_Write, - WriteIter as _embedded_hal_blocking_i2c_WriteIter, + Read as _embedded_hal_blocking_i2c_Read, + Transactional as _embedded_hal_blocking_i2c_Transactional, + Write as _embedded_hal_blocking_i2c_Write, WriteIter as _embedded_hal_blocking_i2c_WriteIter, WriteIterRead as _embedded_hal_blocking_i2c_WriteIterRead, WriteRead as _embedded_hal_blocking_i2c_WriteRead, };