stm32: add timeout to I2C driver

This commit is contained in:
Andres Oliva 2023-10-10 18:10:53 +02:00
parent eff73d6dfa
commit cd12c9cbce
7 changed files with 85 additions and 290 deletions

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/"
features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"] features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any"]
flavors = [ flavors = [
{ regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" },
{ regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" },
@ -33,7 +33,7 @@ flavors = [
[dependencies] [dependencies]
embassy-sync = { version = "0.3.0", path = "../embassy-sync" } embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } embassy-time = { version = "0.1.3", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] }
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
@ -87,7 +87,7 @@ default = ["rt"]
rt = ["stm32-metapac/rt"] rt = ["stm32-metapac/rt"]
## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging ## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging
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"] 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"]
exti = [] exti = []
low-power = [ "dep:embassy-executor", "embassy-executor/arch-cortex-m" ] low-power = [ "dep:embassy-executor", "embassy-executor/arch-cortex-m" ]
@ -112,7 +112,7 @@ unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"]
#! ## Time #! ## Time
## Enables additional driver features that depend on embassy-time ## Enables additional driver features that depend on embassy-time
time = ["dep:embassy-time"] time = []
# Features starting with `_` are for internal use only. They're not intended # Features starting with `_` are for internal use only. They're not intended
# to be enabled by other crates, and are not covered by semver guarantees. # to be enabled by other crates, and are not covered by semver guarantees.

View File

@ -7,11 +7,6 @@ use crate::interrupt;
mod _version; mod _version;
pub use _version::*; pub use _version::*;
#[cfg(feature = "time")]
mod timeout;
#[cfg(feature = "time")]
pub use timeout::*;
use crate::peripherals; use crate::peripherals;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]

View File

@ -1,209 +0,0 @@
use embassy_time::{Duration, Instant};
use super::{Error, I2c, Instance};
/// An I2C wrapper, which provides `embassy-time` based timeouts for all `embedded-hal` trait methods.
///
/// This is useful for recovering from a shorted bus or a device stuck in a clock stretching state.
/// A regular [I2c] would freeze until condition is removed.
pub struct TimeoutI2c<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> {
i2c: &'a mut I2c<'d, T, TXDMA, RXDMA>,
timeout: Duration,
}
fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> {
let deadline = Instant::now() + timeout;
move || {
if Instant::now() > deadline {
Err(Error::Timeout)
} else {
Ok(())
}
}
}
impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> {
pub fn new(i2c: &'a mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration) -> Self {
Self { i2c, timeout }
}
// =========================
// Async public API
#[cfg(i2c_v2)]
pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error>
where
TXDMA: crate::i2c::TxDma<T>,
{
self.write_timeout(address, write, self.timeout).await
}
#[cfg(i2c_v2)]
pub async fn write_timeout(&mut self, address: u8, write: &[u8], timeout: Duration) -> Result<(), Error>
where
TXDMA: crate::i2c::TxDma<T>,
{
self.i2c.write_timeout(address, write, timeout_fn(timeout)).await
}
#[cfg(i2c_v2)]
pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error>
where
TXDMA: crate::i2c::TxDma<T>,
{
self.write_vectored_timeout(address, write, self.timeout).await
}
#[cfg(i2c_v2)]
pub async fn write_vectored_timeout(&mut self, address: u8, write: &[&[u8]], timeout: Duration) -> Result<(), Error>
where
TXDMA: crate::i2c::TxDma<T>,
{
self.i2c
.write_vectored_timeout(address, write, timeout_fn(timeout))
.await
}
#[cfg(i2c_v2)]
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error>
where
RXDMA: crate::i2c::RxDma<T>,
{
self.read_timeout(address, buffer, self.timeout).await
}
#[cfg(i2c_v2)]
pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error>
where
RXDMA: crate::i2c::RxDma<T>,
{
self.i2c.read_timeout(address, buffer, timeout_fn(timeout)).await
}
#[cfg(i2c_v2)]
pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error>
where
TXDMA: super::TxDma<T>,
RXDMA: super::RxDma<T>,
{
self.write_read_timeout(address, write, read, self.timeout).await
}
#[cfg(i2c_v2)]
pub async fn write_read_timeout(
&mut self,
address: u8,
write: &[u8],
read: &mut [u8],
timeout: Duration,
) -> Result<(), Error>
where
TXDMA: super::TxDma<T>,
RXDMA: super::RxDma<T>,
{
self.i2c
.write_read_timeout(address, write, read, timeout_fn(timeout))
.await
}
// =========================
// Blocking public API
/// Blocking read with a custom timeout
pub fn blocking_read_timeout(&mut self, addr: u8, read: &mut [u8], timeout: Duration) -> Result<(), Error> {
self.i2c.blocking_read_timeout(addr, read, timeout_fn(timeout))
}
/// Blocking read with default timeout, provided in [`TimeoutI2c::new()`]
pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> {
self.blocking_read_timeout(addr, read, self.timeout)
}
/// Blocking write with a custom timeout
pub fn blocking_write_timeout(&mut self, addr: u8, write: &[u8], timeout: Duration) -> Result<(), Error> {
self.i2c.blocking_write_timeout(addr, write, timeout_fn(timeout))
}
/// Blocking write with default timeout, provided in [`TimeoutI2c::new()`]
pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> {
self.blocking_write_timeout(addr, write, self.timeout)
}
/// Blocking write-read with a custom timeout
pub fn blocking_write_read_timeout(
&mut self,
addr: u8,
write: &[u8],
read: &mut [u8],
timeout: Duration,
) -> Result<(), Error> {
self.i2c
.blocking_write_read_timeout(addr, write, read, timeout_fn(timeout))
}
/// Blocking write-read with default timeout, provided in [`TimeoutI2c::new()`]
pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
self.blocking_write_read_timeout(addr, write, read, self.timeout)
}
}
impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read
for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA>
{
type Error = Error;
fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(addr, read)
}
}
impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write
for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA>
{
type Error = Error;
fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(addr, write)
}
}
impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead
for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA>
{
type Error = Error;
fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(addr, write, read)
}
}
#[cfg(feature = "unstable-traits")]
mod eh1 {
use super::*;
impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> {
type Error = Error;
}
impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> {
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, read)
}
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(address, write)
}
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(address, write, read)
}
fn transaction(
&mut self,
_address: u8,
_operations: &mut [embedded_hal_1::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
todo!();
}
}
}

