From 7044e53af45e472d52d6e523bddf5632f0375487 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 18 Dec 2023 18:24:55 +0100 Subject: [PATCH] stm32/i2c: remove _timeout public API, share more code between v1/v2. --- embassy-stm32/src/i2c/mod.rs | 164 +++++++++++- embassy-stm32/src/i2c/v1.rs | 145 +++-------- embassy-stm32/src/i2c/v2.rs | 470 +++++++---------------------------- 3 files changed, 268 insertions(+), 511 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index a8dc8e0e..0af291e9 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -1,17 +1,23 @@ #![macro_use] -use core::marker::PhantomData; - -use crate::dma::NoDma; -use crate::interrupt; - #[cfg_attr(i2c_v1, path = "v1.rs")] #[cfg_attr(i2c_v2, path = "v2.rs")] mod _version; -pub use _version::*; -use embassy_sync::waitqueue::AtomicWaker; -use crate::peripherals; +use core::future::Future; +use core::marker::PhantomData; + +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; +#[cfg(feature = "time")] +use embassy_time::{Duration, Instant}; + +use crate::dma::NoDma; +use crate::gpio::sealed::AFType; +use crate::gpio::Pull; +use crate::interrupt::typelevel::Interrupt; +use crate::time::Hertz; +use crate::{interrupt, peripherals}; /// I2C error. #[derive(Debug, PartialEq, Eq)] @@ -33,6 +39,148 @@ pub enum Error { ZeroLengthTransfer, } +/// I2C config +#[non_exhaustive] +#[derive(Copy, Clone)] +pub struct Config { + /// Enable internal pullup on SDA. + /// + /// Using external pullup resistors is recommended for I2C. If you do + /// have external pullups you should not enable this. + pub sda_pullup: bool, + /// Enable internal pullup on SCL. + /// + /// Using external pullup resistors is recommended for I2C. If you do + /// have external pullups you should not enable this. + pub scl_pullup: bool, + /// Timeout. + #[cfg(feature = "time")] + pub timeout: embassy_time::Duration, +} + +impl Default for Config { + fn default() -> Self { + Self { + sda_pullup: false, + scl_pullup: false, + #[cfg(feature = "time")] + timeout: embassy_time::Duration::from_millis(1000), + } + } +} + +/// I2C driver. +pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> { + _peri: PeripheralRef<'d, T>, + #[allow(dead_code)] + tx_dma: PeripheralRef<'d, TXDMA>, + #[allow(dead_code)] + rx_dma: PeripheralRef<'d, RXDMA>, + #[cfg(feature = "time")] + timeout: Duration, +} + +impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { + /// Create a new I2C driver. + pub fn new( + peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + _irq: impl interrupt::typelevel::Binding> + + interrupt::typelevel::Binding> + + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + freq: Hertz, + config: Config, + ) -> Self { + into_ref!(peri, scl, sda, tx_dma, rx_dma); + + T::enable_and_reset(); + + scl.set_as_af_pull( + scl.af_num(), + AFType::OutputOpenDrain, + match config.scl_pullup { + true => Pull::Up, + false => Pull::None, + }, + ); + sda.set_as_af_pull( + sda.af_num(), + AFType::OutputOpenDrain, + match config.sda_pullup { + true => Pull::Up, + false => Pull::None, + }, + ); + + unsafe { T::EventInterrupt::enable() }; + unsafe { T::ErrorInterrupt::enable() }; + + let mut this = Self { + _peri: peri, + tx_dma, + rx_dma, + #[cfg(feature = "time")] + timeout: config.timeout, + }; + + this.init(freq, config); + + this + } + + fn timeout(&self) -> Timeout { + Timeout { + #[cfg(feature = "time")] + deadline: Instant::now() + self.timeout, + } + } +} + +#[derive(Copy, Clone)] +struct Timeout { + #[cfg(feature = "time")] + deadline: Instant, +} + +#[allow(dead_code)] +impl Timeout { + #[cfg(not(feature = "time"))] + #[inline] + fn check(self) -> Result<(), Error> { + Ok(()) + } + + #[cfg(feature = "time")] + #[inline] + fn check(self) -> Result<(), Error> { + if Instant::now() > self.deadline { + Err(Error::Timeout) + } else { + Ok(()) + } + } + + #[cfg(not(feature = "time"))] + #[inline] + fn with(self, fut: impl Future>) -> impl Future> { + fut + } + + #[cfg(feature = "time")] + #[inline] + fn with(self, fut: impl Future>) -> impl Future> { + use futures::FutureExt; + + embassy_futures::select::select(embassy_time::Timer::at(self.deadline), fut).map(|r| match r { + embassy_futures::select::Either::First(_) => Err(Error::Timeout), + embassy_futures::select::Either::Second(r) => r, + }) + } +} + pub(crate) mod sealed { use super::*; diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index b62ee824..84802d12 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -1,20 +1,14 @@ use core::future::poll_fn; -use core::marker::PhantomData; use core::task::Poll; use embassy_embedded_hal::SetConfig; use embassy_futures::select::{select, Either}; use embassy_hal_internal::drop::OnDrop; -use embassy_hal_internal::{into_ref, PeripheralRef}; use super::*; -use crate::dma::{NoDma, Transfer}; -use crate::gpio::sealed::AFType; -use crate::gpio::Pull; -use crate::interrupt::typelevel::Interrupt; +use crate::dma::Transfer; use crate::pac::i2c; use crate::time::Hertz; -use crate::{interrupt, Peripheral}; pub unsafe fn on_interrupt() { let regs = T::regs(); @@ -30,55 +24,8 @@ pub unsafe fn on_interrupt() { }); } -#[non_exhaustive] -#[derive(Copy, Clone, Default)] -pub struct Config { - pub sda_pullup: bool, - pub scl_pullup: bool, -} - -pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> { - phantom: PhantomData<&'d mut T>, - #[allow(dead_code)] - tx_dma: PeripheralRef<'d, TXDMA>, - #[allow(dead_code)] - rx_dma: PeripheralRef<'d, RXDMA>, -} - impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { - pub fn new( - _peri: impl Peripheral

