From 291d81af2e25809379908418a80c3d521ce92c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 31 Oct 2024 12:13:39 +0100 Subject: [PATCH] Move some code out into versioned modules --- esp-hal/src/i2c/mod.rs | 948 +++++++----------------- esp-hal/src/i2c/version/mod.rs | 14 + esp-hal/src/i2c/version/v1.rs | 144 ++++ esp-hal/src/i2c/version/v2.rs | 89 +++ esp-hal/src/i2c/version/v2_v3_common.rs | 162 ++++ esp-hal/src/i2c/version/v3.rs | 121 +++ 6 files changed, 810 insertions(+), 668 deletions(-) create mode 100644 esp-hal/src/i2c/version/mod.rs create mode 100644 esp-hal/src/i2c/version/v1.rs create mode 100644 esp-hal/src/i2c/version/v2.rs create mode 100644 esp-hal/src/i2c/version/v2_v3_common.rs create mode 100644 esp-hal/src/i2c/version/v3.rs diff --git a/esp-hal/src/i2c/mod.rs b/esp-hal/src/i2c/mod.rs index f15c8fdd3b..eb039b6e67 100644 --- a/esp-hal/src/i2c/mod.rs +++ b/esp-hal/src/i2c/mod.rs @@ -52,16 +52,14 @@ //! ``` //! [`embedded-hal`]: https://crates.io/crates/embedded-hal +mod version; + use core::marker::PhantomData; -#[cfg(not(esp32))] -use core::{ - pin::Pin, - task::{Context, Poll}, -}; use embassy_sync::waitqueue::AtomicWaker; use embedded_hal::i2c::Operation as EhalOperation; use fugit::HertzU32; +use version::I2C_CHUNK_SIZE; use crate::{ clock::Clocks, @@ -90,13 +88,6 @@ cfg_if::cfg_if! { } } -// Chunk writes/reads by this size -#[cfg(any(esp32, esp32s2))] -const I2C_CHUNK_SIZE: usize = 32; - -#[cfg(not(any(esp32, esp32s2)))] -const I2C_CHUNK_SIZE: usize = 254; - // on ESP32 there is a chance to get trapped in `wait_for_completion` forever const MAX_ITERATIONS: u32 = 1_000_000; @@ -332,19 +323,22 @@ where let i2c = i2c.with_sda(sda).with_scl(scl); - i2c.info().setup(frequency, None); + i2c.driver().setup(frequency, None); i2c } - fn info(&self) -> &Info { - self.i2c.info() + fn driver(&self) -> Driver<'_> { + Driver { + info: self.i2c.info(), + state: self.i2c.state(), + } } fn internal_recover(&self) { PeripheralClockControl::reset(self.i2c.peripheral()); PeripheralClockControl::enable(self.i2c.peripheral()); - self.info().setup(self.frequency, self.timeout); + self.driver().setup(self.frequency, self.timeout); } /// Set the I2C timeout. @@ -352,7 +346,7 @@ where // timeout, and just what exactly is a timeout in this context? pub fn with_timeout(mut self, timeout: Option) -> Self { self.timeout = timeout; - self.info().setup(self.frequency, self.timeout); + self.driver().setup(self.frequency, self.timeout); self } @@ -371,16 +365,16 @@ where let next_op = op_iter.peek().map(|v| v.kind()); let kind = op.kind(); // Clear all I2C interrupts - self.info().clear_all_interrupts(); + self.driver().clear_all_interrupts(); - let cmd_iterator = &mut self.info().register_block().comd_iter(); + let cmd_iterator = &mut self.driver().info.register_block().comd_iter(); match op { Operation::Write(buffer) => { // execute a write operation: // - issue START/RSTART if op is different from previous // - issue STOP if op is the last one - self.info() - .write_operation( + self.driver() + .write_operation_blocking( address, buffer, !matches!(last_op, Some(OpKind::Write)), @@ -394,8 +388,8 @@ where // - issue START/RSTART if op is different from previous // - issue STOP if op is the last one // - will_continue is true if there is another read operation next - self.info() - .read_operation( + self.driver() + .read_operation_blocking( address, buffer, !matches!(last_op, Some(OpKind::Read)), @@ -414,14 +408,14 @@ where } fn with_sda(self, sda: impl Peripheral

+ 'd) -> Self { - let info = self.info(); + let info = self.driver().info; let input = info.sda_input; let output = info.sda_output; self.with_pin(sda, input, output) } fn with_scl(self, scl: impl Peripheral

