Skip to content
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

Backport non-breaking changes from v1.0 to v0.2.x #301

Merged
merged 4 commits into from Aug 1, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Expand Up @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Added

- `Transactional` SPI interface for executing groups of SPI transactions.
- `Transactional` I2C interface for executing groups of I2C transactions.
- 10-bit addressing mode for I2C traits.
- `set_state` method for `OutputPin` using an input `PinState` value.
- `IoPin` trait for pins that can change between being inputs or outputs
dynamically.

### Changed

Expand Down
206 changes: 187 additions & 19 deletions src/blocking/i2c.rs
@@ -1,12 +1,122 @@
//! Blocking I2C API
//!
//! Slave addresses used by this API are 7-bit I2C addresses ranging from 0 to 127.
//! 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`.
//!
//! Operations on 10-bit slave addresses are not supported by the API yet (but applications might
//! be able to emulate some operations).
//! 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.
//!
//! ## Examples
//!
//! ### `embedded-hal` implementation for an MCU
//! Here is an example of an embedded-hal implementation of the `Write` trait
//! for both modes:
//! ```
//! # use embedded_hal::blocking::i2c::{SevenBitAddress, TenBitAddress, Write};
//! /// I2C0 hardware peripheral which supports both 7-bit and 10-bit addressing.
//! pub struct I2c0;
//!
//! impl Write<SevenBitAddress> for I2c0
//! {
//! # type Error = ();
//! #
//! fn write(&mut self, addr: u8, output: &[u8]) -> Result<(), Self::Error> {
//! // ...
//! # Ok(())
//! }
//! }
//!
//! impl Write<TenBitAddress> for I2c0
//! {
//! # type Error = ();
//! #
//! fn write(&mut self, addr: u16, output: &[u8]) -> Result<(), Self::Error> {
//! // ...
//! # Ok(())
//! }
//! }
//! ```
//!
//! ### Device driver compatible only with 7-bit addresses
//!
//! For demonstration purposes the address mode parameter has been omitted in this example.
//!
//! ```
//! # use embedded_hal::blocking::i2c::WriteRead;
//! const ADDR: u8 = 0x15;
//! # const TEMP_REGISTER: u8 = 0x1;
//! pub struct TemperatureSensorDriver<I2C> {
//! i2c: I2C,
//! }
//!
//! impl<I2C, E> TemperatureSensorDriver<I2C>
//! where
//! I2C: WriteRead<Error = E>,
//! {
//! pub fn read_temperature(&mut self) -> Result<u8, E> {
//! let mut temp = [0];
//! self.i2c
//! .write_read(ADDR, &[TEMP_REGISTER], &mut temp)
//! .and(Ok(temp[0]))
//! }
//! }
//! ```
//!
//! ### Device driver compatible only with 10-bit addresses
//!
//! ```
//! # use embedded_hal::blocking::i2c::{TenBitAddress, WriteRead};
//! const ADDR: u16 = 0x158;
//! # const TEMP_REGISTER: u8 = 0x1;
//! pub struct TemperatureSensorDriver<I2C> {
//! i2c: I2C,
//! }
//!
//! impl<I2C, E> TemperatureSensorDriver<I2C>
//! where
//! I2C: WriteRead<TenBitAddress, Error = E>,
//! {
//! pub fn read_temperature(&mut self) -> Result<u8, E> {
//! let mut temp = [0];
//! self.i2c
//! .write_read(ADDR, &[TEMP_REGISTER], &mut temp)
//! .and(Ok(temp[0]))
//! }
//! }
//! ```

use crate::private;

impl private::Sealed for SevenBitAddress {}
impl private::Sealed for TenBitAddress {}

/// Address mode (7-bit / 10-bit)
///
/// Note: This trait is sealed and should not be implemented outside of this crate.
pub trait AddressMode: private::Sealed {}

/// 7-bit address mode type
pub type SevenBitAddress = u8;

