Skip to content

Commit b867072

Browse files
authored
Merge pull request #4812 from xoviat/i2c
i2cv1 slave mode
2 parents 51f8aea + f8f7820 commit b867072

File tree

6 files changed

+1360
-98
lines changed

6 files changed

+1360
-98
lines changed

embassy-stm32/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3636
- feat: timer: add ability to set master mode
3737
- fix: sdmmc: don't wait for DBCKEND flag on sdmmc_v2 devices as it never fires (Fixes #4723)
3838
- fix: usart: fix race condition in ringbuffered usart
39+
- feat: Add I2C MultiMaster (Slave) support for I2C v1
3940
- feat: stm32/fdcan: add ability to control automatic recovery from bus off ([#4821](https://github.com/embassy-rs/embassy/pull/4821))
4041
- low-power: update rtc api to allow reconfig
4142
- adc: consolidate ringbuffer

embassy-stm32/src/i2c/config.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::gpio::{AfType, OutputType, Speed};
44
use crate::time::Hertz;
55

66
#[repr(u8)]
7-
#[derive(Copy, Clone)]
7+
#[derive(Debug, Copy, Clone)]
88
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
99
/// Bits of the I2C OA2 register to mask out.
1010
pub enum AddrMask {
@@ -60,7 +60,7 @@ impl Address {
6060
}
6161
}
6262

63-
#[derive(Copy, Clone)]
63+
#[derive(Debug, Copy, Clone)]
6464
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6565
/// The second Own Address register.
6666
pub struct OA2 {
@@ -70,7 +70,7 @@ pub struct OA2 {
7070
pub mask: AddrMask,
7171
}
7272

73-
#[derive(Copy, Clone)]
73+
#[derive(Debug, Copy, Clone)]
7474
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
7575
/// The Own Address(es) of the I2C peripheral.
7676
pub enum OwnAddresses {
@@ -88,7 +88,7 @@ pub enum OwnAddresses {
8888
}
8989

9090
/// Slave Configuration
91-
#[derive(Copy, Clone)]
91+
#[derive(Debug, Copy, Clone)]
9292
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9393
pub struct SlaveAddrConfig {
9494
/// Target Address(es)

embassy-stm32/src/i2c/mod.rs

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
476477
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.
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)]
509533
fn 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

Comments
 (0)