+ 'd) -> Self { - let info = self.info(); + let info = self.driver().info; let input = info.scl_input; let output = info.scl_output; self.with_pin(scl, input, output) @@ -482,7 +476,7 @@ where /// Configures the I2C peripheral to operate in asynchronous mode. pub fn into_async(mut self) -> I2c<'d, Async, T> { - self.set_interrupt_handler(self.info().async_handler); + self.set_interrupt_handler(self.driver().info.async_handler); I2c { i2c: self.i2c, @@ -497,12 +491,12 @@ where let chunk_count = buffer.len().div_ceil(I2C_CHUNK_SIZE); for (idx, chunk) in buffer.chunks_mut(I2C_CHUNK_SIZE).enumerate() { // Clear all I2C interrupts - self.info().clear_all_interrupts(); + self.driver().clear_all_interrupts(); - let cmd_iterator = &mut self.info().register_block().comd_iter(); + let cmd_iterator = &mut self.driver().info.register_block().comd_iter(); - self.info() - .read_operation( + self.driver() + .read_operation_blocking( address, chunk, idx == 0, @@ -521,12 +515,12 @@ where let chunk_count = buffer.len().div_ceil(I2C_CHUNK_SIZE); for (idx, chunk) in buffer.chunks(I2C_CHUNK_SIZE).enumerate() { // Clear all I2C interrupts - self.info().clear_all_interrupts(); + self.driver().clear_all_interrupts(); - let cmd_iterator = &mut self.info().register_block().comd_iter(); + let cmd_iterator = &mut self.driver().info.register_block().comd_iter(); - self.info() - .write_operation( + self.driver() + .write_operation_blocking( address, chunk, idx == 0, @@ -552,12 +546,12 @@ where for (idx, chunk) in write_buffer.chunks(I2C_CHUNK_SIZE).enumerate() { // Clear all I2C interrupts - self.info().clear_all_interrupts(); + self.driver().clear_all_interrupts(); - let cmd_iterator = &mut self.info().register_block().comd_iter(); + let cmd_iterator = &mut self.driver().info.register_block().comd_iter(); - self.info() - .write_operation( + self.driver() + .write_operation_blocking( address, chunk, idx == 0, @@ -569,12 +563,12 @@ where for (idx, chunk) in read_buffer.chunks_mut(I2C_CHUNK_SIZE).enumerate() { // Clear all I2C interrupts - self.info().clear_all_interrupts(); + self.driver().clear_all_interrupts(); - let cmd_iterator = &mut self.info().register_block().comd_iter(); + let cmd_iterator = &mut self.driver().info.register_block().comd_iter(); - self.info() - .read_operation( + self.driver() + .read_operation_blocking( address, chunk, idx == 0, @@ -622,7 +616,7 @@ where T: Instance, { fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { - let interrupt = self.info().interrupt; + let interrupt = self.driver().info.interrupt; for core in Cpu::other() { crate::interrupt::disable(core, interrupt); } @@ -631,128 +625,14 @@ where } } -#[cfg_attr(esp32, allow(dead_code))] -pub(crate) enum Event { - EndDetect, - TxComplete, - #[cfg(not(any(esp32, esp32s2)))] - TxFifoWatermark, -} - -#[cfg(not(esp32))] -#[must_use = "futures do nothing unless you `.await` or poll them"] -struct I2cFuture<'a> { - event: Event, - info: &'a Info, - state: &'a State, -} - -#[cfg(not(esp32))] -impl<'a> I2cFuture<'a> { - pub fn new(event: Event, instance: &'a impl Instance) -> Self { - instance.info().register_block().int_ena().modify(|_, w| { - let w = match event { - Event::EndDetect => w.end_detect().set_bit(), - Event::TxComplete => w.trans_complete().set_bit(), - #[cfg(not(any(esp32, esp32s2)))] - Event::TxFifoWatermark => w.txfifo_wm().set_bit(), - }; - - w.arbitration_lost().set_bit(); - w.time_out().set_bit(); - - #[cfg(esp32)] - w.ack_err().set_bit(); - #[cfg(not(esp32))] - w.nack().set_bit(); - - w - }); - - Self { - event, - state: instance.state(), - info: instance.info(), - } - } - - fn event_bit_is_clear(&self) -> bool { - let r = self.info.register_block().int_ena().read(); - - match self.event { - Event::EndDetect => r.end_detect().bit_is_clear(), - Event::TxComplete => r.trans_complete().bit_is_clear(), - #[cfg(not(any(esp32, esp32s2)))] - Event::TxFifoWatermark => r.txfifo_wm().bit_is_clear(), - } - } - - fn check_error(&self) -> Result<(), Error> { - let r = self.info.register_block().int_raw().read(); - - if r.arbitration_lost().bit_is_set() { - return Err(Error::ArbitrationLost); - } - - if r.time_out().bit_is_set() { - return Err(Error::TimeOut); - } - - #[cfg(not(esp32))] - if r.nack().bit_is_set() { - return Err(Error::AckCheckFailed); - } - - #[cfg(esp32)] - if r.ack_err().bit_is_set() { - return Err(Error::AckCheckFailed); - } - - #[cfg(not(esp32))] - if r.trans_complete().bit_is_set() - && self - .info - .register_block() - .sr() - .read() - .resp_rec() - .bit_is_clear() - { - return Err(Error::AckCheckFailed); - } - - Ok(()) - } -} - -#[cfg(not(esp32))] -impl core::future::Future for I2cFuture<'_> { - type Output = Result<(), Error>; - - fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { - self.state.waker.register(ctx.waker()); - - let error = self.check_error(); - - if error.is_err() { - return Poll::Ready(error); - } - - if self.event_bit_is_clear() { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - } -} - impl<'d, T> I2c<'d, Async, T> where T: Instance, { /// Configure the I2C peripheral to operate in blocking mode. pub fn into_blocking(self) -> I2c<'d, Blocking, T> { - crate::interrupt::disable(Cpu::current(), self.i2c.interrupt()); + let interrupt = self.driver().info.interrupt; + crate::interrupt::disable(Cpu::current(), interrupt); I2c { i2c: self.i2c, @@ -762,229 +642,24 @@ where } } - #[cfg(any(esp32, esp32s2))] - async fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { - if buffer.len() > 32 { - panic!("On ESP32 and ESP32-S2 the max I2C read is limited to 32 bytes"); - } - - self.wait_for_completion(false).await?; - - for byte in buffer.iter_mut() { - *byte = read_fifo(self.info().register_block()); - } - - Ok(()) - } - - #[cfg(not(any(esp32, esp32s2)))] - async fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { - self.info().read_all_from_fifo(buffer) - } - - #[cfg(any(esp32, esp32s2))] - async fn write_remaining_tx_fifo(&self, start_index: usize, bytes: &[u8]) -> Result<(), Error> { - if start_index >= bytes.len() { - return Ok(()); - } - - for b in bytes { - write_fifo(self.info().register_block(), *b); - self.info().check_errors()?; - } - - Ok(()) - } - - #[cfg(not(any(esp32, esp32s2)))] - async fn write_remaining_tx_fifo(&self, start_index: usize, bytes: &[u8]) -> Result<(), Error> { - let mut index = start_index; - loop { - self.info().check_errors()?; - - I2cFuture::new(Event::TxFifoWatermark, &*self.i2c).await?; - - self.info() - .register_block() - .int_clr() - .write(|w| w.txfifo_wm().clear_bit_by_one()); - - I2cFuture::new(Event::TxFifoWatermark, &*self.i2c).await?; - - if index >= bytes.len() { - break Ok(()); - } - - write_fifo(self.info().register_block(), bytes[index]); - index += 1; - } - } - - #[cfg(not(esp32))] - async fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> { - self.info().check_errors()?; - - if end_only { - I2cFuture::new(Event::EndDetect, &*self.i2c).await?; - } else { - let res = embassy_futures::select::select( - I2cFuture::new(Event::TxComplete, &*self.i2c), - I2cFuture::new(Event::EndDetect, &*self.i2c), - ) - .await; - - match res { - embassy_futures::select::Either::First(res) => res?, - embassy_futures::select::Either::Second(res) => res?, - } - } - self.info().check_all_commands_done()?; - - Ok(()) - } - - #[cfg(esp32)] - async fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> { - // for ESP32 we need a timeout here but wasting a timer seems unnecessary - // given the short time we spend here - - let mut tout = MAX_ITERATIONS / 10; // adjust the timeout because we are yielding in the loop - loop { - let interrupts = self.info().register_block().int_raw().read(); - - self.info().check_errors()?; - - // Handle completion cases - // A full transmission was completed (either a STOP condition or END was - // processed) - if (!end_only && interrupts.trans_complete().bit_is_set()) - || interrupts.end_detect().bit_is_set() - { - break; - } - - tout -= 1; - if tout == 0 { - return Err(Error::TimeOut); - } - - embassy_futures::yield_now().await; - } - self.info().check_all_commands_done()?; - Ok(()) - } - - /// Executes an async I2C write operation. - /// - `addr` is the address of the slave device. - /// - `bytes` is the data two be sent. - /// - `start` indicates whether the operation should start by a START - /// condition and sending the address. - /// - `stop` indicates whether the operation should end with a STOP - /// condition. - /// - `cmd_iterator` is an iterator over the command registers. - async fn write_operation<'a, I>( - &self, - address: u8, - bytes: &[u8], - start: bool, - stop: bool, - cmd_iterator: &mut I, - ) -> Result<(), Error> - where - I: Iterator, - { - // Short circuit for zero length writes without start or end as that would be an - // invalid operation write lengths in the TRM (at least for ESP32-S3) are 1-255 - if bytes.is_empty() && !start && !stop { - return Ok(()); - } - - // Reset FIFO and command list - self.info().reset_fifo(); - self.info().reset_command_list(); - if start { - add_cmd(cmd_iterator, Command::Start)?; - } - self.i2c - .info() - .setup_write(address, bytes, start, cmd_iterator)?; - add_cmd( - cmd_iterator, - if stop { Command::Stop } else { Command::End }, - )?; - let index = self.info().fill_tx_fifo(bytes); - self.info().start_transmission(); - - // Fill the FIFO with the remaining bytes: - self.write_remaining_tx_fifo(index, bytes).await?; - self.wait_for_completion(!stop).await?; - Ok(()) - } - - /// Executes an async I2C read operation. - /// - `addr` is the address of the slave device. - /// - `buffer` is the buffer to store the read data. - /// - `start` indicates whether the operation should start by a START - /// condition and sending the address. - /// - `stop` indicates whether the operation should end with a STOP - /// condition. - /// - `will_continue` indicates whether there is another read operation - /// following this one and we should not nack the last byte. - /// - `cmd_iterator` is an iterator over the command registers. - async fn read_operation<'a, I>( - &self, - address: u8, - buffer: &mut [u8], - start: bool, - stop: bool, - will_continue: bool, - cmd_iterator: &mut I, - ) -> Result<(), Error> - where - I: Iterator, - { - // Short circuit for zero length reads as that would be an invalid operation - // read lengths in the TRM (at least for ESP32-S3) are 1-255 - if buffer.is_empty() { - return Ok(()); - } - - // Reset FIFO and command list - self.info().reset_fifo(); - self.info().reset_command_list(); - if start { - add_cmd(cmd_iterator, Command::Start)?; - } - self.i2c - .info() - .setup_read(address, buffer, start, will_continue, cmd_iterator)?; - add_cmd( - cmd_iterator, - if stop { Command::Stop } else { Command::End }, - )?; - self.info().start_transmission(); - self.read_all_from_fifo(buffer).await?; - self.wait_for_completion(!stop).await?; - Ok(()) - } - /// Writes bytes to slave with address `address` pub async fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { let chunk_count = buffer.len().div_ceil(I2C_CHUNK_SIZE); for (idx, chunk) in buffer.chunks(I2C_CHUNK_SIZE).enumerate() { // Clear all I2C interrupts - self.info().clear_all_interrupts(); + self.driver().clear_all_interrupts(); - let cmd_iterator = &mut self.info().register_block().comd_iter(); + let cmd_iterator = &mut self.driver().info.register_block().comd_iter(); - self.write_operation( - address, - chunk, - idx == 0, - idx == chunk_count - 1, - cmd_iterator, - ) - .await?; + self.driver() + .write_operation( + address, + chunk, + idx == 0, + idx == chunk_count - 1, + cmd_iterator, + ) + .await?; } Ok(()) @@ -995,19 +670,20 @@ where let chunk_count = buffer.len().div_ceil(I2C_CHUNK_SIZE); for (idx, chunk) in buffer.chunks_mut(I2C_CHUNK_SIZE).enumerate() { // Clear all I2C interrupts - self.info().clear_all_interrupts(); + self.driver().clear_all_interrupts(); - let cmd_iterator = &mut self.info().register_block().comd_iter(); + let cmd_iterator = &mut self.driver().info.register_block().comd_iter(); - self.read_operation( - address, - chunk, - idx == 0, - idx == chunk_count - 1, - idx < chunk_count - 1, - cmd_iterator, - ) - .await?; + self.driver() + .read_operation( + address, + chunk, + idx == 0, + idx == chunk_count - 1, + idx < chunk_count - 1, + cmd_iterator, + ) + .await?; } Ok(()) @@ -1025,35 +701,37 @@ where let read_count = read_buffer.len().div_ceil(I2C_CHUNK_SIZE); for (idx, chunk) in write_buffer.chunks(I2C_CHUNK_SIZE).enumerate() { // Clear all I2C interrupts - self.info().clear_all_interrupts(); + self.driver().clear_all_interrupts(); - let cmd_iterator = &mut self.info().register_block().comd_iter(); + let cmd_iterator = &mut self.driver().info.register_block().comd_iter(); - self.write_operation( - address, - chunk, - idx == 0, - idx == write_count - 1 && read_count == 0, - cmd_iterator, - ) - .await?; + self.driver() + .write_operation( + address, + chunk, + idx == 0, + idx == write_count - 1 && read_count == 0, + cmd_iterator, + ) + .await?; } for (idx, chunk) in read_buffer.chunks_mut(I2C_CHUNK_SIZE).enumerate() { // Clear all I2C interrupts - self.info().clear_all_interrupts(); + self.driver().clear_all_interrupts(); - let cmd_iterator = &mut self.info().register_block().comd_iter(); + let cmd_iterator = &mut self.driver().info.register_block().comd_iter(); - self.read_operation( - address, - chunk, - idx == 0, - idx == read_count - 1, - idx < read_count - 1, - cmd_iterator, - ) - .await?; + self.driver() + .read_operation( + address, + chunk, + idx == 0, + idx == read_count - 1, + idx < read_count - 1, + cmd_iterator, + ) + .await?; } Ok(()) @@ -1102,37 +780,39 @@ where let next_op = op_iter.peek().map(|v| v.kind()); let kind = op.kind(); // Clear all I2C interrupts - self.info().clear_all_interrupts(); + self.driver().clear_all_interrupts(); - let cmd_iterator = &mut self.info().register_block().comd_iter(); + let cmd_iterator = &mut self.driver().info.register_block().comd_iter(); match op { Operation::Write(buffer) => { // execute a write operation: // - issue START/RSTART if op is different from previous // - issue STOP if op is the last one - self.write_operation( - address, - buffer, - !matches!(last_op, Some(OpKind::Write)), - next_op.is_none(), - cmd_iterator, - ) - .await?; + self.driver() + .write_operation( + address, + buffer, + !matches!(last_op, Some(OpKind::Write)), + next_op.is_none(), + cmd_iterator, + ) + .await?; } Operation::Read(buffer) => { // execute a read operation: // - issue START/RSTART if op is different from previous // - issue STOP if op is the last one // - will_continue is true if there is another read operation next - self.read_operation( - address, - buffer, - !matches!(last_op, Some(OpKind::Read)), - next_op.is_none(), - matches!(next_op, Some(OpKind::Read)), - cmd_iterator, - ) - .await?; + self.driver() + .read_operation( + address, + buffer, + !matches!(last_op, Some(OpKind::Read)), + next_op.is_none(), + matches!(next_op, Some(OpKind::Read)), + cmd_iterator, + ) + .await?; } } @@ -1331,11 +1011,19 @@ impl Info { pub fn register_block(&self) -> &RegisterBlock { unsafe { &*self.register_block } } +} + +#[allow(dead_code)] // Some versions don't need `state` +struct Driver<'a> { + info: &'a Info, + state: &'a State, +} +impl Driver<'_> { /// Configures the I2C peripheral with the specified frequency, clocks, and /// optional timeout. fn setup(&self, frequency: HertzU32, timeout: Option) { - self.register_block().ctr().write(|w| { + self.info.register_block().ctr().write(|w| { // Set I2C controller to master mode w.ms_mode().set_bit(); // Use open drain output for SDA and SCL @@ -1349,13 +1037,14 @@ impl Info { }); #[cfg(esp32s2)] - self.register_block() + self.info + .register_block() .ctr() .modify(|_, w| w.ref_always_on().set_bit()); // Configure filter // FIXME if we ever change this we need to adapt `set_frequency` for ESP32 - set_filter(self.register_block(), Some(7), Some(7)); + set_filter(self.info.register_block(), Some(7), Some(7)); // Configure frequency let clocks = Clocks::get(); @@ -1382,12 +1071,14 @@ impl Info { // (the option to reset the FSM is not available // for the ESP32) #[cfg(not(esp32))] - self.register_block() + self.info + .register_block() .ctr() .modify(|_, w| w.fsm_rst().set_bit()); // Clear all I2C interrupts - self.register_block() + self.info + .register_block() .int_clr() .write(|w| unsafe { w.bits(I2C_LL_INTR_MASK) }); @@ -1401,7 +1092,7 @@ impl Info { /// Resets the I2C peripheral's command registers fn reset_command_list(&self) { // Confirm that all commands that were configured were actually executed - for cmd in self.register_block().comd_iter() { + for cmd in self.info.register_block().comd_iter() { cmd.reset(); } } @@ -1464,7 +1155,7 @@ impl Info { let scl_stop_hold_time = hold; configure_clock( - self.register_block(), + self.info.register_block(), 0, scl_low_period, scl_high_period, @@ -1518,7 +1209,7 @@ impl Info { let time_out_en = true; configure_clock( - self.register_block(), + self.info.register_block(), 0, scl_low_period, scl_high_period, @@ -1592,7 +1283,7 @@ impl Info { let time_out_en = true; configure_clock( - self.register_block(), + self.info.register_block(), clkm_div, scl_low_period, scl_high_period, @@ -1651,7 +1342,7 @@ impl Info { if start { // Load address and R/W bit into FIFO write_fifo( - self.register_block(), + self.info.register_block(), addr << 1 | OperationType::Write as u8, ); } @@ -1729,71 +1420,27 @@ impl Info { if start { // Load address and R/W bit into FIFO - write_fifo(self.register_block(), addr << 1 | OperationType::Read as u8); - } - Ok(()) - } - - #[cfg(not(any(esp32, esp32s2)))] - /// Reads all bytes from the RX FIFO. - fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { - // Read bytes from FIFO - // FIXME: Handle case where less data has been provided by the slave than - // requested? Or is this prevented from a protocol perspective? - for byte in buffer.iter_mut() { - loop { - self.check_errors()?; - - let reg = self.register_block().fifo_st().read(); - if reg.rxfifo_raddr().bits() != reg.rxfifo_waddr().bits() { - break; - } - } - - *byte = read_fifo(self.register_block()); - } - - Ok(()) - } - - #[cfg(any(esp32, esp32s2))] - /// Reads all bytes from the RX FIFO. - fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { - // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the - // FIFO apparently it would be possible by using non-fifo mode - // see https://github.com/espressif/arduino-esp32/blob/7e9afe8c5ed7b5bf29624a5cd6e07d431c027b97/cores/esp32/esp32-hal-i2c.c#L615 - - if buffer.len() > 32 { - panic!("On ESP32 and ESP32-S2 the max I2C read is limited to 32 bytes"); - } - - // wait for completion - then we can just read the data from FIFO - // once we change to non-fifo mode to support larger transfers that - // won't work anymore - self.wait_for_completion(false)?; - - // Read bytes from FIFO - // FIXME: Handle case where less data has been provided by the slave than - // requested? Or is this prevented from a protocol perspective? - for byte in buffer.iter_mut() { - *byte = read_fifo(self.register_block()); + write_fifo( + self.info.register_block(), + addr << 1 | OperationType::Read as u8, + ); } - Ok(()) } /// Clears all pending interrupts for the I2C peripheral. fn clear_all_interrupts(&self) { - self.register_block() + self.info + .register_block() .int_clr() .write(|w| unsafe { w.bits(I2C_LL_INTR_MASK) }); } /// Waits for the completion of an I2C transaction. - fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> { + fn wait_for_completion_blocking(&self, end_only: bool) -> Result<(), Error> { let mut tout = MAX_ITERATIONS; loop { - let interrupts = self.register_block().int_raw().read(); + let interrupts = self.info.register_block().int_raw().read(); self.check_errors()?; @@ -1820,7 +1467,7 @@ impl Info { // NOTE: on esp32 executing the end command generates the end_detect interrupt // but does not seem to clear the done bit! So we don't check the done // status of an end command - for cmd_reg in self.register_block().comd_iter() { + for cmd_reg in self.info.register_block().comd_iter() { let cmd = cmd_reg.read(); if cmd.bits() != 0x0 && !cmd.opcode().is_end() && !cmd.command_done().bit_is_set() { @@ -1831,75 +1478,11 @@ impl Info { Ok(()) } - /// Checks for I2C transmission errors and handles them. - /// - /// This function inspects specific I2C-related interrupts to detect errors - /// during communication, such as timeouts, failed acknowledgments, or - /// arbitration loss. If an error is detected, the function handles it - /// by resetting the I2C peripheral to clear the error condition and then - /// returns an appropriate error. - fn check_errors(&self) -> Result<(), Error> { - let interrupts = self.register_block().int_raw().read(); - - // The ESP32 variant has a slightly different interrupt naming - // scheme! - cfg_if::cfg_if! { - if #[cfg(esp32)] { - // Handle error cases - let retval = if interrupts.time_out().bit_is_set() { - Err(Error::TimeOut) - } else if interrupts.ack_err().bit_is_set() { - Err(Error::AckCheckFailed) - } else if interrupts.arbitration_lost().bit_is_set() { - Err(Error::ArbitrationLost) - } else { - Ok(()) - }; - } else { - // Handle error cases - let retval = if interrupts.time_out().bit_is_set() { - Err(Error::TimeOut) - } else if interrupts.nack().bit_is_set() { - Err(Error::AckCheckFailed) - } else if interrupts.arbitration_lost().bit_is_set() { - Err(Error::ArbitrationLost) - } else if interrupts.trans_complete().bit_is_set() && self.register_block().sr().read().resp_rec().bit_is_clear() { - Err(Error::AckCheckFailed) - } else { - Ok(()) - }; - } - } - - if retval.is_err() { - self.reset(); - } - - retval - } - - /// Updates the configuration of the I2C peripheral. - /// - /// This function ensures that the configuration values, such as clock - /// settings, SDA/SCL filtering, timeouts, and other operational - /// parameters, which are configured in other functions, are properly - /// propagated to the I2C hardware. This step is necessary to synchronize - /// the software-configured settings with the peripheral's internal - /// registers, ensuring that the hardware behaves according to the - /// current configuration. - fn update_config(&self) { - // Ensure that the configuration of the peripheral is correctly propagated - // (only necessary for C2, C3, C6, H2 and S3 variant) - #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] - self.register_block() - .ctr() - .modify(|_, w| w.conf_upgate().set_bit()); - } - /// Starts an I2C transmission. fn start_transmission(&self) { // Start transmission - self.register_block() + self.info + .register_block() .ctr() .modify(|_, w| w.trans_start().set_bit()); } @@ -1910,16 +1493,18 @@ impl Info { let mut index = 0; while index < bytes.len() && !self + .info .register_block() .int_raw() .read() .txfifo_ovf() .bit_is_set() { - write_fifo(self.register_block(), bytes[index]); + write_fifo(self.info.register_block(), bytes[index]); index += 1; } if self + .info .register_block() .int_raw() .read() @@ -1927,54 +1512,14 @@ impl Info { .bit_is_set() { index -= 1; - self.register_block() + self.info + .register_block() .int_clr() .write(|w| w.txfifo_ovf().clear_bit_by_one()); } index } - #[cfg(not(any(esp32, esp32s2)))] - /// Writes remaining data from byte slice to the TX FIFO from the specified - /// index. - fn write_remaining_tx_fifo(&self, start_index: usize, bytes: &[u8]) -> Result<(), Error> { - let mut index = start_index; - loop { - self.check_errors()?; - - while !self - .register_block() - .int_raw() - .read() - .txfifo_wm() - .bit_is_set() - { - self.check_errors()?; - } - - self.register_block() - .int_clr() - .write(|w| w.txfifo_wm().clear_bit_by_one()); - - while !self - .register_block() - .int_raw() - .read() - .txfifo_wm() - .bit_is_set() - { - self.check_errors()?; - } - - if index >= bytes.len() { - break Ok(()); - } - - write_fifo(self.register_block(), bytes[index]); - index += 1; - } - } - #[cfg(any(esp32, esp32s2))] /// Fills the TX FIFO with data from the provided slice. fn fill_tx_fifo(&self, bytes: &[u8]) -> usize { @@ -1987,53 +1532,34 @@ impl Info { } for b in bytes { - write_fifo(self.register_block(), *b); + write_fifo(self.info.register_block(), *b); } bytes.len() } - #[cfg(any(esp32, esp32s2))] - /// Writes remaining data from byte slice to the TX FIFO from the specified - /// index. - fn write_remaining_tx_fifo(&self, start_index: usize, bytes: &[u8]) -> Result<(), Error> { - // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the - // FIFO apparently it would be possible by using non-fifo mode - // see https://github.com/espressif/arduino-esp32/blob/7e9afe8c5ed7b5bf29624a5cd6e07d431c027b97/cores/esp32/esp32-hal-i2c.c#L615 - - if start_index >= bytes.len() { - return Ok(()); - } - - // this is only possible when writing the I2C address in release mode - // from [perform_write_read] - for b in bytes { - write_fifo(self.register_block(), *b); - self.check_errors()?; - } - - Ok(()) - } - /// Resets the transmit and receive FIFO buffers #[cfg(not(esp32))] fn reset_fifo(&self) { // First, reset the fifo buffers - self.register_block().fifo_conf().modify(|_, w| unsafe { - w.tx_fifo_rst().set_bit(); - w.rx_fifo_rst().set_bit(); - w.nonfifo_en().clear_bit(); - w.fifo_prt_en().set_bit(); - w.rxfifo_wm_thrhd().bits(1); - w.txfifo_wm_thrhd().bits(8) - }); + self.info + .register_block() + .fifo_conf() + .modify(|_, w| unsafe { + w.tx_fifo_rst().set_bit(); + w.rx_fifo_rst().set_bit(); + w.nonfifo_en().clear_bit(); + w.fifo_prt_en().set_bit(); + w.rxfifo_wm_thrhd().bits(1); + w.txfifo_wm_thrhd().bits(8) + }); - self.register_block().fifo_conf().modify(|_, w| { + self.info.register_block().fifo_conf().modify(|_, w| { w.tx_fifo_rst().clear_bit(); w.rx_fifo_rst().clear_bit() }); - self.register_block().int_clr().write(|w| { + self.info.register_block().int_clr().write(|w| { w.rxfifo_wm().clear_bit_by_one(); w.txfifo_wm().clear_bit_by_one() }); @@ -2045,20 +1571,24 @@ impl Info { #[cfg(esp32)] fn reset_fifo(&self) { // First, reset the fifo buffers - self.register_block().fifo_conf().modify(|_, w| unsafe { - w.tx_fifo_rst().set_bit(); - w.rx_fifo_rst().set_bit(); - w.nonfifo_en().clear_bit(); - w.nonfifo_rx_thres().bits(1); - w.nonfifo_tx_thres().bits(32) - }); + self.info + .register_block() + .fifo_conf() + .modify(|_, w| unsafe { + w.tx_fifo_rst().set_bit(); + w.rx_fifo_rst().set_bit(); + w.nonfifo_en().clear_bit(); + w.nonfifo_rx_thres().bits(1); + w.nonfifo_tx_thres().bits(32) + }); - self.register_block().fifo_conf().modify(|_, w| { + self.info.register_block().fifo_conf().modify(|_, w| { w.tx_fifo_rst().clear_bit(); w.rx_fifo_rst().clear_bit() }); - self.register_block() + self.info + .register_block() .int_clr() .write(|w| w.rxfifo_full().clear_bit_by_one()); } @@ -2071,41 +1601,55 @@ impl Info { /// - `stop` indicates whether the operation should end with a STOP /// condition. /// - `cmd_iterator` is an iterator over the command registers. - fn write_operation<'a, I>( + fn write_operation_blocking<'a>( &self, address: u8, bytes: &[u8], start: bool, stop: bool, - cmd_iterator: &mut I, - ) -> Result<(), Error> - where - I: Iterator, - { + cmd_iterator: &mut impl Iterator, + ) -> Result<(), Error> { // Short circuit for zero length writes without start or end as that would be an // invalid operation write lengths in the TRM (at least for ESP32-S3) are 1-255 if bytes.is_empty() && !start && !stop { return Ok(()); } - // Reset FIFO and command list - self.reset_fifo(); - self.reset_command_list(); + let index = self.start_write_operation(address, bytes, start, stop, cmd_iterator)?; - if start { - add_cmd(cmd_iterator, Command::Start)?; + // Fill the FIFO with the remaining bytes: + self.write_remaining_tx_fifo_blocking(index, bytes)?; + self.wait_for_completion_blocking(!stop)?; + Ok(()) + } + + /// Executes an I2C write operation. + /// - `addr` is the address of the slave device. + /// - `bytes` is the data two be sent. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `stop` indicates whether the operation should end with a STOP + /// condition. + /// - `cmd_iterator` is an iterator over the command registers. + async fn write_operation<'a>( + &self, + address: u8, + bytes: &[u8], + start: bool, + stop: bool, + cmd_iterator: &mut impl Iterator, + ) -> Result<(), Error> { + // Short circuit for zero length writes without start or end as that would be an + // invalid operation write lengths in the TRM (at least for ESP32-S3) are 1-255 + if bytes.is_empty() && !start && !stop { + return Ok(()); } - self.setup_write(address, bytes, start, cmd_iterator)?; - add_cmd( - cmd_iterator, - if stop { Command::Stop } else { Command::End }, - )?; - let index = self.fill_tx_fifo(bytes); - self.start_transmission(); + + let index = self.start_write_operation(address, bytes, start, stop, cmd_iterator)?; // Fill the FIFO with the remaining bytes: - self.write_remaining_tx_fifo(index, bytes)?; - self.wait_for_completion(!stop)?; + self.write_remaining_tx_fifo(index, bytes).await?; + self.wait_for_completion(!stop).await?; Ok(()) } @@ -2119,24 +1663,67 @@ impl Info { /// - `will_continue` indicates whether there is another read operation /// following this one and we should not nack the last byte. /// - `cmd_iterator` is an iterator over the command registers. - fn read_operation<'a, I>( + fn read_operation_blocking<'a>( &self, address: u8, buffer: &mut [u8], start: bool, stop: bool, will_continue: bool, - cmd_iterator: &mut I, - ) -> Result<(), Error> - where - I: Iterator, - { + cmd_iterator: &mut impl Iterator, + ) -> Result<(), Error> { + // Short circuit for zero length reads as that would be an invalid operation + // read lengths in the TRM (at least for ESP32-S3) are 1-255 + if buffer.is_empty() { + return Ok(()); + } + + self.start_read_operation(address, buffer, start, stop, will_continue, cmd_iterator)?; + self.read_all_from_fifo_blocking(buffer)?; + self.wait_for_completion_blocking(!stop)?; + Ok(()) + } + + /// Executes an async I2C read operation. + /// - `addr` is the address of the slave device. + /// - `buffer` is the buffer to store the read data. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `stop` indicates whether the operation should end with a STOP + /// condition. + /// - `will_continue` indicates whether there is another read operation + /// following this one and we should not nack the last byte. + /// - `cmd_iterator` is an iterator over the command registers. + async fn read_operation<'a>( + &self, + address: u8, + buffer: &mut [u8], + start: bool, + stop: bool, + will_continue: bool, + cmd_iterator: &mut impl Iterator, + ) -> Result<(), Error> { // Short circuit for zero length reads as that would be an invalid operation // read lengths in the TRM (at least for ESP32-S3) are 1-255 if buffer.is_empty() { return Ok(()); } + self.start_read_operation(address, buffer, start, stop, will_continue, cmd_iterator)?; + self.read_all_from_fifo(buffer).await?; + self.wait_for_completion(!stop).await?; + Ok(()) + } + + fn start_read_operation<'a>( + &self, + address: u8, + buffer: &mut [u8], + start: bool, + stop: bool, + will_continue: bool, + cmd_iterator: &mut impl Iterator, + ) -> Result<(), Error> { // Reset FIFO and command list self.reset_fifo(); self.reset_command_list(); @@ -2152,10 +1739,35 @@ impl Info { if stop { Command::Stop } else { Command::End }, )?; self.start_transmission(); - self.read_all_from_fifo(buffer)?; - self.wait_for_completion(!stop)?; + Ok(()) } + + fn start_write_operation<'a>( + &self, + address: u8, + bytes: &[u8], + start: bool, + stop: bool, + cmd_iterator: &mut impl Iterator, + ) -> Result { + // Reset FIFO and command list + self.reset_fifo(); + self.reset_command_list(); + + if start { + add_cmd(cmd_iterator, Command::Start)?; + } + self.setup_write(address, bytes, start, cmd_iterator)?; + add_cmd( + cmd_iterator, + if stop { Command::Stop } else { Command::End }, + )?; + let index = self.fill_tx_fifo(bytes); + self.start_transmission(); + + Ok(index) + } } impl PartialEq for Info { diff --git a/esp-hal/src/i2c/version/mod.rs b/esp-hal/src/i2c/version/mod.rs new file mode 100644 index 0000000000..c4bc7903ee --- /dev/null +++ b/esp-hal/src/i2c/version/mod.rs @@ -0,0 +1,14 @@ +cfg_if::cfg_if! { + if #[cfg(esp32)] { + mod v1; + pub(crate) use v1::*; + } else if #[cfg(esp32s2)] { + mod v2; + mod v2_v3_common; + pub(crate) use v2::*; + } else { + mod v3; + mod v2_v3_common; + pub(crate) use v3::*; + } +} diff --git a/esp-hal/src/i2c/version/v1.rs b/esp-hal/src/i2c/version/v1.rs new file mode 100644 index 0000000000..7ba05ec7ee --- /dev/null +++ b/esp-hal/src/i2c/version/v1.rs @@ -0,0 +1,144 @@ +//! ESP32-specific implementation +use crate::i2c::*; + +pub(crate) const I2C_CHUNK_SIZE: usize = 32; + +impl Driver<'_> { + pub(crate) async fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> { + // for ESP32 we need a timeout here but wasting a timer seems unnecessary + // given the short time we spend here + + let mut tout = MAX_ITERATIONS / 10; // adjust the timeout because we are yielding in the loop + loop { + let interrupts = self.info.register_block().int_raw().read(); + + self.check_errors()?; + + // Handle completion cases + // A full transmission was completed (either a STOP condition or END was + // processed) + if (!end_only && interrupts.trans_complete().bit_is_set()) + || interrupts.end_detect().bit_is_set() + { + break; + } + + tout -= 1; + if tout == 0 { + return Err(Error::TimeOut); + } + + embassy_futures::yield_now().await; + } + self.check_all_commands_done()?; + Ok(()) + } + + /// Writes remaining data from byte slice to the TX FIFO from the specified + /// index. + pub(crate) fn write_remaining_tx_fifo_blocking( + &self, + start_index: usize, + bytes: &[u8], + ) -> Result<(), Error> { + // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the + // FIFO apparently it would be possible by using non-fifo mode + // see https://github.com/espressif/arduino-esp32/blob/7e9afe8c5ed7b5bf29624a5cd6e07d431c027b97/cores/esp32/esp32-hal-i2c.c#L615 + + if start_index >= bytes.len() { + return Ok(()); + } + + // this is only possible when writing the I2C address in release mode + // from [perform_write_read] + for b in bytes { + write_fifo(self.info.register_block(), *b); + self.check_errors()?; + } + + Ok(()) + } + + pub(crate) async fn write_remaining_tx_fifo( + &self, + start_index: usize, + bytes: &[u8], + ) -> Result<(), Error> { + if start_index >= bytes.len() { + return Ok(()); + } + + for b in bytes { + write_fifo(self.info.register_block(), *b); + self.check_errors()?; + } + + Ok(()) + } + + pub(crate) fn read_all_from_fifo_blocking(&self, buffer: &mut [u8]) -> Result<(), Error> { + // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the + // FIFO apparently it would be possible by using non-fifo mode + // see https://github.com/espressif/arduino-esp32/blob/7e9afe8c5ed7b5bf29624a5cd6e07d431c027b97/cores/esp32/esp32-hal-i2c.c#L615 + + if buffer.len() > 32 { + panic!("On ESP32 the max I2C read is limited to 32 bytes"); + } + + // wait for completion - then we can just read the data from FIFO + // once we change to non-fifo mode to support larger transfers that + // won't work anymore + self.wait_for_completion_blocking(false)?; + + // Read bytes from FIFO + // FIXME: Handle case where less data has been provided by the slave than + // requested? Or is this prevented from a protocol perspective? + for byte in buffer.iter_mut() { + *byte = read_fifo(self.info.register_block()); + } + + Ok(()) + } + + pub(crate) async fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { + if buffer.len() > 32 { + panic!("On ESP32 the max I2C read is limited to 32 bytes"); + } + + self.wait_for_completion(false).await?; + + for byte in buffer.iter_mut() { + *byte = read_fifo(self.info.register_block()); + } + + Ok(()) + } + + /// Updates the configuration of the I2C peripheral. + pub(crate) fn update_config(&self) {} + + /// Checks for I2C transmission errors and handles them. + /// + /// This function inspects specific I2C-related interrupts to detect errors + /// during communication, such as timeouts, failed acknowledgments, or + /// arbitration loss. If an error is detected, the function handles it + /// by resetting the I2C peripheral to clear the error condition and then + /// returns an appropriate error. + pub(crate) fn check_errors(&self) -> Result<(), Error> { + let interrupts = self.info.register_block().int_raw().read(); + + let retval = if interrupts.time_out().bit_is_set() { + Error::TimeOut + } else if interrupts.ack_err().bit_is_set() { + Error::AckCheckFailed + } else if interrupts.arbitration_lost().bit_is_set() { + Error::ArbitrationLost + } else { + return Ok(()); + }; + + self.reset(); + + Err(retval) + } +} diff --git a/esp-hal/src/i2c/version/v2.rs b/esp-hal/src/i2c/version/v2.rs new file mode 100644 index 0000000000..da5ba12931 --- /dev/null +++ b/esp-hal/src/i2c/version/v2.rs @@ -0,0 +1,89 @@ +//! ESP32-S2-specific implementation +use crate::i2c::*; + +pub(crate) const I2C_CHUNK_SIZE: usize = 32; + +impl Driver<'_> { + /// Writes remaining data from byte slice to the TX FIFO from the specified + /// index. + pub(crate) fn write_remaining_tx_fifo_blocking( + &self, + start_index: usize, + bytes: &[u8], + ) -> Result<(), Error> { + // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the + // FIFO apparently it would be possible by using non-fifo mode + // see https://github.com/espressif/arduino-esp32/blob/7e9afe8c5ed7b5bf29624a5cd6e07d431c027b97/cores/esp32/esp32-hal-i2c.c#L615 + + if start_index >= bytes.len() { + return Ok(()); + } + + // this is only possible when writing the I2C address in release mode + // from [perform_write_read] + for b in bytes { + write_fifo(self.info.register_block(), *b); + self.check_errors()?; + } + + Ok(()) + } + + pub(crate) async fn write_remaining_tx_fifo( + &self, + start_index: usize, + bytes: &[u8], + ) -> Result<(), Error> { + if start_index >= bytes.len() { + return Ok(()); + } + + for b in bytes { + write_fifo(self.info.register_block(), *b); + self.check_errors()?; + } + + Ok(()) + } + + pub(crate) fn read_all_from_fifo_blocking(&self, buffer: &mut [u8]) -> Result<(), Error> { + // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the + // FIFO apparently it would be possible by using non-fifo mode + // see https://github.com/espressif/arduino-esp32/blob/7e9afe8c5ed7b5bf29624a5cd6e07d431c027b97/cores/esp32/esp32-hal-i2c.c#L615 + + if buffer.len() > 32 { + panic!("On ESP32-S2 the max I2C read is limited to 32 bytes"); + } + + // wait for completion - then we can just read the data from FIFO + // once we change to non-fifo mode to support larger transfers that + // won't work anymore + self.wait_for_completion_blocking(false)?; + + // Read bytes from FIFO + // FIXME: Handle case where less data has been provided by the slave than + // requested? Or is this prevented from a protocol perspective? + for byte in buffer.iter_mut() { + *byte = read_fifo(self.info.register_block()); + } + + Ok(()) + } + + pub(crate) async fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { + if buffer.len() > 32 { + panic!("On ESP32-S2 the max I2C read is limited to 32 bytes"); + } + + self.wait_for_completion(false).await?; + + for byte in buffer.iter_mut() { + *byte = read_fifo(self.info.register_block()); + } + + Ok(()) + } + + /// Updates the configuration of the I2C peripheral. + pub(crate) fn update_config(&self) {} +} diff --git a/esp-hal/src/i2c/version/v2_v3_common.rs b/esp-hal/src/i2c/version/v2_v3_common.rs new file mode 100644 index 0000000000..8089d305e4 --- /dev/null +++ b/esp-hal/src/i2c/version/v2_v3_common.rs @@ -0,0 +1,162 @@ +use core::{ + pin::Pin, + task::{Context, Poll}, +}; + +use crate::i2c::*; + +pub enum Event { + EndDetect, + TxComplete, + #[cfg(not(esp32s2))] + TxFifoWatermark, +} + +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct I2cFuture<'a> { + event: Event, + info: &'a Info, + state: &'a State, +} + +impl<'a> I2cFuture<'a> { + pub fn new(event: Event, info: &'a Info, state: &'a State) -> Self { + info.register_block().int_ena().modify(|_, w| { + let w = match event { + Event::EndDetect => w.end_detect().set_bit(), + Event::TxComplete => w.trans_complete().set_bit(), + #[cfg(not(esp32s2))] + Event::TxFifoWatermark => w.txfifo_wm().set_bit(), + }; + + w.arbitration_lost().set_bit(); + w.time_out().set_bit(); + w.nack().set_bit() + }); + + Self { event, state, info } + } + + fn event_bit_is_clear(&self) -> bool { + let r = self.info.register_block().int_ena().read(); + + match self.event { + Event::EndDetect => r.end_detect().bit_is_clear(), + Event::TxComplete => r.trans_complete().bit_is_clear(), + #[cfg(not(esp32s2))] + Event::TxFifoWatermark => r.txfifo_wm().bit_is_clear(), + } + } + + fn check_error(&self) -> Result<(), Error> { + let r = self.info.register_block().int_raw().read(); + + if r.arbitration_lost().bit_is_set() { + return Err(Error::ArbitrationLost); + } + + if r.time_out().bit_is_set() { + return Err(Error::TimeOut); + } + + if r.nack().bit_is_set() { + return Err(Error::AckCheckFailed); + } + + if r.trans_complete().bit_is_set() + && self + .info + .register_block() + .sr() + .read() + .resp_rec() + .bit_is_clear() + { + return Err(Error::AckCheckFailed); + } + + Ok(()) + } +} + +impl core::future::Future for I2cFuture<'_> { + type Output = Result<(), Error>; + + fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { + self.state.waker.register(ctx.waker()); + + let error = self.check_error(); + + if error.is_err() { + return Poll::Ready(error); + } + + if self.event_bit_is_clear() { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + } +} + +impl Driver<'_> { + pub(crate) async fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> { + self.check_errors()?; + + if end_only { + I2cFuture::new(Event::EndDetect, self.info, self.state).await?; + } else { + let res = embassy_futures::select::select( + I2cFuture::new(Event::TxComplete, self.info, self.state), + I2cFuture::new(Event::EndDetect, self.info, self.state), + ) + .await; + + match res { + embassy_futures::select::Either::First(res) => res?, + embassy_futures::select::Either::Second(res) => res?, + } + } + self.check_all_commands_done()?; + + Ok(()) + } +} + +impl Driver<'_> { + /// Checks for I2C transmission errors and handles them. + /// + /// This function inspects specific I2C-related interrupts to detect errors + /// during communication, such as timeouts, failed acknowledgments, or + /// arbitration loss. If an error is detected, the function handles it + /// by resetting the I2C peripheral to clear the error condition and then + /// returns an appropriate error. + pub(crate) fn check_errors(&self) -> Result<(), Error> { + let interrupts = self.info.register_block().int_raw().read(); + + // Handle error cases + let retval = if interrupts.time_out().bit_is_set() { + Error::TimeOut + } else if interrupts.nack().bit_is_set() { + Error::AckCheckFailed + } else if interrupts.arbitration_lost().bit_is_set() { + Error::ArbitrationLost + } else if interrupts.trans_complete().bit_is_set() + && self + .info + .register_block() + .sr() + .read() + .resp_rec() + .bit_is_clear() + { + Error::AckCheckFailed + } else { + return Ok(()); + }; + + self.reset(); + + Err(retval) + } +} diff --git a/esp-hal/src/i2c/version/v3.rs b/esp-hal/src/i2c/version/v3.rs new file mode 100644 index 0000000000..1e51688357 --- /dev/null +++ b/esp-hal/src/i2c/version/v3.rs @@ -0,0 +1,121 @@ +use crate::i2c::{version::v2_v3_common::*, *}; + +pub(crate) const I2C_CHUNK_SIZE: usize = 254; + +impl Driver<'_> { + /// Writes remaining data from byte slice to the TX FIFO from the specified + /// index. + pub(crate) fn write_remaining_tx_fifo_blocking( + &self, + start_index: usize, + bytes: &[u8], + ) -> Result<(), Error> { + let mut index = start_index; + loop { + self.check_errors()?; + + while !self + .info + .register_block() + .int_raw() + .read() + .txfifo_wm() + .bit_is_set() + { + self.check_errors()?; + } + + self.info + .register_block() + .int_clr() + .write(|w| w.txfifo_wm().clear_bit_by_one()); + + while !self + .info + .register_block() + .int_raw() + .read() + .txfifo_wm() + .bit_is_set() + { + self.check_errors()?; + } + + if index >= bytes.len() { + break Ok(()); + } + + write_fifo(self.info.register_block(), bytes[index]); + index += 1; + } + } + + pub(crate) async fn write_remaining_tx_fifo( + &self, + start_index: usize, + bytes: &[u8], + ) -> Result<(), Error> { + let mut index = start_index; + loop { + self.check_errors()?; + + I2cFuture::new(Event::TxFifoWatermark, self.info, self.state).await?; + + self.info + .register_block() + .int_clr() + .write(|w| w.txfifo_wm().clear_bit_by_one()); + + I2cFuture::new(Event::TxFifoWatermark, self.info, self.state).await?; + + if index >= bytes.len() { + break Ok(()); + } + + write_fifo(self.info.register_block(), bytes[index]); + index += 1; + } + } + + /// Reads all bytes from the RX FIFO. + pub(crate) fn read_all_from_fifo_blocking(&self, buffer: &mut [u8]) -> Result<(), Error> { + // Read bytes from FIFO + // FIXME: Handle case where less data has been provided by the slave than + // requested? Or is this prevented from a protocol perspective? + for byte in buffer.iter_mut() { + loop { + self.check_errors()?; + + let reg = self.info.register_block().fifo_st().read(); + if reg.rxfifo_raddr().bits() != reg.rxfifo_waddr().bits() { + break; + } + } + + *byte = read_fifo(self.info.register_block()); + } + + Ok(()) + } + + pub(crate) async fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { + self.read_all_from_fifo_blocking(buffer) + } + + /// Updates the configuration of the I2C peripheral. + /// + /// This function ensures that the configuration values, such as clock + /// settings, SDA/SCL filtering, timeouts, and other operational + /// parameters, which are configured in other functions, are properly + /// propagated to the I2C hardware. This step is necessary to synchronize + /// the software-configured settings with the peripheral's internal + /// registers, ensuring that the hardware behaves according to the + /// current configuration. + pub(crate) fn update_config(&self) { + // Ensure that the configuration of the peripheral is correctly propagated + self.info + .register_block() + .ctr() + .modify(|_, w| w.conf_upgate().set_bit()); + } +}