aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHybridChild <[email protected]>2025-07-27 10:35:52 +0200
committerHybridChild <[email protected]>2025-08-23 08:48:46 +0200
commit399ec8d90f1c4f62fd20d21dabbb9ce64c6986eb (patch)
tree25e9ef00eda482e229f956d658426a579aa5726f
parentbf01151bbb793c36431820bd814db775bf10a870 (diff)
stm32/i2c: Rename FrameOptions enum to OperationFraming
-rw-r--r--embassy-stm32/src/i2c/mod.rs185
-rw-r--r--embassy-stm32/src/i2c/v1.rs28
2 files changed, 124 insertions, 89 deletions
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs
index 5fb49f943..f51682900 100644
--- a/embassy-stm32/src/i2c/mod.rs
+++ b/embassy-stm32/src/i2c/mod.rs
@@ -435,88 +435,117 @@ impl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> {
435 } 435 }
436} 436}
437 437
438/// Frame type in I2C transaction. 438/// Operation framing configuration for I2C transactions.
439/// 439///
440/// This tells each method what kind of framing to use, to generate a (repeated) start condition (ST 440/// This determines the I2C frame boundaries for each operation within a transaction,
441/// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an 441/// controlling the generation of start conditions (ST or SR), stop conditions (SP),
442/// ACK or NACK after the last byte received. 442/// and ACK/NACK behavior for read operations.
443/// 443///
444/// For write operations, the following options are identical because they differ only in the (N)ACK 444/// For write operations, some framing configurations are functionally identical
445/// treatment relevant for read operations: 445/// because they differ only in ACK/NACK treatment which is relevant only for reads:
446/// 446///
447/// - `FirstFrame` and `FirstAndNextFrame` 447/// - `First` and `FirstAndNext` behave identically for writes
448/// - `NextFrame` and `LastFrameNoStop` 448/// - `Next` and `LastNoStop` behave identically for writes
449///
450/// Abbreviations used below:
451/// 449///
450/// **Framing Legend:**
452/// - `ST` = start condition 451/// - `ST` = start condition
453/// - `SR` = repeated start condition 452/// - `SR` = repeated start condition
454/// - `SP` = stop condition 453/// - `SP` = stop condition
455/// - `ACK`/`NACK` = last byte in read operation 454/// - `ACK/NACK` = acknowledgment behavior for the final byte of read operations
456#[derive(Copy, Clone)] 455#[derive(Copy, Clone)]
457#[allow(dead_code)] 456#[allow(dead_code)]
458enum FrameOptions { 457enum OperationFraming {
459 /// `[ST/SR]+[NACK]+[SP]` First frame (of this type) in transaction and also last frame overall. 458 /// `[ST/SR]+[NACK]+[SP]` - First operation of its type in the transaction and also the final operation overall.
460 FirstAndLastFrame, 459 FirstAndLast,
461 /// `[ST/SR]+[NACK]` First frame of this type in transaction, last frame in a read operation but 460 /// `[ST/SR]+[NACK]` - First operation of its type in the transaction, final operation in a read sequence, but not the final operation overall.
462 /// not the last frame overall. 461 First,
463 FirstFrame, 462 /// `[ST/SR]+[ACK]` - First operation of its type in the transaction, but neither the final operation overall nor the final operation in a read sequence.
464 /// `[ST/SR]+[ACK]` First frame of this type in transaction, neither last frame overall nor last 463 FirstAndNext,
465 /// frame in a read operation. 464 /// `[ACK]` - Continuation operation in a read sequence (neither first nor last).
466 FirstAndNextFrame, 465 Next,
467 /// `[ACK]` Middle frame in a read operation (neither first nor last). 466 /// `[NACK]+[SP]` - Final operation overall in the transaction, but not the first operation of its type.
468 NextFrame, 467 Last,
469 /// `[NACK]+[SP]` Last frame overall in this transaction but not the first frame. 468 /// `[NACK]` - Final operation in a read sequence, but not the final operation overall in the transaction.
470 LastFrame, 469 LastNoStop,
471 /// `[NACK]` Last frame in a read operation but not last frame overall in this transaction.
472 LastFrameNoStop,
473} 470}
474 471
475#[allow(dead_code)] 472#[allow(dead_code)]
476impl FrameOptions { 473impl OperationFraming {
477 /// Sends start or repeated start condition before transfer. 474 /// Returns true if a start or repeated start condition should be generated before this operation.
478 fn send_start(self) -> bool { 475 fn send_start(self) -> bool {
479 match self { 476 match self {
480 Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true, 477 Self::FirstAndLast | Self::First | Self::FirstAndNext => true,
481 Self::NextFrame | Self::LastFrame | Self::LastFrameNoStop => false, 478 Self::Next | Self::Last | Self::LastNoStop => false,
482 } 479 }
483 } 480 }
484 481
485 /// Sends stop condition after transfer. 482 /// Returns true if a stop condition should be generated after this operation.
486 fn send_stop(self) -> bool { 483 fn send_stop(self) -> bool {
487 match self { 484 match self {
488 Self::FirstAndLastFrame | Self::LastFrame => true, 485 Self::FirstAndLast | Self::Last => true,
489 Self::FirstFrame | Self::FirstAndNextFrame | Self::NextFrame | Self::LastFrameNoStop => false, 486 Self::First | Self::FirstAndNext | Self::Next | Self::LastNoStop => false,
490 } 487 }
491 } 488 }
492 489
493 /// Sends NACK after last byte received, indicating end of read operation. 490 /// Returns true if NACK should be sent after the last byte received in a read operation.
491 ///
492 /// This signals the end of a read sequence and releases the bus for the master's
493 /// next transmission (or stop condition).
494 fn send_nack(self) -> bool { 494 fn send_nack(self) -> bool {
495 match self { 495 match self {
496 Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true, 496 Self::FirstAndLast | Self::First | Self::Last | Self::LastNoStop => true,
497 Self::FirstAndNextFrame | Self::NextFrame => false, 497 Self::FirstAndNext | Self::Next => false,
498 } 498 }
499 } 499 }
500} 500}
501 501
502/// Iterates over operations in transaction. 502/// Analyzes I2C transaction operations and assigns appropriate framing to each.
503///
504/// This function processes a sequence of I2C operations and determines the correct
505/// framing configuration for each operation to ensure proper I2C protocol compliance.
506/// It handles the complex logic of:
507///
508/// - Generating start conditions for the first operation of each type (read/write)
509/// - Generating stop conditions for the final operation in the entire transaction
510/// - Managing ACK/NACK behavior for read operations, including merging consecutive reads
511/// - Ensuring proper bus handoff between different operation types
512///
513/// **Transaction Contract Compliance:**
514/// The framing assignments ensure compliance with the embedded-hal I2C transaction contract,
515/// where consecutive operations of the same type are logically merged while maintaining
516/// proper protocol boundaries.
503/// 517///
504/// Returns necessary frame options for each operation to uphold the [transaction contract] and have 518/// **Error Handling:**
505/// the right start/stop/(N)ACK conditions on the wire. 519/// Returns an error if any read operation has an empty buffer, as this would create
520/// an invalid I2C transaction that could halt mid-execution.
506/// 521///
507/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction 522/// # Arguments
523/// * `operations` - Mutable slice of I2C operations from embedded-hal
524///
525/// # Returns
526/// An iterator over (operation, framing) pairs, or an error if the transaction is invalid
527///
528/// # Example
529/// ```rust
530/// for (op, framing) in assign_operation_framing(operations)? {
531/// match op {
532/// Operation::Read(buffer) => self.read_with_framing(addr, buffer, framing).await?,
533/// Operation::Write(data) => self.write_with_framing(addr, data, framing).await?,
534/// }
535/// }
536/// ```
508#[allow(dead_code)] 537#[allow(dead_code)]
509fn operation_frames<'a, 'b: 'a>( 538fn assign_operation_framing<'a, 'b: 'a>(
510 operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], 539 operations: &'a mut [embedded_hal_1::i2c::Operation<'b>],
511) -> Result<impl IntoIterator<Item = (&'a mut embedded_hal_1::i2c::Operation<'b>, FrameOptions)>, Error> { 540) -> Result<impl IntoIterator<Item = (&'a mut embedded_hal_1::i2c::Operation<'b>, OperationFraming)>, Error> {
512 use embedded_hal_1::i2c::Operation::{Read, Write}; 541 use embedded_hal_1::i2c::Operation::{Read, Write};
513 542
514 // Check empty read buffer before starting transaction. Otherwise, we would risk halting with an 543 // Validate that no read operations have empty buffers before starting the transaction.
515 // error in the middle of the transaction. 544 // Empty read operations would risk halting with an error mid-transaction.
516 // 545 //
517 // In principle, we could allow empty read frames within consecutive read operations, as long as 546 // 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 547 // sequences as long as the final merged read has at least one byte, but this would
519 // complicated and error-prone. 548 // complicate the logic significantly and create error-prone edge cases.
520 if operations.iter().any(|op| match op { 549 if operations.iter().any(|op| match op {
521 Read(read) => read.is_empty(), 550 Read(read) => read.is_empty(),
522 Write(_) => false, 551 Write(_) => false,
@@ -525,46 +554,52 @@ fn operation_frames<'a, 'b: 'a>(
525 } 554 }
526 555
527 let mut operations = operations.iter_mut().peekable(); 556 let mut operations = operations.iter_mut().peekable();
528 557 let mut next_first_operation = true;
529 let mut next_first_frame = true;
530 558
531 Ok(iter::from_fn(move || { 559 Ok(iter::from_fn(move || {
532 let op = operations.next()?; 560 let current_op = operations.next()?;
533 561
534 // Is `op` first frame of its type? 562 // Determine if this is the first operation of its type (read or write)
535 let first_frame = next_first_frame; 563 let is_first_of_type = next_first_operation;
536 let next_op = operations.peek(); 564 let next_op = operations.peek();
537 565
538 // Get appropriate frame options as combination of the following properties: 566 // Compute the appropriate framing based on three key properties:
539 // 567 //
540 // - For each first operation of its type, generate a (repeated) start condition. 568 // 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. 569 // 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 570 // 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 571 // 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 // 572 //
547 // We check the third property unconditionally, i.e. even for write opeartions. This is okay 573 // The third property is checked for all operations since the resulting framing
548 // because the resulting frame options are identical for write operations. 574 // configurations are identical for write operations regardless of ACK/NACK treatment.
549 let frame = match (first_frame, next_op) { 575 let framing = match (is_first_of_type, next_op) {
550 (true, None) => FrameOptions::FirstAndLastFrame, 576 // First operation of type, and it's also the final operation overall
551 (true, Some(Read(_))) => FrameOptions::FirstAndNextFrame, 577 (true, None) => OperationFraming::FirstAndLast,
552 (true, Some(Write(_))) => FrameOptions::FirstFrame, 578 // First operation of type, next operation is also a read (continue read sequence)
553 // 579 (true, Some(Read(_))) => OperationFraming::FirstAndNext,
554 (false, None) => FrameOptions::LastFrame, 580 // First operation of type, next operation is write (end current sequence)
555 (false, Some(Read(_))) => FrameOptions::NextFrame, 581 (true, Some(Write(_))) => OperationFraming::First,
556 (false, Some(Write(_))) => FrameOptions::LastFrameNoStop, 582
583 // Continuation operation, and it's the final operation overall
584 (false, None) => OperationFraming::Last,
585 // Continuation operation, next operation is also a read (continue read sequence)
586 (false, Some(Read(_))) => OperationFraming::Next,
587 // Continuation operation, next operation is write (end current sequence, no stop)
588 (false, Some(Write(_))) => OperationFraming::LastNoStop,
557 }; 589 };
558 590
559 // Pre-calculate if `next_op` is the first operation of its type. We do this here and not at 591 // 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 592 // This is done here because we consume `current_op` as the iterator value
561 // anymore in the next iteration. 593 // and cannot access it in the next iteration.
562 next_first_frame = match (&op, next_op) { 594 next_first_operation = match (&current_op, next_op) {
595 // No next operation
563 (_, None) => false, 596 (_, None) => false,
597 // Operation type changes: next will be first of its type
564 (Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true, 598 (Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true,
599 // Operation type continues: next will not be first of its type
565 (Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false, 600 (Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false,
566 }; 601 };
567 602
568 Some((op, frame)) 603 Some((current_op, framing))
569 })) 604 }))
570} 605}
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs
index a1ad9caef..7d2b731d5 100644
--- a/embassy-stm32/src/i2c/v1.rs
+++ b/embassy-stm32/src/i2c/v1.rs
@@ -151,7 +151,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
151 Ok(sr1) 151 Ok(sr1)
152 } 152 }
153 153
154 fn write_bytes(&mut self, addr: u8, bytes: &[u8], timeout: Timeout, frame: FrameOptions) -> Result<(), Error> { 154 fn write_bytes(&mut self, addr: u8, bytes: &[u8], timeout: Timeout, frame: OperationFraming) -> Result<(), Error> {
155 if frame.send_start() { 155 if frame.send_start() {
156 // Send a START condition 156 // Send a START condition
157 157
@@ -239,7 +239,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
239 addr: u8, 239 addr: u8,
240 buffer: &mut [u8], 240 buffer: &mut [u8],
241 timeout: Timeout, 241 timeout: Timeout,
242 frame: FrameOptions, 242 frame: OperationFraming,
243 ) -> Result<(), Error> { 243 ) -> Result<(), Error> {
244 let Some((last, buffer)) = buffer.split_last_mut() else { 244 let Some((last, buffer)) = buffer.split_last_mut() else {
245 return Err(Error::Overrun); 245 return Err(Error::Overrun);
@@ -299,12 +299,12 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
299 299
300 /// Blocking read. 300 /// Blocking read.
301 pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> { 301 pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> {
302 self.blocking_read_timeout(addr, read, self.timeout(), FrameOptions::FirstAndLastFrame) 302 self.blocking_read_timeout(addr, read, self.timeout(), OperationFraming::FirstAndLast)
303 } 303 }
304 304
305 /// Blocking write. 305 /// Blocking write.
306 pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> { 306 pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> {
307 self.write_bytes(addr, write, self.timeout(), FrameOptions::FirstAndLastFrame)?; 307 self.write_bytes(addr, write, self.timeout(), OperationFraming::FirstAndLast)?;
308 308
309 // Fallthrough is success 309 // Fallthrough is success
310 Ok(()) 310 Ok(())
@@ -320,8 +320,8 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
320 320
321 let timeout = self.timeout(); 321 let timeout = self.timeout();
322 322
323 self.write_bytes(addr, write, timeout, FrameOptions::FirstFrame)?; 323 self.write_bytes(addr, write, timeout, OperationFraming::First)?;
324 self.blocking_read_timeout(addr, read, timeout, FrameOptions::FirstAndLastFrame)?; 324 self.blocking_read_timeout(addr, read, timeout, OperationFraming::FirstAndLast)?;
325 325
326 Ok(()) 326 Ok(())
327 } 327 }
@@ -334,7 +334,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
334 pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { 334 pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
335 let timeout = self.timeout(); 335 let timeout = self.timeout();
336 336
337 for (op, frame) in operation_frames(operations)? { 337 for (op, frame) in assign_operation_framing(operations)? {
338 match op { 338 match op {
339 Operation::Read(read) => self.blocking_read_timeout(addr, read, timeout, frame)?, 339 Operation::Read(read) => self.blocking_read_timeout(addr, read, timeout, frame)?,
340 Operation::Write(write) => self.write_bytes(addr, write, timeout, frame)?, 340 Operation::Write(write) => self.write_bytes(addr, write, timeout, frame)?,
@@ -356,7 +356,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
356} 356}
357 357
358impl<'d, IM: MasterMode> I2c<'d, Async, IM> { 358impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
359 async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> { 359 async fn write_frame(&mut self, address: u8, write: &[u8], frame: OperationFraming) -> Result<(), Error> {
360 self.info.regs.cr2().modify(|w| { 360 self.info.regs.cr2().modify(|w| {
361 // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for 361 // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for
362 // reception. 362 // reception.
@@ -502,7 +502,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
502 502
503 /// Write. 503 /// Write.
504 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { 504 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
505 self.write_frame(address, write, FrameOptions::FirstAndLastFrame) 505 self.write_frame(address, write, OperationFraming::FirstAndLast)
506 .await?; 506 .await?;
507 507
508 Ok(()) 508 Ok(())
@@ -510,13 +510,13 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
510 510
511 /// Read. 511 /// Read.
512 pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { 512 pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
513 self.read_frame(address, buffer, FrameOptions::FirstAndLastFrame) 513 self.read_frame(address, buffer, OperationFraming::FirstAndLast)
514 .await?; 514 .await?;
515 515
516 Ok(()) 516 Ok(())
517 } 517 }
518 518
519 async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> { 519 async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: OperationFraming) -> Result<(), Error> {
520 if buffer.is_empty() { 520 if buffer.is_empty() {
521 return Err(Error::Overrun); 521 return Err(Error::Overrun);
522 } 522 }
@@ -680,8 +680,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
680 return Err(Error::Overrun); 680 return Err(Error::Overrun);
681 } 681 }
682 682
683 self.write_frame(address, write, FrameOptions::FirstFrame).await?; 683 self.write_frame(address, write, OperationFraming::First).await?;
684 self.read_frame(address, read, FrameOptions::FirstAndLastFrame).await 684 self.read_frame(address, read, OperationFraming::FirstAndLast).await
685 } 685 }
686 686
687 /// Transaction with operations. 687 /// Transaction with operations.
@@ -690,7 +690,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
690 /// 690 ///
691 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction 691 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
692 pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { 692 pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
693 for (op, frame) in operation_frames(operations)? { 693 for (op, frame) in assign_operation_framing(operations)? {
694 match op { 694 match op {
695 Operation::Read(read) => self.read_frame(addr, read, frame).await?, 695 Operation::Read(read) => self.read_frame(addr, read, frame).await?,
696 Operation::Write(write) => self.write_frame(addr, write, frame).await?, 696 Operation::Write(write) => self.write_frame(addr, write, frame).await?,