1025: Implement I2C timeouts, second attempt r=Dirbaio a=chemicstry

This is an alterrnative to #1022 as discussed there.

Timeouts are implemented using suggested `check_timeout: impl Fn() -> Result<(), Error>` function, which does not depend on `embassy-time` by default and is a noop for regular I2C.

This also adds `time` feature like in `embassy-nrf` to enable `embassy-time` dependencies. While at it, I also gated some other peripherals that depend on `embassy-time`, notably `usb` and (partially) `subghz`.

`TimeoutI2c` is currently only implemented for i2cv1, because i2cv2 has additional complications:
- Async methods still use a lot of busy waiting code in between DMA transfers, so simple `with_timeout()` will not work and it will have to use both types of timeouts. It could probably be rewritten to replace busy waits with IRQs, but that's outside the scope of this PR.
- I2C definition `I2c<'d, T, TXDMA, RXDMA>` is different from i2cv1 `I2c<'d, T>` making it hard to share single `TimeoutI2c` wrapper. A couple of options here:
  - Duplicate `TimeoutI2c` code
  - Add dummy `TXDMA`, `RXDMA` types to i2cv1 considering that in the future it should also support DMA

Co-authored-by: chemicstry <chemicstry@gmail.com>
This commit is contained in:
bors[bot] 2022-10-26 19:34:43 +00:00 committed by GitHub
commit 01e23bf9dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 508 additions and 81 deletions

View file

@ -82,9 +82,12 @@ memory-x = ["stm32-metapac/memory-x"]
subghz = []
exti = []
# Enables additional driver features that depend on embassy-time
time = ["dep:embassy-time"]
# 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.
_time-driver = ["dep:embassy-time"]
_time-driver = ["time"]
time-driver-any = ["_time-driver"]
time-driver-tim2 = ["_time-driver"]
time-driver-tim3 = ["_time-driver"]

View file

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

View file

@ -0,0 +1,142 @@
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<'d, T: Instance, TXDMA, RXDMA> {
i2c: &'d 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<'d, T: Instance, TXDMA, RXDMA> TimeoutI2c<'d, T, TXDMA, RXDMA> {
pub fn new(i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration) -> Self {
Self { i2c, timeout }
}
/// Blocking read with a custom timeout
pub fn blocking_read_timeout(&mut self, addr: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> {
self.i2c.blocking_read_timeout(addr, buffer, timeout_fn(timeout))
}
/// Blocking read with default timeout, provided in [`TimeoutI2c::new()`]
pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
self.blocking_read_timeout(addr, buffer, self.timeout)
}
/// Blocking write with a custom timeout
pub fn blocking_write_timeout(&mut self, addr: u8, bytes: &[u8], timeout: Duration) -> Result<(), Error> {
self.i2c.blocking_write_timeout(addr, bytes, timeout_fn(timeout))
}
/// Blocking write with default timeout, provided in [`TimeoutI2c::new()`]
pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
self.blocking_write_timeout(addr, bytes, self.timeout)
}
/// Blocking write-read with a custom timeout
pub fn blocking_write_read_timeout(
&mut self,
addr: u8,
bytes: &[u8],
buffer: &mut [u8],
timeout: Duration,
) -> Result<(), Error> {
self.i2c
.blocking_write_read_timeout(addr, bytes, buffer, timeout_fn(timeout))
}
/// Blocking write-read with default timeout, provided in [`TimeoutI2c::new()`]
pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
self.blocking_write_read_timeout(addr, bytes, buffer, self.timeout)
}
}
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T, TXDMA, RXDMA> {
type Error = Error;
fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(addr, buffer)
}
}
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T, TXDMA, RXDMA> {
type Error = Error;
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(addr, bytes)
}
}
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T, TXDMA, RXDMA> {
type Error = Error;
fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(addr, bytes, buffer)
}
}
#[cfg(feature = "unstable-traits")]
mod eh1 {
use super::*;
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T, TXDMA, RXDMA> {
type Error = Error;
}
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T, TXDMA, RXDMA> {
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, buffer)
}
fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(address, buffer)
}
fn write_iter<B>(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error>
where
B: IntoIterator<Item = u8>,
{
todo!();
}
fn write_iter_read<B>(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error>
where
B: IntoIterator<Item = u8>,
{
todo!();
}
fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(address, wr_buffer, rd_buffer)
}
fn transaction<'a>(
&mut self,
_address: u8,
_operations: &mut [embedded_hal_1::i2c::Operation<'a>],
) -> Result<(), Self::Error> {
todo!();
}
fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error>
where
O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
{
todo!();
}
}
}

