diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 2a99d0a96d..6c36bc1089 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -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 diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 6b213484c6..933cca9cb1 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -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 { - let state = self.state; + /// This method blocks until the slave address is matched by a master. + pub fn blocking_listen(&mut self) -> Result { + 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 { + 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. @@ -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 { + 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.