diff options
| author | Sebastian Goll <[email protected]> | 2024-03-20 02:55:18 +0100 |
|---|---|---|
| committer | Sebastian Goll <[email protected]> | 2024-03-20 02:59:30 +0100 |
| commit | c96062fbcdb32b5ecc9b9d3507097f09bbe4b7ca (patch) | |
| tree | 8a52e9afe921da5ed2a64578ddb0ef8426be041d | |
| parent | 7c08616c02d8e0f49daa7402fd000887d13d4fe5 (diff) | |
Implement blocking transaction handling for I2C v1
| -rw-r--r-- | embassy-stm32/src/i2c/mod.rs | 6 | ||||
| -rw-r--r-- | embassy-stm32/src/i2c/v1.rs | 61 |
2 files changed, 64 insertions, 3 deletions
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 2416005b5..2c606c3c9 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs | |||
| @@ -311,10 +311,10 @@ impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> { | |||
| 311 | 311 | ||
| 312 | fn transaction( | 312 | fn transaction( |
| 313 | &mut self, | 313 | &mut self, |
| 314 | _address: u8, | 314 | address: u8, |
| 315 | _operations: &mut [embedded_hal_1::i2c::Operation<'_>], | 315 | operations: &mut [embedded_hal_1::i2c::Operation<'_>], |
| 316 | ) -> Result<(), Self::Error> { | 316 | ) -> Result<(), Self::Error> { |
| 317 | todo!(); | 317 | self.blocking_transaction(address, operations) |
| 318 | } | 318 | } |
| 319 | } | 319 | } |
| 320 | 320 | ||
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 0843f45cf..678568706 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs | |||
| @@ -10,6 +10,7 @@ use core::task::Poll; | |||
| 10 | use embassy_embedded_hal::SetConfig; | 10 | use embassy_embedded_hal::SetConfig; |
| 11 | use embassy_futures::select::{select, Either}; | 11 | use embassy_futures::select::{select, Either}; |
| 12 | use embassy_hal_internal::drop::OnDrop; | 12 | use embassy_hal_internal::drop::OnDrop; |
| 13 | use embedded_hal_1::i2c::Operation; | ||
| 13 | 14 | ||
| 14 | use super::*; | 15 | use super::*; |
| 15 | use crate::dma::Transfer; | 16 | use crate::dma::Transfer; |
| @@ -374,6 +375,66 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 374 | Ok(()) | 375 | Ok(()) |
| 375 | } | 376 | } |
| 376 | 377 | ||
| 378 | /// Blocking transaction with operations. | ||
| 379 | /// | ||
| 380 | /// Consecutive operations of same type are merged. See [transaction contract] for details. | ||
| 381 | /// | ||
| 382 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | ||
| 383 | pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | ||
| 384 | let timeout = self.timeout(); | ||
| 385 | |||
| 386 | let mut operations = operations.iter_mut(); | ||
| 387 | |||
| 388 | let mut prev_op: Option<&mut Operation<'_>> = None; | ||
| 389 | let mut next_op = operations.next(); | ||
| 390 | |||
| 391 | while let Some(mut op) = next_op { | ||
| 392 | next_op = operations.next(); | ||
| 393 | |||
| 394 | // Check if this is the first frame of this type. This is the case for the first overall | ||
| 395 | // frame in the transaction and whenever the type of operation changes. | ||
| 396 | let first_frame = | ||
| 397 | match (prev_op.as_ref(), &op) { | ||
| 398 | (None, _) => true, | ||
| 399 | (Some(Operation::Read(_)), Operation::Write(_)) | ||
| 400 | | (Some(Operation::Write(_)), Operation::Read(_)) => true, | ||
| 401 | (Some(Operation::Read(_)), Operation::Read(_)) | ||
| 402 | | (Some(Operation::Write(_)), Operation::Write(_)) => false, | ||
| 403 | }; | ||
| 404 | |||
| 405 | let frame = match (first_frame, next_op.as_ref()) { | ||
| 406 | // If this is the first frame of this type, we generate a (repeated) start condition | ||
| 407 | // but have to consider the next operation: if it is the last, we generate the final | ||
| 408 | // stop condition. Otherwise, we branch on the operation: with read operations, only | ||
| 409 | // the last byte overall (before a write operation or the end of the transaction) is | ||
| 410 | // to be NACK'd, i.e. if another read operation follows, we must ACK this last byte. | ||
| 411 | (true, None) => FrameOptions::FirstAndLastFrame, | ||
| 412 | // Make sure to keep sending ACK for last byte in read operation when it is followed | ||
| 413 | // by another consecutive read operation. If the current operation is write, this is | ||
| 414 | // identical to `FirstFrame`. | ||
| 415 | (true, Some(Operation::Read(_))) => FrameOptions::FirstAndNextFrame, | ||
| 416 | // Otherwise, send NACK for last byte (in read operation). (For write, this does not | ||
| 417 | // matter and could also be `FirstAndNextFrame`.) | ||
| 418 | (true, Some(Operation::Write(_))) => FrameOptions::FirstFrame, | ||
| 419 | |||
| 420 | // If this is not the first frame of its type, we do not generate a (repeated) start | ||
| 421 | // condition. Otherwise, we branch the same way as above. | ||
| 422 | (false, None) => FrameOptions::LastFrame, | ||
| 423 | (false, Some(Operation::Read(_))) => FrameOptions::NextFrame, | ||
| 424 | (false, Some(Operation::Write(_))) => FrameOptions::LastFrameNoStop, | ||
| 425 | }; | ||
| 426 | |||
| 427 | match &mut op { | ||
| 428 | Operation::Read(read) => self.blocking_read_timeout(addr, read, timeout, frame)?, | ||
| 429 | Operation::Write(write) => self.write_bytes(addr, write, timeout, frame)?, | ||
| 430 | } | ||
| 431 | |||
| 432 | prev_op = Some(op); | ||
| 433 | } | ||
| 434 | |||
| 435 | Ok(()) | ||
| 436 | } | ||
| 437 | |||
| 377 | // Async | 438 | // Async |
| 378 | 439 | ||
| 379 | #[inline] // pretty sure this should always be inlined | 440 | #[inline] // pretty sure this should always be inlined |
