diff options
| author | xoviat <[email protected]> | 2021-03-02 15:09:47 -0600 |
|---|---|---|
| committer | xoviat <[email protected]> | 2021-03-02 15:09:47 -0600 |
| commit | 492f7aeea6c309a0a8e60fb2028278fb906164cb (patch) | |
| tree | 7ac9ce0ea58932c8a090890a3b548ca622fedb1b | |
| parent | 88946840d1f6fdc924a4f0a9980d17f1d2c456fa (diff) | |
add i2c trait
| -rw-r--r-- | embassy-traits/src/i2c.rs | 409 | ||||
| -rw-r--r-- | embassy-traits/src/lib.rs | 2 |
2 files changed, 411 insertions, 0 deletions
diff --git a/embassy-traits/src/i2c.rs b/embassy-traits/src/i2c.rs new file mode 100644 index 000000000..bd8c72ab5 --- /dev/null +++ b/embassy-traits/src/i2c.rs | |||
| @@ -0,0 +1,409 @@ | |||
| 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 | //! ## Examples | ||
| 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::blocking::i2c::{SevenBitAddress, TenBitAddress, Write}; | ||
| 26 | //! /// I2C0 hardware peripheral which supports both 7-bit and 10-bit addressing. | ||
| 27 | //! pub struct I2c0; | ||
| 28 | //! | ||
| 29 | //! impl Write<SevenBitAddress> for I2c0 | ||
| 30 | //! { | ||
| 31 | //! # type Error = (); | ||
| 32 | //! # | ||
| 33 | //! fn try_write(&mut self, addr: u8, output: &[u8]) -> Result<(), Self::Error> { | ||
| 34 | //! // ... | ||
| 35 | //! # Ok(()) | ||
| 36 | //! } | ||
| 37 | //! } | ||
| 38 | //! | ||
| 39 | //! impl Write<TenBitAddress> for I2c0 | ||
| 40 | //! { | ||
| 41 | //! # type Error = (); | ||
| 42 | //! # | ||
| 43 | //! fn try_write(&mut self, addr: u16, output: &[u8]) -> Result<(), Self::Error> { | ||
| 44 | //! // ... | ||
| 45 | //! # Ok(()) | ||
| 46 | //! } | ||
| 47 | //! } | ||
| 48 | //! ``` | ||
| 49 | //! | ||
| 50 | //! ### Device driver compatible only with 7-bit addresses | ||
| 51 | //! | ||
| 52 | //! For demonstration purposes the address mode parameter has been omitted in this example. | ||
| 53 | //! | ||
| 54 | //! ``` | ||
| 55 | //! # use embedded_hal::blocking::i2c::WriteRead; | ||
| 56 | //! const ADDR: u8 = 0x15; | ||
| 57 | //! # const TEMP_REGISTER: u8 = 0x1; | ||
| 58 | //! pub struct TemperatureSensorDriver<I2C> { | ||
| 59 | //! i2c: I2C, | ||
| 60 | //! } | ||
| 61 | //! | ||
| 62 | //! impl<I2C, E> TemperatureSensorDriver<I2C> | ||
| 63 | //! where | ||
| 64 | //! I2C: WriteRead<Error = E>, | ||
| 65 | //! { | ||
| 66 | //! pub fn read_temperature(&mut self) -> Result<u8, E> { | ||
| 67 | //! let mut temp = [0]; | ||
| 68 | //! self.i2c | ||
| 69 | //! .try_write_read(ADDR, &[TEMP_REGISTER], &mut temp) | ||
| 70 | //! .and(Ok(temp[0])) | ||
| 71 | //! } | ||
| 72 | //! } | ||
| 73 | //! ``` | ||
| 74 | //! | ||
| 75 | //! ### Device driver compatible only with 10-bit addresses | ||
| 76 | //! | ||
| 77 | //! ``` | ||
| 78 | //! # use embedded_hal::blocking::i2c::{TenBitAddress, WriteRead}; | ||
| 79 | //! const ADDR: u16 = 0x158; | ||
| 80 | //! # const TEMP_REGISTER: u8 = 0x1; | ||
| 81 | //! pub struct TemperatureSensorDriver<I2C> { | ||
| 82 | //! i2c: I2C, | ||
| 83 | //! } | ||
| 84 | //! | ||
| 85 | //! impl<I2C, E> TemperatureSensorDriver<I2C> | ||
| 86 | //! where | ||
| 87 | //! I2C: WriteRead<TenBitAddress, Error = E>, | ||
| 88 | //! { | ||
| 89 | //! pub fn read_temperature(&mut self) -> Result<u8, E> { | ||
| 90 | //! let mut temp = [0]; | ||
| 91 | //! self.i2c | ||
| 92 | //! .try_write_read(ADDR, &[TEMP_REGISTER], &mut temp) | ||
| 93 | //! .and(Ok(temp[0])) | ||
| 94 | //! } | ||
| 95 | //! } | ||
| 96 | //! ``` | ||
| 97 | |||
| 98 | use core::future::Future; | ||
| 99 | |||
| 100 | mod private { | ||
| 101 | pub trait Sealed {} | ||
| 102 | } | ||
| 103 | |||
| 104 | /// Address mode (7-bit / 10-bit) | ||
| 105 | /// | ||
| 106 | /// Note: This trait is sealed and should not be implemented outside of this crate. | ||
| 107 | pub trait AddressMode: private::Sealed {} | ||
| 108 | |||
| 109 | /// 7-bit address mode type | ||
| 110 | pub type SevenBitAddress = u8; | ||
| 111 | |||
| 112 | /// 10-bit address mode type | ||
| 113 | pub type TenBitAddress = u16; | ||
| 114 | |||
| 115 | impl private::Sealed for SevenBitAddress {} | ||
| 116 | impl private::Sealed for TenBitAddress {} | ||
| 117 | |||
| 118 | impl AddressMode for SevenBitAddress {} | ||
| 119 | |||
| 120 | impl AddressMode for TenBitAddress {} | ||
| 121 | |||
| 122 | /// Blocking read | ||
| 123 | pub trait Read<A: AddressMode = SevenBitAddress> { | ||
| 124 | /// Error type | ||
| 125 | type Error; | ||
| 126 | |||
| 127 | type ReadFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a; | ||
| 128 | |||
| 129 | /// Reads enough bytes from slave with `address` to fill `buffer` | ||
| 130 | /// | ||
| 131 | /// # I2C Events (contract) | ||
| 132 | /// | ||
| 133 | /// ``` text | ||
| 134 | /// Master: ST SAD+R MAK MAK ... NMAK SP | ||
| 135 | /// Slave: SAK B0 B1 ... BN | ||
| 136 | /// ``` | ||
| 137 | /// | ||
| 138 | /// Where | ||
| 139 | /// | ||
| 140 | /// - `ST` = start condition | ||
| 141 | /// - `SAD+R` = slave address followed by bit 1 to indicate reading | ||
| 142 | /// - `SAK` = slave acknowledge | ||
| 143 | /// - `Bi` = ith byte of data | ||
| 144 | /// - `MAK` = master acknowledge | ||
| 145 | /// - `NMAK` = master no acknowledge | ||
| 146 | /// - `SP` = stop condition | ||
| 147 | fn read<'a>(&mut self, address: A, buffer: &mut [u8]) -> Self::ReadFuture<'a>; | ||
| 148 | } | ||
| 149 | |||
| 150 | /// Blocking write | ||
| 151 | pub trait Write<A: AddressMode = SevenBitAddress> { | ||
| 152 | /// Error type | ||
| 153 | type Error; | ||
| 154 | |||
| 155 | type WriteFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a; | ||
| 156 | |||
| 157 | /// Sends bytes to slave with address `address` | ||
| 158 | /// | ||
| 159 | /// # I2C Events (contract) | ||
| 160 | /// | ||
| 161 | /// ``` text | ||
| 162 | /// Master: ST SAD+W B0 B1 ... BN SP | ||
| 163 | /// Slave: SAK SAK SAK ... SAK | ||
| 164 | /// ``` | ||
| 165 | /// | ||
| 166 | /// Where | ||
| 167 | /// | ||
| 168 | /// - `ST` = start condition | ||
| 169 | /// - `SAD+W` = slave address followed by bit 0 to indicate writing | ||
| 170 | /// - `SAK` = slave acknowledge | ||
| 171 | /// - `Bi` = ith byte of data | ||
| 172 | /// - `SP` = stop condition | ||
| 173 | fn write<'a>(&mut self, address: A, bytes: &[u8]) -> Self::WriteFuture<'a>; | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Blocking write (iterator version) | ||
| 177 | pub trait WriteIter<A: AddressMode = SevenBitAddress> { | ||
| 178 | /// Error type | ||
| 179 | type Error; | ||
| 180 | |||
| 181 | type WriteIterFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a; | ||
| 182 | |||
| 183 | /// Sends bytes to slave with address `address` | ||
| 184 | /// | ||
| 185 | /// # I2C Events (contract) | ||
| 186 | /// | ||
| 187 | /// Same as `Write` | ||
| 188 | fn write_iter<'a, B>(&mut self, address: A, bytes: B) -> Self::WriteIterFuture<'a> | ||
| 189 | where | ||
| 190 | B: IntoIterator<Item = u8>; | ||
| 191 | } | ||
| 192 | |||
| 193 | /// Blocking write + read | ||
| 194 | pub trait WriteRead<A: AddressMode = SevenBitAddress> { | ||
| 195 | /// Error type | ||
| 196 | type Error; | ||
| 197 | |||
| 198 | type WriteReadFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a; | ||
| 199 | |||
| 200 | /// Sends bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a | ||
| 201 | /// single transaction* | ||
| 202 | /// | ||
| 203 | /// # I2C Events (contract) | ||
| 204 | /// | ||
| 205 | /// ``` text | ||
| 206 | /// Master: ST SAD+W O0 O1 ... OM SR SAD+R MAK MAK ... NMAK SP | ||
| 207 | /// Slave: SAK SAK SAK ... SAK SAK I0 I1 ... IN | ||
| 208 | /// ``` | ||
| 209 | /// | ||
| 210 | /// Where | ||
| 211 | /// | ||
| 212 | /// - `ST` = start condition | ||
| 213 | /// - `SAD+W` = slave address followed by bit 0 to indicate writing | ||
| 214 | /// - `SAK` = slave acknowledge | ||
| 215 | /// - `Oi` = ith outgoing byte of data | ||
| 216 | /// - `SR` = repeated start condition | ||
| 217 | /// - `SAD+R` = slave address followed by bit 1 to indicate reading | ||
| 218 | /// - `Ii` = ith incoming byte of data | ||
| 219 | /// - `MAK` = master acknowledge | ||
| 220 | /// - `NMAK` = master no acknowledge | ||
| 221 | /// - `SP` = stop condition | ||
| 222 | fn write_read<'a>( | ||
| 223 | &mut self, | ||
| 224 | address: A, | ||
| 225 | bytes: &[u8], | ||
| 226 | buffer: &mut [u8], | ||
| 227 | ) -> Self::WriteReadFuture<'a>; | ||
| 228 | } | ||
| 229 | |||
| 230 | /// Blocking write (iterator version) + read | ||
| 231 | pub trait WriteIterRead<A: AddressMode = SevenBitAddress> { | ||
| 232 | /// Error type | ||
| 233 | type Error; | ||
| 234 | |||
| 235 | type WriteIterReadFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a; | ||
| 236 | |||
| 237 | /// Sends bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a | ||
| 238 | /// single transaction* | ||
| 239 | /// | ||
| 240 | /// # I2C Events (contract) | ||
| 241 | /// | ||
| 242 | /// Same as the `WriteRead` trait | ||
| 243 | fn write_iter_read<'a, B>( | ||
| 244 | &mut self, | ||
| 245 | address: A, | ||
| 246 | bytes: B, | ||
| 247 | buffer: &mut [u8], | ||
| 248 | ) -> Self::WriteIterReadFuture<'a> | ||
| 249 | where | ||
| 250 | B: IntoIterator<Item = u8>; | ||
| 251 | } | ||
| 252 | |||
| 253 | /// Transactional I2C operation. | ||
| 254 | /// | ||
| 255 | /// Several operations can be combined as part of a transaction. | ||
| 256 | #[derive(Debug, PartialEq)] | ||
| 257 | pub enum Operation<'a> { | ||
| 258 | /// Read data into the provided buffer | ||
| 259 | Read(&'a mut [u8]), | ||
| 260 | /// Write data from the provided buffer | ||
| 261 | Write(&'a [u8]), | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Transactional I2C interface. | ||
| 265 | /// | ||
| 266 | /// This allows combining operations within an I2C transaction. | ||
| 267 | pub trait Transactional<A: AddressMode = SevenBitAddress> { | ||
| 268 | /// Error type | ||
| 269 | type Error; | ||
| 270 | |||
| 271 | type TransactionalFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a; | ||
| 272 | |||
| 273 | /// Execute the provided operations on the I2C bus. | ||
| 274 | /// | ||
| 275 | /// Transaction contract: | ||
| 276 | /// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate. | ||
| 277 | /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. | ||
| 278 | /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. | ||
| 279 | /// - After executing the last operation an SP is sent automatically. | ||
| 280 | /// - If the last operation is a `Read` the master does not send an acknowledge for the last byte. | ||
| 281 | /// | ||
| 282 | /// - `ST` = start condition | ||
| 283 | /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing | ||
| 284 | /// - `SR` = repeated start condition | ||
| 285 | /// - `SP` = stop condition | ||
| 286 | fn exec<'a>( | ||
| 287 | &mut self, | ||
| 288 | address: A, | ||
| 289 | operations: &mut [Operation<'a>], | ||
| 290 | ) -> Self::TransactionalFuture<'a>; | ||
| 291 | } | ||
| 292 | |||
| 293 | /// Transactional I2C interface (iterator version). | ||
| 294 | /// | ||
| 295 | /// This allows combining operation within an I2C transaction. | ||
| 296 | pub trait TransactionalIter<A: AddressMode = SevenBitAddress> { | ||
| 297 | /// Error type | ||
| 298 | type Error; | ||
| 299 | |||
| 300 | type TransactionalIterFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a; | ||
| 301 | |||
| 302 | /// Execute the provided operations on the I2C bus (iterator version). | ||
| 303 | /// | ||
| 304 | /// Transaction contract: | ||
| 305 | /// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate. | ||
| 306 | /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. | ||
| 307 | /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. | ||
| 308 | /// - After executing the last operation an SP is sent automatically. | ||
| 309 | /// - If the last operation is a `Read` the master does not send an acknowledge for the last byte. | ||
| 310 | /// | ||
| 311 | /// - `ST` = start condition | ||
| 312 | /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing | ||
| 313 | /// - `SR` = repeated start condition | ||
| 314 | /// - `SP` = stop condition | ||
| 315 | fn exec_iter<'a, O>(&mut self, address: A, operations: O) -> Self::TransactionalIterFuture<'a> | ||
| 316 | where | ||
| 317 | O: IntoIterator<Item = Operation<'a>>; | ||
| 318 | } | ||
| 319 | |||
| 320 | /// Default implementation of `blocking::i2c::Write`, `blocking::i2c::Read` and | ||
| 321 | /// `blocking::i2c::WriteRead` traits for `blocking::i2c::Transactional` implementers. | ||
| 322 | /// | ||
| 323 | /// If you implement `blocking::i2c::Transactional` for your I2C peripheral, | ||
| 324 | /// you can use this default implementation so that you do not need to implement | ||
| 325 | /// the `blocking::i2c::Write`, `blocking::i2c::Read` and `blocking::i2c::WriteRead` | ||
| 326 | /// traits as well. | ||
| 327 | /// ``` | ||
| 328 | /// use embedded_hal::blocking::i2c; | ||
| 329 | /// | ||
| 330 | /// struct I2c1; | ||
| 331 | /// | ||
| 332 | /// impl i2c::Transactional<i2c::SevenBitAddress> for I2c1 { | ||
| 333 | /// # type Error = (); | ||
| 334 | /// fn try_exec<'a>( | ||
| 335 | /// &mut self, | ||
| 336 | /// address: i2c::SevenBitAddress, | ||
| 337 | /// operations: &mut [i2c::Operation<'a>], | ||
| 338 | /// ) -> Result<(), Self::Error> { | ||
| 339 | /// // ... | ||
| 340 | /// # Ok(()) | ||
| 341 | /// } | ||
| 342 | /// } | ||
| 343 | /// | ||
| 344 | /// // This is all you need to do: | ||
| 345 | /// impl i2c::transactional::Default<i2c::SevenBitAddress> for I2c1 {}; | ||
| 346 | /// | ||
| 347 | /// // Then you can use `Write` and so on: | ||
| 348 | /// use i2c::Write; | ||
| 349 | /// | ||
| 350 | /// let mut i2c1 = I2c1{}; | ||
| 351 | /// i2c1.try_write(0x01, &[0xAB, 0xCD]).unwrap(); | ||
| 352 | /// ``` | ||
| 353 | pub mod transactional { | ||
| 354 | use core::future::Future; | ||
| 355 | |||
| 356 | use super::{AddressMode, Operation, Read, Transactional, Write, WriteRead}; | ||
| 357 | |||
| 358 | /// Default implementation of `blocking::i2c::Write`, `blocking::i2c::Read` and | ||
| 359 | /// `blocking::i2c::WriteRead` traits for `blocking::i2c::Transactional` implementers. | ||
| 360 | pub trait Default<A: AddressMode>: Transactional<A> {} | ||
| 361 | |||
| 362 | // impl<A, E, S> Write<A> for S | ||
| 363 | // where | ||
| 364 | // A: AddressMode + 'static, | ||
| 365 | // S: self::Default<A> + Transactional<A, Error = E> + 'static, | ||
| 366 | // E: 'static, | ||
| 367 | // { | ||
| 368 | // type Error = E; | ||
| 369 | // | ||
| 370 | // type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a; | ||
| 371 | // | ||
| 372 | // fn write<'a>(&mut self, address: A, bytes: &[u8]) -> Self::WriteFuture<'a> { | ||
| 373 | // self.exec(address, &mut [Operation::Write(bytes)]) | ||
| 374 | // } | ||
| 375 | // } | ||
| 376 | /* | ||
| 377 | impl<A, E, S> Read<A> for S | ||
| 378 | where | ||
| 379 | A: AddressMode, | ||
| 380 | S: self::Default<A> + Transactional<A, Error = E>, | ||
| 381 | { | ||
| 382 | type Error = E; | ||
| 383 | |||
| 384 | fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error> { | ||
| 385 | self.exec(address, &mut [Operation::Read(buffer)]) | ||
| 386 | } | ||
| 387 | } | ||
| 388 | |||
| 389 | impl<A, E, S> WriteRead<A> for S | ||
| 390 | where | ||
| 391 | A: AddressMode, | ||
| 392 | S: self::Default<A> + Transactional<A, Error = E>, | ||
| 393 | { | ||
| 394 | type Error = E; | ||
| 395 | |||
| 396 | fn write_read( | ||
| 397 | &mut self, | ||
| 398 | address: A, | ||
| 399 | bytes: &[u8], | ||
| 400 | buffer: &mut [u8], | ||
| 401 | ) -> Result<(), Self::Error> { | ||
| 402 | self.exec( | ||
| 403 | address, | ||
| 404 | &mut [Operation::Write(bytes), Operation::Read(buffer)], | ||
| 405 | ) | ||
| 406 | } | ||
| 407 | } | ||
| 408 | */ | ||
| 409 | } | ||
diff --git a/embassy-traits/src/lib.rs b/embassy-traits/src/lib.rs index 849bbaec3..10d44d9de 100644 --- a/embassy-traits/src/lib.rs +++ b/embassy-traits/src/lib.rs | |||
| @@ -4,8 +4,10 @@ | |||
| 4 | #![feature(const_fn_fn_ptr_basics)] | 4 | #![feature(const_fn_fn_ptr_basics)] |
| 5 | #![feature(const_option)] | 5 | #![feature(const_option)] |
| 6 | #![allow(incomplete_features)] | 6 | #![allow(incomplete_features)] |
| 7 | #![feature(type_alias_impl_trait)] | ||
| 7 | 8 | ||
| 8 | pub mod delay; | 9 | pub mod delay; |
| 9 | pub mod flash; | 10 | pub mod flash; |
| 10 | pub mod gpio; | 11 | pub mod gpio; |
| 12 | pub mod i2c; | ||
| 11 | pub mod uart; | 13 | pub mod uart; |
