diff options
Diffstat (limited to 'embassy-stm32/src/i2c/mod.rs')
| -rw-r--r-- | embassy-stm32/src/i2c/mod.rs | 110 |
1 files changed, 70 insertions, 40 deletions
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 249bac41c..0aa2d1da9 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs | |||
| @@ -129,7 +129,7 @@ impl<'d> Drop for I2CDropGuard<'d> { | |||
| 129 | x.set_as_disconnected() | 129 | x.set_as_disconnected() |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | self.info.rcc.disable(); | 132 | self.info.rcc.disable_without_stop(); |
| 133 | } | 133 | } |
| 134 | } | 134 | } |
| 135 | 135 | ||
| @@ -154,8 +154,8 @@ impl<'d> I2c<'d, Async, Master> { | |||
| 154 | scl: Peri<'d, if_afio!(impl SclPin<T, A>)>, | 154 | scl: Peri<'d, if_afio!(impl SclPin<T, A>)>, |
| 155 | sda: Peri<'d, if_afio!(impl SdaPin<T, A>)>, | 155 | sda: Peri<'d, if_afio!(impl SdaPin<T, A>)>, |
| 156 | _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>> | 156 | _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>> |
| 157 | + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>> | 157 | + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>> |
| 158 | + 'd, | 158 | + 'd, |
| 159 | tx_dma: Peri<'d, impl TxDma<T>>, | 159 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 160 | rx_dma: Peri<'d, impl RxDma<T>>, | 160 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 161 | config: Config, | 161 | config: Config, |
| @@ -219,13 +219,14 @@ impl<'d, M: Mode> I2c<'d, M, Master> { | |||
| 219 | sda, | 219 | sda, |
| 220 | }, | 220 | }, |
| 221 | }; | 221 | }; |
| 222 | |||
| 222 | this.enable_and_init(config); | 223 | this.enable_and_init(config); |
| 223 | 224 | ||
| 224 | this | 225 | this |
| 225 | } | 226 | } |
| 226 | 227 | ||
| 227 | fn enable_and_init(&mut self, config: Config) { | 228 | fn enable_and_init(&mut self, config: Config) { |
| 228 | self.info.rcc.enable_and_reset(); | 229 | self.info.rcc.enable_and_reset_without_stop(); |
| 229 | self.init(config); | 230 | self.init(config); |
| 230 | } | 231 | } |
| 231 | } | 232 | } |
| @@ -437,15 +438,15 @@ impl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> { | |||
| 437 | 438 | ||
| 438 | /// Frame type in I2C transaction. | 439 | /// Frame type in I2C transaction. |
| 439 | /// | 440 | /// |
| 440 | /// This tells each method what kind of framing to use, to generate a (repeated) start condition (ST | 441 | /// This tells each method what kind of frame to use, to generate a (repeated) start condition (ST |
| 441 | /// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an | 442 | /// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an |
| 442 | /// ACK or NACK after the last byte received. | 443 | /// ACK or NACK after the last byte received. |
| 443 | /// | 444 | /// |
| 444 | /// For write operations, the following options are identical because they differ only in the (N)ACK | 445 | /// For write operations, the following options are identical because they differ only in the (N)ACK |
| 445 | /// treatment relevant for read operations: | 446 | /// treatment relevant for read operations: |
| 446 | /// | 447 | /// |
| 447 | /// - `FirstFrame` and `FirstAndNextFrame` | 448 | /// - `FirstFrame` and `FirstAndNextFrame` behave identically for writes |
| 448 | /// - `NextFrame` and `LastFrameNoStop` | 449 | /// - `NextFrame` and `LastFrameNoStop` behave identically for writes |
| 449 | /// | 450 | /// |
| 450 | /// Abbreviations used below: | 451 | /// Abbreviations used below: |
| 451 | /// | 452 | /// |
| @@ -474,7 +475,7 @@ enum FrameOptions { | |||
| 474 | 475 | ||
| 475 | #[allow(dead_code)] | 476 | #[allow(dead_code)] |
| 476 | impl FrameOptions { | 477 | impl FrameOptions { |
| 477 | /// Sends start or repeated start condition before transfer. | 478 | /// Returns true if a start or repeated start condition should be generated before this operation. |
| 478 | fn send_start(self) -> bool { | 479 | fn send_start(self) -> bool { |
| 479 | match self { | 480 | match self { |
| 480 | Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true, | 481 | Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true, |
| @@ -482,7 +483,7 @@ impl FrameOptions { | |||
| 482 | } | 483 | } |
| 483 | } | 484 | } |
| 484 | 485 | ||
| 485 | /// Sends stop condition after transfer. | 486 | /// Returns true if a stop condition should be generated after this operation. |
| 486 | fn send_stop(self) -> bool { | 487 | fn send_stop(self) -> bool { |
| 487 | match self { | 488 | match self { |
| 488 | Self::FirstAndLastFrame | Self::LastFrame => true, | 489 | Self::FirstAndLastFrame | Self::LastFrame => true, |
| @@ -490,7 +491,10 @@ impl FrameOptions { | |||
| 490 | } | 491 | } |
| 491 | } | 492 | } |
| 492 | 493 | ||
| 493 | /// Sends NACK after last byte received, indicating end of read operation. | 494 | /// Returns true if NACK should be sent after the last byte received in a read operation. |
| 495 | /// | ||
| 496 | /// This signals the end of a read sequence and releases the bus for the master's | ||
| 497 | /// next transmission (or stop condition). | ||
| 494 | fn send_nack(self) -> bool { | 498 | fn send_nack(self) -> bool { |
| 495 | match self { | 499 | match self { |
| 496 | Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true, | 500 | Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true, |
| @@ -499,24 +503,44 @@ impl FrameOptions { | |||
| 499 | } | 503 | } |
| 500 | } | 504 | } |
| 501 | 505 | ||
| 502 | /// Iterates over operations in transaction. | 506 | /// Analyzes I2C transaction operations and assigns appropriate frame to each. |
| 507 | /// | ||
| 508 | /// This function processes a sequence of I2C operations and determines the correct | ||
| 509 | /// frame configuration for each operation to ensure proper I2C protocol compliance. | ||
| 510 | /// It handles the complex logic of: | ||
| 511 | /// | ||
| 512 | /// - Generating start conditions for the first operation of each type (read/write) | ||
| 513 | /// - Generating stop conditions for the final operation in the entire transaction | ||
| 514 | /// - Managing ACK/NACK behavior for read operations, including merging consecutive reads | ||
| 515 | /// - Ensuring proper bus handoff between different operation types | ||
| 516 | /// | ||
| 517 | /// **Transaction Contract Compliance:** | ||
| 518 | /// The frame assignments ensure compliance with the embedded-hal I2C transaction contract, | ||
| 519 | /// where consecutive operations of the same type are logically merged while maintaining | ||
| 520 | /// proper protocol boundaries. | ||
| 503 | /// | 521 | /// |
| 504 | /// Returns necessary frame options for each operation to uphold the [transaction contract] and have | 522 | /// **Error Handling:** |
| 505 | /// the right start/stop/(N)ACK conditions on the wire. | 523 | /// Returns an error if any read operation has an empty buffer, as this would create |
| 524 | /// an invalid I2C transaction that could halt mid-execution. | ||
| 525 | /// | ||
| 526 | /// # Arguments | ||
| 527 | /// * `operations` - Mutable slice of I2C operations from embedded-hal | ||
| 528 | /// | ||
| 529 | /// # Returns | ||
| 530 | /// An iterator over (operation, frame) pairs, or an error if the transaction is invalid | ||
| 506 | /// | 531 | /// |
| 507 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | ||
| 508 | #[allow(dead_code)] | 532 | #[allow(dead_code)] |
| 509 | fn operation_frames<'a, 'b: 'a>( | 533 | fn operation_frames<'a, 'b: 'a>( |
| 510 | operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], | 534 | operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], |
| 511 | ) -> Result<impl IntoIterator<Item = (&'a mut embedded_hal_1::i2c::Operation<'b>, FrameOptions)>, Error> { | 535 | ) -> Result<impl IntoIterator<Item = (&'a mut embedded_hal_1::i2c::Operation<'b>, FrameOptions)>, Error> { |
| 512 | use embedded_hal_1::i2c::Operation::{Read, Write}; | 536 | use embedded_hal_1::i2c::Operation::{Read, Write}; |
| 513 | 537 | ||
| 514 | // Check empty read buffer before starting transaction. Otherwise, we would risk halting with an | 538 | // Validate that no read operations have empty buffers before starting the transaction. |
| 515 | // error in the middle of the transaction. | 539 | // Empty read operations would risk halting with an error mid-transaction. |
| 516 | // | 540 | // |
| 517 | // In principle, we could allow empty read frames within consecutive read operations, as long as | 541 | // Note: We could theoretically allow empty read operations within consecutive read |
| 518 | // at least one byte remains in the final (merged) read operation, but that makes the logic more | 542 | // sequences as long as the final merged read has at least one byte, but this would |
| 519 | // complicated and error-prone. | 543 | // complicate the logic significantly and create error-prone edge cases. |
| 520 | if operations.iter().any(|op| match op { | 544 | if operations.iter().any(|op| match op { |
| 521 | Read(read) => read.is_empty(), | 545 | Read(read) => read.is_empty(), |
| 522 | Write(_) => false, | 546 | Write(_) => false, |
| @@ -525,46 +549,52 @@ fn operation_frames<'a, 'b: 'a>( | |||
| 525 | } | 549 | } |
| 526 | 550 | ||
| 527 | let mut operations = operations.iter_mut().peekable(); | 551 | let mut operations = operations.iter_mut().peekable(); |
| 528 | 552 | let mut next_first_operation = true; | |
| 529 | let mut next_first_frame = true; | ||
| 530 | 553 | ||
| 531 | Ok(iter::from_fn(move || { | 554 | Ok(iter::from_fn(move || { |
| 532 | let op = operations.next()?; | 555 | let current_op = operations.next()?; |
| 533 | 556 | ||
| 534 | // Is `op` first frame of its type? | 557 | // Determine if this is the first operation of its type (read or write) |
| 535 | let first_frame = next_first_frame; | 558 | let is_first_of_type = next_first_operation; |
| 536 | let next_op = operations.peek(); | 559 | let next_op = operations.peek(); |
| 537 | 560 | ||
| 538 | // Get appropriate frame options as combination of the following properties: | 561 | // Compute the appropriate frame based on three key properties: |
| 539 | // | 562 | // |
| 540 | // - For each first operation of its type, generate a (repeated) start condition. | 563 | // 1. **Start Condition**: Generate (repeated) start for first operation of each type |
| 541 | // - For the last operation overall in the entire transaction, generate a stop condition. | 564 | // 2. **Stop Condition**: Generate stop for the final operation in the entire transaction |
| 542 | // - For read operations, check the next operation: if it is also a read operation, we merge | 565 | // 3. **ACK/NACK for Reads**: For read operations, send ACK if more reads follow in the |
| 543 | // these and send ACK for all bytes in the current operation; send NACK only for the final | 566 | // sequence, or NACK for the final read in a sequence (before write or transaction end) |
| 544 | // read operation's last byte (before write or end of entire transaction) to indicate last | ||
| 545 | // byte read and release the bus for transmission of the bus master's next byte (or stop). | ||
| 546 | // | 567 | // |
| 547 | // We check the third property unconditionally, i.e. even for write opeartions. This is okay | 568 | // The third property is checked for all operations since the resulting frame |
| 548 | // because the resulting frame options are identical for write operations. | 569 | // configurations are identical for write operations regardless of ACK/NACK treatment. |
| 549 | let frame = match (first_frame, next_op) { | 570 | let frame = match (is_first_of_type, next_op) { |
| 571 | // First operation of type, and it's also the final operation overall | ||
| 550 | (true, None) => FrameOptions::FirstAndLastFrame, | 572 | (true, None) => FrameOptions::FirstAndLastFrame, |
| 573 | // First operation of type, next operation is also a read (continue read sequence) | ||
| 551 | (true, Some(Read(_))) => FrameOptions::FirstAndNextFrame, | 574 | (true, Some(Read(_))) => FrameOptions::FirstAndNextFrame, |
| 575 | // First operation of type, next operation is write (end current sequence) | ||
| 552 | (true, Some(Write(_))) => FrameOptions::FirstFrame, | 576 | (true, Some(Write(_))) => FrameOptions::FirstFrame, |
| 553 | // | 577 | |
| 578 | // Continuation operation, and it's the final operation overall | ||
| 554 | (false, None) => FrameOptions::LastFrame, | 579 | (false, None) => FrameOptions::LastFrame, |
| 580 | // Continuation operation, next operation is also a read (continue read sequence) | ||
| 555 | (false, Some(Read(_))) => FrameOptions::NextFrame, | 581 | (false, Some(Read(_))) => FrameOptions::NextFrame, |
| 582 | // Continuation operation, next operation is write (end current sequence, no stop) | ||
| 556 | (false, Some(Write(_))) => FrameOptions::LastFrameNoStop, | 583 | (false, Some(Write(_))) => FrameOptions::LastFrameNoStop, |
| 557 | }; | 584 | }; |
| 558 | 585 | ||
| 559 | // Pre-calculate if `next_op` is the first operation of its type. We do this here and not at | 586 | // Pre-calculate whether the next operation will be the first of its type. |
| 560 | // the beginning of the loop because we hand out `op` as iterator value and cannot access it | 587 | // This is done here because we consume `current_op` as the iterator value |
| 561 | // anymore in the next iteration. | 588 | // and cannot access it in the next iteration. |
| 562 | next_first_frame = match (&op, next_op) { | 589 | next_first_operation = match (¤t_op, next_op) { |
| 590 | // No next operation | ||
| 563 | (_, None) => false, | 591 | (_, None) => false, |
| 592 | // Operation type changes: next will be first of its type | ||
| 564 | (Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true, | 593 | (Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true, |
| 594 | // Operation type continues: next will not be first of its type | ||
| 565 | (Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false, | 595 | (Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false, |
| 566 | }; | 596 | }; |
| 567 | 597 | ||
| 568 | Some((op, frame)) | 598 | Some((current_op, frame)) |
| 569 | })) | 599 | })) |
| 570 | } | 600 | } |