/// 10-bit address mode type
pub type TenBitAddress = u16;

impl AddressMode for SevenBitAddress {}

impl AddressMode for TenBitAddress {}

/// Blocking read
pub trait Read {
pub trait Read<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;

Expand All @@ -28,15 +138,15 @@ pub trait Read {
/// - `MAK` = master acknowledge
/// - `NMAK` = master no acknowledge
/// - `SP` = stop condition
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error>;
fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error>;
}

/// Blocking write
pub trait Write {
pub trait Write<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;

/// Sends bytes to slave with address `addr`
/// Writes bytes to slave with address `address`
///
/// # I2C Events (contract)
///
Expand All @@ -52,31 +162,30 @@ pub trait Write {
/// - `SAK` = slave acknowledge
/// - `Bi` = ith byte of data
/// - `SP` = stop condition
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error>;
fn write(&mut self, address: A, bytes: &[u8]) -> Result<(), Self::Error>;
}

/// Blocking write (iterator version)
#[cfg(feature = "unproven")]
pub trait WriteIter {
pub trait WriteIter<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;

/// Sends bytes to slave with address `addr`
/// Writes bytes to slave with address `address`
///
/// # I2C Events (contract)
///
/// Same as `Write`
fn write<B>(&mut self, addr: u8, bytes: B) -> Result<(), Self::Error>
fn write<B>(&mut self, address: A, bytes: B) -> Result<(), Self::Error>
where
B: IntoIterator<Item = u8>;
}

/// Blocking write + read
pub trait WriteRead {
pub trait WriteRead<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;

/// Sends bytes to slave with address `addr` and then reads enough bytes to fill `buffer` *in a
/// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a
/// single transaction*
///
/// # I2C Events (contract)
Expand All @@ -100,30 +209,89 @@ pub trait WriteRead {
/// - `SP` = stop condition
fn write_read(
&mut self,
address: u8,
address: A,
bytes: &[u8],
buffer: &mut [u8],
) -> Result<(), Self::Error>;
}

/// Blocking write (iterator version) + read
#[cfg(feature = "unproven")]
pub trait WriteIterRead {
pub trait WriteIterRead<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;

/// Sends bytes to slave with address `addr` and then reads enough bytes to fill `buffer` *in a
/// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a
/// single transaction*
///
/// # I2C Events (contract)
///
/// Same as the `WriteRead` trait
fn write_iter_read<B>(
&mut self,
address: u8,
address: A,
bytes: B,
buffer: &mut [u8],
) -> Result<(), Self::Error>
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<A: AddressMode = SevenBitAddress> {
/// 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 exec<'a>(&mut self, address: A, operations: &mut [Operation<'a>])
-> Result<(), Self::Error>;
}

/// Transactional I2C interface (iterator version).
///
/// This allows combining operation within an I2C transaction.
pub trait TransactionalIter<A: AddressMode = SevenBitAddress> {
/// 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 exec_iter<'a, O>(&mut self, address: A, operations: O) -> Result<(), Self::Error>
where
O: IntoIterator<Item = Operation<'a>>;
}
21 changes: 21 additions & 0 deletions src/blocking/spi.rs
Expand Up @@ -104,3 +104,24 @@ pub mod write_iter {
}
}
}

/// Operation for transactional SPI trait
///
/// This allows composition of SPI operations into a single bus transaction
#[derive(Debug, PartialEq)]
pub enum Operation<'a, W: 'static> {
/// Write data from the provided buffer, discarding read data
Write(&'a [W]),
/// Write data out while reading data into the provided buffer
Transfer(&'a mut [W]),
}

/// Transactional trait allows multiple actions to be executed
/// as part of a single SPI transaction
pub trait Transactional<W: 'static> {
/// Associated error type
type Error;

/// Execute the provided transactions
fn exec<'a>(&mut self, operations: &mut [Operation<'a, W>]) -> Result<(), Self::Error>;
}