Skip to content

Commit 6bf6b02

Browse files
committed
I2c: expand docs, document shared bus usage.
1 parent dd74149 commit 6bf6b02

File tree

2 files changed

+114
-50
lines changed

2 files changed

+114
-50
lines changed

embedded-hal/src/i2c-shared-bus.svg

Lines changed: 4 additions & 0 deletions
Loading

embedded-hal/src/i2c.rs

Lines changed: 110 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! Blocking I2C API
22
//!
3-
//! This API supports 7-bit and 10-bit addresses. Traits feature an `AddressMode`
4-
//! marker type parameter. Two implementation of the `AddressMode` exist:
5-
//! `SevenBitAddress` and `TenBitAddress`.
3+
//! This API supports 7-bit and 10-bit addresses. Traits feature an [`AddressMode`]
4+
//! marker type parameter. Two implementation of the [`AddressMode`] exist:
5+
//! [`SevenBitAddress`] and [`TenBitAddress`].
66
//!
77
//! Through this marker types it is possible to implement each address mode for
88
//! the traits independently in `embedded-hal` implementations and device drivers
@@ -14,80 +14,140 @@
1414
//! is not supported by the hardware.
1515
//!
1616
//! Since 7-bit addressing is the mode of the majority of I2C devices,
17-
//! `SevenBitAddress` has been set as default mode and thus can be omitted if desired.
17+
//! [`SevenBitAddress`] has been set as default mode and thus can be omitted if desired.
1818
//!
19-
//! ## Examples
19+
//! # Bus sharing
2020
//!
21-
//! ### `embedded-hal` implementation for an MCU
22-
//! Here is an example of an embedded-hal implementation of the `Write` trait
23-
//! for both modes:
24-
//! ```
25-
//! # use embedded_hal::i2c::{ErrorKind, ErrorType, SevenBitAddress, TenBitAddress, I2c, Operation};
26-
//! /// I2C0 hardware peripheral which supports both 7-bit and 10-bit addressing.
27-
//! pub struct I2c0;
21+
//! I2C allows sharing a single bus between many I2C devices. The SDA and SCL lines are
22+
//! wired in parallel to all devices. When starting a transfer an "address" is sent
23+
//! so that the addressed device can respond and all the others can ignore the transfer.
2824
//!
29-
//! # impl ErrorType for I2c0 { type Error = ErrorKind; }
30-
//! impl I2c<SevenBitAddress> for I2c0
31-
//! {
32-
//! fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> {
33-
//! // ...
34-
//! # Ok(())
35-
//! }
36-
//! }
25+
#![doc = include_str!("i2c-shared-bus.svg")]
3726
//!
38-
//! impl I2c<TenBitAddress> for I2c0
39-
//! {
40-
//! fn transaction(&mut self, address: u16, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> {
41-
//! // ...
42-
//! # Ok(())
43-
//! }
44-
//! }
45-
//! ```
27+
//! This bus sharing is common when having multiple I2C devices in the same board, since it uses fewer MCU
28+
//! pins (`2` instead of `2*n`), and fewer MCU I2C peripherals (`1` instead of `n`).
4629
//!
47-
//! ### Device driver compatible only with 7-bit addresses
30+
//! This API supports bus sharing natively. Types implementing [`I2c`] are allowed
31+
//! to represent either exclusive or shared access to an I2C bus. HALs typically
32+
//! provide exclusive access implementations. Drivers shouldn't care which
33+
//! kind they receive, they just do transactions on it and let the
34+
//! underlying implementation share or not.
35+
//!
36+
//! The [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus) crate provides several
37+
//! implementations for sharing I2C buses. You can use them to take an exclusive instance
38+
//! you've received from a HAL and "split" it into mulitple shared ones, to instantiate
39+
//! several drivers on the same bus.
40+
//!
41+
//! # For driver authors
42+
//!
43+
//! Drivers can be generic over `I2c<SevenBitAddress>` or `I2c<TenBitAddress>` depending
44+
//! on the kind of address supported by the target device. If it can use either, the driver can
45+
//! be generic over the address kind as well, though this is rare.
46+
//!
47+
//! Drivers should take the `I2c` instance as an argument to `new()`, and store it in their
48+
//! struct. They **should not** take `&mut I2c`, the trait has a blanket impl for all `&mut T`
49+
//! so taking just `I2c` ensures the user can still pass a `&mut`, but is not forced to.
50+
//!
51+
//! Drivers **should not** try to enable bus sharing by taking `&mut I2c` at every method.
52+
//! This is much less ergonomic than owning the `I2c`, and allowing the user to pass an
53+
//! implementation that does sharing behind the scenes
54+
//! (from [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus), or others).
55+
//!
56+
//! ## Device driver compatible only with 7-bit addresses
4857
//!
4958
//! For demonstration purposes the address mode parameter has been omitted in this example.
5059
//!
5160
//! ```
52-
//! # use embedded_hal::i2c::{I2c, Error};
53-
//! const ADDR: u8 = 0x15;
61+
//! use embedded_hal::i2c::{I2c, Error};
62+
//!
63+
//! const ADDR: u8 = 0x15;
5464
//! # const TEMP_REGISTER: u8 = 0x1;
5565
//! pub struct TemperatureSensorDriver<I2C> {
5666
//! i2c: I2C,
5767
//! }
5868
//!
59-
//! impl<I2C, E: Error> TemperatureSensorDriver<I2C>
60-
//! where
61-
//! I2C: I2c<Error = E>,
62-
//! {
63-
//! pub fn read_temperature(&mut self) -> Result<u8, E> {
69+
//! impl<I2C: I2c> TemperatureSensorDriver<I2C> {
70+
//! pub fn new(i2c: I2C) -> Self {
71+
//! Self { i2c }
72+
//! }
73+
//!
74+
//! pub fn read_temperature(&mut self) -> Result<u8, I2C::Error> {
6475
//! let mut temp = [0];
65-
//! self.i2c
66-
//! .write_read(ADDR, &[TEMP_REGISTER], &mut temp)
67-
//! .and(Ok(temp[0]))
76+
//! self.i2c.write_read(ADDR, &[TEMP_REGISTER], &mut temp)?;
77+
//! Ok(temp[0])
6878
//! }
6979
//! }
7080
//! ```
7181
//!
72-
//! ### Device driver compatible only with 10-bit addresses
82+
//! ## Device driver compatible only with 10-bit addresses
7383
//!
7484
//! ```
75-
//! # use embedded_hal::i2c::{Error, TenBitAddress, I2c};
76-
//! const ADDR: u16 = 0x158;
85+
//! use embedded_hal::i2c::{Error, TenBitAddress, I2c};
86+
//!
87+
//! const ADDR: u16 = 0x158;
7788
//! # const TEMP_REGISTER: u8 = 0x1;
7889
//! pub struct TemperatureSensorDriver<I2C> {
7990
//! i2c: I2C,
8091
//! }
8192
//!
82-
//! impl<I2C, E: Error> TemperatureSensorDriver<I2C>
83-
//! where
84-
//! I2C: I2c<TenBitAddress, Error = E>,
85-
//! {
86-
//! pub fn read_temperature(&mut self) -> Result<u8, E> {
93+
//! impl<I2C: I2c<TenBitAddress>> TemperatureSensorDriver<I2C> {
94+
//! pub fn new(i2c: I2C) -> Self {
95+
//! Self { i2c }
96+
//! }
97+
//!
98+
//! pub fn read_temperature(&mut self) -> Result<u8, I2C::Error> {
8799
//! let mut temp = [0];
88-
//! self.i2c
89-
//! .write_read(ADDR, &[TEMP_REGISTER], &mut temp)
90-
//! .and(Ok(temp[0]))
100+
//! self.i2c.write_read(ADDR, &[TEMP_REGISTER], &mut temp)?;
101+
//! Ok(temp[0])
102+
//! }
103+
//! }
104+
//! ```
105+
//!
106+
//! # For HAL authors
107+
//!
108+
//! HALs **should not** include bus sharing mechanisms. They should expose a single type representing
109+
//! exclusive ownership over the bus, and let the user use [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus)
110+
//! if they want to share it. (One exception is if the underlying platform already
111+
//! supports sharing, such as Linux or some RTOSs.)
112+
//!
113+
//! Here is an example of an embedded-hal implementation of the `I2C` trait
114+
//! for both modes. All trait methods have have default implementations in terms of `transaction`,
115+
//! so that's the only one that requires implementing.
116+
//!
117+
//! ```
118+
//! use embedded_hal::i2c::{self, SevenBitAddress, TenBitAddress, I2c, Operation};
119+
//!
120+
//! /// I2C0 hardware peripheral which supports both 7-bit and 10-bit addressing.
121+
//! pub struct I2c0;
122+
//!
123+
//! #[derive(Debug, Copy, Clone, Eq, PartialEq)]
124+
//! pub enum Error {
125+
//! // ...
126+
//! }
127+
//!
128+
//! impl i2c::Error for Error {
129+
//! fn kind(&self) -> i2c::ErrorKind {
130+
//! match *self {
131+
//! // ...
132+
//! }
133+
//! }
134+
//! }
135+
//!
136+
//! impl i2c::ErrorType for I2c0 {
137+
//! type Error = Error;
138+
//! }
139+
//!
140+
//! impl I2c<SevenBitAddress> for I2c0 {
141+
//! fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> {
142+
//! // ...
143+
//! # Ok(())
144+
//! }
145+
//! }
146+
//!
147+
//! impl I2c<TenBitAddress> for I2c0 {
148+
//! fn transaction(&mut self, address: u16, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> {
149+
//! // ...
150+
//! # Ok(())
91151
//! }
92152
//! }
93153
//! ```

0 commit comments

Comments
 (0)