diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index cbbc201de..0843f45cf 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -41,6 +41,68 @@ pub unsafe fn on_interrupt() { }); } +/// Frame type in I2C transaction. +/// +/// This tells each method what kind of framing to use, to generate a (repeated) start condition (ST +/// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an +/// ACK or NACK after the last byte received. +/// +/// For write operations, the following options are identical because they differ only in the (N)ACK +/// treatment relevant for read operations: +/// +/// - `FirstFrame` and `FirstAndNextFrame` +/// - `NextFrame` and `LastFrameNoStop` +/// +/// Abbreviations used below: +/// +/// - `ST` = start condition +/// - `SR` = repeated start condition +/// - `SP` = stop condition +#[derive(Copy, Clone)] +enum FrameOptions { + /// `[ST/SR]+[NACK]+[SP]` First frame (of this type) in operation and last frame overall in this + /// transaction. + FirstAndLastFrame, + /// `[ST/SR]+[NACK]` First frame of this type in transaction, last frame in a read operation but + /// not the last frame overall. + FirstFrame, + /// `[ST/SR]+[ACK]` First frame of this type in transaction, neither last frame overall nor last + /// frame in a read operation. + FirstAndNextFrame, + /// `[ACK]` Middle frame in a read operation (neither first nor last). + NextFrame, + /// `[NACK]+[SP]` Last frame overall in this transaction but not the first frame. + LastFrame, + /// `[NACK]` Last frame in a read operation but not last frame overall in this transaction. + LastFrameNoStop, +} + +impl FrameOptions { + /// Sends start or repeated start condition before transfer. + fn send_start(self) -> bool { + match self { + Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true, + Self::NextFrame | Self::LastFrame | Self::LastFrameNoStop => false, + } + } + + /// Sends stop condition after transfer. + fn send_stop(self) -> bool { + match self { + Self::FirstAndLastFrame | Self::LastFrame => true, + Self::FirstFrame | Self::FirstAndNextFrame | Self::NextFrame | Self::LastFrameNoStop => false, + } + } + + /// Sends NACK after last byte received, indicating end of read operation. + fn send_nack(self) -> bool { + match self { + Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true, + Self::FirstAndNextFrame | Self::NextFrame => false, + } + } +} + impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { T::regs().cr1().modify(|reg| { @@ -124,46 +186,57 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(sr1) } - fn write_bytes(&mut self, addr: u8, bytes: &[u8], timeout: Timeout) -> Result<(), Error> { - // Send a START condition + fn write_bytes(&mut self, addr: u8, bytes: &[u8], timeout: Timeout, frame: FrameOptions) -> Result<(), Error> { + if frame.send_start() { + // Send a START condition - T::regs().cr1().modify(|reg| { - reg.set_start(true); - }); + T::regs().cr1().modify(|reg| { + reg.set_start(true); + }); - // Wait until START condition was generated - while !Self::check_and_clear_error_flags()?.start() { - timeout.check()?; + // Wait until START condition was generated + while !Self::check_and_clear_error_flags()?.start() { + timeout.check()?; + } + + // Also wait until signalled we're master and everything is waiting for us + while { + Self::check_and_clear_error_flags()?; + + let sr2 = T::regs().sr2().read(); + !sr2.msl() && !sr2.busy() + } { + timeout.check()?; + } + + // Set up current address, we're trying to talk to + T::regs().dr().write(|reg| reg.set_dr(addr << 1)); + + // Wait until address was sent + // Wait for the address to be acknowledged + // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. + while !Self::check_and_clear_error_flags()?.addr() { + timeout.check()?; + } + + // Clear condition by reading SR2 + let _ = T::regs().sr2().read(); } - // Also wait until signalled we're master and everything is waiting for us - while { - Self::check_and_clear_error_flags()?; - - let sr2 = T::regs().sr2().read(); - !sr2.msl() && !sr2.busy() - } { - timeout.check()?; - } - - // Set up current address, we're trying to talk to - T::regs().dr().write(|reg| reg.set_dr(addr << 1)); - - // Wait until address was sent - // Wait for the address to be acknowledged - // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. - while !Self::check_and_clear_error_flags()?.addr() { - timeout.check()?; - } - - // Clear condition by reading SR2 - let _ = T::regs().sr2().read(); - // Send bytes for c in bytes { self.send_byte(*c, timeout)?; } + if frame.send_stop() { + // Send a STOP condition + T::regs().cr1().modify(|reg| reg.set_stop(true)); + // Wait for STOP condition to transmit. + while T::regs().cr1().read().stop() { + timeout.check()?; + } + } + // Fallthrough is success Ok(()) } @@ -205,8 +278,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(value) } - fn blocking_read_timeout(&mut self, addr: u8, buffer: &mut [u8], timeout: Timeout) -> Result<(), Error> { - if let Some((last, buffer)) = buffer.split_last_mut() { + fn blocking_read_timeout( + &mut self, + addr: u8, + buffer: &mut [u8], + timeout: Timeout, + frame: FrameOptions, + ) -> Result<(), Error> { + let Some((last, buffer)) = buffer.split_last_mut() else { + return Err(Error::Overrun); + }; + + if frame.send_start() { // Send a START condition and set ACK bit T::regs().cr1().modify(|reg| { reg.set_start(true); @@ -237,49 +320,45 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Clear condition by reading SR2 let _ = T::regs().sr2().read(); + } - // Receive bytes into buffer - for c in buffer { - *c = self.recv_byte(timeout)?; - } + // Receive bytes into buffer + for c in buffer { + *c = self.recv_byte(timeout)?; + } - // Prepare to send NACK then STOP after next byte - T::regs().cr1().modify(|reg| { + // Prepare to send NACK then STOP after next byte + T::regs().cr1().modify(|reg| { + if frame.send_nack() { reg.set_ack(false); + } + if frame.send_stop() { reg.set_stop(true); - }); + } + }); - // Receive last byte - *last = self.recv_byte(timeout)?; + // Receive last byte + *last = self.recv_byte(timeout)?; + if frame.send_stop() { // Wait for the STOP to be sent. while T::regs().cr1().read().stop() { timeout.check()?; } - - // Fallthrough is success - Ok(()) - } else { - Err(Error::Overrun) } + + // Fallthrough is success + Ok(()) } /// Blocking read. pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> { - self.blocking_read_timeout(addr, read, self.timeout()) + self.blocking_read_timeout(addr, read, self.timeout(), FrameOptions::FirstAndLastFrame) } /// Blocking write. pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> { - let timeout = self.timeout(); - - self.write_bytes(addr, write, timeout)?; - // Send a STOP condition - T::regs().cr1().modify(|reg| reg.set_stop(true)); - // Wait for STOP condition to transmit. - while T::regs().cr1().read().stop() { - timeout.check()?; - } + self.write_bytes(addr, write, self.timeout(), FrameOptions::FirstAndLastFrame)?; // Fallthrough is success Ok(()) @@ -289,8 +368,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { let timeout = self.timeout(); - self.write_bytes(addr, write, timeout)?; - self.blocking_read_timeout(addr, read, timeout)?; + self.write_bytes(addr, write, timeout, FrameOptions::FirstFrame)?; + self.blocking_read_timeout(addr, read, timeout, FrameOptions::FirstAndLastFrame)?; Ok(()) }