diff --git a/hal/CHANGELOG.md b/hal/CHANGELOG.md index c584c7c3fc7c..cbe9876a4d61 100644 --- a/hal/CHANGELOG.md +++ b/hal/CHANGELOG.md @@ -1,5 +1,6 @@ # Unreleased Changes +- Fix I2C transaction to be as continuous as possible according to `embedded-hal` specification - Allow configuring USB clock with `GenericClockController` on atsamd11 - fix samd51j not having i2s support - remove i2s functionality for samd51g since it does not have it diff --git a/hal/src/sercom/i2c.rs b/hal/src/sercom/i2c.rs index c45acdd3d729..2e2704db5b13 100644 --- a/hal/src/sercom/i2c.rs +++ b/hal/src/sercom/i2c.rs @@ -370,11 +370,27 @@ impl I2c { self.config.as_mut().registers.do_write(addr, bytes) } + /// Continue a write operation that was issued before with + /// [`do_write`](Self::do_write) or [`continue_write`](Self::continue_write) + /// without a repeated start condition in between + #[inline] + fn continue_write(&mut self, bytes: &[u8]) -> Result<(), Error> { + self.config.as_mut().registers.continue_write(bytes) + } + #[inline] fn do_read(&mut self, addr: u8, bytes: &mut [u8]) -> Result<(), Error> { self.config.as_mut().registers.do_read(addr, bytes) } + /// Continue a read operation that was issued before with + /// [`do_read`](Self::do_read) or [`continue_read`](Self::continue_read) + /// without a repeated start condition in between + #[inline] + fn continue_read(&mut self, bytes: &mut [u8]) -> Result<(), Error> { + self.config.as_mut().registers.continue_read(bytes) + } + #[inline] fn do_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { self.config diff --git a/hal/src/sercom/i2c/impl_ehal.rs b/hal/src/sercom/i2c/impl_ehal.rs index 6b4c19297ae7..ed775501d40f 100644 --- a/hal/src/sercom/i2c/impl_ehal.rs +++ b/hal/src/sercom/i2c/impl_ehal.rs @@ -25,19 +25,40 @@ impl i2c::I2c for I2c { address: u8, operations: &mut [i2c::Operation<'_>], ) -> Result<(), Self::Error> { + /// Helper type for keeping track of the type of operation that was + /// executed last + #[derive(Clone, Copy)] + enum Operation { + Read, + Write, + } + + // Keep track of the last executed operation type. The method + // specification demands, that no repeated start condition is sent + // between adjacent operations of the same type. + let mut last_op = None; for op in operations { match op { i2c::Operation::Read(buf) => { - self.do_read(address, buf)?; - self.cmd_stop(); + if let Some(Operation::Read) = last_op { + self.continue_read(buf)?; + } else { + self.do_read(address, buf)?; + last_op = Some(Operation::Read); + } } i2c::Operation::Write(bytes) => { - self.do_write(address, bytes)?; - self.cmd_stop(); + if let Some(Operation::Write) = last_op { + self.continue_write(bytes)?; + } else { + self.do_write(address, bytes)?; + last_op = Some(Operation::Write); + } } } } + self.cmd_stop(); Ok(()) } diff --git a/hal/src/sercom/i2c/reg.rs b/hal/src/sercom/i2c/reg.rs index 7044c6a50083..7111c569c5d8 100644 --- a/hal/src/sercom/i2c/reg.rs +++ b/hal/src/sercom/i2c/reg.rs @@ -401,12 +401,28 @@ impl Registers { self.send_bytes(bytes) } + /// Continue a write operation that was issued before with + /// [`do_write`](Self::do_write) or [`continue_write`](Self::continue_write) + /// without a repeated start condition in between + #[inline] + pub(super) fn continue_write(&mut self, bytes: &[u8]) -> Result<(), Error> { + self.send_bytes(bytes) + } + #[inline] pub(super) fn do_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { self.start_read_blocking(addr)?; self.fill_buffer(buffer) } + /// Continue a read operation that was issued before with + /// [`do_read`](Self::do_read) or [`continue_read`](Self::continue_read) + /// without a repeated start condition in between + #[inline] + pub(super) fn continue_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.fill_buffer(buffer) + } + #[inline] pub(super) fn do_write_read( &mut self,