Skip to content

Add transactional I2C interface #223

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
114 changes: 114 additions & 0 deletions src/blocking/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,117 @@ pub trait WriteIterRead<A: AddressMode = SevenBitAddress> {
where
B: IntoIterator<Item = u8>;
}

/// 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<Item = Operation<'a>>;
}

/// 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<E> {}

impl<E, S> Write for S
where
S: self::Default<E> + Transactional<Error = E>,
{
type Error = E;

fn try_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
self.try_exec(address, &mut [Operation::Write(bytes)])
}
}

impl<E, S> Read for S
where
S: self::Default<E> + Transactional<Error = E>,
{
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<E, S> WriteRead for S
where
S: self::Default<E> + Transactional<Error = E>,
{
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)],
)
}
}
}
5 changes: 3 additions & 2 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down