View file

@ -1,8 +1,9 @@
use core::marker::PhantomData;
use embassy_embedded_hal::SetConfig;
use embassy_hal_common::into_ref;
use embassy_hal_common::{into_ref, PeripheralRef};
use crate::dma::NoDma;
use crate::gpio::sealed::AFType;
use crate::gpio::Pull;
use crate::i2c::{Error, Instance, SclPin, SdaPin};
@ -34,19 +35,26 @@ impl State {
}
}
pub struct I2c<'d, T: Instance> {
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
phantom: PhantomData<&'d mut T>,
#[allow(dead_code)]
tx_dma: PeripheralRef<'d, TXDMA>,
#[allow(dead_code)]
rx_dma: PeripheralRef<'d, RXDMA>,
}
impl<'d, T: Instance> I2c<'d, T> {
impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
pub fn new(
_peri: impl Peripheral<P = T> + 'd,
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
_irq: impl Peripheral<P = T::Interrupt> + 'd,
tx_dma: impl Peripheral<P = TXDMA> + 'd,
rx_dma: impl Peripheral<P = RXDMA> + 'd,
freq: Hertz,
config: Config,
) -> Self {
into_ref!(scl, sda);
into_ref!(scl, sda, tx_dma, rx_dma);
T::enable();
T::reset();
@ -99,7 +107,11 @@ impl<'d, T: Instance> I2c<'d, T> {
});
}
Self { phantom: PhantomData }
Self {
phantom: PhantomData,
tx_dma,
rx_dma,
}
}
unsafe fn check_and_clear_error_flags(&self) -> Result<i2c::regs::Sr1, Error> {
@ -141,7 +153,12 @@ impl<'d, T: Instance> I2c<'d, T> {
Ok(sr1)
}
unsafe fn write_bytes(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
unsafe fn write_bytes(
&mut self,
addr: u8,
bytes: &[u8],
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
// Send a START condition
T::regs().cr1().modify(|reg| {
@ -149,7 +166,9 @@ impl<'d, T: Instance> I2c<'d, T> {
});
// Wait until START condition was generated
while !self.check_and_clear_error_flags()?.start() {}
while !self.check_and_clear_error_flags()?.start() {
check_timeout()?;
}
// Also wait until signalled we're master and everything is waiting for us
while {
@ -157,7 +176,9 @@ impl<'d, T: Instance> I2c<'d, T> {
let sr2 = T::regs().sr2().read();
!sr2.msl() && !sr2.busy()
} {}
} {
check_timeout()?;
}
// Set up current address, we're trying to talk to
T::regs().dr().write(|reg| reg.set_dr(addr << 1));
@ -165,26 +186,30 @@ impl<'d, T: Instance> I2c<'d, T> {
// Wait until address was sent
// Wait for the address to be acknowledged
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
while !self.check_and_clear_error_flags()?.addr() {}
while !self.check_and_clear_error_flags()?.addr() {
check_timeout()?;
}
// Clear condition by reading SR2
let _ = T::regs().sr2().read();
// Send bytes
for c in bytes {
self.send_byte(*c)?;
self.send_byte(*c, &check_timeout)?;
}
// Fallthrough is success
Ok(())
}
unsafe fn send_byte(&self, byte: u8) -> Result<(), Error> {
unsafe fn send_byte(&self, byte: u8, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
// Wait until we're ready for sending
while {
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
!self.check_and_clear_error_flags()?.txe()
} {}
} {
check_timeout()?;
}
// Push out a byte of data
T::regs().dr().write(|reg| reg.set_dr(byte));
@ -193,24 +218,33 @@ impl<'d, T: Instance> I2c<'d, T> {
while {
// Check for any potential error conditions.
!self.check_and_clear_error_flags()?.btf()
} {}
} {
check_timeout()?;
}
Ok(())
}
unsafe fn recv_byte(&self) -> Result<u8, Error> {
unsafe fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> {
while {
// Check for any potential error conditions.
self.check_and_clear_error_flags()?;
!T::regs().sr1().read().rxne()
} {}
} {
check_timeout()?;
}
let value = T::regs().dr().read().dr();
Ok(value)
}
pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
pub fn blocking_read_timeout(
&mut self,
addr: u8,
buffer: &mut [u8],
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
if let Some((last, buffer)) = buffer.split_last_mut() {
// Send a START condition and set ACK bit
unsafe {
@ -221,27 +255,33 @@ impl<'d, T: Instance> I2c<'d, T> {
}
// Wait until START condition was generated
while unsafe { !T::regs().sr1().read().start() } {}
while unsafe { !self.check_and_clear_error_flags()?.start() } {
check_timeout()?;
}
// Also wait until signalled we're master and everything is waiting for us
while {
let sr2 = unsafe { T::regs().sr2().read() };
!sr2.msl() && !sr2.busy()
} {}
} {
check_timeout()?;
}
// Set up current address, we're trying to talk to
unsafe { T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1)) }
// Wait until address was sent
// Wait for the address to be acknowledged
while unsafe { !self.check_and_clear_error_flags()?.addr() } {}
while unsafe { !self.check_and_clear_error_flags()?.addr() } {
check_timeout()?;
}
// Clear condition by reading SR2
let _ = unsafe { T::regs().sr2().read() };
// Receive bytes into buffer
for c in buffer {
*c = unsafe { self.recv_byte()? };
*c = unsafe { self.recv_byte(&check_timeout)? };
}
// Prepare to send NACK then STOP after next byte
@ -253,10 +293,12 @@ impl<'d, T: Instance> I2c<'d, T> {
}
// Receive last byte
*last = unsafe { self.recv_byte()? };
*last = unsafe { self.recv_byte(&check_timeout)? };
// Wait for the STOP to be sent.
while unsafe { T::regs().cr1().read().stop() } {}
while unsafe { T::regs().cr1().read().stop() } {
check_timeout()?;
}
// Fallthrough is success
Ok(())
@ -265,25 +307,50 @@ impl<'d, T: Instance> I2c<'d, T> {
}
}
pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
self.blocking_read_timeout(addr, buffer, || Ok(()))
}
pub fn blocking_write_timeout(
&mut self,
addr: u8,
bytes: &[u8],
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
unsafe {
self.write_bytes(addr, bytes)?;
self.write_bytes(addr, bytes, &check_timeout)?;
// Send a STOP condition
T::regs().cr1().modify(|reg| reg.set_stop(true));
// Wait for STOP condition to transmit.
while T::regs().cr1().read().stop() {}
while T::regs().cr1().read().stop() {
check_timeout()?;
}
};
// Fallthrough is success
Ok(())
}
pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
unsafe { self.write_bytes(addr, bytes)? };
self.blocking_read(addr, buffer)?;
pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
self.blocking_write_timeout(addr, bytes, || Ok(()))
}
pub fn blocking_write_read_timeout(
&mut self,
addr: u8,
bytes: &[u8],
buffer: &mut [u8],
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
unsafe { self.write_bytes(addr, bytes, &check_timeout)? };
self.blocking_read_timeout(addr, buffer, &check_timeout)?;
Ok(())
}
pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
self.blocking_write_read_timeout(addr, bytes, buffer, || Ok(()))
}
}
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {

View file

@ -147,14 +147,23 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}
}
unsafe fn master_read(address: u8, length: usize, stop: Stop, reload: bool, restart: bool) {
unsafe fn master_read(
address: u8,
length: usize,
stop: Stop,
reload: bool,
restart: bool,
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
assert!(length < 256);
if !restart {
// Wait for any previous address sequence to end
// automatically. This could be up to 50% of a bus
// cycle (ie. up to 0.5/freq)
while T::regs().cr2().read().start() {}
while T::regs().cr2().read().start() {
check_timeout()?;
}
}
// Set START and prepare to receive bytes into
@ -176,15 +185,25 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
w.set_autoend(stop.autoend());
w.set_reload(reload);
});
Ok(())
}
unsafe fn master_write(address: u8, length: usize, stop: Stop, reload: bool) {
unsafe fn master_write(
address: u8,
length: usize,
stop: Stop,
reload: bool,
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
assert!(length < 256);
// Wait for any previous address sequence to end
// automatically. This could be up to 50% of a bus
// cycle (ie. up to 0.5/freq)
while T::regs().cr2().read().start() {}
while T::regs().cr2().read().start() {
check_timeout()?;
}
let reload = if reload {
i2c::vals::Reload::NOTCOMPLETED
@ -204,12 +223,20 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
w.set_autoend(stop.autoend());
w.set_reload(reload);
});
Ok(())
}
unsafe fn master_continue(length: usize, reload: bool) {
unsafe fn master_continue(
length: usize,
reload: bool,
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
assert!(length < 256 && length > 0);
while !T::regs().isr().read().tcr() {}
while !T::regs().isr().read().tcr() {
check_timeout()?;
}
let reload = if reload {
i2c::vals::Reload::NOTCOMPLETED
@ -221,6 +248,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
w.set_nbytes(length as u8);
w.set_reload(reload);
});
Ok(())
}
fn flush_txdr(&self) {
@ -243,7 +272,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
//}
}
fn wait_txe(&self) -> Result<(), Error> {
fn wait_txe(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
loop {
unsafe {
let isr = T::regs().isr().read();
@ -261,10 +290,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
return Err(Error::Nack);
}
}
check_timeout()?;
}
}
fn wait_rxne(&self) -> Result<(), Error> {
fn wait_rxne(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
loop {
unsafe {
let isr = T::regs().isr().read();
@ -282,10 +313,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
return Err(Error::Nack);
}
}
check_timeout()?;
}
}
fn wait_tc(&self) -> Result<(), Error> {
fn wait_tc(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
loop {
unsafe {
let isr = T::regs().isr().read();
@ -303,10 +336,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
return Err(Error::Nack);
}
}
check_timeout()?;
}
}
fn read_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool) -> Result<(), Error> {
fn read_internal(
&mut self,
address: u8,
buffer: &mut [u8],
restart: bool,
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
let completed_chunks = buffer.len() / 255;
let total_chunks = if completed_chunks * 255 == buffer.len() {
completed_chunks
@ -322,20 +363,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
Stop::Automatic,
last_chunk_idx != 0,
restart,
);
&check_timeout,
)?;
}
for (number, chunk) in buffer.chunks_mut(255).enumerate() {
if number != 0 {
// NOTE(unsafe) We have &mut self
unsafe {
Self::master_continue(chunk.len(), number != last_chunk_idx);
Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?;
}
}
for byte in chunk {
// Wait until we have received something
self.wait_rxne()?;
self.wait_rxne(&check_timeout)?;
unsafe {
*byte = T::regs().rxdr().read().rxdata();
@ -345,7 +387,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
Ok(())
}
fn write_internal(&mut self, address: u8, bytes: &[u8], send_stop: bool) -> Result<(), Error> {
fn write_internal(
&mut self,
address: u8,
bytes: &[u8],
send_stop: bool,
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
let completed_chunks = bytes.len() / 255;
let total_chunks = if completed_chunks * 255 == bytes.len() {
completed_chunks
@ -359,14 +407,20 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// ST SAD+W
// NOTE(unsafe) We have &mut self
unsafe {
Self::master_write(address, bytes.len().min(255), Stop::Software, last_chunk_idx != 0);
Self::master_write(
address,
bytes.len().min(255),
Stop::Software,
last_chunk_idx != 0,
&check_timeout,
)?;
}
for (number, chunk) in bytes.chunks(255).enumerate() {
if number != 0 {
// NOTE(unsafe) We have &mut self
unsafe {
Self::master_continue(chunk.len(), number != last_chunk_idx);
Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?;
}
}
@ -374,7 +428,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// Wait until we are allowed to send data
// (START has been ACKed or last byte when
// through)
self.wait_txe()?;
self.wait_txe(&check_timeout)?;
unsafe {
T::regs().txdr().write(|w| w.set_txdata(*byte));
@ -382,7 +436,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}
}
// Wait until the write finishes
self.wait_tc()?;
self.wait_tc(&check_timeout)?;
if send_stop {
self.master_stop();
@ -396,6 +450,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
bytes: &[u8],
first_slice: bool,
last_slice: bool,
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error>
where
TXDMA: crate::i2c::TxDma<T>,
@ -447,11 +502,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
total_len.min(255),
Stop::Software,
(total_chunks != 1) || !last_slice,
);
&check_timeout,
)?;
}
} else {
unsafe {
Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice);
Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice, &check_timeout)?;
T::regs().cr1().modify(|w| w.set_tcie(true));
}
}
@ -461,32 +517,40 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed);
if chunks_transferred == total_chunks {
return Poll::Ready(());
return Poll::Ready(Ok(()));
} else if chunks_transferred != 0 {
remaining_len = remaining_len.saturating_sub(255);
let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice;
// NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
unsafe {
Self::master_continue(remaining_len.min(255), !last_piece);
if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) {
return Poll::Ready(Err(e));
}
T::regs().cr1().modify(|w| w.set_tcie(true));
}
}
Poll::Pending
})
.await;
.await?;
dma_transfer.await;
if last_slice {
// This should be done already
self.wait_tc()?;
self.wait_tc(&check_timeout)?;
self.master_stop();
}
Ok(())
}
async fn read_dma_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool) -> Result<(), Error>
async fn read_dma_internal(
&mut self,
address: u8,
buffer: &mut [u8],
restart: bool,
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error>
where
RXDMA: crate::i2c::RxDma<T>,
{
@ -527,7 +591,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
unsafe {
Self::master_read(address, total_len.min(255), Stop::Software, total_chunks != 1, restart);
Self::master_read(
address,
total_len.min(255),
Stop::Software,
total_chunks != 1,
restart,
&check_timeout,
)?;
}
poll_fn(|cx| {
@ -535,25 +606,27 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed);
if chunks_transferred == total_chunks {
return Poll::Ready(());
return Poll::Ready(Ok(()));
} else if chunks_transferred != 0 {
remaining_len = remaining_len.saturating_sub(255);
let last_piece = chunks_transferred + 1 == total_chunks;
// NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
unsafe {
Self::master_continue(remaining_len.min(255), !last_piece);
if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) {
return Poll::Ready(Err(e));
}
T::regs().cr1().modify(|w| w.set_tcie(true));
}
}
Poll::Pending
})
.await;
.await?;
dma_transfer.await;
// This should be done already
self.wait_tc()?;
self.wait_tc(&check_timeout)?;
self.master_stop();
Ok(())
}
@ -566,9 +639,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
TXDMA: crate::i2c::TxDma<T>,
{
if bytes.is_empty() {
self.write_internal(address, bytes, true)
self.write_internal(address, bytes, true, || Ok(()))
} else {
self.write_dma_internal(address, bytes, true, true).await
self.write_dma_internal(address, bytes, true, true, || Ok(())).await
}
}
@ -587,7 +660,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
let next = iter.next();
let is_last = next.is_none();
self.write_dma_internal(address, c, first, is_last).await?;
self.write_dma_internal(address, c, first, is_last, || Ok(())).await?;
first = false;
current = next;
}
@ -599,9 +672,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
RXDMA: crate::i2c::RxDma<T>,
{
if buffer.is_empty() {
self.read_internal(address, buffer, false)
self.read_internal(address, buffer, false, || Ok(()))
} else {
self.read_dma_internal(address, buffer, false).await
self.read_dma_internal(address, buffer, false, || Ok(())).await
}
}
@ -611,15 +684,15 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
RXDMA: super::RxDma<T>,
{
if bytes.is_empty() {
self.write_internal(address, bytes, false)?;
self.write_internal(address, bytes, false, || Ok(()))?;
} else {
self.write_dma_internal(address, bytes, true, true).await?;
self.write_dma_internal(address, bytes, true, true, || Ok(())).await?;
}
if buffer.is_empty() {
self.read_internal(address, buffer, true)?;
self.read_internal(address, buffer, true, || Ok(()))?;
} else {
self.read_dma_internal(address, buffer, true).await?;
self.read_dma_internal(address, buffer, true, || Ok(())).await?;
}
Ok(())
@ -628,22 +701,55 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// =========================
// Blocking public API
pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
self.read_internal(address, buffer, false)
pub fn blocking_read_timeout(
&mut self,
address: u8,
buffer: &mut [u8],
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
self.read_internal(address, buffer, false, &check_timeout)
// Automatic Stop
}
pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
self.blocking_read_timeout(address, buffer, || Ok(()))
}
pub fn blocking_write_timeout(
&mut self,
address: u8,
bytes: &[u8],
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
self.write_internal(address, bytes, true, &check_timeout)
}
pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> {
self.write_internal(address, bytes, true)
self.blocking_write_timeout(address, bytes, || Ok(()))
}
pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
self.write_internal(address, bytes, false)?;
self.read_internal(address, buffer, true)
pub fn blocking_write_read_timeout(
&mut self,
address: u8,
bytes: &[u8],
buffer: &mut [u8],
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
self.write_internal(address, bytes, false, &check_timeout)?;
self.read_internal(address, buffer, true, &check_timeout)
// Automatic Stop
}
pub fn blocking_write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> {
pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
self.blocking_write_read_timeout(address, bytes, buffer, || Ok(()))
}
pub fn blocking_write_vectored_timeout(
&mut self,
address: u8,
bytes: &[&[u8]],
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
if bytes.is_empty() {
return Err(Error::ZeroLengthTransfer);
}
@ -657,7 +763,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
first_length.min(255),
Stop::Software,
(first_length > 255) || (last_slice_index != 0),
);
&check_timeout,
)?;
}
for (idx, slice) in bytes.iter().enumerate() {
@ -673,7 +780,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
if idx != 0 {
// NOTE(unsafe) We have &mut self
unsafe {
Self::master_continue(slice_len.min(255), (idx != last_slice_index) || (slice_len > 255));
Self::master_continue(
slice_len.min(255),
(idx != last_slice_index) || (slice_len > 255),
&check_timeout,
)?;
}
}
@ -681,7 +792,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
if number != 0 {
// NOTE(unsafe) We have &mut self
unsafe {
Self::master_continue(chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index));
Self::master_continue(
chunk.len(),
(number != last_chunk_idx) || (idx != last_slice_index),
&check_timeout,
)?;
}
}
@ -689,7 +804,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// Wait until we are allowed to send data
// (START has been ACKed or last byte when
// through)
self.wait_txe()?;
self.wait_txe(&check_timeout)?;
// Put byte on the wire
//self.i2c.txdr.write(|w| w.txdata().bits(*byte));
@ -700,11 +815,15 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}
}
// Wait until the write finishes
self.wait_tc()?;
self.wait_tc(&check_timeout)?;
self.master_stop();
Ok(())
}
pub fn blocking_write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> {
self.blocking_write_vectored_timeout(address, bytes, || Ok(()))
}
}
mod eh02 {

View file

@ -52,7 +52,7 @@ pub mod sdmmc;
pub mod spi;
#[cfg(usart)]
pub mod usart;
#[cfg(usb)]
#[cfg(all(usb, feature = "time"))]
pub mod usb;
#[cfg(any(otgfs, otghs))]
pub mod usb_otg;

View file

@ -439,6 +439,7 @@ impl From<Timeout> for [u8; 3] {
}
}
#[cfg(feature = "time")]
impl From<Timeout> for embassy_time::Duration {
fn from(to: Timeout) -> Self {
embassy_time::Duration::from_micros(to.as_micros().into())

View file

@ -44,6 +44,7 @@ impl From<RampTime> for core::time::Duration {
}
}
#[cfg(feature = "time")]
impl From<RampTime> for embassy_time::Duration {
fn from(rt: RampTime) -> Self {
match rt {

View file

@ -0,0 +1,45 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::dma::NoDma;
use embassy_stm32::i2c::{Error, I2c, TimeoutI2c};
use embassy_stm32::interrupt;
use embassy_stm32::time::Hertz;
use embassy_time::Duration;
use {defmt_rtt as _, panic_probe as _};
const ADDRESS: u8 = 0x5F;
const WHOAMI: u8 = 0x0F;
#[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! {
info!("Hello world!");
let p = embassy_stm32::init(Default::default());
let irq = interrupt::take!(I2C2_EV);
let mut i2c = I2c::new(
p.I2C2,
p.PB10,
p.PB11,
irq,
NoDma,
NoDma,
Hertz(100_000),
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];
match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) {
Ok(()) => info!("Whoami: {}", data[0]),
Err(Error::Timeout) => error!("Operation timed out"),
Err(e) => error!("I2c Error: {:?}", e),
}
}

View file

@ -0,0 +1,44 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::i2c::{Error, I2c, TimeoutI2c};
use embassy_stm32::interrupt;
use embassy_stm32::time::Hertz;
use embassy_time::Duration;
use {defmt_rtt as _, panic_probe as _};
const ADDRESS: u8 = 0x5F;
const WHOAMI: u8 = 0x0F;
#[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! {
info!("Hello world!");
let p = embassy_stm32::init(Default::default());
let irq = interrupt::take!(I2C2_EV);
let mut i2c = I2c::new(
p.I2C2,
p.PB10,
p.PB11,
irq,
p.DMA1_CH4,
p.DMA1_CH5,
Hertz(100_000),
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];
match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) {
Ok(()) => info!("Whoami: {}", data[0]),
Err(Error::Timeout) => error!("Operation timed out"),
Err(e) => error!("I2c Error: {:?}", e),
}
}