+ 'd, - scl: impl Peripheral

> + 'd, - sda: impl Peripheral

> + 'd, - _irq: impl interrupt::typelevel::Binding> - + interrupt::typelevel::Binding> - + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, - freq: Hertz, - config: Config, - ) -> Self { - into_ref!(scl, sda, tx_dma, rx_dma); - - T::enable_and_reset(); - - scl.set_as_af_pull( - scl.af_num(), - AFType::OutputOpenDrain, - match config.scl_pullup { - true => Pull::Up, - false => Pull::None, - }, - ); - sda.set_as_af_pull( - sda.af_num(), - AFType::OutputOpenDrain, - match config.sda_pullup { - true => Pull::Up, - false => Pull::None, - }, - ); - + pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { T::regs().cr1().modify(|reg| { reg.set_pe(false); //reg.set_anfoff(false); @@ -101,15 +48,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { T::regs().cr1().modify(|reg| { reg.set_pe(true); }); - - unsafe { T::EventInterrupt::enable() }; - unsafe { T::ErrorInterrupt::enable() }; - - Self { - phantom: PhantomData, - tx_dma, - rx_dma, - } } fn check_and_clear_error_flags() -> Result { @@ -169,12 +107,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(sr1) } - fn write_bytes( - &mut self, - addr: u8, - bytes: &[u8], - check_timeout: impl Fn() -> Result<(), Error>, - ) -> Result<(), Error> { + fn write_bytes(&mut self, addr: u8, bytes: &[u8], timeout: Timeout) -> Result<(), Error> { // Send a START condition T::regs().cr1().modify(|reg| { @@ -183,7 +116,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Wait until START condition was generated while !Self::check_and_clear_error_flags()?.start() { - check_timeout()?; + timeout.check()?; } // Also wait until signalled we're master and everything is waiting for us @@ -193,7 +126,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let sr2 = T::regs().sr2().read(); !sr2.msl() && !sr2.busy() } { - check_timeout()?; + timeout.check()?; } // Set up current address, we're trying to talk to @@ -203,7 +136,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // 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() { - check_timeout()?; + timeout.check()?; } // Clear condition by reading SR2 @@ -211,20 +144,20 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Send bytes for c in bytes { - self.send_byte(*c, &check_timeout)?; + self.send_byte(*c, timeout)?; } // Fallthrough is success Ok(()) } - fn send_byte(&self, byte: u8, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + fn send_byte(&self, byte: u8, timeout: Timeout) -> Result<(), Error> { // Wait until we're ready for sending while { // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. !Self::check_and_clear_error_flags()?.txe() } { - check_timeout()?; + timeout.check()?; } // Push out a byte of data @@ -235,32 +168,27 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Check for any potential error conditions. !Self::check_and_clear_error_flags()?.btf() } { - check_timeout()?; + timeout.check()?; } Ok(()) } - fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result { + fn recv_byte(&self, timeout: Timeout) -> Result { while { // Check for any potential error conditions. Self::check_and_clear_error_flags()?; !T::regs().sr1().read().rxne() } { - check_timeout()?; + timeout.check()?; } let value = T::regs().dr().read().dr(); Ok(value) } - pub fn blocking_read_timeout( - &mut self, - addr: u8, - buffer: &mut [u8], - check_timeout: impl Fn() -> Result<(), Error>, - ) -> Result<(), Error> { + fn blocking_read_timeout(&mut self, addr: u8, buffer: &mut [u8], timeout: Timeout) -> Result<(), Error> { if let Some((last, buffer)) = buffer.split_last_mut() { // Send a START condition and set ACK bit T::regs().cr1().modify(|reg| { @@ -270,7 +198,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Wait until START condition was generated while !Self::check_and_clear_error_flags()?.start() { - check_timeout()?; + timeout.check()?; } // Also wait until signalled we're master and everything is waiting for us @@ -278,7 +206,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let sr2 = T::regs().sr2().read(); !sr2.msl() && !sr2.busy() } { - check_timeout()?; + timeout.check()?; } // Set up current address, we're trying to talk to @@ -287,7 +215,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Wait until address was sent // Wait for the address to be acknowledged while !Self::check_and_clear_error_flags()?.addr() { - check_timeout()?; + timeout.check()?; } // Clear condition by reading SR2 @@ -295,7 +223,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Receive bytes into buffer for c in buffer { - *c = self.recv_byte(&check_timeout)?; + *c = self.recv_byte(timeout)?; } // Prepare to send NACK then STOP after next byte @@ -305,11 +233,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }); // Receive last byte - *last = self.recv_byte(&check_timeout)?; + *last = self.recv_byte(timeout)?; // Wait for the STOP to be sent. while T::regs().cr1().read().stop() { - check_timeout()?; + timeout.check()?; } // Fallthrough is success @@ -320,48 +248,33 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> { - self.blocking_read_timeout(addr, read, || Ok(())) + self.blocking_read_timeout(addr, read, self.timeout()) } - pub fn blocking_write_timeout( - &mut self, - addr: u8, - write: &[u8], - check_timeout: impl Fn() -> Result<(), Error>, - ) -> Result<(), Error> { - self.write_bytes(addr, write, &check_timeout)?; + 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() { - check_timeout()?; + timeout.check()?; } // Fallthrough is success Ok(()) } - pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> { - self.blocking_write_timeout(addr, write, || Ok(())) - } + pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + let timeout = self.timeout(); - pub fn blocking_write_read_timeout( - &mut self, - addr: u8, - write: &[u8], - read: &mut [u8], - check_timeout: impl Fn() -> Result<(), Error>, - ) -> Result<(), Error> { - self.write_bytes(addr, write, &check_timeout)?; - self.blocking_read_timeout(addr, read, &check_timeout)?; + self.write_bytes(addr, write, timeout)?; + self.blocking_read_timeout(addr, read, timeout)?; Ok(()) } - pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { - self.blocking_write_read_timeout(addr, write, read, || Ok(())) - } - // Async #[inline] // pretty sure this should always be inlined diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 8c20e1c5..bd3abaac 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -4,37 +4,13 @@ use core::task::Poll; use embassy_embedded_hal::SetConfig; use embassy_hal_internal::drop::OnDrop; -use embassy_hal_internal::{into_ref, PeripheralRef}; -#[cfg(feature = "time")] -use embassy_time::{Duration, Instant}; use super::*; -use crate::dma::{NoDma, Transfer}; -use crate::gpio::sealed::AFType; -use crate::gpio::Pull; -use crate::interrupt::typelevel::Interrupt; +use crate::dma::Transfer; use crate::pac::i2c; use crate::time::Hertz; -use crate::{interrupt, Peripheral}; -#[cfg(feature = "time")] -fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { - let deadline = Instant::now() + timeout; - move || { - if Instant::now() > deadline { - Err(Error::Timeout) - } else { - Ok(()) - } - } -} - -#[cfg(not(feature = "time"))] -pub fn no_timeout_fn() -> impl Fn() -> Result<(), Error> { - move || Ok(()) -} - -pub unsafe fn on_interrupt() { +pub(crate) unsafe fn on_interrupt() { let regs = T::regs(); let isr = regs.isr().read(); @@ -48,70 +24,8 @@ pub unsafe fn on_interrupt() { }); } -#[non_exhaustive] -#[derive(Copy, Clone)] -pub struct Config { - pub sda_pullup: bool, - pub scl_pullup: bool, - #[cfg(feature = "time")] - pub transaction_timeout: Duration, -} - -impl Default for Config { - fn default() -> Self { - Self { - sda_pullup: false, - scl_pullup: false, - #[cfg(feature = "time")] - transaction_timeout: Duration::from_millis(100), - } - } -} - -pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> { - _peri: PeripheralRef<'d, T>, - #[allow(dead_code)] - tx_dma: PeripheralRef<'d, TXDMA>, - #[allow(dead_code)] - rx_dma: PeripheralRef<'d, RXDMA>, - #[cfg(feature = "time")] - timeout: Duration, -} - impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { - pub fn new( - peri: impl Peripheral

