@@ -219,6 +219,7 @@ impl<'d, M: Mode> I2c<'d, M, Master> {
219219 sda,
220220 } ,
221221 } ;
222+
222223 this. enable_and_init ( config) ;
223224
224225 this
@@ -437,15 +438,15 @@ impl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> {
437438
438439/// Frame type in I2C transaction.
439440///
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
441442/// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an
442443/// ACK or NACK after the last byte received.
443444///
444445/// For write operations, the following options are identical because they differ only in the (N)ACK
445446/// treatment relevant for read operations:
446447///
447- /// - `FirstFrame` and `FirstAndNextFrame`
448- /// - `NextFrame` and `LastFrameNoStop`
448+ /// - `FirstFrame` and `FirstAndNextFrame` behave identically for writes
449+ /// - `NextFrame` and `LastFrameNoStop` behave identically for writes
449450///
450451/// Abbreviations used below:
451452///
@@ -474,23 +475,26 @@ enum FrameOptions {
474475
475476#[ allow( dead_code) ]
476477impl 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 .
478479 fn send_start ( self ) -> bool {
479480 match self {
480481 Self :: FirstAndLastFrame | Self :: FirstFrame | Self :: FirstAndNextFrame => true ,
481482 Self :: NextFrame | Self :: LastFrame | Self :: LastFrameNoStop => false ,
482483 }
483484 }
484485
485- /// Sends stop condition after transfer .
486+ /// Returns true if a stop condition should be generated after this operation .
486487 fn send_stop ( self ) -> bool {
487488 match self {
488489 Self :: FirstAndLastFrame | Self :: LastFrame => true ,
489490 Self :: FirstFrame | Self :: FirstAndNextFrame | Self :: NextFrame | Self :: LastFrameNoStop => false ,
490491 }
491492 }
492493
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).
494498 fn send_nack ( self ) -> bool {
495499 match self {
496500 Self :: FirstAndLastFrame | Self :: FirstFrame | Self :: LastFrame | Self :: LastFrameNoStop => true ,
@@ -499,24 +503,44 @@ impl FrameOptions {
499503 }
500504}
501505
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.
503521///
504- /// Returns necessary frame options for each operation to uphold the [transaction contract] and have
505- /// the right start/stop/(N)ACK conditions on the wire.
522+ /// **Error Handling:**
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
506531///
507- /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
508532#[ allow( dead_code) ]
509533fn operation_frames < ' a , ' b : ' a > (
510534 operations : & ' a mut [ embedded_hal_1:: i2c:: Operation < ' b > ] ,
511535) -> Result < impl IntoIterator < Item = ( & ' a mut embedded_hal_1:: i2c:: Operation < ' b > , FrameOptions ) > , Error > {
512536 use embedded_hal_1:: i2c:: Operation :: { Read , Write } ;
513537
514- // Check empty read buffer before starting transaction. Otherwise, we would risk halting with an
515- // error in the middle of the transaction.
538+ // Validate that no read operations have empty buffers before starting the transaction.
539+ // Empty read operations would risk halting with an error mid- transaction.
516540 //
517- // In principle, we could allow empty read frames within consecutive read operations, as long as
518- // at least one byte remains in the final ( merged) read operation, but that makes the logic more
519- // complicated and error-prone.
541+ // Note: We could theoretically allow empty read operations within consecutive read
542+ // sequences as long as the final merged read has at least one byte, but this would
543+ // complicate the logic significantly and create error-prone edge cases .
520544 if operations. iter ( ) . any ( |op| match op {
521545 Read ( read) => read. is_empty ( ) ,
522546 Write ( _) => false ,
@@ -525,46 +549,52 @@ fn operation_frames<'a, 'b: 'a>(
525549 }
526550
527551 let mut operations = operations. iter_mut ( ) . peekable ( ) ;
528-
529- let mut next_first_frame = true ;
552+ let mut next_first_operation = true ;
530553
531554 Ok ( iter:: from_fn ( move || {
532- let op = operations. next ( ) ?;
555+ let current_op = operations. next ( ) ?;
533556
534- // Is `op` first frame of its type?
535- let first_frame = next_first_frame ;
557+ // Determine if this is the first operation of its type (read or write)
558+ let is_first_of_type = next_first_operation ;
536559 let next_op = operations. peek ( ) ;
537560
538- // Get appropriate frame options as combination of the following properties:
561+ // Compute the appropriate frame based on three key properties:
539562 //
540- // - For each first operation of its type, generate a (repeated) start condition.
541- // - For the last operation overall in the entire transaction, generate a stop condition.
542- // - For read operations, check the next operation: if it is also a read operation, we merge
543- // these and send ACK for all bytes in the current operation; send NACK only for the final
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).
563+ // 1. **Start Condition**: Generate (repeated) start for first operation of each type
564+ // 2. **Stop Condition**: Generate stop for the final operation in the entire transaction
565+ // 3. **ACK/NACK for Reads**: For read operations, send ACK if more reads follow in the
566+ // sequence, or NACK for the final read in a sequence (before write or transaction end)
546567 //
547- // We check the third property unconditionally, i.e. even for write opeartions. This is okay
548- // because the resulting frame options are identical for write operations.
549- let frame = match ( first_frame, next_op) {
568+ // The third property is checked for all operations since the resulting frame
569+ // configurations are identical for write operations regardless of ACK/NACK treatment.
570+ let frame = match ( is_first_of_type, next_op) {
571+ // First operation of type, and it's also the final operation overall
550572 ( true , None ) => FrameOptions :: FirstAndLastFrame ,
573+ // First operation of type, next operation is also a read (continue read sequence)
551574 ( true , Some ( Read ( _) ) ) => FrameOptions :: FirstAndNextFrame ,
575+ // First operation of type, next operation is write (end current sequence)
552576 ( true , Some ( Write ( _) ) ) => FrameOptions :: FirstFrame ,
553- //
577+
578+ // Continuation operation, and it's the final operation overall
554579 ( false , None ) => FrameOptions :: LastFrame ,
580+ // Continuation operation, next operation is also a read (continue read sequence)
555581 ( false , Some ( Read ( _) ) ) => FrameOptions :: NextFrame ,
582+ // Continuation operation, next operation is write (end current sequence, no stop)
556583 ( false , Some ( Write ( _) ) ) => FrameOptions :: LastFrameNoStop ,
557584 } ;
558585
559- // Pre-calculate if `next_op` is the first operation of its type. We do this here and not at
560- // the beginning of the loop because we hand out `op` as iterator value and cannot access it
561- // anymore in the next iteration.
562- next_first_frame = match ( & op, next_op) {
586+ // Pre-calculate whether the next operation will be the first of its type.
587+ // This is done here because we consume `current_op` as the iterator value
588+ // and cannot access it in the next iteration.
589+ next_first_operation = match ( & current_op, next_op) {
590+ // No next operation
563591 ( _, None ) => false ,
592+ // Operation type changes: next will be first of its type
564593 ( Read ( _) , Some ( Write ( _) ) ) | ( Write ( _) , Some ( Read ( _) ) ) => true ,
594+ // Operation type continues: next will not be first of its type
565595 ( Read ( _) , Some ( Read ( _) ) ) | ( Write ( _) , Some ( Write ( _) ) ) => false ,
566596 } ;
567597
568- Some ( ( op , frame) )
598+ Some ( ( current_op , frame) )
569599 } ) )
570600}
0 commit comments