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 = [] subghz = []
exti = [] 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 # 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.
_time-driver = ["dep:embassy-time"] _time-driver = ["time"]
time-driver-any = ["_time-driver"] time-driver-any = ["_time-driver"]
time-driver-tim2 = ["_time-driver"] time-driver-tim2 = ["_time-driver"]
time-driver-tim3 = ["_time-driver"] time-driver-tim3 = ["_time-driver"]

View File

@ -7,6 +7,11 @@ use crate::interrupt::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)] #[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 core::marker::PhantomData;
use embassy_embedded_hal::SetConfig; 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::sealed::AFType;
use crate::gpio::Pull; use crate::gpio::Pull;
use crate::i2c::{Error, Instance, SclPin, SdaPin}; 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>, 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( pub fn new(
_peri: impl Peripheral<P = T> + 'd, _peri: impl Peripheral<P = T> + 'd,
scl: impl Peripheral<P = impl SclPin<T>> + 'd, scl: impl Peripheral<P = impl SclPin<T>> + 'd,
sda: impl Peripheral<P = impl SdaPin<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, freq: Hertz,
config: Config, config: Config,
) -> Self { ) -> Self {
into_ref!(scl, sda); into_ref!(scl, sda, tx_dma, rx_dma);
T::enable(); T::enable();
T::reset(); 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> { 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) 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 // Send a START condition
T::regs().cr1().modify(|reg| { T::regs().cr1().modify(|reg| {
@ -149,7 +166,9 @@ impl<'d, T: Instance> I2c<'d, T> {
}); });
// Wait until START condition was generated // 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 // Also wait until signalled we're master and everything is waiting for us
while { while {
@ -157,7 +176,9 @@ impl<'d, T: Instance> I2c<'d, T> {
let sr2 = T::regs().sr2().read(); let sr2 = T::regs().sr2().read();
!sr2.msl() && !sr2.busy() !sr2.msl() && !sr2.busy()
} {} } {
check_timeout()?;
}
// Set up current address, we're trying to talk to // Set up current address, we're trying to talk to
T::regs().dr().write(|reg| reg.set_dr(addr << 1)); 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 until address was sent
// Wait for the address to be acknowledged // Wait for the address to be acknowledged
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. // 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 // Clear condition by reading SR2
let _ = T::regs().sr2().read(); let _ = T::regs().sr2().read();
// Send bytes // Send bytes
for c in bytes { for c in bytes {
self.send_byte(*c)?; self.send_byte(*c, &check_timeout)?;
} }
// Fallthrough is success // Fallthrough is success
Ok(()) 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 // Wait until we're ready for sending
while { while {
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
!self.check_and_clear_error_flags()?.txe() !self.check_and_clear_error_flags()?.txe()
} {} } {
check_timeout()?;
}
// Push out a byte of data // Push out a byte of data
T::regs().dr().write(|reg| reg.set_dr(byte)); T::regs().dr().write(|reg| reg.set_dr(byte));
@ -193,24 +218,33 @@ impl<'d, T: Instance> I2c<'d, T> {
while { while {
// Check for any potential error conditions. // Check for any potential error conditions.
!self.check_and_clear_error_flags()?.btf() !self.check_and_clear_error_flags()?.btf()
} {} } {
check_timeout()?;
}
Ok(()) Ok(())
} }
unsafe fn recv_byte(&self) -> Result<u8, Error> { unsafe fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> {
while { while {
// Check for any potential error conditions. // Check for any potential error conditions.
self.check_and_clear_error_flags()?; self.check_and_clear_error_flags()?;
!T::regs().sr1().read().rxne() !T::regs().sr1().read().rxne()
} {} } {
check_timeout()?;
}
let value = T::regs().dr().read().dr(); let value = T::regs().dr().read().dr();
Ok(value) 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() { if let Some((last, buffer)) = buffer.split_last_mut() {
// Send a START condition and set ACK bit // Send a START condition and set ACK bit
unsafe { unsafe {
@ -221,27 +255,33 @@ impl<'d, T: Instance> I2c<'d, T> {
} }
// Wait until START condition was generated // 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 // Also wait until signalled we're master and everything is waiting for us
while { while {
let sr2 = unsafe { T::regs().sr2().read() }; let sr2 = unsafe { T::regs().sr2().read() };
!sr2.msl() && !sr2.busy() !sr2.msl() && !sr2.busy()
} {} } {
check_timeout()?;
}
// Set up current address, we're trying to talk to // Set up current address, we're trying to talk to
unsafe { T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1)) } unsafe { T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1)) }
// Wait until address was sent // Wait until address was sent
// Wait for the address to be acknowledged // 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 // Clear condition by reading SR2
let _ = unsafe { T::regs().sr2().read() }; let _ = unsafe { T::regs().sr2().read() };
// Receive bytes into buffer // Receive bytes into buffer
for c in 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 // Prepare to send NACK then STOP after next byte
@ -253,10 +293,12 @@ impl<'d, T: Instance> I2c<'d, T> {
} }
// Receive last byte // Receive last byte
*last = unsafe { self.recv_byte()? }; *last = unsafe { self.recv_byte(&check_timeout)? };
// Wait for the STOP to be sent. // 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 // Fallthrough is success
Ok(()) 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 { unsafe {
self.write_bytes(addr, bytes)?; self.write_bytes(addr, bytes, &check_timeout)?;
// Send a STOP condition // Send a STOP condition
T::regs().cr1().modify(|reg| reg.set_stop(true)); T::regs().cr1().modify(|reg| reg.set_stop(true));
// Wait for STOP condition to transmit. // Wait for STOP condition to transmit.
while T::regs().cr1().read().stop() {} while T::regs().cr1().read().stop() {
check_timeout()?;
}
}; };
// Fallthrough is success // Fallthrough is success
Ok(()) Ok(())
} }
pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
unsafe { self.write_bytes(addr, bytes)? }; self.blocking_write_timeout(addr, bytes, || Ok(()))
self.blocking_read(addr, buffer)?; }
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(()) 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> { 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); assert!(length < 256);
if !restart { if !restart {
// Wait for any previous address sequence to end // Wait for any previous address sequence to end
// automatically. This could be up to 50% of a bus // automatically. This could be up to 50% of a bus
// cycle (ie. up to 0.5/freq) // 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 // 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_autoend(stop.autoend());
w.set_reload(reload); 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); assert!(length < 256);
// Wait for any previous address sequence to end // Wait for any previous address sequence to end
// automatically. This could be up to 50% of a bus // automatically. This could be up to 50% of a bus
// cycle (ie. up to 0.5/freq) // 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 { let reload = if reload {
i2c::vals::Reload::NOTCOMPLETED 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_autoend(stop.autoend());
w.set_reload(reload); 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); assert!(length < 256 && length > 0);
while !T::regs().isr().read().tcr() {} while !T::regs().isr().read().tcr() {
check_timeout()?;
}
let reload = if reload { let reload = if reload {
i2c::vals::Reload::NOTCOMPLETED 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_nbytes(length as u8);
w.set_reload(reload); w.set_reload(reload);
}); });
Ok(())
} }
fn flush_txdr(&self) { 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 { loop {
unsafe { unsafe {
let isr = T::regs().isr().read(); 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); 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 { loop {
unsafe { unsafe {
let isr = T::regs().isr().read(); 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); 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 { loop {
unsafe { unsafe {
let isr = T::regs().isr().read(); 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); 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 completed_chunks = buffer.len() / 255;
let total_chunks = if completed_chunks * 255 == buffer.len() { let total_chunks = if completed_chunks * 255 == buffer.len() {
completed_chunks completed_chunks
@ -322,20 +363,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
Stop::Automatic, Stop::Automatic,
last_chunk_idx != 0, last_chunk_idx != 0,
restart, restart,
); &check_timeout,
)?;
} }
for (number, chunk) in buffer.chunks_mut(255).enumerate() { for (number, chunk) in buffer.chunks_mut(255).enumerate() {
if number != 0 { if number != 0 {
// NOTE(unsafe) We have &mut self // NOTE(unsafe) We have &mut self
unsafe { 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 { for byte in chunk {
// Wait until we have received something // Wait until we have received something
self.wait_rxne()?; self.wait_rxne(&check_timeout)?;
unsafe { unsafe {
*byte = T::regs().rxdr().read().rxdata(); *byte = T::regs().rxdr().read().rxdata();
@ -345,7 +387,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
Ok(()) 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 completed_chunks = bytes.len() / 255;
let total_chunks = if completed_chunks * 255 == bytes.len() { let total_chunks = if completed_chunks * 255 == bytes.len() {
completed_chunks completed_chunks
@ -359,14 +407,20 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// ST SAD+W // ST SAD+W
// NOTE(unsafe) We have &mut self // NOTE(unsafe) We have &mut self
unsafe { 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() { for (number, chunk) in bytes.chunks(255).enumerate() {
if number != 0 { if number != 0 {
// NOTE(unsafe) We have &mut self // NOTE(unsafe) We have &mut self
unsafe { 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 // Wait until we are allowed to send data
// (START has been ACKed or last byte when // (START has been ACKed or last byte when
// through) // through)
self.wait_txe()?; self.wait_txe(&check_timeout)?;
unsafe { unsafe {
T::regs().txdr().write(|w| w.set_txdata(*byte)); 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 // Wait until the write finishes
self.wait_tc()?; self.wait_tc(&check_timeout)?;
if send_stop { if send_stop {
self.master_stop(); self.master_stop();
@ -396,6 +450,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
bytes: &[u8], bytes: &[u8],
first_slice: bool, first_slice: bool,
last_slice: bool, last_slice: bool,
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> ) -> Result<(), Error>
where where
TXDMA: crate::i2c::TxDma<T>, TXDMA: crate::i2c::TxDma<T>,
@ -447,11 +502,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
total_len.min(255), total_len.min(255),
Stop::Software, Stop::Software,
(total_chunks != 1) || !last_slice, (total_chunks != 1) || !last_slice,
); &check_timeout,
)?;
} }
} else { } else {
unsafe { 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)); 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); let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed);
if chunks_transferred == total_chunks { if chunks_transferred == total_chunks {
return Poll::Ready(()); return Poll::Ready(Ok(()));
} else if chunks_transferred != 0 { } else if chunks_transferred != 0 {
remaining_len = remaining_len.saturating_sub(255); remaining_len = remaining_len.saturating_sub(255);
let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice; let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice;
// NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
unsafe { 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)); T::regs().cr1().modify(|w| w.set_tcie(true));
} }
} }
Poll::Pending Poll::Pending
}) })
.await; .await?;
dma_transfer.await; dma_transfer.await;
if last_slice { if last_slice {
// This should be done already // This should be done already
self.wait_tc()?; self.wait_tc(&check_timeout)?;
self.master_stop(); self.master_stop();
} }
Ok(()) 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 where
RXDMA: crate::i2c::RxDma<T>, 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 // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
unsafe { 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| { 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); let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed);
if chunks_transferred == total_chunks { if chunks_transferred == total_chunks {
return Poll::Ready(()); return Poll::Ready(Ok(()));
} else if chunks_transferred != 0 { } else if chunks_transferred != 0 {
remaining_len = remaining_len.saturating_sub(255); remaining_len = remaining_len.saturating_sub(255);
let last_piece = chunks_transferred + 1 == total_chunks; let last_piece = chunks_transferred + 1 == total_chunks;
// NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
unsafe { 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)); T::regs().cr1().modify(|w| w.set_tcie(true));
} }
} }
Poll::Pending Poll::Pending
}) })
.await; .await?;
dma_transfer.await; dma_transfer.await;
// This should be done already // This should be done already
self.wait_tc()?; self.wait_tc(&check_timeout)?;
self.master_stop(); self.master_stop();
Ok(()) Ok(())
} }
@ -566,9 +639,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
TXDMA: crate::i2c::TxDma<T>, TXDMA: crate::i2c::TxDma<T>,
{ {
if bytes.is_empty() { if bytes.is_empty() {
self.write_internal(address, bytes, true) self.write_internal(address, bytes, true, || Ok(()))
} else { } 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 next = iter.next();
let is_last = next.is_none(); 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; first = false;
current = next; current = next;
} }
@ -599,9 +672,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
RXDMA: crate::i2c::RxDma<T>, RXDMA: crate::i2c::RxDma<T>,
{ {
if buffer.is_empty() { if buffer.is_empty() {
self.read_internal(address, buffer, false) self.read_internal(address, buffer, false, || Ok(()))
} else { } 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>, RXDMA: super::RxDma<T>,
{ {
if bytes.is_empty() { if bytes.is_empty() {
self.write_internal(address, bytes, false)?; self.write_internal(address, bytes, false, || Ok(()))?;
} else { } else {
self.write_dma_internal(address, bytes, true, true).await?; self.write_dma_internal(address, bytes, true, true, || Ok(())).await?;
} }
if buffer.is_empty() { if buffer.is_empty() {
self.read_internal(address, buffer, true)?; self.read_internal(address, buffer, true, || Ok(()))?;
} else { } else {
self.read_dma_internal(address, buffer, true).await?; self.read_dma_internal(address, buffer, true, || Ok(())).await?;
} }
Ok(()) Ok(())
@ -628,22 +701,55 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// ========================= // =========================
// Blocking public API // Blocking public API
pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { pub fn blocking_read_timeout(
self.read_internal(address, buffer, false) &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 // 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> { 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> { pub fn blocking_write_read_timeout(
self.write_internal(address, bytes, false)?; &mut self,
self.read_internal(address, buffer, true) 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 // 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() { if bytes.is_empty() {
return Err(Error::ZeroLengthTransfer); return Err(Error::ZeroLengthTransfer);
} }
@ -657,7 +763,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
first_length.min(255), first_length.min(255),
Stop::Software, Stop::Software,
(first_length > 255) || (last_slice_index != 0), (first_length > 255) || (last_slice_index != 0),
); &check_timeout,
)?;
} }
for (idx, slice) in bytes.iter().enumerate() { 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 { if idx != 0 {
// NOTE(unsafe) We have &mut self // NOTE(unsafe) We have &mut self
unsafe { 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 { if number != 0 {
// NOTE(unsafe) We have &mut self // NOTE(unsafe) We have &mut self
unsafe { 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 // Wait until we are allowed to send data
// (START has been ACKed or last byte when // (START has been ACKed or last byte when
// through) // through)
self.wait_txe()?; self.wait_txe(&check_timeout)?;
// Put byte on the wire // Put byte on the wire
//self.i2c.txdr.write(|w| w.txdata().bits(*byte)); //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 // Wait until the write finishes
self.wait_tc()?; self.wait_tc(&check_timeout)?;
self.master_stop(); self.master_stop();
Ok(()) Ok(())
} }
pub fn blocking_write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> {
self.blocking_write_vectored_timeout(address, bytes, || Ok(()))
}
} }
mod eh02 { mod eh02 {

View File

@ -52,7 +52,7 @@ pub mod sdmmc;
pub mod spi; pub mod spi;
#[cfg(usart)] #[cfg(usart)]
pub mod usart; pub mod usart;
#[cfg(usb)] #[cfg(all(usb, feature = "time"))]
pub mod usb; pub mod usb;
#[cfg(any(otgfs, otghs))] #[cfg(any(otgfs, otghs))]
pub mod usb_otg; 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 { impl From<Timeout> for embassy_time::Duration {
fn from(to: Timeout) -> Self { fn from(to: Timeout) -> Self {
embassy_time::Duration::from_micros(to.as_micros().into()) 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 { impl From<RampTime> for embassy_time::Duration {
fn from(rt: RampTime) -> Self { fn from(rt: RampTime) -> Self {
match rt { 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),
}
}