aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndres Oliva <[email protected]>2023-10-10 18:10:53 +0200
committerAndres Oliva <[email protected]>2023-10-10 18:20:46 +0200
commitcd12c9cbceb0ba90a493194d8200b1d8e98bba1b (patch)
treead6f45f8dbe7de2b6e93e9678fdebef4993bcb46
parenteff73d6dfa4c5920a55a5ee2bf5c0b2ef68fbae1 (diff)
stm32: add timeout to I2C driver
-rw-r--r--embassy-stm32/Cargo.toml8
-rw-r--r--embassy-stm32/src/i2c/mod.rs5
-rw-r--r--embassy-stm32/src/i2c/timeout.rs209
-rw-r--r--embassy-stm32/src/i2c/v2.rs129
-rw-r--r--examples/stm32f4/src/bin/i2c.rs8
-rw-r--r--examples/stm32h5/src/bin/i2c.rs8
-rw-r--r--examples/stm32h7/src/bin/i2c.rs8
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"
8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" 8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/"
9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" 9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/"
10 10
11features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"] 11features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any"]
12flavors = [ 12flavors = [
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]
35embassy-sync = { version = "0.3.0", path = "../embassy-sync" } 35embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
36embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } 36embassy-time = { version = "0.1.3", path = "../embassy-time" }
37embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 37embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
38embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } 38embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] }
39embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } 39embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
@@ -87,7 +87,7 @@ default = ["rt"]
87rt = ["stm32-metapac/rt"] 87rt = ["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
90defmt = ["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"] 90defmt = ["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
92exti = [] 92exti = []
93low-power = [ "dep:embassy-executor", "embassy-executor/arch-cortex-m" ] 93low-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
115time = ["dep:embassy-time"] 115time = []
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;
7mod _version; 7mod _version;
8pub use _version::*; 8pub use _version::*;
9 9
10#[cfg(feature = "time")]
11mod timeout;
12#[cfg(feature = "time")]
13pub use timeout::*;
14
15use crate::peripherals; 10use 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 @@
1use embassy_time::{Duration, Instant};
2
3use 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.
9pub struct TimeoutI2c<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> {
10 i2c: &'a mut I2c<'d, T, TXDMA, RXDMA>,
11 timeout: Duration,
12}
13
14fn 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
25impl<'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
150impl<'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
160impl<'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
170impl<'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")]
181mod 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;
7use embassy_hal_internal::drop::OnDrop; 7use embassy_hal_internal::drop::OnDrop;
8use embassy_hal_internal::{into_ref, PeripheralRef}; 8use embassy_hal_internal::{into_ref, PeripheralRef};
9use embassy_sync::waitqueue::AtomicWaker; 9use embassy_sync::waitqueue::AtomicWaker;
10use embassy_time::{Duration, Instant};
10 11
11use crate::dma::{NoDma, Transfer}; 12use crate::dma::{NoDma, Transfer};
12use crate::gpio::sealed::AFType; 13use crate::gpio::sealed::AFType;
@@ -43,6 +44,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
43pub struct Config { 44pub 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
48impl Default for Config { 50impl 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
76impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { 80impl<'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
1103fn 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 @@
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::dma::NoDma; 7use embassy_stm32::dma::NoDma;
8use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; 8use embassy_stm32::i2c::{Error, I2c};
9use embassy_stm32::time::Hertz; 9use embassy_stm32::time::Hertz;
10use embassy_stm32::{bind_interrupts, i2c, peripherals}; 10use embassy_stm32::{bind_interrupts, i2c, peripherals};
11use embassy_time::Duration; 11use 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
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; 7use embassy_stm32::i2c::{Error, I2c};
8use embassy_stm32::time::Hertz; 8use embassy_stm32::time::Hertz;
9use embassy_stm32::{bind_interrupts, i2c, peripherals}; 9use embassy_stm32::{bind_interrupts, i2c, peripherals};
10use embassy_time::Duration; 10use 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
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; 7use embassy_stm32::i2c::{Error, I2c};
8use embassy_stm32::time::Hertz; 8use embassy_stm32::time::Hertz;
9use embassy_stm32::{bind_interrupts, i2c, peripherals}; 9use embassy_stm32::{bind_interrupts, i2c, peripherals};
10use embassy_time::Duration; 10use 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),