|
1 | 1 | //! Blocking I2C API
|
2 | 2 | //!
|
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`]. |
6 | 6 | //!
|
7 | 7 | //! Through this marker types it is possible to implement each address mode for
|
8 | 8 | //! the traits independently in `embedded-hal` implementations and device drivers
|
|
14 | 14 | //! is not supported by the hardware.
|
15 | 15 | //!
|
16 | 16 | //! 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. |
18 | 18 | //!
|
19 |
| -//! ## Examples |
| 19 | +//! # Bus sharing |
20 | 20 | //!
|
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. |
28 | 24 | //!
|
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")] |
37 | 26 | //!
|
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`). |
46 | 29 | //!
|
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 |
48 | 57 | //!
|
49 | 58 | //! For demonstration purposes the address mode parameter has been omitted in this example.
|
50 | 59 | //!
|
51 | 60 | //! ```
|
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; |
54 | 64 | //! # const TEMP_REGISTER: u8 = 0x1;
|
55 | 65 | //! pub struct TemperatureSensorDriver<I2C> {
|
56 | 66 | //! i2c: I2C,
|
57 | 67 | //! }
|
58 | 68 | //!
|
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> { |
64 | 75 | //! 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]) |
68 | 78 | //! }
|
69 | 79 | //! }
|
70 | 80 | //! ```
|
71 | 81 | //!
|
72 |
| -//! ### Device driver compatible only with 10-bit addresses |
| 82 | +//! ## Device driver compatible only with 10-bit addresses |
73 | 83 | //!
|
74 | 84 | //! ```
|
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; |
77 | 88 | //! # const TEMP_REGISTER: u8 = 0x1;
|
78 | 89 | //! pub struct TemperatureSensorDriver<I2C> {
|
79 | 90 | //! i2c: I2C,
|
80 | 91 | //! }
|
81 | 92 | //!
|
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> { |
87 | 99 | //! 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(()) |
91 | 151 | //! }
|
92 | 152 | //! }
|
93 | 153 | //! ```
|
|
0 commit comments