Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions embassy-stm32/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- feat: stm32/lcd: added implementation
- change: add error messages to can timing calculations ([#4961](https://github.com/embassy-rs/embassy/pull/4961))
- fix: stm32/i2c v2: add stop flag on stop received
- stm32: Add blocking_listen for blocking I2C driver

## 0.4.0 - 2025-08-26

Expand Down
91 changes: 62 additions & 29 deletions embassy-stm32/src/i2c/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1675,42 +1675,50 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> {

/// Listen for incoming I2C messages.
///
/// The listen method is an asynchronous method but it does not require DMA to be asynchronous.
pub async fn listen(&mut self) -> Result<SlaveCommand, Error> {
let state = self.state;
/// This method blocks until the slave address is matched by a master.
pub fn blocking_listen(&mut self) -> Result<SlaveCommand, Error> {
let timeout = self.timeout();

self.info.regs.cr1().modify(|reg| {
reg.set_addrie(true);
trace!("Enable ADDRIE");
});

poll_fn(|cx| {
state.waker.register(cx.waker());
loop {
let isr = self.info.regs.isr().read();
if !isr.addr() {
Poll::Pending
} else {
trace!("ADDR triggered (address match)");
// we do not clear the address flag here as it will be cleared by the dma read/write
// if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it
match isr.dir() {
i2c::vals::Dir::WRITE => {
trace!("DIR: write");
Poll::Ready(Ok(SlaveCommand {
kind: SlaveCommandKind::Write,
address: self.determine_matched_address()?,
}))
}
i2c::vals::Dir::READ => {
trace!("DIR: read");
Poll::Ready(Ok(SlaveCommand {
kind: SlaveCommandKind::Read,
address: self.determine_matched_address()?,
}))
}
}
if isr.addr() {
break;
}
})
.await
timeout.check()?;
}

trace!("ADDR triggered (address match)");

// we do not clear the address flag here as it will be cleared by the dma read/write
// if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it
self.slave_command()
}

/// Determine the received slave command.
fn slave_command(&self) -> Result<SlaveCommand, Error> {
let isr = self.info.regs.isr().read();

match isr.dir() {
i2c::vals::Dir::WRITE => {
trace!("DIR: write");
Ok(SlaveCommand {
kind: SlaveCommandKind::Write,
address: self.determine_matched_address()?,
})
}
i2c::vals::Dir::READ => {
trace!("DIR: read");
Ok(SlaveCommand {
kind: SlaveCommandKind::Read,
address: self.determine_matched_address()?,
})
}
}
}

/// Respond to a write command.
Expand All @@ -1729,6 +1737,31 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> {
}

impl<'d> I2c<'d, Async, MultiMaster> {
/// Listen for incoming I2C messages.
///
/// The listen method is an asynchronous method but it does not require DMA to be asynchronous.
pub async fn listen(&mut self) -> Result<SlaveCommand, Error> {
let state = self.state;
self.info.regs.cr1().modify(|reg| {
reg.set_addrie(true);
trace!("Enable ADDRIE");
});

poll_fn(|cx| {
state.waker.register(cx.waker());
let isr = self.info.regs.isr().read();
if !isr.addr() {
Poll::Pending
} else {
trace!("ADDR triggered (address match)");
// we do not clear the address flag here as it will be cleared by the dma read/write
// if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it
Poll::Ready(self.slave_command())
}
})
.await
}

/// Respond to a write command.
///
/// Returns the total number of bytes received.
Expand Down