+ 'd, - scl: impl Peripheral

> + 'd, - sda: impl Peripheral

> + 'd, - _irq: impl interrupt::typelevel::Binding> - + interrupt::typelevel::Binding> - + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, - freq: Hertz, - config: Config, - ) -> Self { - into_ref!(peri, scl, sda, tx_dma, rx_dma); - - T::enable_and_reset(); - - scl.set_as_af_pull( - scl.af_num(), - AFType::OutputOpenDrain, - match config.scl_pullup { - true => Pull::Up, - false => Pull::None, - }, - ); - sda.set_as_af_pull( - sda.af_num(), - AFType::OutputOpenDrain, - match config.sda_pullup { - true => Pull::Up, - false => Pull::None, - }, - ); - + pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { T::regs().cr1().modify(|reg| { reg.set_pe(false); reg.set_anfoff(false); @@ -130,17 +44,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { T::regs().cr1().modify(|reg| { reg.set_pe(true); }); - - unsafe { T::EventInterrupt::enable() }; - unsafe { T::ErrorInterrupt::enable() }; - - Self { - _peri: peri, - tx_dma, - rx_dma, - #[cfg(feature = "time")] - timeout: config.transaction_timeout, - } } fn master_stop(&mut self) { @@ -153,7 +56,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { stop: Stop, reload: bool, restart: bool, - check_timeout: impl Fn() -> Result<(), Error>, + timeout: Timeout, ) -> Result<(), Error> { assert!(length < 256); @@ -162,7 +65,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // automatically. This could be up to 50% of a bus // cycle (ie. up to 0.5/freq) while T::regs().cr2().read().start() { - check_timeout()?; + timeout.check()?; } } @@ -189,20 +92,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - fn master_write( - address: u8, - length: usize, - stop: Stop, - reload: bool, - check_timeout: impl Fn() -> Result<(), Error>, - ) -> Result<(), Error> { + fn master_write(address: u8, length: usize, stop: Stop, reload: bool, timeout: Timeout) -> Result<(), Error> { assert!(length < 256); // Wait for any previous address sequence to end // automatically. This could be up to 50% of a bus // cycle (ie. up to 0.5/freq) while T::regs().cr2().read().start() { - check_timeout()?; + timeout.check()?; } let reload = if reload { @@ -227,15 +124,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - fn master_continue( - length: usize, - reload: bool, - check_timeout: impl Fn() -> Result<(), Error>, - ) -> Result<(), Error> { + fn master_continue(length: usize, reload: bool, timeout: Timeout) -> Result<(), Error> { assert!(length < 256 && length > 0); while !T::regs().isr().read().tcr() { - check_timeout()?; + timeout.check()?; } let reload = if reload { @@ -261,7 +154,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } - fn wait_txe(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + fn wait_txe(&self, timeout: Timeout) -> Result<(), Error> { loop { let isr = T::regs().isr().read(); if isr.txe() { @@ -278,11 +171,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Nack); } - check_timeout()?; + timeout.check()?; } } - fn wait_rxne(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + fn wait_rxne(&self, timeout: Timeout) -> Result<(), Error> { loop { let isr = T::regs().isr().read(); if isr.rxne() { @@ -299,11 +192,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Nack); } - check_timeout()?; + timeout.check()?; } } - fn wait_tc(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + fn wait_tc(&self, timeout: Timeout) -> Result<(), Error> { loop { let isr = T::regs().isr().read(); if isr.tc() { @@ -320,17 +213,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Nack); } - check_timeout()?; + timeout.check()?; } } - fn read_internal( - &mut self, - address: u8, - read: &mut [u8], - restart: bool, - check_timeout: impl Fn() -> Result<(), Error>, - ) -> Result<(), Error> { + fn read_internal(&mut self, address: u8, read: &mut [u8], restart: bool, timeout: Timeout) -> Result<(), Error> { let completed_chunks = read.len() / 255; let total_chunks = if completed_chunks * 255 == read.len() { completed_chunks @@ -345,17 +232,17 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Stop::Automatic, last_chunk_idx != 0, restart, - &check_timeout, + timeout, )?; for (number, chunk) in read.chunks_mut(255).enumerate() { if number != 0 { - Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?; + Self::master_continue(chunk.len(), number != last_chunk_idx, timeout)?; } for byte in chunk { // Wait until we have received something - self.wait_rxne(&check_timeout)?; + self.wait_rxne(timeout)?; *byte = T::regs().rxdr().read().rxdata(); } @@ -363,13 +250,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - fn write_internal( - &mut self, - address: u8, - write: &[u8], - send_stop: bool, - check_timeout: impl Fn() -> Result<(), Error>, - ) -> Result<(), Error> { + fn write_internal(&mut self, address: u8, write: &[u8], send_stop: bool, timeout: Timeout) -> Result<(), Error> { let completed_chunks = write.len() / 255; let total_chunks = if completed_chunks * 255 == write.len() { completed_chunks @@ -386,7 +267,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { write.len().min(255), Stop::Software, last_chunk_idx != 0, - &check_timeout, + timeout, ) { if send_stop { self.master_stop(); @@ -396,14 +277,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { for (number, chunk) in write.chunks(255).enumerate() { if number != 0 { - Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?; + Self::master_continue(chunk.len(), number != last_chunk_idx, timeout)?; } for byte in chunk { // Wait until we are allowed to send data // (START has been ACKed or last byte when // through) - if let Err(err) = self.wait_txe(&check_timeout) { + if let Err(err) = self.wait_txe(timeout) { if send_stop { self.master_stop(); } @@ -414,7 +295,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } // Wait until the write finishes - let result = self.wait_tc(&check_timeout); + let result = self.wait_tc(timeout); if send_stop { self.master_stop(); } @@ -427,7 +308,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { write: &[u8], first_slice: bool, last_slice: bool, - check_timeout: impl Fn() -> Result<(), Error>, + timeout: Timeout, ) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, @@ -473,10 +354,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { total_len.min(255), Stop::Software, (total_len > 255) || !last_slice, - &check_timeout, + timeout, )?; } else { - Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, &check_timeout)?; + Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, timeout)?; T::regs().cr1().modify(|w| w.set_tcie(true)); } } else if !(isr.tcr() || isr.tc()) { @@ -487,7 +368,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } else { let last_piece = (remaining_len <= 255) && last_slice; - if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { + if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) { return Poll::Ready(Err(e)); } T::regs().cr1().modify(|w| w.set_tcie(true)); @@ -502,7 +383,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if last_slice { // This should be done already - self.wait_tc(&check_timeout)?; + self.wait_tc(timeout)?; self.master_stop(); } @@ -516,7 +397,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { address: u8, buffer: &mut [u8], restart: bool, - check_timeout: impl Fn() -> Result<(), Error>, + timeout: Timeout, ) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, @@ -558,7 +439,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Stop::Software, total_len > 255, restart, - &check_timeout, + timeout, )?; } else if !(isr.tcr() || isr.tc()) { // poll_fn was woken without an interrupt present @@ -568,7 +449,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } else { let last_piece = remaining_len <= 255; - if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { + if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) { return Poll::Ready(Err(e)); } T::regs().cr1().modify(|w| w.set_tcie(true)); @@ -582,7 +463,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { dma_transfer.await; // This should be done already - self.wait_tc(&check_timeout)?; + self.wait_tc(timeout)?; self.master_stop(); drop(on_drop); @@ -592,41 +473,31 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ========================= // Async public API - #[cfg(feature = "time")] - pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { - if write.is_empty() { - self.write_internal(address, write, true, timeout_fn(self.timeout)) - } else { - embassy_time::with_timeout( - self.timeout, - self.write_dma_internal(address, write, true, true, timeout_fn(self.timeout)), - ) - .await - .unwrap_or(Err(Error::Timeout)) - } - } - #[cfg(not(feature = "time"))] + /// Write. pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { + let timeout = self.timeout(); if write.is_empty() { - self.write_internal(address, write, true, no_timeout_fn()) + self.write_internal(address, write, true, timeout) } else { - self.write_dma_internal(address, write, true, true, no_timeout_fn()) + timeout + .with(self.write_dma_internal(address, write, true, true, timeout)) .await } } - #[cfg(feature = "time")] + /// Write multiple buffers. + /// + /// The buffers are concatenated in a single write transaction. pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { + let timeout = self.timeout(); + if write.is_empty() { return Err(Error::ZeroLengthTransfer); } @@ -638,123 +509,49 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let next = iter.next(); let is_last = next.is_none(); - embassy_time::with_timeout( - self.timeout, - self.write_dma_internal(address, c, first, is_last, timeout_fn(self.timeout)), - ) - .await - .unwrap_or(Err(Error::Timeout))?; + let fut = self.write_dma_internal(address, c, first, is_last, timeout); + timeout.with(fut).await?; first = false; current = next; } Ok(()) } - #[cfg(not(feature = "time"))] - pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { - if write.is_empty() { - return Err(Error::ZeroLengthTransfer); - } - let mut iter = write.iter(); - - let mut first = true; - let mut current = iter.next(); - while let Some(c) = current { - let next = iter.next(); - let is_last = next.is_none(); - - self.write_dma_internal(address, c, first, is_last, no_timeout_fn()) - .await?; - first = false; - current = next; - } - Ok(()) - } - - #[cfg(feature = "time")] + /// Read. pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, { + let timeout = self.timeout(); + if buffer.is_empty() { - self.read_internal(address, buffer, false, timeout_fn(self.timeout)) + self.read_internal(address, buffer, false, timeout) } else { - embassy_time::with_timeout( - self.timeout, - self.read_dma_internal(address, buffer, false, timeout_fn(self.timeout)), - ) - .await - .unwrap_or(Err(Error::Timeout)) + let fut = self.read_dma_internal(address, buffer, false, timeout); + timeout.with(fut).await } } - #[cfg(not(feature = "time"))] - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - { - if buffer.is_empty() { - self.read_internal(address, buffer, false, no_timeout_fn()) - } else { - self.read_dma_internal(address, buffer, false, no_timeout_fn()).await - } - } - - #[cfg(feature = "time")] + /// Write, restart, read. pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> where TXDMA: super::TxDma, RXDMA: super::RxDma, { - let start_instant = Instant::now(); - let check_timeout = timeout_fn(self.timeout); + let timeout = self.timeout(); + if write.is_empty() { - self.write_internal(address, write, false, &check_timeout)?; + self.write_internal(address, write, false, timeout)?; } else { - embassy_time::with_timeout( - self.timeout, - self.write_dma_internal(address, write, true, true, &check_timeout), - ) - .await - .unwrap_or(Err(Error::Timeout))?; - } - - let time_left_until_timeout = self.timeout - Instant::now().duration_since(start_instant); - - if read.is_empty() { - self.read_internal(address, read, true, &check_timeout)?; - } else { - embassy_time::with_timeout( - time_left_until_timeout, - self.read_dma_internal(address, read, true, &check_timeout), - ) - .await - .unwrap_or(Err(Error::Timeout))?; - } - - Ok(()) - } - - #[cfg(not(feature = "time"))] - pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> - where - TXDMA: super::TxDma, - RXDMA: super::RxDma, - { - let no_timeout = no_timeout_fn(); - if write.is_empty() { - self.write_internal(address, write, false, &no_timeout)?; - } else { - self.write_dma_internal(address, write, true, true, &no_timeout).await?; + let fut = self.write_dma_internal(address, write, true, true, timeout); + timeout.with(fut).await?; } if read.is_empty() { - self.read_internal(address, read, true, &no_timeout)?; + self.read_internal(address, read, true, timeout)?; } else { - self.read_dma_internal(address, read, true, &no_timeout).await?; + let fut = self.read_dma_internal(address, read, true, timeout); + timeout.with(fut).await?; } Ok(()) @@ -763,105 +560,35 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ========================= // Blocking public API - #[cfg(feature = "time")] - pub fn blocking_read_timeout(&mut self, address: u8, read: &mut [u8], timeout: Duration) -> Result<(), Error> { - self.read_internal(address, read, false, timeout_fn(timeout)) - // Automatic Stop - } - - #[cfg(not(feature = "time"))] - pub fn blocking_read_timeout( - &mut self, - address: u8, - read: &mut [u8], - check_timeout: impl Fn() -> Result<(), Error>, - ) -> Result<(), Error> { - self.read_internal(address, read, false, check_timeout) - // Automatic Stop - } - - #[cfg(feature = "time")] + /// Blocking read. pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { - self.blocking_read_timeout(address, read, self.timeout) - } - - #[cfg(not(feature = "time"))] - pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { - self.blocking_read_timeout(address, read, || Ok(())) - } - - #[cfg(feature = "time")] - pub fn blocking_write_timeout(&mut self, address: u8, write: &[u8], timeout: Duration) -> Result<(), Error> { - self.write_internal(address, write, true, timeout_fn(timeout)) - } - - #[cfg(not(feature = "time"))] - pub fn blocking_write_timeout( - &mut self, - address: u8, - write: &[u8], - check_timeout: impl Fn() -> Result<(), Error>, - ) -> Result<(), Error> { - self.write_internal(address, write, true, check_timeout) - } - - #[cfg(feature = "time")] - pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { - self.blocking_write_timeout(address, write, self.timeout) - } - - #[cfg(not(feature = "time"))] - pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { - self.blocking_write_timeout(address, write, || Ok(())) - } - - #[cfg(feature = "time")] - pub fn blocking_write_read_timeout( - &mut self, - address: u8, - write: &[u8], - read: &mut [u8], - timeout: Duration, - ) -> Result<(), Error> { - let check_timeout = timeout_fn(timeout); - self.write_internal(address, write, false, &check_timeout)?; - self.read_internal(address, read, true, &check_timeout) + self.read_internal(address, read, false, self.timeout()) // Automatic Stop } - #[cfg(not(feature = "time"))] - pub fn blocking_write_read_timeout( - &mut self, - address: u8, - write: &[u8], - read: &mut [u8], - check_timeout: impl Fn() -> Result<(), Error>, - ) -> Result<(), Error> { - self.write_internal(address, write, false, &check_timeout)?; - self.read_internal(address, read, true, &check_timeout) + /// Blocking write. + pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { + self.write_internal(address, write, true, self.timeout()) + } + + /// Blocking write, restart, read. + pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + let timeout = self.timeout(); + self.write_internal(address, write, false, timeout)?; + self.read_internal(address, read, true, timeout) // Automatic Stop } - #[cfg(feature = "time")] - pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { - self.blocking_write_read_timeout(address, write, read, self.timeout) - } - - #[cfg(not(feature = "time"))] - pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { - self.blocking_write_read_timeout(address, write, read, || Ok(())) - } - - fn blocking_write_vectored_with_timeout( - &mut self, - address: u8, - write: &[&[u8]], - check_timeout: impl Fn() -> Result<(), Error>, - ) -> Result<(), Error> { + /// Blocking write multiple buffers. + /// + /// The buffers are concatenated in a single write transaction. + pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { if write.is_empty() { return Err(Error::ZeroLengthTransfer); } + let timeout = self.timeout(); + let first_length = write[0].len(); let last_slice_index = write.len() - 1; @@ -870,7 +597,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { first_length.min(255), Stop::Software, (first_length > 255) || (last_slice_index != 0), - &check_timeout, + timeout, ) { self.master_stop(); return Err(err); @@ -890,7 +617,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if let Err(err) = Self::master_continue( slice_len.min(255), (idx != last_slice_index) || (slice_len > 255), - &check_timeout, + timeout, ) { self.master_stop(); return Err(err); @@ -902,7 +629,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if let Err(err) = Self::master_continue( chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index), - &check_timeout, + timeout, ) { self.master_stop(); return Err(err); @@ -913,7 +640,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Wait until we are allowed to send data // (START has been ACKed or last byte when // through) - if let Err(err) = self.wait_txe(&check_timeout) { + if let Err(err) = self.wait_txe(timeout) { self.master_stop(); return Err(err); } @@ -925,41 +652,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } // Wait until the write finishes - let result = self.wait_tc(&check_timeout); + let result = self.wait_tc(timeout); self.master_stop(); result } - - #[cfg(feature = "time")] - pub fn blocking_write_vectored_timeout( - &mut self, - address: u8, - write: &[&[u8]], - timeout: Duration, - ) -> Result<(), Error> { - let check_timeout = timeout_fn(timeout); - self.blocking_write_vectored_with_timeout(address, write, check_timeout) - } - - #[cfg(not(feature = "time"))] - pub fn blocking_write_vectored_timeout( - &mut self, - address: u8, - write: &[&[u8]], - check_timeout: impl Fn() -> Result<(), Error>, - ) -> Result<(), Error> { - self.blocking_write_vectored_with_timeout(address, write, check_timeout) - } - - #[cfg(feature = "time")] - pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { - self.blocking_write_vectored_timeout(address, write, self.timeout) - } - - #[cfg(not(feature = "time"))] - pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { - self.blocking_write_vectored_timeout(address, write, || Ok(())) - } } impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {