From 1d30c2e425e39dad1af9ea0513ef0bf9627b9016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 30 Jun 2025 18:12:33 +0200 Subject: [PATCH] Place I2C async interrupt handler into ram, wait for bus_busy to actually clear (#3722) * Place I2C async interrupt handler into ram * Wait for bus_busy to go away * Changelog --- esp-hal/CHANGELOG.md | 1 + esp-hal/src/i2c/master/mod.rs | 25 +++++++++++++++---------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 4e4d5700f..845d24e78 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - RMT `ChannelCreator` methods have been renamed from `configure` to `configure_tx` and `configure_rx` to avoid trait disambiguation issues. (#3505) - The RMT `Error` type has been marked `non_exhaustive` (#3701) - Increase ESP32 DRAM memory region by 16K (#3703) +- The I2C async interrupt handler is now placed into IRAM (#3722) ### Fixed diff --git a/esp-hal/src/i2c/master/mod.rs b/esp-hal/src/i2c/master/mod.rs index 194a0d07d..64ee29941 100644 --- a/esp-hal/src/i2c/master/mod.rs +++ b/esp-hal/src/i2c/master/mod.rs @@ -137,9 +137,11 @@ use crate::{ Pull, interconnect::{self, PeripheralOutput}, }, + handler, interrupt::InterruptHandler, pac::i2c0::{COMD, RegisterBlock}, private, + ram, system::PeripheralGuard, time::{Duration, Instant, Rate}, }; @@ -1209,6 +1211,7 @@ impl embedded_hal_async::i2c::I2c for I2c<'_, Async> { } } +#[ram] fn async_handler(info: &Info, state: &State) { // Disable all interrupts. The I2C Future will check events based on the // interrupt status bits. @@ -2801,13 +2804,9 @@ mod bus_clear { } pub fn poll_completion(&mut self) -> Poll<()> { - if self - .driver - .regs() - .scl_sp_conf() - .read() - .scl_rst_slv_en() - .bit_is_set() + let regs = self.driver.regs(); + if regs.scl_sp_conf().read().scl_rst_slv_en().bit_is_set() + || regs.sr().read().bus_busy().bit_is_set() { Poll::Pending } else { @@ -2913,7 +2912,12 @@ mod bus_clear { let now = Instant::now(); match self.state { - State::Idle => return Poll::Ready(()), + State::Idle => { + if self.driver.regs().sr().read().bus_busy().bit_is_set() { + return Poll::Pending; + } + return Poll::Ready(()); + } _ if now < self.wait => { // Still waiting for the end of the SCL pulse return Poll::Pending; @@ -2923,7 +2927,7 @@ mod bus_clear { sda.set_output_high(true); // STOP, SDA low -> high while SCL is HIGH } self.state = State::Idle; - return Poll::Ready(()); + return Poll::Pending; } State::SendClock(0, false) => { if let (Some(scl), Some(sda)) = (self.scl.as_ref(), self.sda.as_ref()) { @@ -3100,7 +3104,8 @@ crate::peripherals::for_each_i2c_master!( ($inst:ident, $peri:ident, $scl:ident, $sda:ident) => { impl Instance for crate::peripherals::$inst<'_> { fn parts(&self) -> (&Info, &State) { - #[crate::handler] + #[handler] + #[ram] pub(super) fn irq_handler() { async_handler(&PERIPHERAL, &STATE); }