stm32: add timeout to I2C driver
This commit is contained in:
parent
eff73d6dfa
commit
cd12c9cbce
@ -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.
|
||||||
|
@ -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)]
|
||||||
|
@ -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!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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),
|
||||||
|
@ -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),
|
||||||
|
@ -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),
|
||||||
|
Loading…
Reference in New Issue
Block a user