Merge #1025
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:
commit
01e23bf9dd
@ -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"]
|
||||
|
@ -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)]
|
||||
|
142
embassy-stm32/src/i2c/timeout.rs
Normal file
142
embassy-stm32/src/i2c/timeout.rs
Normal 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!();
|
||||
}
|
||||
}
|
||||
}
|
@ -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> {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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())
|
||||
|
@ -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 {
|
||||
|
45
examples/stm32f4/src/bin/i2c.rs
Normal file
45
examples/stm32f4/src/bin/i2c.rs
Normal 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),
|
||||
}
|
||||
}
|
44
examples/stm32h7/src/bin/i2c.rs
Normal file
44
examples/stm32h7/src/bin/i2c.rs
Normal 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),
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user