1 | //! Blocking I2C API. |
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`]. |
6 | //! |
7 | //! Through this marker types it is possible to implement each address mode for |
8 | //! the traits independently in `embedded-hal` implementations and device drivers |
9 | //! can depend only on the mode that they support. |
10 | //! |
11 | //! Additionally, the I2C 10-bit address mode has been developed to be fully |
12 | //! backwards compatible with the 7-bit address mode. This allows for a |
13 | //! software-emulated 10-bit addressing implementation if the address mode |
14 | //! is not supported by the hardware. |
15 | //! |
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. |
18 | //! |
19 | //! # Bus sharing |
20 | //! |
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. |
24 | //! |
25 | #![doc = include_str!("i2c-shared-bus.svg" )] |
26 | //! |
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`). |
29 | //! |
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 multiple shared ones, to instantiate |
39 | //! several drivers on the same bus. |
40 | //! |
41 | //! # Flushing |
42 | //! |
43 | //! Implementations must flush the transfer, ensuring the bus has returned to an idle state before returning. |
44 | //! No pipelining is allowed. Users must be able to shut down the I2C peripheral immediately after a transfer |
45 | //! returns, without any risk of e.g. cutting short a stop condition. |
46 | //! |
47 | //! (Implementations must wait until the last ACK bit to report it as an error anyway. Therefore pipelining would only |
48 | //! yield very small time savings, not worth the complexity) |
49 | //! |
50 | //! # For driver authors |
51 | //! |
52 | //! Drivers can select the adequate address length with `I2c<SevenBitAddress>` or `I2c<TenBitAddress>` depending |
53 | //! on the target device. If it can use either, the driver can |
54 | //! be generic over the address kind as well, though this is rare. |
55 | //! |
56 | //! Drivers should take the `I2c` instance as an argument to `new()`, and store it in their |
57 | //! struct. They **should not** take `&mut I2c`, the trait has a blanket impl for all `&mut T` |
58 | //! so taking just `I2c` ensures the user can still pass a `&mut`, but is not forced to. |
59 | //! |
60 | //! Drivers **should not** try to enable bus sharing by taking `&mut I2c` at every method. |
61 | //! This is much less ergonomic than owning the `I2c`, which still allows the user to pass an |
62 | //! implementation that does sharing behind the scenes |
63 | //! (from [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus), or others). |
64 | //! |
65 | //! ## Device driver compatible only with 7-bit addresses |
66 | //! |
67 | //! For demonstration purposes the address mode parameter has been omitted in this example. |
68 | //! |
69 | //! ``` |
70 | //! use embedded_hal::i2c::{I2c, Error}; |
71 | //! |
72 | //! const ADDR: u8 = 0x15; |
73 | //! # const TEMP_REGISTER: u8 = 0x1; |
74 | //! pub struct TemperatureSensorDriver<I2C> { |
75 | //! i2c: I2C, |
76 | //! } |
77 | //! |
78 | //! impl<I2C: I2c> TemperatureSensorDriver<I2C> { |
79 | //! pub fn new(i2c: I2C) -> Self { |
80 | //! Self { i2c } |
81 | //! } |
82 | //! |
83 | //! pub fn read_temperature(&mut self) -> Result<u8, I2C::Error> { |
84 | //! let mut temp = [0]; |
85 | //! self.i2c.write_read(ADDR, &[TEMP_REGISTER], &mut temp)?; |
86 | //! Ok(temp[0]) |
87 | //! } |
88 | //! } |
89 | //! ``` |
90 | //! |
91 | //! ## Device driver compatible only with 10-bit addresses |
92 | //! |
93 | //! ``` |
94 | //! use embedded_hal::i2c::{Error, TenBitAddress, I2c}; |
95 | //! |
96 | //! const ADDR: u16 = 0x158; |
97 | //! # const TEMP_REGISTER: u8 = 0x1; |
98 | //! pub struct TemperatureSensorDriver<I2C> { |
99 | //! i2c: I2C, |
100 | //! } |
101 | //! |
102 | //! impl<I2C: I2c<TenBitAddress>> TemperatureSensorDriver<I2C> { |
103 | //! pub fn new(i2c: I2C) -> Self { |
104 | //! Self { i2c } |
105 | //! } |
106 | //! |
107 | //! pub fn read_temperature(&mut self) -> Result<u8, I2C::Error> { |
108 | //! let mut temp = [0]; |
109 | //! self.i2c.write_read(ADDR, &[TEMP_REGISTER], &mut temp)?; |
110 | //! Ok(temp[0]) |
111 | //! } |
112 | //! } |
113 | //! ``` |
114 | //! |
115 | //! # For HAL authors |
116 | //! |
117 | //! HALs **should not** include bus sharing mechanisms. They should expose a single type representing |
118 | //! exclusive ownership over the bus, and let the user use [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus) |
119 | //! if they want to share it. (One exception is if the underlying platform already |
120 | //! supports sharing, such as Linux or some RTOSs.) |
121 | //! |
122 | //! Here is an example of an embedded-hal implementation of the `I2C` trait |
123 | //! for both addressing modes. All trait methods have have default implementations in terms of `transaction`. |
124 | //! As such, that is the only method that requires implementation in the HAL. |
125 | //! |
126 | //! ``` |
127 | //! use embedded_hal::i2c::{self, SevenBitAddress, TenBitAddress, I2c, Operation}; |
128 | //! |
129 | //! /// I2C0 hardware peripheral which supports both 7-bit and 10-bit addressing. |
130 | //! pub struct I2c0; |
131 | //! |
132 | //! #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
133 | //! pub enum Error { |
134 | //! // ... |
135 | //! } |
136 | //! |
137 | //! impl i2c::Error for Error { |
138 | //! fn kind(&self) -> i2c::ErrorKind { |
139 | //! match *self { |
140 | //! // ... |
141 | //! } |
142 | //! } |
143 | //! } |
144 | //! |
145 | //! impl i2c::ErrorType for I2c0 { |
146 | //! type Error = Error; |
147 | //! } |
148 | //! |
149 | //! impl I2c<SevenBitAddress> for I2c0 { |
150 | //! fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { |
151 | //! // ... |
152 | //! # Ok(()) |
153 | //! } |
154 | //! } |
155 | //! |
156 | //! impl I2c<TenBitAddress> for I2c0 { |
157 | //! fn transaction(&mut self, address: u16, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { |
158 | //! // ... |
159 | //! # Ok(()) |
160 | //! } |
161 | //! } |
162 | //! ``` |
163 | |
164 | use crate::private; |
165 | |
166 | #[cfg (feature = "defmt-03" )] |
167 | use crate::defmt; |
168 | |
169 | /// I2C error. |
170 | pub trait Error: core::fmt::Debug { |
171 | /// Convert error to a generic I2C error kind. |
172 | /// |
173 | /// By using this method, I2C errors freely defined by HAL implementations |
174 | /// can be converted to a set of generic I2C errors upon which generic |
175 | /// code can act. |
176 | fn kind(&self) -> ErrorKind; |
177 | } |
178 | |
179 | impl Error for core::convert::Infallible { |
180 | #[inline ] |
181 | fn kind(&self) -> ErrorKind { |
182 | match *self {} |
183 | } |
184 | } |
185 | |
186 | /// I2C error kind. |
187 | /// |
188 | /// This represents a common set of I2C operation errors. HAL implementations are |
189 | /// free to define more specific or additional error types. However, by providing |
190 | /// a mapping to these common I2C errors, generic code can still react to them. |
191 | #[derive (Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
192 | #[cfg_attr (feature = "defmt-03" , derive(defmt::Format))] |
193 | #[non_exhaustive ] |
194 | pub enum ErrorKind { |
195 | /// Bus error occurred. e.g. A START or a STOP condition is detected and is not |
196 | /// located after a multiple of 9 SCL clock pulses. |
197 | Bus, |
198 | /// The arbitration was lost, e.g. electrical problems with the clock signal. |
199 | ArbitrationLoss, |
200 | /// A bus operation was not acknowledged, e.g. due to the addressed device not |
201 | /// being available on the bus or the device not being ready to process requests |
202 | /// at the moment. |
203 | NoAcknowledge(NoAcknowledgeSource), |
204 | /// The peripheral receive buffer was overrun. |
205 | Overrun, |
206 | /// A different error occurred. The original error may contain more information. |
207 | Other, |
208 | } |
209 | |
210 | /// I2C no acknowledge error source. |
211 | /// |
212 | /// In cases where it is possible, a device should indicate if a no acknowledge |
213 | /// response was received to an address versus a no acknowledge to a data byte. |
214 | /// Where it is not possible to differentiate, `Unknown` should be indicated. |
215 | #[derive (Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
216 | #[cfg_attr (feature = "defmt-03" , derive(defmt::Format))] |
217 | pub enum NoAcknowledgeSource { |
218 | /// The device did not acknowledge its address. The device may be missing. |
219 | Address, |
220 | /// The device did not acknowledge the data. It may not be ready to process |
221 | /// requests at the moment. |
222 | Data, |
223 | /// Either the device did not acknowledge its address or the data, but it is |
224 | /// unknown which. |
225 | Unknown, |
226 | } |
227 | |
228 | impl Error for ErrorKind { |
229 | #[inline ] |
230 | fn kind(&self) -> ErrorKind { |
231 | *self |
232 | } |
233 | } |
234 | |
235 | impl core::fmt::Display for ErrorKind { |
236 | #[inline ] |
237 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
238 | match self { |
239 | Self::Bus => write!(f, "Bus error occurred" ), |
240 | Self::ArbitrationLoss => write!(f, "The arbitration was lost" ), |
241 | Self::NoAcknowledge(s: &NoAcknowledgeSource) => s.fmt(f), |
242 | Self::Overrun => write!(f, "The peripheral receive buffer was overrun" ), |
243 | Self::Other => write!( |
244 | f, |
245 | "A different error occurred. The original error may contain more information" |
246 | ), |
247 | } |
248 | } |
249 | } |
250 | |
251 | impl core::fmt::Display for NoAcknowledgeSource { |
252 | #[inline ] |
253 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
254 | match self { |
255 | Self::Address => write!(f, "The device did not acknowledge its address" ), |
256 | Self::Data => write!(f, "The device did not acknowledge the data" ), |
257 | Self::Unknown => write!(f, "The device did not acknowledge its address or the data" ), |
258 | } |
259 | } |
260 | } |
261 | |
262 | /// I2C error type trait. |
263 | /// |
264 | /// This just defines the error type, to be used by the other traits. |
265 | pub trait ErrorType { |
266 | /// Error type |
267 | type Error: Error; |
268 | } |
269 | |
270 | impl<T: ErrorType + ?Sized> ErrorType for &mut T { |
271 | type Error = T::Error; |
272 | } |
273 | |
274 | /// Address mode (7-bit / 10-bit). |
275 | /// |
276 | /// Note: This trait is sealed and should not be implemented outside of this crate. |
277 | pub trait AddressMode: private::Sealed + 'static {} |
278 | |
279 | /// 7-bit address mode type. |
280 | /// |
281 | /// Note that 7-bit addresses defined by drivers should be specified in **right-aligned** form, |
282 | /// e.g. in the range `0x00..=0x7F`. |
283 | /// |
284 | /// For example, a device that has the seven bit address of `0b011_0010`, and therefore is addressed on the wire using: |
285 | /// |
286 | /// * `0b0110010_0` or `0x64` for *writes* |
287 | /// * `0b0110010_1` or `0x65` for *reads* |
288 | /// |
289 | /// Should be specified as `0b0011_0010` or `0x32`, NOT `0x64` or `0x65`. Care should be taken by both HAL and driver |
290 | /// crate writers to use this scheme consistently. |
291 | pub type SevenBitAddress = u8; |
292 | |
293 | /// 10-bit address mode type. |
294 | pub type TenBitAddress = u16; |
295 | |
296 | impl AddressMode for SevenBitAddress {} |
297 | |
298 | impl AddressMode for TenBitAddress {} |
299 | |
300 | /// I2C operation. |
301 | /// |
302 | /// Several operations can be combined as part of a transaction. |
303 | #[derive (Debug, PartialEq, Eq)] |
304 | #[cfg_attr (feature = "defmt-03" , derive(defmt::Format))] |
305 | pub enum Operation<'a> { |
306 | /// Read data into the provided buffer. |
307 | Read(&'a mut [u8]), |
308 | /// Write data from the provided buffer. |
309 | Write(&'a [u8]), |
310 | } |
311 | |
312 | /// Blocking I2C. |
313 | pub trait I2c<A: AddressMode = SevenBitAddress>: ErrorType { |
314 | /// Reads enough bytes from slave with `address` to fill `read`. |
315 | /// |
316 | /// # I2C Events (contract) |
317 | /// |
318 | /// ``` text |
319 | /// Master: ST SAD+R MAK MAK ... NMAK SP |
320 | /// Slave: SAK B0 B1 ... BN |
321 | /// ``` |
322 | /// |
323 | /// Where |
324 | /// |
325 | /// - `ST` = start condition |
326 | /// - `SAD+R` = slave address followed by bit 1 to indicate reading |
327 | /// - `SAK` = slave acknowledge |
328 | /// - `Bi` = ith byte of data |
329 | /// - `MAK` = master acknowledge |
330 | /// - `NMAK` = master no acknowledge |
331 | /// - `SP` = stop condition |
332 | #[inline ] |
333 | fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { |
334 | self.transaction(address, &mut [Operation::Read(read)]) |
335 | } |
336 | |
337 | /// Writes bytes to slave with address `address`. |
338 | /// |
339 | /// # I2C Events (contract) |
340 | /// |
341 | /// ``` text |
342 | /// Master: ST SAD+W B0 B1 ... BN SP |
343 | /// Slave: SAK SAK SAK ... SAK |
344 | /// ``` |
345 | /// |
346 | /// Where |
347 | /// |
348 | /// - `ST` = start condition |
349 | /// - `SAD+W` = slave address followed by bit 0 to indicate writing |
350 | /// - `SAK` = slave acknowledge |
351 | /// - `Bi` = ith byte of data |
352 | /// - `SP` = stop condition |
353 | #[inline ] |
354 | fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { |
355 | self.transaction(address, &mut [Operation::Write(write)]) |
356 | } |
357 | |
358 | /// Writes bytes to slave with address `address` and then reads enough bytes to fill `read` *in a |
359 | /// single transaction*. |
360 | /// |
361 | /// # I2C Events (contract) |
362 | /// |
363 | /// ``` text |
364 | /// Master: ST SAD+W O0 O1 ... OM SR SAD+R MAK MAK ... NMAK SP |
365 | /// Slave: SAK SAK SAK ... SAK SAK I0 I1 ... IN |
366 | /// ``` |
367 | /// |
368 | /// Where |
369 | /// |
370 | /// - `ST` = start condition |
371 | /// - `SAD+W` = slave address followed by bit 0 to indicate writing |
372 | /// - `SAK` = slave acknowledge |
373 | /// - `Oi` = ith outgoing byte of data |
374 | /// - `SR` = repeated start condition |
375 | /// - `SAD+R` = slave address followed by bit 1 to indicate reading |
376 | /// - `Ii` = ith incoming byte of data |
377 | /// - `MAK` = master acknowledge |
378 | /// - `NMAK` = master no acknowledge |
379 | /// - `SP` = stop condition |
380 | #[inline ] |
381 | fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { |
382 | self.transaction( |
383 | address, |
384 | &mut [Operation::Write(write), Operation::Read(read)], |
385 | ) |
386 | } |
387 | |
388 | /// Execute the provided operations on the I2C bus. |
389 | /// |
390 | /// Transaction contract: |
391 | /// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate. |
392 | /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. |
393 | /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. |
394 | /// - After executing the last operation an SP is sent automatically. |
395 | /// - If the last operation is a `Read` the master does not send an acknowledge for the last byte. |
396 | /// |
397 | /// - `ST` = start condition |
398 | /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing |
399 | /// - `SR` = repeated start condition |
400 | /// - `SP` = stop condition |
401 | fn transaction( |
402 | &mut self, |
403 | address: A, |
404 | operations: &mut [Operation<'_>], |
405 | ) -> Result<(), Self::Error>; |
406 | } |
407 | |
408 | impl<A: AddressMode, T: I2c<A> + ?Sized> I2c<A> for &mut T { |
409 | #[inline ] |
410 | fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { |
411 | T::read(self, address, read) |
412 | } |
413 | |
414 | #[inline ] |
415 | fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { |
416 | T::write(self, address, write) |
417 | } |
418 | |
419 | #[inline ] |
420 | fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { |
421 | T::write_read(self, address, write, read) |
422 | } |
423 | |
424 | #[inline ] |
425 | fn transaction( |
426 | &mut self, |
427 | address: A, |
428 | operations: &mut [Operation<'_>], |
429 | ) -> Result<(), Self::Error> { |
430 | T::transaction(self, address, operations) |
431 | } |
432 | } |
433 | |