View File

@ -7,6 +7,7 @@ use embassy_embedded_hal::SetConfig;
use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use embassy_time::{Duration, Instant};
use crate::dma::{NoDma, Transfer}; use crate::dma::{NoDma, Transfer};
use crate::gpio::sealed::AFType; use crate::gpio::sealed::AFType;
@ -43,6 +44,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
pub struct Config { pub struct Config {
pub sda_pullup: bool, pub sda_pullup: bool,
pub scl_pullup: bool, pub scl_pullup: bool,
pub transaction_timeout: Duration,
} }
impl Default for Config { impl Default for Config {
@ -50,6 +52,7 @@ impl Default for Config {
Self { Self {
sda_pullup: false, sda_pullup: false,
scl_pullup: false, scl_pullup: false,
transaction_timeout: Duration::from_millis(100),
} }
} }
} }
@ -71,6 +74,7 @@ pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
tx_dma: PeripheralRef<'d, TXDMA>, tx_dma: PeripheralRef<'d, TXDMA>,
#[allow(dead_code)] #[allow(dead_code)]
rx_dma: PeripheralRef<'d, RXDMA>, rx_dma: PeripheralRef<'d, RXDMA>,
timeout: Duration,
} }
impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { 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> {
_peri: peri, _peri: peri,
tx_dma, tx_dma,
rx_dma, rx_dma,
timeout: config.transaction_timeout,
} }
} }
@ -598,22 +603,22 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
where where
TXDMA: crate::i2c::TxDma<T>, TXDMA: crate::i2c::TxDma<T>,
{ {
self.write_timeout(address, write, || Ok(())).await self.write_timeout(address, write, self.timeout).await
} }
pub async fn write_timeout( pub async fn write_timeout(&mut self, address: u8, write: &[u8], timeout: Duration) -> Result<(), Error>
&mut self,
address: u8,
write: &[u8],
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error>
where where
TXDMA: crate::i2c::TxDma<T>, TXDMA: crate::i2c::TxDma<T>,
{ {
if write.is_empty() { if write.is_empty() {
self.write_internal(address, write, true, check_timeout) self.write_internal(address, write, true, timeout_fn(timeout))
} else { } else {
self.write_dma_internal(address, write, true, true, check_timeout).await embassy_time::with_timeout(
timeout,
self.write_dma_internal(address, write, true, true, timeout_fn(timeout)),
)
.await
.unwrap_or(Err(Error::Timeout))
} }
} }
@ -621,15 +626,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
where where
TXDMA: crate::i2c::TxDma<T>, TXDMA: crate::i2c::TxDma<T>,
{ {
self.write_vectored_timeout(address, write, || Ok(())).await self.write_vectored_timeout(address, write, self.timeout).await
} }
pub async fn write_vectored_timeout( pub async fn write_vectored_timeout(&mut self, address: u8, write: &[&[u8]], timeout: Duration) -> Result<(), Error>
&mut self,
address: u8,
write: &[&[u8]],
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error>
where where
TXDMA: crate::i2c::TxDma<T>, TXDMA: crate::i2c::TxDma<T>,
{ {
@ -644,8 +644,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
let next = iter.next(); let next = iter.next();
let is_last = next.is_none(); let is_last = next.is_none();
self.write_dma_internal(address, c, first, is_last, || check_timeout()) embassy_time::with_timeout(
.await?; timeout,
self.write_dma_internal(address, c, first, is_last, timeout_fn(timeout)),
)
.await
.unwrap_or(Err(Error::Timeout))?;
first = false; first = false;
current = next; current = next;
} }
@ -656,22 +660,22 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
where where
RXDMA: crate::i2c::RxDma<T>, RXDMA: crate::i2c::RxDma<T>,
{ {
self.read_timeout(address, buffer, || Ok(())).await self.read_timeout(address, buffer, self.timeout).await
} }
pub async fn read_timeout( pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error>
&mut self,
address: u8,
buffer: &mut [u8],
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error>
where where
RXDMA: crate::i2c::RxDma<T>, RXDMA: crate::i2c::RxDma<T>,
{ {
if buffer.is_empty() { if buffer.is_empty() {
self.read_internal(address, buffer, false, check_timeout) self.read_internal(address, buffer, false, timeout_fn(timeout))
} else { } else {
self.read_dma_internal(address, buffer, false, check_timeout).await embassy_time::with_timeout(
timeout,
self.read_dma_internal(address, buffer, false, timeout_fn(timeout)),
)
.await
.unwrap_or(Err(Error::Timeout))
} }
} }
@ -680,7 +684,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
TXDMA: super::TxDma<T>, TXDMA: super::TxDma<T>,
RXDMA: super::RxDma<T>, RXDMA: super::RxDma<T>,
{ {
self.write_read_timeout(address, write, read, || Ok(())).await self.write_read_timeout(address, write, read, self.timeout).await
} }
pub async fn write_read_timeout( pub async fn write_read_timeout(
@ -688,23 +692,36 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
address: u8, address: u8,
write: &[u8], write: &[u8],
read: &mut [u8], read: &mut [u8],
check_timeout: impl Fn() -> Result<(), Error>, timeout: Duration,
) -> Result<(), Error> ) -> Result<(), Error>
where where
TXDMA: super::TxDma<T>, TXDMA: super::TxDma<T>,
RXDMA: super::RxDma<T>, RXDMA: super::RxDma<T>,
{ {
let start_instant = Instant::now();
let check_timeout = timeout_fn(timeout);
if write.is_empty() { if write.is_empty() {
self.write_internal(address, write, false, || check_timeout())?; self.write_internal(address, write, false, &check_timeout)?;
} else { } else {
self.write_dma_internal(address, write, true, true, || check_timeout()) embassy_time::with_timeout(
.await?; timeout,
self.write_dma_internal(address, write, true, true, &check_timeout),
)
.await
.unwrap_or(Err(Error::Timeout))?;
} }
let time_left_until_timeout = timeout - Instant::now().duration_since(start_instant);
if read.is_empty() { if read.is_empty() {
self.read_internal(address, read, true, check_timeout)?; self.read_internal(address, read, true, &check_timeout)?;
} else { } else {
self.read_dma_internal(address, read, true, check_timeout).await?; embassy_time::with_timeout(
time_left_until_timeout,
self.read_dma_internal(address, read, true, &check_timeout),
)
.await
.unwrap_or(Err(Error::Timeout))?;
} }
Ok(()) Ok(())
@ -713,31 +730,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// ========================= // =========================
// Blocking public API // Blocking public API
pub fn blocking_read_timeout( pub fn blocking_read_timeout(&mut self, address: u8, read: &mut [u8], timeout: Duration) -> Result<(), Error> {
&mut self, self.read_internal(address, read, false, timeout_fn(timeout))
address: u8,
read: &mut [u8],
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
self.read_internal(address, read, false, &check_timeout)
// Automatic Stop // Automatic Stop
} }
pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> {
self.blocking_read_timeout(address, read, || Ok(())) self.blocking_read_timeout(address, read, self.timeout)
} }
pub fn blocking_write_timeout( pub fn blocking_write_timeout(&mut self, address: u8, write: &[u8], timeout: Duration) -> Result<(), Error> {
&mut self, self.write_internal(address, write, true, timeout_fn(timeout))
address: u8,
write: &[u8],
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
self.write_internal(address, write, true, &check_timeout)
} }
pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
self.blocking_write_timeout(address, write, || Ok(())) self.blocking_write_timeout(address, write, self.timeout)
} }
pub fn blocking_write_read_timeout( pub fn blocking_write_read_timeout(
@ -745,26 +752,29 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
address: u8, address: u8,
write: &[u8], write: &[u8],
read: &mut [u8], read: &mut [u8],
check_timeout: impl Fn() -> Result<(), Error>, timeout: Duration,
) -> Result<(), Error> { ) -> Result<(), Error> {
let check_timeout = timeout_fn(timeout);
self.write_internal(address, write, false, &check_timeout)?; self.write_internal(address, write, false, &check_timeout)?;
self.read_internal(address, read, true, &check_timeout) self.read_internal(address, read, true, &check_timeout)
// Automatic Stop // Automatic Stop
} }
pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { 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(())) self.blocking_write_read_timeout(address, write, read, self.timeout)
} }
pub fn blocking_write_vectored_timeout( pub fn blocking_write_vectored_timeout(
&mut self, &mut self,
address: u8, address: u8,
write: &[&[u8]], write: &[&[u8]],
check_timeout: impl Fn() -> Result<(), Error>, timeout: Duration,
) -> Result<(), Error> { ) -> Result<(), Error> {
if write.is_empty() { if write.is_empty() {
return Err(Error::ZeroLengthTransfer); return Err(Error::ZeroLengthTransfer);
} }
let check_timeout = timeout_fn(timeout);
let first_length = write[0].len(); let first_length = write[0].len();
let last_slice_index = write.len() - 1; let last_slice_index = write.len() - 1;
@ -834,7 +844,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
} }
pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> {
self.blocking_write_vectored_timeout(address, write, || Ok(())) self.blocking_write_vectored_timeout(address, write, self.timeout)
} }
} }
@ -1089,3 +1099,14 @@ impl<'d, T: Instance> SetConfig for I2c<'d, T> {
Ok(()) Ok(())
} }
} }
fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> {
let deadline = Instant::now() + timeout;
move || {
if Instant::now() > deadline {
Err(Error::Timeout)
} else {
Ok(())
}
}
}

