diff options
| author | Andres Oliva <[email protected]> | 2023-10-10 18:10:53 +0200 |
|---|---|---|
| committer | Andres Oliva <[email protected]> | 2023-10-10 18:20:46 +0200 |
| commit | cd12c9cbceb0ba90a493194d8200b1d8e98bba1b (patch) | |
| tree | ad6f45f8dbe7de2b6e93e9678fdebef4993bcb46 | |
| parent | eff73d6dfa4c5920a55a5ee2bf5c0b2ef68fbae1 (diff) | |
stm32: add timeout to I2C driver
| -rw-r--r-- | embassy-stm32/Cargo.toml | 8 | ||||
| -rw-r--r-- | embassy-stm32/src/i2c/mod.rs | 5 | ||||
| -rw-r--r-- | embassy-stm32/src/i2c/timeout.rs | 209 | ||||
| -rw-r--r-- | embassy-stm32/src/i2c/v2.rs | 129 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/i2c.rs | 8 | ||||
| -rw-r--r-- | examples/stm32h5/src/bin/i2c.rs | 8 | ||||
| -rw-r--r-- | examples/stm32h7/src/bin/i2c.rs | 8 |
7 files changed, 85 insertions, 290 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 70e8f2e29..a4e4e51d6 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" | |||
| 8 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" | 8 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" |
| 9 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" | 9 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" |
| 10 | 10 | ||
| 11 | features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"] | 11 | features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any"] |
| 12 | flavors = [ | 12 | flavors = [ |
| 13 | { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, | 13 | { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, |
| 14 | { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, | 14 | { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, |
| @@ -33,7 +33,7 @@ flavors = [ | |||
| 33 | 33 | ||
| 34 | [dependencies] | 34 | [dependencies] |
| 35 | embassy-sync = { version = "0.3.0", path = "../embassy-sync" } | 35 | embassy-sync = { version = "0.3.0", path = "../embassy-sync" } |
| 36 | embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } | 36 | embassy-time = { version = "0.1.3", path = "../embassy-time" } |
| 37 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 37 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 38 | embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } | 38 | embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } |
| 39 | embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } | 39 | embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } |
| @@ -87,7 +87,7 @@ default = ["rt"] | |||
| 87 | rt = ["stm32-metapac/rt"] | 87 | rt = ["stm32-metapac/rt"] |
| 88 | 88 | ||
| 89 | ## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging | 89 | ## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging |
| 90 | defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async?/defmt-03", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"] | 90 | defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async?/defmt-03", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] |
| 91 | 91 | ||
| 92 | exti = [] | 92 | exti = [] |
| 93 | low-power = [ "dep:embassy-executor", "embassy-executor/arch-cortex-m" ] | 93 | low-power = [ "dep:embassy-executor", "embassy-executor/arch-cortex-m" ] |
| @@ -112,7 +112,7 @@ unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] | |||
| 112 | #! ## Time | 112 | #! ## Time |
| 113 | 113 | ||
| 114 | ## Enables additional driver features that depend on embassy-time | 114 | ## Enables additional driver features that depend on embassy-time |
| 115 | time = ["dep:embassy-time"] | 115 | time = [] |
| 116 | 116 | ||
| 117 | # Features starting with `_` are for internal use only. They're not intended | 117 | # Features starting with `_` are for internal use only. They're not intended |
| 118 | # to be enabled by other crates, and are not covered by semver guarantees. | 118 | # to be enabled by other crates, and are not covered by semver guarantees. |
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 62d13e909..dde1a5040 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs | |||
| @@ -7,11 +7,6 @@ use crate::interrupt; | |||
| 7 | mod _version; | 7 | mod _version; |
| 8 | pub use _version::*; | 8 | pub use _version::*; |
| 9 | 9 | ||
| 10 | #[cfg(feature = "time")] | ||
| 11 | mod timeout; | ||
| 12 | #[cfg(feature = "time")] | ||
| 13 | pub use timeout::*; | ||
| 14 | |||
| 15 | use crate::peripherals; | 10 | use crate::peripherals; |
| 16 | 11 | ||
| 17 | #[derive(Debug, PartialEq, Eq)] | 12 | #[derive(Debug, PartialEq, Eq)] |
diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs deleted file mode 100644 index 103017cd1..000000000 --- a/embassy-stm32/src/i2c/timeout.rs +++ /dev/null | |||
| @@ -1,209 +0,0 @@ | |||
| 1 | use embassy_time::{Duration, Instant}; | ||
| 2 | |||
| 3 | use super::{Error, I2c, Instance}; | ||
| 4 | |||
| 5 | /// An I2C wrapper, which provides `embassy-time` based timeouts for all `embedded-hal` trait methods. | ||
| 6 | /// | ||
| 7 | /// This is useful for recovering from a shorted bus or a device stuck in a clock stretching state. | ||
| 8 | /// A regular [I2c] would freeze until condition is removed. | ||
| 9 | pub struct TimeoutI2c<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> { | ||
| 10 | i2c: &'a mut I2c<'d, T, TXDMA, RXDMA>, | ||
| 11 | timeout: Duration, | ||
| 12 | } | ||
| 13 | |||
| 14 | fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { | ||
| 15 | let deadline = Instant::now() + timeout; | ||
| 16 | move || { | ||
| 17 | if Instant::now() > deadline { | ||
| 18 | Err(Error::Timeout) | ||
| 19 | } else { | ||
| 20 | Ok(()) | ||
| 21 | } | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { | ||
| 26 | pub fn new(i2c: &'a mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration) -> Self { | ||
| 27 | Self { i2c, timeout } | ||
| 28 | } | ||
| 29 | |||
| 30 | // ========================= | ||
| 31 | // Async public API | ||
| 32 | |||
| 33 | #[cfg(i2c_v2)] | ||
| 34 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> | ||
| 35 | where | ||
| 36 | TXDMA: crate::i2c::TxDma<T>, | ||
| 37 | { | ||
| 38 | self.write_timeout(address, write, self.timeout).await | ||
| 39 | } | ||
| 40 | |||
| 41 | #[cfg(i2c_v2)] | ||
| 42 | pub async fn write_timeout(&mut self, address: u8, write: &[u8], timeout: Duration) -> Result<(), Error> | ||
| 43 | where | ||
| 44 | TXDMA: crate::i2c::TxDma<T>, | ||
| 45 | { | ||
| 46 | self.i2c.write_timeout(address, write, timeout_fn(timeout)).await | ||
| 47 | } | ||
| 48 | |||
| 49 | #[cfg(i2c_v2)] | ||
| 50 | pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> | ||
| 51 | where | ||
| 52 | TXDMA: crate::i2c::TxDma<T>, | ||
| 53 | { | ||
| 54 | self.write_vectored_timeout(address, write, self.timeout).await | ||
| 55 | } | ||
| 56 | |||
| 57 | #[cfg(i2c_v2)] | ||
| 58 | pub async fn write_vectored_timeout(&mut self, address: u8, write: &[&[u8]], timeout: Duration) -> Result<(), Error> | ||
| 59 | where | ||
| 60 | TXDMA: crate::i2c::TxDma<T>, | ||
| 61 | { | ||
| 62 | self.i2c | ||
| 63 | .write_vectored_timeout(address, write, timeout_fn(timeout)) | ||
| 64 | .await | ||
| 65 | } | ||
| 66 | |||
| 67 | #[cfg(i2c_v2)] | ||
| 68 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> | ||
| 69 | where | ||
| 70 | RXDMA: crate::i2c::RxDma<T>, | ||
| 71 | { | ||
| 72 | self.read_timeout(address, buffer, self.timeout).await | ||
| 73 | } | ||
| 74 | |||
| 75 | #[cfg(i2c_v2)] | ||
| 76 | pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> | ||
| 77 | where | ||
| 78 | RXDMA: crate::i2c::RxDma<T>, | ||
| 79 | { | ||
| 80 | self.i2c.read_timeout(address, buffer, timeout_fn(timeout)).await | ||
| 81 | } | ||
| 82 | |||
| 83 | #[cfg(i2c_v2)] | ||
| 84 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> | ||
| 85 | where | ||
| 86 | TXDMA: super::TxDma<T>, | ||
| 87 | RXDMA: super::RxDma<T>, | ||
| 88 | { | ||
| 89 | self.write_read_timeout(address, write, read, self.timeout).await | ||
| 90 | } | ||
| 91 | |||
| 92 | #[cfg(i2c_v2)] | ||
| 93 | pub async fn write_read_timeout( | ||
| 94 | &mut self, | ||
| 95 | address: u8, | ||
| 96 | write: &[u8], | ||
| 97 | read: &mut [u8], | ||
| 98 | timeout: Duration, | ||
| 99 | ) -> Result<(), Error> | ||
| 100 | where | ||
| 101 | TXDMA: super::TxDma<T>, | ||
| 102 | RXDMA: super::RxDma<T>, | ||
| 103 | { | ||
| 104 | self.i2c | ||
| 105 | .write_read_timeout(address, write, read, timeout_fn(timeout)) | ||
| 106 | .await | ||
| 107 | } | ||
| 108 | |||
| 109 | // ========================= | ||
| 110 | // Blocking public API | ||
| 111 | |||
| 112 | /// Blocking read with a custom timeout | ||
| 113 | pub fn blocking_read_timeout(&mut self, addr: u8, read: &mut [u8], timeout: Duration) -> Result<(), Error> { | ||
| 114 | self.i2c.blocking_read_timeout(addr, read, timeout_fn(timeout)) | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Blocking read with default timeout, provided in [`TimeoutI2c::new()`] | ||
| 118 | pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> { | ||
| 119 | self.blocking_read_timeout(addr, read, self.timeout) | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Blocking write with a custom timeout | ||
| 123 | pub fn blocking_write_timeout(&mut self, addr: u8, write: &[u8], timeout: Duration) -> Result<(), Error> { | ||
| 124 | self.i2c.blocking_write_timeout(addr, write, timeout_fn(timeout)) | ||
| 125 | } | ||
| 126 | |||
| 127 | /// Blocking write with default timeout, provided in [`TimeoutI2c::new()`] | ||
| 128 | pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> { | ||
| 129 | self.blocking_write_timeout(addr, write, self.timeout) | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Blocking write-read with a custom timeout | ||
| 133 | pub fn blocking_write_read_timeout( | ||
| 134 | &mut self, | ||
| 135 | addr: u8, | ||
| 136 | write: &[u8], | ||
| 137 | read: &mut [u8], | ||
| 138 | timeout: Duration, | ||
| 139 | ) -> Result<(), Error> { | ||
| 140 | self.i2c | ||
| 141 | .blocking_write_read_timeout(addr, write, read, timeout_fn(timeout)) | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Blocking write-read with default timeout, provided in [`TimeoutI2c::new()`] | ||
| 145 | pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | ||
| 146 | self.blocking_write_read_timeout(addr, write, read, self.timeout) | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read | ||
| 151 | for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> | ||
| 152 | { | ||
| 153 | type Error = Error; | ||
| 154 | |||
| 155 | fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> { | ||
| 156 | self.blocking_read(addr, read) | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write | ||
| 161 | for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> | ||
| 162 | { | ||
| 163 | type Error = Error; | ||
| 164 | |||
| 165 | fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> { | ||
| 166 | self.blocking_write(addr, write) | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead | ||
| 171 | for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> | ||
| 172 | { | ||
| 173 | type Error = Error; | ||
| 174 | |||
| 175 | fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { | ||
| 176 | self.blocking_write_read(addr, write, read) | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | #[cfg(feature = "unstable-traits")] | ||
| 181 | mod eh1 { | ||
| 182 | use super::*; | ||
| 183 | |||
| 184 | impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { | ||
| 185 | type Error = Error; | ||
| 186 | } | ||
| 187 | |||
| 188 | impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { | ||
| 189 | fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { | ||
| 190 | self.blocking_read(address, read) | ||
| 191 | } | ||
| 192 | |||
| 193 | fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { | ||
| 194 | self.blocking_write(address, write) | ||
| 195 | } | ||
| 196 | |||
| 197 | fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { | ||
| 198 | self.blocking_write_read(address, write, read) | ||
| 199 | } | ||
| 200 | |||
| 201 | fn transaction( | ||
| 202 | &mut self, | ||
| 203 | _address: u8, | ||
| 204 | _operations: &mut [embedded_hal_1::i2c::Operation<'_>], | ||
| 205 | ) -> Result<(), Self::Error> { | ||
| 206 | todo!(); | ||
| 207 | } | ||
| 208 | } | ||
| 209 | } | ||
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 543d8f1b4..256b39638 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -7,6 +7,7 @@ use embassy_embedded_hal::SetConfig; | |||
| 7 | use embassy_hal_internal::drop::OnDrop; | 7 | use embassy_hal_internal::drop::OnDrop; |
| 8 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 8 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | use embassy_time::{Duration, Instant}; | ||
| 10 | 11 | ||
| 11 | use crate::dma::{NoDma, Transfer}; | 12 | use crate::dma::{NoDma, Transfer}; |
| 12 | use crate::gpio::sealed::AFType; | 13 | use crate::gpio::sealed::AFType; |
| @@ -43,6 +44,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 43 | pub struct Config { | 44 | pub struct Config { |
| 44 | pub sda_pullup: bool, | 45 | pub sda_pullup: bool, |
| 45 | pub scl_pullup: bool, | 46 | pub scl_pullup: bool, |
| 47 | pub transaction_timeout: Duration, | ||
| 46 | } | 48 | } |
| 47 | 49 | ||
| 48 | impl Default for Config { | 50 | impl Default for Config { |
| @@ -50,6 +52,7 @@ impl Default for Config { | |||
| 50 | Self { | 52 | Self { |
| 51 | sda_pullup: false, | 53 | sda_pullup: false, |
| 52 | scl_pullup: false, | 54 | scl_pullup: false, |
| 55 | transaction_timeout: Duration::from_millis(100), | ||
| 53 | } | 56 | } |
| 54 | } | 57 | } |
| 55 | } | 58 | } |
| @@ -71,6 +74,7 @@ pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> { | |||
| 71 | tx_dma: PeripheralRef<'d, TXDMA>, | 74 | tx_dma: PeripheralRef<'d, TXDMA>, |
| 72 | #[allow(dead_code)] | 75 | #[allow(dead_code)] |
| 73 | rx_dma: PeripheralRef<'d, RXDMA>, | 76 | rx_dma: PeripheralRef<'d, RXDMA>, |
| 77 | timeout: Duration, | ||
| 74 | } | 78 | } |
| 75 | 79 | ||
| 76 | impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | 80 | impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { |
| @@ -132,6 +136,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 132 | _peri: peri, | 136 | _peri: peri, |
| 133 | tx_dma, | 137 | tx_dma, |
| 134 | rx_dma, | 138 | rx_dma, |
| 139 | timeout: config.transaction_timeout, | ||
| 135 | } | 140 | } |
| 136 | } | 141 | } |
| 137 | 142 | ||
| @@ -598,22 +603,22 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 598 | where | 603 | where |
| 599 | TXDMA: crate::i2c::TxDma<T>, | 604 | TXDMA: crate::i2c::TxDma<T>, |
| 600 | { | 605 | { |
| 601 | self.write_timeout(address, write, || Ok(())).await | 606 | self.write_timeout(address, write, self.timeout).await |
| 602 | } | 607 | } |
| 603 | 608 | ||
| 604 | pub async fn write_timeout( | 609 | pub async fn write_timeout(&mut self, address: u8, write: &[u8], timeout: Duration) -> Result<(), Error> |
| 605 | &mut self, | ||
| 606 | address: u8, | ||
| 607 | write: &[u8], | ||
| 608 | check_timeout: impl Fn() -> Result<(), Error>, | ||
| 609 | ) -> Result<(), Error> | ||
| 610 | where | 610 | where |
| 611 | TXDMA: crate::i2c::TxDma<T>, | 611 | TXDMA: crate::i2c::TxDma<T>, |
| 612 | { | 612 | { |
| 613 | if write.is_empty() { | 613 | if write.is_empty() { |
| 614 | self.write_internal(address, write, true, check_timeout) | 614 | self.write_internal(address, write, true, timeout_fn(timeout)) |
| 615 | } else { | 615 | } else { |
| 616 | self.write_dma_internal(address, write, true, true, check_timeout).await | 616 | embassy_time::with_timeout( |
| 617 | timeout, | ||
| 618 | self.write_dma_internal(address, write, true, true, timeout_fn(timeout)), | ||
| 619 | ) | ||
| 620 | .await | ||
| 621 | .unwrap_or(Err(Error::Timeout)) | ||
| 617 | } | 622 | } |
| 618 | } | 623 | } |
| 619 | 624 | ||
| @@ -621,15 +626,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 621 | where | 626 | where |
| 622 | TXDMA: crate::i2c::TxDma<T>, | 627 | TXDMA: crate::i2c::TxDma<T>, |
| 623 | { | 628 | { |
| 624 | self.write_vectored_timeout(address, write, || Ok(())).await | 629 | self.write_vectored_timeout(address, write, self.timeout).await |
| 625 | } | 630 | } |
| 626 | 631 | ||
| 627 | pub async fn write_vectored_timeout( | 632 | pub async fn write_vectored_timeout(&mut self, address: u8, write: &[&[u8]], timeout: Duration) -> Result<(), Error> |
| 628 | &mut self, | ||
| 629 | address: u8, | ||
| 630 | write: &[&[u8]], | ||
| 631 | check_timeout: impl Fn() -> Result<(), Error>, | ||
| 632 | ) -> Result<(), Error> | ||
| 633 | where | 633 | where |
| 634 | TXDMA: crate::i2c::TxDma<T>, | 634 | TXDMA: crate::i2c::TxDma<T>, |
| 635 | { | 635 | { |
| @@ -644,8 +644,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 644 | let next = iter.next(); | 644 | let next = iter.next(); |
| 645 | let is_last = next.is_none(); | 645 | let is_last = next.is_none(); |
| 646 | 646 | ||
| 647 | self.write_dma_internal(address, c, first, is_last, || check_timeout()) | 647 | embassy_time::with_timeout( |
| 648 | .await?; | 648 | timeout, |
| 649 | self.write_dma_internal(address, c, first, is_last, timeout_fn(timeout)), | ||
| 650 | ) | ||
| 651 | .await | ||
| 652 | .unwrap_or(Err(Error::Timeout))?; | ||
| 649 | first = false; | 653 | first = false; |
| 650 | current = next; | 654 | current = next; |
| 651 | } | 655 | } |
| @@ -656,22 +660,22 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 656 | where | 660 | where |
| 657 | RXDMA: crate::i2c::RxDma<T>, | 661 | RXDMA: crate::i2c::RxDma<T>, |
| 658 | { | 662 | { |
| 659 | self.read_timeout(address, buffer, || Ok(())).await | 663 | self.read_timeout(address, buffer, self.timeout).await |
| 660 | } | 664 | } |
| 661 | 665 | ||
| 662 | pub async fn read_timeout( | 666 | pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> |
| 663 | &mut self, | ||
| 664 | address: u8, | ||
| 665 | buffer: &mut [u8], | ||
| 666 | check_timeout: impl Fn() -> Result<(), Error>, | ||
| 667 | ) -> Result<(), Error> | ||
| 668 | where | 667 | where |
| 669 | RXDMA: crate::i2c::RxDma<T>, | 668 | RXDMA: crate::i2c::RxDma<T>, |
| 670 | { | 669 | { |
| 671 | if buffer.is_empty() { | 670 | if buffer.is_empty() { |
| 672 | self.read_internal(address, buffer, false, check_timeout) | 671 | self.read_internal(address, buffer, false, timeout_fn(timeout)) |
| 673 | } else { | 672 | } else { |
| 674 | self.read_dma_internal(address, buffer, false, check_timeout).await | 673 | embassy_time::with_timeout( |
| 674 | timeout, | ||
| 675 | self.read_dma_internal(address, buffer, false, timeout_fn(timeout)), | ||
| 676 | ) | ||
| 677 | .await | ||
| 678 | .unwrap_or(Err(Error::Timeout)) | ||
| 675 | } | 679 | } |
| 676 | } | 680 | } |
| 677 | 681 | ||
| @@ -680,7 +684,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 680 | TXDMA: super::TxDma<T>, | 684 | TXDMA: super::TxDma<T>, |
| 681 | RXDMA: super::RxDma<T>, | 685 | RXDMA: super::RxDma<T>, |
| 682 | { | 686 | { |
| 683 | self.write_read_timeout(address, write, read, || Ok(())).await | 687 | self.write_read_timeout(address, write, read, self.timeout).await |
| 684 | } | 688 | } |
| 685 | 689 | ||
| 686 | pub async fn write_read_timeout( | 690 | pub async fn write_read_timeout( |
| @@ -688,23 +692,36 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 688 | address: u8, | 692 | address: u8, |
| 689 | write: &[u8], | 693 | write: &[u8], |
| 690 | read: &mut [u8], | 694 | read: &mut [u8], |
| 691 | check_timeout: impl Fn() -> Result<(), Error>, | 695 | timeout: Duration, |
| 692 | ) -> Result<(), Error> | 696 | ) -> Result<(), Error> |
| 693 | where | 697 | where |
| 694 | TXDMA: super::TxDma<T>, | 698 | TXDMA: super::TxDma<T>, |
| 695 | RXDMA: super::RxDma<T>, | 699 | RXDMA: super::RxDma<T>, |
| 696 | { | 700 | { |
| 701 | let start_instant = Instant::now(); | ||
| 702 | let check_timeout = timeout_fn(timeout); | ||
| 697 | if write.is_empty() { | 703 | if write.is_empty() { |
| 698 | self.write_internal(address, write, false, || check_timeout())?; | 704 | self.write_internal(address, write, false, &check_timeout)?; |
| 699 | } else { | 705 | } else { |
| 700 | self.write_dma_internal(address, write, true, true, || check_timeout()) | 706 | embassy_time::with_timeout( |
| 701 | .await?; | 707 | timeout, |
| 708 | self.write_dma_internal(address, write, true, true, &check_timeout), | ||
| 709 | ) | ||
| 710 | .await | ||
| 711 | .unwrap_or(Err(Error::Timeout))?; | ||
| 702 | } | 712 | } |
| 703 | 713 | ||
| 714 | let time_left_until_timeout = timeout - Instant::now().duration_since(start_instant); | ||
| 715 | |||
| 704 | if read.is_empty() { | 716 | if read.is_empty() { |
| 705 | self.read_internal(address, read, true, check_timeout)?; | 717 | self.read_internal(address, read, true, &check_timeout)?; |
| 706 | } else { | 718 | } else { |
| 707 | self.read_dma_internal(address, read, true, check_timeout).await?; | 719 | embassy_time::with_timeout( |
| 720 | time_left_until_timeout, | ||
| 721 | self.read_dma_internal(address, read, true, &check_timeout), | ||
| 722 | ) | ||
| 723 | .await | ||
| 724 | .unwrap_or(Err(Error::Timeout))?; | ||
| 708 | } | 725 | } |
| 709 | 726 | ||
| 710 | Ok(()) | 727 | Ok(()) |
| @@ -713,31 +730,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 713 | // ========================= | 730 | // ========================= |
| 714 | // Blocking public API | 731 | // Blocking public API |
| 715 | 732 | ||
| 716 | pub fn blocking_read_timeout( | 733 | pub fn blocking_read_timeout(&mut self, address: u8, read: &mut [u8], timeout: Duration) -> Result<(), Error> { |
| 717 | &mut self, | 734 | self.read_internal(address, read, false, timeout_fn(timeout)) |
| 718 | address: u8, | ||
| 719 | read: &mut [u8], | ||
| 720 | check_timeout: impl Fn() -> Result<(), Error>, | ||
| 721 | ) -> Result<(), Error> { | ||
| 722 | self.read_internal(address, read, false, &check_timeout) | ||
| 723 | // Automatic Stop | 735 | // Automatic Stop |
| 724 | } | 736 | } |
| 725 | 737 | ||
| 726 | pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { | 738 | pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { |
| 727 | self.blocking_read_timeout(address, read, || Ok(())) | 739 | self.blocking_read_timeout(address, read, self.timeout) |
| 728 | } | 740 | } |
| 729 | 741 | ||
| 730 | pub fn blocking_write_timeout( | 742 | pub fn blocking_write_timeout(&mut self, address: u8, write: &[u8], timeout: Duration) -> Result<(), Error> { |
| 731 | &mut self, | 743 | self.write_internal(address, write, true, timeout_fn(timeout)) |
| 732 | address: u8, | ||
| 733 | write: &[u8], | ||
| 734 | check_timeout: impl Fn() -> Result<(), Error>, | ||
| 735 | ) -> Result<(), Error> { | ||
| 736 | self.write_internal(address, write, true, &check_timeout) | ||
| 737 | } | 744 | } |
| 738 | 745 | ||
| 739 | pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { | 746 | pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { |
| 740 | self.blocking_write_timeout(address, write, || Ok(())) | 747 | self.blocking_write_timeout(address, write, self.timeout) |
| 741 | } | 748 | } |
| 742 | 749 | ||
| 743 | pub fn blocking_write_read_timeout( | 750 | pub fn blocking_write_read_timeout( |
| @@ -745,26 +752,29 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 745 | address: u8, | 752 | address: u8, |
| 746 | write: &[u8], | 753 | write: &[u8], |
| 747 | read: &mut [u8], | 754 | read: &mut [u8], |
| 748 | check_timeout: impl Fn() -> Result<(), Error>, | 755 | timeout: Duration, |
| 749 | ) -> Result<(), Error> { | 756 | ) -> Result<(), Error> { |
| 757 | let check_timeout = timeout_fn(timeout); | ||
| 750 | self.write_internal(address, write, false, &check_timeout)?; | 758 | self.write_internal(address, write, false, &check_timeout)?; |
| 751 | self.read_internal(address, read, true, &check_timeout) | 759 | self.read_internal(address, read, true, &check_timeout) |
| 752 | // Automatic Stop | 760 | // Automatic Stop |
| 753 | } | 761 | } |
| 754 | 762 | ||
| 755 | pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 763 | pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { |
| 756 | self.blocking_write_read_timeout(address, write, read, || Ok(())) | 764 | self.blocking_write_read_timeout(address, write, read, self.timeout) |
| 757 | } | 765 | } |
| 758 | 766 | ||
| 759 | pub fn blocking_write_vectored_timeout( | 767 | pub fn blocking_write_vectored_timeout( |
| 760 | &mut self, | 768 | &mut self, |
| 761 | address: u8, | 769 | address: u8, |
| 762 | write: &[&[u8]], | 770 | write: &[&[u8]], |
| 763 | check_timeout: impl Fn() -> Result<(), Error>, | 771 | timeout: Duration, |
| 764 | ) -> Result<(), Error> { | 772 | ) -> Result<(), Error> { |
| 765 | if write.is_empty() { | 773 | if write.is_empty() { |
| 766 | return Err(Error::ZeroLengthTransfer); | 774 | return Err(Error::ZeroLengthTransfer); |
| 767 | } | 775 | } |
| 776 | |||
| 777 | let check_timeout = timeout_fn(timeout); | ||
| 768 | let first_length = write[0].len(); | 778 | let first_length = write[0].len(); |
| 769 | let last_slice_index = write.len() - 1; | 779 | let last_slice_index = write.len() - 1; |
| 770 | 780 | ||
| @@ -834,7 +844,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 834 | } | 844 | } |
| 835 | 845 | ||
| 836 | pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { | 846 | pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { |
| 837 | self.blocking_write_vectored_timeout(address, write, || Ok(())) | 847 | self.blocking_write_vectored_timeout(address, write, self.timeout) |
| 838 | } | 848 | } |
| 839 | } | 849 | } |
| 840 | 850 | ||
| @@ -1089,3 +1099,14 @@ impl<'d, T: Instance> SetConfig for I2c<'d, T> { | |||
| 1089 | Ok(()) | 1099 | Ok(()) |
| 1090 | } | 1100 | } |
| 1091 | } | 1101 | } |
| 1102 | |||
| 1103 | fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { | ||
| 1104 | let deadline = Instant::now() + timeout; | ||
| 1105 | move || { | ||
| 1106 | if Instant::now() > deadline { | ||
| 1107 | Err(Error::Timeout) | ||
| 1108 | } else { | ||
| 1109 | Ok(()) | ||
| 1110 | } | ||
| 1111 | } | ||
| 1112 | } | ||
diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs index a92957325..10ca2bdc7 100644 --- a/examples/stm32f4/src/bin/i2c.rs +++ b/examples/stm32f4/src/bin/i2c.rs | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::dma::NoDma; | 7 | use embassy_stm32::dma::NoDma; |
| 8 | use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; | 8 | use embassy_stm32::i2c::{Error, I2c}; |
| 9 | use embassy_stm32::time::Hertz; | 9 | use embassy_stm32::time::Hertz; |
| 10 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | 10 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; |
| 11 | use embassy_time::Duration; | 11 | use embassy_time::Duration; |
| @@ -34,13 +34,9 @@ async fn main(_spawner: Spawner) { | |||
| 34 | Default::default(), | 34 | Default::default(), |
| 35 | ); | 35 | ); |
| 36 | 36 | ||
| 37 | // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. | ||
| 38 | // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. | ||
| 39 | let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); | ||
| 40 | |||
| 41 | let mut data = [0u8; 1]; | 37 | let mut data = [0u8; 1]; |
| 42 | 38 | ||
| 43 | match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { | 39 | match i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { |
| 44 | Ok(()) => info!("Whoami: {}", data[0]), | 40 | Ok(()) => info!("Whoami: {}", data[0]), |
| 45 | Err(Error::Timeout) => error!("Operation timed out"), | 41 | Err(Error::Timeout) => error!("Operation timed out"), |
| 46 | Err(e) => error!("I2c Error: {:?}", e), | 42 | Err(e) => error!("I2c Error: {:?}", e), |
diff --git a/examples/stm32h5/src/bin/i2c.rs b/examples/stm32h5/src/bin/i2c.rs index 8b6fe71ae..4ce378e9b 100644 --- a/examples/stm32h5/src/bin/i2c.rs +++ b/examples/stm32h5/src/bin/i2c.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; | 7 | use embassy_stm32::i2c::{Error, I2c}; |
| 8 | use embassy_stm32::time::Hertz; | 8 | use embassy_stm32::time::Hertz; |
| 9 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | 9 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; |
| 10 | use embassy_time::Duration; | 10 | use embassy_time::Duration; |
| @@ -33,13 +33,9 @@ async fn main(_spawner: Spawner) { | |||
| 33 | Default::default(), | 33 | Default::default(), |
| 34 | ); | 34 | ); |
| 35 | 35 | ||
| 36 | // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. | ||
| 37 | // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. | ||
| 38 | let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); | ||
| 39 | |||
| 40 | let mut data = [0u8; 1]; | 36 | let mut data = [0u8; 1]; |
| 41 | 37 | ||
| 42 | match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { | 38 | match i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { |
| 43 | Ok(()) => info!("Whoami: {}", data[0]), | 39 | Ok(()) => info!("Whoami: {}", data[0]), |
| 44 | Err(Error::Timeout) => error!("Operation timed out"), | 40 | Err(Error::Timeout) => error!("Operation timed out"), |
| 45 | Err(e) => error!("I2c Error: {:?}", e), | 41 | Err(e) => error!("I2c Error: {:?}", e), |
diff --git a/examples/stm32h7/src/bin/i2c.rs b/examples/stm32h7/src/bin/i2c.rs index c2979c59b..7cd12e5eb 100644 --- a/examples/stm32h7/src/bin/i2c.rs +++ b/examples/stm32h7/src/bin/i2c.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; | 7 | use embassy_stm32::i2c::{Error, I2c}; |
| 8 | use embassy_stm32::time::Hertz; | 8 | use embassy_stm32::time::Hertz; |
| 9 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | 9 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; |
| 10 | use embassy_time::Duration; | 10 | use embassy_time::Duration; |
| @@ -33,13 +33,9 @@ async fn main(_spawner: Spawner) { | |||
| 33 | Default::default(), | 33 | Default::default(), |
| 34 | ); | 34 | ); |
| 35 | 35 | ||
| 36 | // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. | ||
| 37 | // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. | ||
| 38 | let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); | ||
| 39 | |||
| 40 | let mut data = [0u8; 1]; | 36 | let mut data = [0u8; 1]; |
| 41 | 37 | ||
| 42 | match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { | 38 | match i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { |
| 43 | Ok(()) => info!("Whoami: {}", data[0]), | 39 | Ok(()) => info!("Whoami: {}", data[0]), |
| 44 | Err(Error::Timeout) => error!("Operation timed out"), | 40 | Err(Error::Timeout) => error!("Operation timed out"), |
| 45 | Err(e) => error!("I2c Error: {:?}", e), | 41 | Err(e) => error!("I2c Error: {:?}", e), |
