From f70ef1a5934f662ae55e0a3f59a6e4b6dcd945ab Mon Sep 17 00:00:00 2001 From: liebman Date: Tue, 30 Apr 2024 06:41:07 -0700 Subject: [PATCH] i2c: implement `I2C:transaction` for `embedded-hal` and `embedded-hal-async` (#1505) * i2c: * i2c: refactor transaction() and reuse for master_read, master_write, and master_write_read * i2c: cargo fmt * i2c: fix an issue with not clearing interrupt bits & move where we reset fifo and command_list * i2c: fix async compile error * i2c: fix for esp32 & esp32s2 * i2c: real fix for esp32 (End command never gets cmd_done bit set!) * i2c: fmt and removal of an unwrap() that I was using while debugging * i2c: only define opcode values in one place i2c: use CommandReg in add_cmd * i2c: async direct & embedded_hal support working * i2c: cargo fmt * examples: cargo fmt --- esp-hal/CHANGELOG.md | 1 + esp-hal/src/i2c.rs | 781 ++++++++++++------ .../embassy_i2c_bmp180_calibration_data.rs | 71 ++ 3 files changed, 590 insertions(+), 263 deletions(-) create mode 100644 examples/src/bin/embassy_i2c_bmp180_calibration_data.rs diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 3cb831770..9f823b445 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- i2c: implement `I2C:transaction` for `embedded-hal` and `embedded-hal-async` - ESP32-PICO-V3-02: Initial support (#1155) - `time::current_time` API (#1503) - ESP32-S3: Add LCD_CAM Camera driver (#1483) diff --git a/esp-hal/src/i2c.rs b/esp-hal/src/i2c.rs index 4f54d7ada..97a2fc75b 100644 --- a/esp-hal/src/i2c.rs +++ b/esp-hal/src/i2c.rs @@ -66,6 +66,17 @@ pub enum Error { CommandNrExceeded, } +#[cfg(any(feature = "embedded-hal", feature = "async"))] +#[derive(PartialEq)] +// This enum is used to keep track of the last operation that was performed +// in an embedded-hal(-async) I2C::transaction. It used to determine whether +// a START condition should be issued at the start of the current operation. +enum LastOpWas { + Write, + Read, + None, +} + #[cfg(feature = "embedded-hal")] impl embedded_hal::i2c::Error for Error { fn kind(&self) -> embedded_hal::i2c::ErrorKind { @@ -80,10 +91,79 @@ impl embedded_hal::i2c::Error for Error { } } +// This should really be defined in the PAC, but the PAC only +// defines the "command" field as a 16-bit field :-( +bitfield::bitfield! { + struct CommandReg(u32); + cmd_done, _: 31; + from into Opcode, opcode, set_opcode: 13, 11; + from into Ack, ack_value, set_ack_value: 10, 10; + from into Ack, ack_exp, set_ack_exp: 9, 9; + ack_check_en, set_ack_check_en: 8; + length, set_length: 7, 0; +} + +impl CommandReg { + fn bits(&self) -> u32 { + self.0 + } + + fn new_start() -> Self { + let mut cmd = Self(0); + cmd.set_opcode(Opcode::RStart); + cmd + } + + fn new_end() -> Self { + let mut cmd = Self(0); + cmd.set_opcode(Opcode::End); + cmd + } + + fn new_stop() -> Self { + let mut cmd = Self(0); + cmd.set_opcode(Opcode::Stop); + cmd + } + + fn new_write(ack_exp: Ack, ack_check_en: bool, length: u8) -> Self { + let mut cmd = Self(0); + cmd.set_opcode(Opcode::Write); + cmd.set_ack_exp(ack_exp); + cmd.set_ack_check_en(ack_check_en); + cmd.set_length(length as u32); + cmd + } + + fn new_read(ack_value: Ack, length: u8) -> Self { + let mut cmd = Self(0); + cmd.set_opcode(Opcode::Read); + cmd.set_ack_value(ack_value); + cmd.set_length(length as u32); + cmd + } +} + +#[cfg(feature = "debug")] +impl core::fmt::Debug for CommandReg { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("CommandReg") + .field("cmd_done", &self.cmd_done()) + .field("opcode", &self.opcode()) + .field("ack_value", &self.ack_value()) + .field("ack_exp", &self.ack_exp()) + .field("ack_check_en", &self.ack_check_en()) + .field("length", &self.length()) + .finish() + } +} + /// A generic I2C Command +#[cfg_attr(feature = "debug", derive(Debug))] enum Command { Start, Stop, + End, Write { /// This bit is to set an expected ACK value for the transmitter. ack_exp: Ack, @@ -103,60 +183,19 @@ enum Command { }, } -impl From for u16 { - fn from(c: Command) -> u16 { - let opcode = match c { - Command::Start => Opcode::RStart, - Command::Stop => Opcode::Stop, - Command::Write { .. } => Opcode::Write, - Command::Read { .. } => Opcode::Read, - }; - - let length = match c { - Command::Start | Command::Stop => 0, - Command::Write { length: l, .. } | Command::Read { length: l, .. } => l, - }; - - let ack_exp = match c { - Command::Start | Command::Stop | Command::Read { .. } => Ack::Nack, - Command::Write { ack_exp: exp, .. } => exp, - }; - - let ack_check_en = match c { - Command::Start | Command::Stop | Command::Read { .. } => false, +impl From for CommandReg { + fn from(c: Command) -> Self { + match c { + Command::Start => CommandReg::new_start(), + Command::End => CommandReg::new_end(), + Command::Stop => CommandReg::new_stop(), Command::Write { - ack_check_en: en, .. - } => en, - }; - - let ack_value = match c { - Command::Start | Command::Stop | Command::Write { .. } => Ack::Nack, - Command::Read { ack_value: ack, .. } => ack, - }; - - let mut cmd: u16 = length.into(); - - if ack_check_en { - cmd |= 1 << 8; - } else { - cmd &= !(1 << 8); + ack_exp, + ack_check_en, + length, + } => CommandReg::new_write(ack_exp, ack_check_en, length), + Command::Read { ack_value, length } => CommandReg::new_read(ack_value, length), } - - if ack_exp == Ack::Nack { - cmd |= 1 << 9; - } else { - cmd &= !(1 << 9); - } - - if ack_value == Ack::Nack { - cmd |= 1 << 10; - } else { - cmd &= !(1 << 10); - } - - cmd |= (opcode as u16) << 11; - - cmd } } @@ -166,25 +205,73 @@ enum OperationType { } #[derive(Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "debug", derive(Debug))] enum Ack { - Ack, - Nack, + Ack = 0, + Nack = 1, +} +impl From for Ack { + fn from(ack: u32) -> Self { + match ack { + 0 => Ack::Ack, + 1 => Ack::Nack, + _ => unreachable!(), + } + } +} +impl From for u32 { + fn from(ack: Ack) -> u32 { + ack as u32 + } } -#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] +cfg_if::cfg_if! { + if #[cfg(any(esp32, esp32s2))] { + const OPCODE_RSTART: u32 = 0; + const OPCODE_WRITE: u32 = 1; + const OPCODE_READ: u32 = 2; + const OPCODE_STOP: u32 = 3; + const OPCODE_END: u32 = 4; + } else if #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] { + const OPCODE_RSTART: u32 = 6; + const OPCODE_WRITE: u32 = 1; + const OPCODE_READ: u32 = 3; + const OPCODE_STOP: u32 = 2; + const OPCODE_END: u32 = 4; + } +} +#[cfg_attr(feature = "debug", derive(Debug))] +#[derive(PartialEq)] enum Opcode { - RStart = 6, - Write = 1, - Read = 3, - Stop = 2, + RStart, + Write, + Read, + Stop, + End, } -#[cfg(any(esp32, esp32s2))] -enum Opcode { - RStart = 0, - Write = 1, - Read = 2, - Stop = 3, +impl From for u32 { + fn from(opcode: Opcode) -> u32 { + match opcode { + Opcode::RStart => OPCODE_RSTART, + Opcode::Write => OPCODE_WRITE, + Opcode::Read => OPCODE_READ, + Opcode::Stop => OPCODE_STOP, + Opcode::End => OPCODE_END, + } + } +} +impl From for Opcode { + fn from(opcode: u32) -> Self { + match opcode { + OPCODE_RSTART => Opcode::RStart, + OPCODE_WRITE => Opcode::Write, + OPCODE_READ => Opcode::Read, + OPCODE_STOP => Opcode::Stop, + OPCODE_END => Opcode::End, + _ => unreachable!(), + } + } } /// I2C peripheral container (I2C) @@ -193,10 +280,9 @@ pub struct I2C<'d, T, DM: crate::Mode> { phantom: PhantomData, } -impl I2C<'_, T, DM> +impl I2C<'_, T, crate::Blocking> where T: Instance, - DM: crate::Mode, { /// Reads enough bytes from slave with `address` to fill `buffer` pub fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { @@ -221,31 +307,31 @@ where } #[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::blocking::i2c::Read for I2C<'_, T, DM> +impl embedded_hal_02::blocking::i2c::Read for I2C<'_, T, crate::Blocking> where T: Instance, { type Error = Error; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.read(address, buffer) + self.peripheral.master_read(address, buffer) } } #[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::blocking::i2c::Write for I2C<'_, T, DM> +impl embedded_hal_02::blocking::i2c::Write for I2C<'_, T, crate::Blocking> where T: Instance, { type Error = Error; fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { - self.write(addr, bytes) + self.peripheral.master_write(addr, bytes) } } #[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::blocking::i2c::WriteRead for I2C<'_, T, DM> +impl embedded_hal_02::blocking::i2c::WriteRead for I2C<'_, T, crate::Blocking> where T: Instance, { @@ -257,7 +343,7 @@ where bytes: &[u8], buffer: &mut [u8], ) -> Result<(), Self::Error> { - self.write_read(address, bytes, buffer) + self.peripheral.master_write_read(address, bytes, buffer) } } @@ -271,29 +357,50 @@ impl embedded_hal::i2c::I2c for I2C<'_, T, DM> where T: Instance, { - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.peripheral.master_read(address, buffer) - } - - fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { - self.peripheral.master_write(address, bytes) - } - - fn write_read( - &mut self, - address: u8, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Self::Error> { - self.peripheral.master_write_read(address, bytes, buffer) - } - fn transaction( &mut self, - _address: u8, - _operations: &mut [embedded_hal::i2c::Operation<'_>], + address: u8, + operations: &mut [embedded_hal::i2c::Operation<'_>], ) -> Result<(), Self::Error> { - todo!() + use embedded_hal::i2c::Operation; + let mut last_op = LastOpWas::None; + let mut op_iter = operations.iter_mut().peekable(); + while let Some(op) = op_iter.next() { + // Clear all I2C interrupts + self.peripheral.clear_all_interrupts(); + + // TODO somehow know that we can combine a write and a read into one transaction + let cmd_iterator = &mut self.peripheral.register_block().comd_iter(); + match op { + Operation::Write(bytes) => { + // execute a write operation: + // - issue START/RSTART if op is different from previous + // - issue STOP if op is the last one + self.peripheral.write_operation( + address, + bytes, + last_op != LastOpWas::Write, + op_iter.peek().is_none(), + cmd_iterator, + )?; + last_op = LastOpWas::Write; + } + Operation::Read(buffer) => { + // execute a read operation: + // - issue START/RSTART if op is different from previous + // - issue STOP if op is the last one + self.peripheral.read_operation( + address, + buffer, + last_op != LastOpWas::Read, + op_iter.peek().is_none(), + cmd_iterator, + )?; + last_op = LastOpWas::Read; + } + } + } + Ok(()) } } @@ -522,44 +629,13 @@ mod asynch { where T: Instance, { - async fn master_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { - // Reset FIFO and command list - self.peripheral.reset_fifo(); - self.peripheral.reset_command_list(); - - self.perform_read( - addr, - buffer, - &mut self.peripheral.register_block().comd_iter(), - ) - .await - } - - async fn perform_read<'a, I>( - &self, - addr: u8, - buffer: &mut [u8], - cmd_iterator: &mut I, - ) -> Result<(), Error> - where - I: Iterator, - { - self.peripheral.setup_read(addr, buffer, cmd_iterator)?; - self.peripheral.start_transmission(); - - self.read_all_from_fifo(buffer).await?; - self.wait_for_completion().await?; - - Ok(()) - } - #[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().await?; + self.wait_for_completion(false).await?; for byte in buffer.iter_mut() { *byte = read_fifo(self.peripheral.register_block()); @@ -573,39 +649,6 @@ mod asynch { self.peripheral.read_all_from_fifo(buffer) } - async fn master_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { - // Reset FIFO and command list - self.peripheral.reset_fifo(); - self.peripheral.reset_command_list(); - - self.perform_write( - addr, - bytes, - &mut self.peripheral.register_block().comd_iter(), - ) - .await - } - - async fn perform_write<'a, I>( - &self, - addr: u8, - bytes: &[u8], - cmd_iterator: &mut I, - ) -> Result<(), Error> - where - I: Iterator, - { - self.peripheral.setup_write(addr, bytes, cmd_iterator)?; - let index = self.peripheral.fill_tx_fifo(bytes); - self.peripheral.start_transmission(); - - // Fill the FIFO with the remaining bytes: - self.write_remaining_tx_fifo(index, bytes).await?; - self.wait_for_completion().await?; - - Ok(()) - } - #[cfg(any(esp32, esp32s2))] async fn write_remaining_tx_fifo( &self, @@ -652,55 +695,221 @@ mod asynch { } } - async fn wait_for_completion(&self) -> Result<(), Error> { + async fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> { self.peripheral.check_errors()?; - select( - I2cFuture::new(Event::TxComplete, self.inner()), - I2cFuture::new(Event::EndDetect, self.inner()), - ) - .await; - - for cmd in self.peripheral.register_block().comd_iter() { - if cmd.read().command().bits() != 0x0 && cmd.read().command_done().bit_is_clear() { - return Err(Error::ExecIncomplete); - } + if end_only { + I2cFuture::new(Event::EndDetect, self.inner()).await; + } else { + select( + I2cFuture::new(Event::TxComplete, self.inner()), + I2cFuture::new(Event::EndDetect, self.inner()), + ) + .await; } + self.peripheral.check_all_commands_done()?; Ok(()) } + + async fn write_operation<'a, I>( + &self, + address: u8, + bytes: &[u8], + start: bool, + stop: bool, + cmd_iterator: &mut I, + ) -> Result<(), Error> + where + I: Iterator, + { + // Reset FIFO and command list + self.peripheral.reset_fifo(); + self.peripheral.reset_command_list(); + if start { + add_cmd(cmd_iterator, Command::Start)?; + } + self.peripheral.setup_write(address, bytes, cmd_iterator)?; + add_cmd( + cmd_iterator, + if stop { Command::Stop } else { Command::End }, + )?; + let index = self.peripheral.fill_tx_fifo(bytes); + self.peripheral.start_transmission(); + + // Fill the FIFO with the remaining bytes: + self.write_remaining_tx_fifo(index, bytes).await?; + self.wait_for_completion(!stop).await?; + Ok(()) + } + + async fn read_operation<'a, I>( + &self, + address: u8, + buffer: &mut [u8], + start: bool, + stop: bool, + cmd_iterator: &mut I, + ) -> Result<(), Error> + where + I: Iterator, + { + // Reset FIFO and command list + self.peripheral.reset_fifo(); + self.peripheral.reset_command_list(); + if start { + add_cmd(cmd_iterator, Command::Start)?; + } + self.peripheral.setup_read(address, buffer, cmd_iterator)?; + add_cmd( + cmd_iterator, + if stop { Command::Stop } else { Command::End }, + )?; + self.peripheral.start_transmission(); + self.read_all_from_fifo(buffer).await?; + self.wait_for_completion(!stop).await?; + Ok(()) + } + + /// Send data bytes from the `bytes` array to a target slave with the + /// address `addr` + async fn master_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + // Clear all I2C interrupts + self.peripheral.clear_all_interrupts(); + self.write_operation( + addr, + bytes, + true, + true, + &mut self.peripheral.register_block().comd_iter(), + ) + .await?; + Ok(()) + } + + /// Read bytes from a target slave with the address `addr` + /// The number of read bytes is deterimed by the size of the `buffer` + /// argument + async fn master_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { + // Clear all I2C interrupts + self.peripheral.clear_all_interrupts(); + self.read_operation( + addr, + buffer, + true, + true, + &mut self.peripheral.register_block().comd_iter(), + ) + .await?; + Ok(()) + } + + /// Write bytes from the `bytes` array first and then read n bytes into + /// the `buffer` array with n being the size of the array. + async fn master_write_read( + &mut self, + addr: u8, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Error> { + // it would be possible to combine the write and read + // in one transaction but filling the tx fifo with + // the current code is somewhat slow even in release mode + // which can cause issues + + // Clear all I2C interrupts + self.peripheral.clear_all_interrupts(); + self.write_operation( + addr, + bytes, + true, + false, + &mut self.peripheral.register_block().comd_iter(), + ) + .await?; + self.peripheral.clear_all_interrupts(); + self.read_operation( + addr, + buffer, + true, + true, + &mut self.peripheral.register_block().comd_iter(), + ) + .await?; + Ok(()) + } + + /// Writes bytes to slave with address `address` + pub async fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + self.master_write(addr, bytes).await?; + Ok(()) + } + + /// Reads enough bytes from slave with `address` to fill `buffer` + pub async fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { + self.master_read(addr, buffer).await?; + Ok(()) + } + + /// Writes bytes to slave with address `address` and then reads enough + /// bytes to fill `buffer` *in a single transaction* + pub async fn write_read( + &mut self, + addr: u8, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Error> { + self.master_write_read(addr, bytes, buffer).await?; + Ok(()) + } } + #[cfg(feature = "embedded-hal")] impl<'d, T> embedded_hal_async::i2c::I2c for I2C<'d, T, crate::Async> where T: Instance, { - async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { - self.master_read(address, read).await - } - - async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { - self.master_write(address, write).await - } - - async fn write_read( - &mut self, - address: u8, - write: &[u8], - read: &mut [u8], - ) -> Result<(), Self::Error> { - self.master_write(address, write).await?; - self.master_read(address, read).await?; - - Ok(()) - } - async fn transaction( &mut self, - _address: u8, - _operations: &mut [Operation<'_>], + address: u8, + operations: &mut [Operation<'_>], ) -> Result<(), Self::Error> { - todo!() + let mut last_op = LastOpWas::None; + let mut op_iter = operations.iter_mut().peekable(); + while let Some(op) = op_iter.next() { + // Clear all I2C interrupts + self.peripheral.clear_all_interrupts(); + + let cmd_iterator = &mut self.peripheral.register_block().comd_iter(); + match op { + Operation::Write(bytes) => { + self.write_operation( + address, + bytes, + last_op != LastOpWas::Write, + op_iter.peek().is_none(), + cmd_iterator, + ) + .await?; + last_op = LastOpWas::Write; + } + Operation::Read(buffer) => { + // execute a read operation: + // - issue START/RSTART if op is different from previous + // - issue STOP if op is the last one + self.read_operation( + address, + buffer, + last_op != LastOpWas::Read, + op_iter.peek().is_none(), + cmd_iterator, + ) + .await?; + last_op = LastOpWas::Read; + } + } + } + Ok(()) } } @@ -1176,12 +1385,6 @@ pub trait Instance: crate::private::Sealed { return Err(Error::ExceedingFifo); } - // Clear all I2C interrupts - self.clear_all_interrupts(); - - // RSTART command - add_cmd(cmd_iterator, Command::Start)?; - // WRITE command add_cmd( cmd_iterator, @@ -1192,8 +1395,6 @@ pub trait Instance: crate::private::Sealed { }, )?; - add_cmd(cmd_iterator, Command::Stop)?; - self.update_config(); // Load address and R/W bit into FIFO @@ -1205,26 +1406,6 @@ pub trait Instance: crate::private::Sealed { Ok(()) } - fn perform_write<'a, I>( - &self, - addr: u8, - bytes: &[u8], - cmd_iterator: &mut I, - ) -> Result<(), Error> - where - I: Iterator, - { - self.setup_write(addr, bytes, cmd_iterator)?; - let index = self.fill_tx_fifo(bytes); - self.start_transmission(); - - // Fill the FIFO with the remaining bytes: - self.write_remaining_tx_fifo(index, bytes)?; - self.wait_for_completion()?; - - Ok(()) - } - fn setup_read<'a, I>( &self, addr: u8, @@ -1239,12 +1420,6 @@ pub trait Instance: crate::private::Sealed { return Err(Error::ExceedingFifo); } - // Clear all I2C interrupts - self.clear_all_interrupts(); - - // RSTART command - add_cmd(cmd_iterator, Command::Start)?; - // WRITE command add_cmd( cmd_iterator, @@ -1275,8 +1450,6 @@ pub trait Instance: crate::private::Sealed { }, )?; - add_cmd(cmd_iterator, Command::Stop)?; - self.update_config(); // Load address and R/W bit into FIFO @@ -1285,23 +1458,6 @@ pub trait Instance: crate::private::Sealed { Ok(()) } - fn perform_read<'a, I>( - &self, - addr: u8, - buffer: &mut [u8], - cmd_iterator: &mut I, - ) -> Result<(), Error> - where - I: Iterator, - { - self.setup_read(addr, buffer, cmd_iterator)?; - self.start_transmission(); - self.read_all_from_fifo(buffer)?; - self.wait_for_completion()?; - - Ok(()) - } - #[cfg(not(any(esp32, esp32s2)))] fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { // Read bytes from FIFO @@ -1336,7 +1492,7 @@ pub trait Instance: crate::private::Sealed { // 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()?; + self.wait_for_completion(false)?; // Read bytes from FIFO // FIXME: Handle case where less data has been provided by the slave than @@ -1354,27 +1510,38 @@ pub trait Instance: crate::private::Sealed { .write(|w| unsafe { w.bits(I2C_LL_INTR_MASK) }); } - fn wait_for_completion(&self) -> Result<(), Error> { + fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> { loop { let interrupts = self.register_block().int_raw().read(); self.check_errors()?; // Handle completion cases - // A full transmission was completed - if interrupts.trans_complete().bit_is_set() || interrupts.end_detect().bit_is_set() { + // 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; } } - for cmd in self.register_block().comd_iter() { - if cmd.read().command().bits() != 0x0 && cmd.read().command_done().bit_is_clear() { + self.check_all_commands_done()?; + Ok(()) + } + + fn check_all_commands_done(&self) -> Result<(), Error> { + // 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() { + let cmd = CommandReg(cmd_reg.read().bits()); + if cmd.bits() != 0x0 && cmd.opcode() != Opcode::End && !cmd.cmd_done() { return Err(Error::ExecIncomplete); } } Ok(()) } - fn check_errors(&self) -> Result<(), Error> { let interrupts = self.register_block().int_raw().read(); @@ -1591,13 +1758,79 @@ pub trait Instance: crate::private::Sealed { .write(|w| w.rxfifo_full().clear_bit_by_one()); } - /// Send data bytes from the `bytes` array to a target slave with the - /// address `addr` - fn master_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + fn write_operation<'a, I>( + &self, + address: u8, + bytes: &[u8], + start: bool, + stop: bool, + cmd_iterator: &mut I, + ) -> Result<(), Error> + where + I: Iterator, + { // Reset FIFO and command list self.reset_fifo(); self.reset_command_list(); - self.perform_write(addr, bytes, &mut self.register_block().comd_iter())?; + + if start { + add_cmd(cmd_iterator, Command::Start)?; + } + self.setup_write(address, bytes, cmd_iterator)?; + add_cmd( + cmd_iterator, + if stop { Command::Stop } else { Command::End }, + )?; + let index = self.fill_tx_fifo(bytes); + self.start_transmission(); + + // Fill the FIFO with the remaining bytes: + self.write_remaining_tx_fifo(index, bytes)?; + self.wait_for_completion(!stop)?; + Ok(()) + } + + fn read_operation<'a, I>( + &self, + address: u8, + buffer: &mut [u8], + start: bool, + stop: bool, + cmd_iterator: &mut I, + ) -> Result<(), Error> + where + I: Iterator, + { + // Reset FIFO and command list + self.reset_fifo(); + self.reset_command_list(); + + if start { + add_cmd(cmd_iterator, Command::Start)?; + } + self.setup_read(address, buffer, cmd_iterator)?; + add_cmd( + cmd_iterator, + if stop { Command::Stop } else { Command::End }, + )?; + self.start_transmission(); + self.read_all_from_fifo(buffer)?; + self.wait_for_completion(!stop)?; + Ok(()) + } + + /// Send data bytes from the `bytes` array to a target slave with the + /// address `addr` + fn master_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + // Clear all I2C interrupts + self.clear_all_interrupts(); + self.write_operation( + addr, + bytes, + true, + true, + &mut self.register_block().comd_iter(), + )?; Ok(()) } @@ -1605,10 +1838,15 @@ pub trait Instance: crate::private::Sealed { /// The number of read bytes is deterimed by the size of the `buffer` /// argument fn master_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { - // Reset FIFO and command list - self.reset_fifo(); - self.reset_command_list(); - self.perform_read(addr, buffer, &mut self.register_block().comd_iter())?; + // Clear all I2C interrupts + self.clear_all_interrupts(); + self.read_operation( + addr, + buffer, + true, + true, + &mut self.register_block().comd_iter(), + )?; Ok(()) } @@ -1624,8 +1862,24 @@ pub trait Instance: crate::private::Sealed { // in one transaction but filling the tx fifo with // the current code is somewhat slow even in release mode // which can cause issues - self.master_write(addr, bytes)?; - self.master_read(addr, buffer)?; + + // Clear all I2C interrupts + self.clear_all_interrupts(); + self.write_operation( + addr, + bytes, + true, + false, + &mut self.register_block().comd_iter(), + )?; + self.clear_all_interrupts(); + self.read_operation( + addr, + buffer, + true, + true, + &mut self.register_block().comd_iter(), + )?; Ok(()) } } @@ -1635,7 +1889,8 @@ where I: Iterator, { let cmd = cmd_iterator.next().ok_or(Error::CommandNrExceeded)?; - cmd.write(|w| unsafe { w.command().bits(command.into()) }); + let cmd_reg: CommandReg = command.into(); + cmd.write(|w| unsafe { w.bits(cmd_reg.bits()) }); Ok(()) } diff --git a/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs b/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs new file mode 100644 index 000000000..83a208216 --- /dev/null +++ b/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs @@ -0,0 +1,71 @@ +//! Embassy "async" vesrion of ead calibration data from BMP180 sensor +//! +//! This example dumps the calibration data from a BMP180 sensor by reading by reading +//! with the direct I2C API and the embedded-hal-async I2C API. +//! +//! //! Folowing pins are used: +//! SDA GPIO4 +//! SCL GPIO5 +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! + +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: async embassy embassy-executor-thread embassy-time-timg0 embassy-generic-timers + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Spawner; +use embassy_time::{Duration, Timer}; +use esp_backtrace as _; +use esp_hal::{ + clock::ClockControl, + embassy::{self}, + gpio::Io, + i2c::I2C, + peripherals::Peripherals, + prelude::*, + system::SystemControl, + timer::TimerGroup, +}; + +#[main] +async fn main(_spawner: Spawner) { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timg0 = TimerGroup::new_async(peripherals.TIMG0, &clocks); + embassy::init(&clocks, timg0); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let mut i2c = I2C::new_async( + peripherals.I2C0, + io.pins.gpio4, + io.pins.gpio5, + 400.kHz(), + &clocks, + ); + + loop { + let mut data = [0u8; 22]; + i2c.write_read(0x77, &[0xaa], &mut data).await.unwrap(); + esp_println::println!("direct: {:02x?}", data); + read_data(&mut i2c).await; + Timer::after(Duration::from_millis(1000)).await; + } +} + +async fn read_data(i2c: &mut I2C) +where + I2C: embedded_hal_async::i2c::I2c, +{ + let mut data = [0u8; 22]; + i2c.write_read(0x77, &[0xaa], &mut data).await.unwrap(); + + esp_println::println!("embedded_hal: {:02x?}", data); +}