From 7b88d29ad5c9db7e05fb834aaf0f85b3f2d555dd Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Sun, 5 Jul 2020 17:24:32 +0200 Subject: [PATCH] Support 7-bit and 10-bit I2C address modes --- CHANGELOG.md | 7 +++ src/blocking/i2c.rs | 140 ++++++++++++++++++++++++++++++++++++++++---- src/lib.rs | 8 +++ 3 files changed, 142 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aab46c15a..2af891676 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added +- 10-bit addressing mode for I2C traits. + ### Changed +- I2C addressing modes are now selected via an `AddressMode` type parameter. + The trait features implementations for marker types `SevenBitAddress` and + `TenBitAddress`. `SevenBitAddress` is the default mode so this is not a + breaking change. - The method `try_write` from the trait `blocking::i2c::WriteIter` trait has been renamed `try_write_iter` for consistency. diff --git a/src/blocking/i2c.rs b/src/blocking/i2c.rs index b6a78345f..fdfb45a3c 100644 --- a/src/blocking/i2c.rs +++ b/src/blocking/i2c.rs @@ -1,12 +1,126 @@ //! 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 for I2c0 +//! { +//! # type Error = (); +//! # +//! fn try_write(&mut self, addr: u8, output: &[u8]) -> Result<(), Self::Error> { +//! // ... +//! # Ok(()) +//! } +//! } +//! +//! impl Write for I2c0 +//! { +//! # type Error = (); +//! # +//! fn try_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, +//! } +//! +//! impl TemperatureSensorDriver +//! where +//! I2C: WriteRead, +//! { +//! pub fn read_temperature(&mut self) -> Result { +//! let mut temp = [0]; +//! self.i2c +//! .try_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, +//! } +//! +//! impl TemperatureSensorDriver +//! where +//! I2C: WriteRead, +//! { +//! pub fn read_temperature(&mut self) -> Result { +//! let mut temp = [0]; +//! self.i2c +//! .try_write_read(ADDR, &[TEMP_REGISTER], &mut temp) +//! .and(Ok(temp[0])) +//! } +//! } +//! ``` + +use crate::private; + +/// 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 { + /// Address value type + type Address; +} + +/// 7-bit address mode type +pub struct SevenBitAddress; + +/// 10-bit address mode type +pub struct TenBitAddress; + +impl AddressMode for SevenBitAddress { + type Address = u8; +} + +impl AddressMode for TenBitAddress { + type Address = u16; +} /// Blocking read -pub trait Read { +pub trait Read { /// Error type type Error; @@ -28,11 +142,11 @@ pub trait Read { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition - fn try_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error>; + fn try_read(&mut self, address: A::Address, buffer: &mut [u8]) -> Result<(), Self::Error>; } /// Blocking write -pub trait Write { +pub trait Write { /// Error type type Error; @@ -52,11 +166,11 @@ pub trait Write { /// - `SAK` = slave acknowledge /// - `Bi` = ith byte of data /// - `SP` = stop condition - fn try_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error>; + fn try_write(&mut self, address: A::Address, bytes: &[u8]) -> Result<(), Self::Error>; } /// Blocking write (iterator version) -pub trait WriteIter { +pub trait WriteIter { /// Error type type Error; @@ -65,13 +179,13 @@ pub trait WriteIter { /// # I2C Events (contract) /// /// Same as `Write` - fn try_write_iter(&mut self, address: u8, bytes: B) -> Result<(), Self::Error> + fn try_write_iter(&mut self, address: A::Address, bytes: B) -> Result<(), Self::Error> where B: IntoIterator; } /// Blocking write + read -pub trait WriteRead { +pub trait WriteRead { /// Error type type Error; @@ -99,14 +213,14 @@ pub trait WriteRead { /// - `SP` = stop condition fn try_write_read( &mut self, - address: u8, + address: A::Address, bytes: &[u8], buffer: &mut [u8], ) -> Result<(), Self::Error>; } /// Blocking write (iterator version) + read -pub trait WriteIterRead { +pub trait WriteIterRead { /// Error type type Error; @@ -118,7 +232,7 @@ pub trait WriteIterRead { /// Same as the `WriteRead` trait fn try_write_iter_read( &mut self, - address: u8, + address: A::Address, bytes: B, buffer: &mut [u8], ) -> Result<(), Self::Error> diff --git a/src/lib.rs b/src/lib.rs index dd2592660..552d7388f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -700,3 +700,11 @@ pub mod serial; pub mod spi; pub mod timer; pub mod watchdog; + +mod private { + use crate::blocking::i2c::{SevenBitAddress, TenBitAddress}; + pub trait Sealed {} + + impl Sealed for SevenBitAddress {} + impl Sealed for TenBitAddress {} +}