View File

@ -5,7 +5,7 @@
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::dma::NoDma; use embassy_stm32::dma::NoDma;
use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; use embassy_stm32::i2c::{Error, I2c};
use embassy_stm32::time::Hertz; use embassy_stm32::time::Hertz;
use embassy_stm32::{bind_interrupts, i2c, peripherals}; use embassy_stm32::{bind_interrupts, i2c, peripherals};
use embassy_time::Duration; use embassy_time::Duration;
@ -34,13 +34,9 @@ async fn main(_spawner: Spawner) {
Default::default(), Default::default(),
); );
// I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long.
// TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay.
let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000));
let mut data = [0u8; 1]; let mut data = [0u8; 1];
match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { match i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) {
Ok(()) => info!("Whoami: {}", data[0]), Ok(()) => info!("Whoami: {}", data[0]),
Err(Error::Timeout) => error!("Operation timed out"), Err(Error::Timeout) => error!("Operation timed out"),
Err(e) => error!("I2c Error: {:?}", e), Err(e) => error!("I2c Error: {:?}", e),

View File

@ -4,7 +4,7 @@
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; use embassy_stm32::i2c::{Error, I2c};
use embassy_stm32::time::Hertz; use embassy_stm32::time::Hertz;
use embassy_stm32::{bind_interrupts, i2c, peripherals}; use embassy_stm32::{bind_interrupts, i2c, peripherals};
use embassy_time::Duration; use embassy_time::Duration;
@ -33,13 +33,9 @@ async fn main(_spawner: Spawner) {
Default::default(), Default::default(),
); );
// I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long.
// TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay.
let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000));
let mut data = [0u8; 1]; let mut data = [0u8; 1];
match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { match i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) {
Ok(()) => info!("Whoami: {}", data[0]), Ok(()) => info!("Whoami: {}", data[0]),
Err(Error::Timeout) => error!("Operation timed out"), Err(Error::Timeout) => error!("Operation timed out"),
Err(e) => error!("I2c Error: {:?}", e), Err(e) => error!("I2c Error: {:?}", e),

View File

@ -4,7 +4,7 @@
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; use embassy_stm32::i2c::{Error, I2c};
use embassy_stm32::time::Hertz; use embassy_stm32::time::Hertz;
use embassy_stm32::{bind_interrupts, i2c, peripherals}; use embassy_stm32::{bind_interrupts, i2c, peripherals};
use embassy_time::Duration; use embassy_time::Duration;
@ -33,13 +33,9 @@ async fn main(_spawner: Spawner) {
Default::default(), Default::default(),
); );
// I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long.
// TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay.
let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000));
let mut data = [0u8; 1]; let mut data = [0u8; 1];
match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { match i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) {
Ok(()) => info!("Whoami: {}", data[0]), Ok(()) => info!("Whoami: {}", data[0]),
Err(Error::Timeout) => error!("Operation timed out"), Err(Error::Timeout) => error!("Operation timed out"),
Err(e) => error!("I2c Error: {:?}", e), Err(e) => error!("I2c Error: {:?}", e),