Implement i2cv1 timeout
This commit is contained in:
parent
ce1cba761c
commit
4ce4131f8b
@ -7,6 +7,9 @@ use crate::interrupt::Interrupt;
|
|||||||
mod _version;
|
mod _version;
|
||||||
pub use _version::*;
|
pub use _version::*;
|
||||||
|
|
||||||
|
mod timeout;
|
||||||
|
pub use timeout::*;
|
||||||
|
|
||||||
use crate::peripherals;
|
use crate::peripherals;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
132
embassy-stm32/src/i2c/timeout.rs
Normal file
132
embassy-stm32/src/i2c/timeout.rs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
use embassy_time::{Duration, Instant};
|
||||||
|
|
||||||
|
use super::{Error, I2c, Instance};
|
||||||
|
|
||||||
|
pub struct TimeoutI2c<'d, T: Instance> {
|
||||||
|
i2c: &'d mut I2c<'d, T>,
|
||||||
|
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> TimeoutI2c<'d, T> {
|
||||||
|
pub fn new(i2c: &'d mut I2c<'d, T>, timeout: Duration) -> Self {
|
||||||
|
Self { i2c, 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))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
|
self.blocking_read_timeout(addr, buffer, self.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))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
|
||||||
|
self.blocking_write_timeout(addr, bytes, self.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))
|
||||||
|
}
|
||||||
|
|
||||||
|
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> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T> {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
|
self.blocking_read(addr, buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T> {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
self.blocking_write(addr, bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T> {
|
||||||
|
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> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T> {
|
||||||
|
type Error = Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T> {
|
||||||
|
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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -141,7 +141,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 +154,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 +164,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 +174,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 +206,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 +243,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 +281,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 +295,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> {
|
||||||
|
30
examples/stm32f4/src/bin/i2c.rs
Normal file
30
examples/stm32f4/src/bin/i2c.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#![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::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 mut i2c = I2c::new(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default());
|
||||||
|
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