stm32/i2c: implement async i2c v1.
This commit is contained in:
parent
bc65b8f7ec
commit
3efc3eee57
@ -1,10 +1,14 @@
|
|||||||
|
use core::future::poll_fn;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_embedded_hal::SetConfig;
|
use embassy_embedded_hal::SetConfig;
|
||||||
|
use embassy_futures::select::{select, Either};
|
||||||
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::dma::NoDma;
|
use crate::dma::{NoDma, Transfer};
|
||||||
use crate::gpio::sealed::AFType;
|
use crate::gpio::sealed::AFType;
|
||||||
use crate::gpio::Pull;
|
use crate::gpio::Pull;
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
@ -13,7 +17,17 @@ use crate::time::Hertz;
|
|||||||
use crate::{interrupt, Peripheral};
|
use crate::{interrupt, Peripheral};
|
||||||
|
|
||||||
pub unsafe fn on_interrupt<T: Instance>() {
|
pub unsafe fn on_interrupt<T: Instance>() {
|
||||||
// todo
|
let regs = T::regs();
|
||||||
|
// i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of
|
||||||
|
// other stuff, so we wake the task on every interrupt.
|
||||||
|
T::state().waker.wake();
|
||||||
|
critical_section::with(|_| {
|
||||||
|
// Clear event interrupt flag.
|
||||||
|
regs.cr2().modify(|w| {
|
||||||
|
w.set_itevten(false);
|
||||||
|
w.set_iterren(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@ -98,40 +112,58 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_and_clear_error_flags(&self) -> Result<i2c::regs::Sr1, Error> {
|
fn check_and_clear_error_flags() -> Result<i2c::regs::Sr1, Error> {
|
||||||
// Note that flags should only be cleared once they have been registered. If flags are
|
// Note that flags should only be cleared once they have been registered. If flags are
|
||||||
// cleared otherwise, there may be an inherent race condition and flags may be missed.
|
// cleared otherwise, there may be an inherent race condition and flags may be missed.
|
||||||
let sr1 = T::regs().sr1().read();
|
let sr1 = T::regs().sr1().read();
|
||||||
|
|
||||||
if sr1.timeout() {
|
if sr1.timeout() {
|
||||||
T::regs().sr1().modify(|reg| reg.set_timeout(false));
|
T::regs().sr1().write(|reg| {
|
||||||
|
reg.0 = !0;
|
||||||
|
reg.set_timeout(false);
|
||||||
|
});
|
||||||
return Err(Error::Timeout);
|
return Err(Error::Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if sr1.pecerr() {
|
if sr1.pecerr() {
|
||||||
T::regs().sr1().modify(|reg| reg.set_pecerr(false));
|
T::regs().sr1().write(|reg| {
|
||||||
|
reg.0 = !0;
|
||||||
|
reg.set_pecerr(false);
|
||||||
|
});
|
||||||
return Err(Error::Crc);
|
return Err(Error::Crc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if sr1.ovr() {
|
if sr1.ovr() {
|
||||||
T::regs().sr1().modify(|reg| reg.set_ovr(false));
|
T::regs().sr1().write(|reg| {
|
||||||
|
reg.0 = !0;
|
||||||
|
reg.set_ovr(false);
|
||||||
|
});
|
||||||
return Err(Error::Overrun);
|
return Err(Error::Overrun);
|
||||||
}
|
}
|
||||||
|
|
||||||
if sr1.af() {
|
if sr1.af() {
|
||||||
T::regs().sr1().modify(|reg| reg.set_af(false));
|
T::regs().sr1().write(|reg| {
|
||||||
|
reg.0 = !0;
|
||||||
|
reg.set_af(false);
|
||||||
|
});
|
||||||
return Err(Error::Nack);
|
return Err(Error::Nack);
|
||||||
}
|
}
|
||||||
|
|
||||||
if sr1.arlo() {
|
if sr1.arlo() {
|
||||||
T::regs().sr1().modify(|reg| reg.set_arlo(false));
|
T::regs().sr1().write(|reg| {
|
||||||
|
reg.0 = !0;
|
||||||
|
reg.set_arlo(false);
|
||||||
|
});
|
||||||
return Err(Error::Arbitration);
|
return Err(Error::Arbitration);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The errata indicates that BERR may be incorrectly detected. It recommends ignoring and
|
// The errata indicates that BERR may be incorrectly detected. It recommends ignoring and
|
||||||
// clearing the BERR bit instead.
|
// clearing the BERR bit instead.
|
||||||
if sr1.berr() {
|
if sr1.berr() {
|
||||||
T::regs().sr1().modify(|reg| reg.set_berr(false));
|
T::regs().sr1().write(|reg| {
|
||||||
|
reg.0 = !0;
|
||||||
|
reg.set_berr(false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(sr1)
|
Ok(sr1)
|
||||||
@ -150,13 +182,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 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()?;
|
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 {
|
||||||
self.check_and_clear_error_flags()?;
|
Self::check_and_clear_error_flags()?;
|
||||||
|
|
||||||
let sr2 = T::regs().sr2().read();
|
let sr2 = T::regs().sr2().read();
|
||||||
!sr2.msl() && !sr2.busy()
|
!sr2.msl() && !sr2.busy()
|
||||||
@ -170,7 +202,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
// 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()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +222,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
// 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()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
@ -201,7 +233,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
// Wait until byte is transferred
|
// Wait until byte is transferred
|
||||||
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()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
@ -212,7 +244,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> {
|
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()
|
||||||
} {
|
} {
|
||||||
@ -237,7 +269,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 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()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,7 +286,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
|
|
||||||
// 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 !self.check_and_clear_error_flags()?.addr() {
|
while !Self::check_and_clear_error_flags()?.addr() {
|
||||||
check_timeout()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,26 +364,352 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
|
|
||||||
// Async
|
// Async
|
||||||
|
|
||||||
pub async fn write(&mut self, _address: u8, _write: &[u8]) -> Result<(), Error>
|
#[inline] // pretty sure this should always be inlined
|
||||||
|
fn enable_interrupts() -> () {
|
||||||
|
T::regs().cr2().modify(|w| {
|
||||||
|
w.set_iterren(true);
|
||||||
|
w.set_itevten(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn write_with_stop(&mut self, address: u8, write: &[u8], send_stop: bool) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
TXDMA: crate::i2c::TxDma<T>,
|
TXDMA: crate::i2c::TxDma<T>,
|
||||||
{
|
{
|
||||||
todo!()
|
let dma_transfer = unsafe {
|
||||||
|
let regs = T::regs();
|
||||||
|
regs.cr2().modify(|w| {
|
||||||
|
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register.
|
||||||
|
w.set_dmaen(true);
|
||||||
|
w.set_itbufen(false);
|
||||||
|
});
|
||||||
|
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event.
|
||||||
|
let dst = regs.dr().as_ptr() as *mut u8;
|
||||||
|
|
||||||
|
let ch = &mut self.tx_dma;
|
||||||
|
let request = ch.request();
|
||||||
|
Transfer::new_write(ch, request, write, dst, Default::default())
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_drop = OnDrop::new(|| {
|
||||||
|
let regs = T::regs();
|
||||||
|
regs.cr2().modify(|w| {
|
||||||
|
w.set_dmaen(false);
|
||||||
|
w.set_iterren(false);
|
||||||
|
w.set_itevten(false);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Self::enable_interrupts();
|
||||||
|
|
||||||
|
// Send a START condition
|
||||||
|
T::regs().cr1().modify(|reg| {
|
||||||
|
reg.set_start(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
let state = T::state();
|
||||||
|
|
||||||
|
// Wait until START condition was generated
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err(e)),
|
||||||
|
Ok(sr1) => {
|
||||||
|
if sr1.start() {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Also wait until signalled we're master and everything is waiting for us
|
||||||
|
Self::enable_interrupts();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err(e)),
|
||||||
|
Ok(_) => {
|
||||||
|
let sr2 = T::regs().sr2().read();
|
||||||
|
if !sr2.msl() && !sr2.busy() {
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Set up current address, we're trying to talk to
|
||||||
|
Self::enable_interrupts();
|
||||||
|
T::regs().dr().write(|reg| reg.set_dr(address << 1));
|
||||||
|
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err(e)),
|
||||||
|
Ok(sr1) => {
|
||||||
|
if sr1.addr() {
|
||||||
|
// Clear the ADDR condition by reading SR2.
|
||||||
|
T::regs().sr2().read();
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Self::enable_interrupts();
|
||||||
|
let poll_error = poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
// Unclear why the Err turbofish is necessary here? The compiler didn’t require it in the other
|
||||||
|
// identical poll_fn check_and_clear matches.
|
||||||
|
Err(e) => Poll::Ready(Err::<T, Error>(e)),
|
||||||
|
Ok(_) => Poll::Pending,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for either the DMA transfer to successfully finish, or an I2C error to occur.
|
||||||
|
match select(dma_transfer, poll_error).await {
|
||||||
|
Either::Second(Err(e)) => Err(e),
|
||||||
|
_ => Ok(()),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
// The I2C transfer itself will take longer than the DMA transfer, so wait for that to finish too.
|
||||||
|
|
||||||
|
// 18.3.8 “Master transmitter: In the interrupt routine after the EOT interrupt, disable DMA
|
||||||
|
// requests then wait for a BTF event before programming the Stop condition.”
|
||||||
|
|
||||||
|
// TODO: If this has to be done “in the interrupt routine after the EOT interrupt”, where to put it?
|
||||||
|
T::regs().cr2().modify(|w| {
|
||||||
|
w.set_dmaen(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
Self::enable_interrupts();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err(e)),
|
||||||
|
Ok(sr1) => {
|
||||||
|
if sr1.btf() {
|
||||||
|
if send_stop {
|
||||||
|
T::regs().cr1().modify(|w| {
|
||||||
|
w.set_stop(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read(&mut self, _address: u8, _buffer: &mut [u8]) -> Result<(), Error>
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
drop(on_drop);
|
||||||
|
|
||||||
|
// Fallthrough is success
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
TXDMA: crate::i2c::TxDma<T>,
|
||||||
|
{
|
||||||
|
self.write_with_stop(address, write, true).await?;
|
||||||
|
|
||||||
|
// Wait for STOP condition to transmit.
|
||||||
|
Self::enable_interrupts();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
T::state().waker.register(cx.waker());
|
||||||
|
// TODO: error interrupts are enabled here, should we additional check for and return errors?
|
||||||
|
if T::regs().cr1().read().stop() {
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
RXDMA: crate::i2c::RxDma<T>,
|
RXDMA: crate::i2c::RxDma<T>,
|
||||||
{
|
{
|
||||||
todo!()
|
let state = T::state();
|
||||||
|
let buffer_len = buffer.len();
|
||||||
|
|
||||||
|
let dma_transfer = unsafe {
|
||||||
|
let regs = T::regs();
|
||||||
|
regs.cr2().modify(|w| {
|
||||||
|
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register.
|
||||||
|
w.set_itbufen(false);
|
||||||
|
w.set_dmaen(true);
|
||||||
|
});
|
||||||
|
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event.
|
||||||
|
let src = regs.dr().as_ptr() as *mut u8;
|
||||||
|
|
||||||
|
let ch = &mut self.rx_dma;
|
||||||
|
let request = ch.request();
|
||||||
|
Transfer::new_read(ch, request, src, buffer, Default::default())
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_drop = OnDrop::new(|| {
|
||||||
|
let regs = T::regs();
|
||||||
|
regs.cr2().modify(|w| {
|
||||||
|
w.set_dmaen(false);
|
||||||
|
w.set_iterren(false);
|
||||||
|
w.set_itevten(false);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Self::enable_interrupts();
|
||||||
|
|
||||||
|
// Send a START condition and set ACK bit
|
||||||
|
T::regs().cr1().modify(|reg| {
|
||||||
|
reg.set_start(true);
|
||||||
|
reg.set_ack(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait until START condition was generated
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err(e)),
|
||||||
|
Ok(sr1) => {
|
||||||
|
if sr1.start() {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Also wait until signalled we're master and everything is waiting for us
|
||||||
|
Self::enable_interrupts();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
// blocking read didn’t have a check_and_clear call here, but blocking write did so
|
||||||
|
// I’m adding it here in case that was an oversight.
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err(e)),
|
||||||
|
Ok(_) => {
|
||||||
|
let sr2 = T::regs().sr2().read();
|
||||||
|
if !sr2.msl() && !sr2.busy() {
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Set up current address, we're trying to talk to
|
||||||
|
T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1));
|
||||||
|
|
||||||
|
// Wait for the address to be acknowledged
|
||||||
|
|
||||||
|
Self::enable_interrupts();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err(e)),
|
||||||
|
Ok(sr1) => {
|
||||||
|
if sr1.addr() {
|
||||||
|
// 18.3.8: When a single byte must be received: the NACK must be programmed during EV6
|
||||||
|
// event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag.
|
||||||
|
if buffer_len == 1 {
|
||||||
|
T::regs().cr1().modify(|w| {
|
||||||
|
w.set_ack(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Clear ADDR condition by reading SR2
|
||||||
|
T::regs().sr2().read();
|
||||||
|
|
||||||
|
// 18.3.8: When a single byte must be received: [snip] Then the
|
||||||
|
// user can program the STOP condition either after clearing ADDR flag, or in the
|
||||||
|
// DMA Transfer Complete interrupt routine.
|
||||||
|
if buffer_len == 1 {
|
||||||
|
T::regs().cr1().modify(|w| {
|
||||||
|
w.set_stop(true);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// If, in the I2C_CR2 register, the LAST bit is set, I2C
|
||||||
|
// automatically sends a NACK after the next byte following EOT_1. The user can
|
||||||
|
// generate a Stop condition in the DMA Transfer Complete interrupt routine if enabled.
|
||||||
|
T::regs().cr2().modify(|w| {
|
||||||
|
w.set_last(true);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn write_read(&mut self, _address: u8, _write: &[u8], _read: &mut [u8]) -> Result<(), Error>
|
// Wait for bytes to be received, or an error to occur.
|
||||||
|
Self::enable_interrupts();
|
||||||
|
let poll_error = poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err::<T, Error>(e)),
|
||||||
|
_ => Poll::Pending,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
match select(dma_transfer, poll_error).await {
|
||||||
|
Either::Second(Err(e)) => Err(e),
|
||||||
|
_ => Ok(()),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
// Wait for the STOP to be sent (STOP bit cleared).
|
||||||
|
Self::enable_interrupts();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
// TODO: error interrupts are enabled here, should we additional check for and return errors?
|
||||||
|
if T::regs().cr1().read().stop() {
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
drop(on_drop);
|
||||||
|
|
||||||
|
// Fallthrough is success
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
RXDMA: crate::i2c::RxDma<T>,
|
RXDMA: crate::i2c::RxDma<T>,
|
||||||
TXDMA: crate::i2c::TxDma<T>,
|
TXDMA: crate::i2c::TxDma<T>,
|
||||||
{
|
{
|
||||||
todo!()
|
self.write_with_stop(address, write, false).await?;
|
||||||
|
self.read(address, read).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
62
examples/stm32f4/src/bin/i2c_async.rs
Normal file
62
examples/stm32f4/src/bin/i2c_async.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
// Example originally designed for stm32f411ceu6 reading an A1454 hall effect sensor on I2C1
|
||||||
|
// DMA peripherals changed to compile for stm32f429zi, for the CI.
|
||||||
|
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_stm32::i2c::I2c;
|
||||||
|
use embassy_stm32::time::Hertz;
|
||||||
|
use embassy_stm32::{bind_interrupts, i2c, peripherals};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
const ADDRESS: u8 = 96;
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
|
||||||
|
I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
|
||||||
|
});
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
info!("Hello world!");
|
||||||
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
|
let mut i2c = I2c::new(
|
||||||
|
p.I2C1,
|
||||||
|
p.PB8,
|
||||||
|
p.PB7,
|
||||||
|
Irqs,
|
||||||
|
p.DMA1_CH6,
|
||||||
|
p.DMA1_CH0,
|
||||||
|
Hertz(100_000),
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let a1454_read_sensor_command = [0x1F];
|
||||||
|
let mut sensor_data_buffer: [u8; 4] = [0, 0, 0, 0];
|
||||||
|
|
||||||
|
match i2c
|
||||||
|
.write_read(ADDRESS, &a1454_read_sensor_command, &mut sensor_data_buffer)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(()) => {
|
||||||
|
// Convert 12-bit signed integer into 16-bit signed integer.
|
||||||
|
// Is the 12 bit number negative?
|
||||||
|
if (sensor_data_buffer[2] & 0b00001000) == 0b0001000 {
|
||||||
|
sensor_data_buffer[2] = sensor_data_buffer[2] | 0b11110000;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sensor_value_raw: u16 = sensor_data_buffer[3].into();
|
||||||
|
sensor_value_raw |= (sensor_data_buffer[2] as u16) << 8;
|
||||||
|
let sensor_value: u16 = sensor_value_raw.into();
|
||||||
|
let sensor_value = sensor_value as i16;
|
||||||
|
info!("Data: {}", sensor_value);
|
||||||
|
}
|
||||||
|
Err(e) => error!("I2C Error during read: {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
135
examples/stm32f4/src/bin/i2c_comparison.rs
Normal file
135
examples/stm32f4/src/bin/i2c_comparison.rs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
// Example originally designed for stm32f411ceu6 with three A1454 hall effect sensors, connected to I2C1, 2 and 3
|
||||||
|
// on the pins referenced in the peripheral definitions.
|
||||||
|
// Pins and DMA peripherals changed to compile for stm32f429zi, to work with the CI.
|
||||||
|
// MUST be compiled in release mode to see actual performance, otherwise the async transactions take 2x
|
||||||
|
// as long to complete as the blocking ones!
|
||||||
|
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_stm32::i2c::I2c;
|
||||||
|
use embassy_stm32::time::Hertz;
|
||||||
|
use embassy_stm32::{bind_interrupts, i2c, peripherals};
|
||||||
|
use embassy_time::Instant;
|
||||||
|
use futures::future::try_join3;
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
const ADDRESS: u8 = 96;
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
|
||||||
|
I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
|
||||||
|
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||||
|
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||||
|
I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>;
|
||||||
|
I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>;
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Convert 12-bit signed integer within a 4 byte long buffer into 16-bit signed integer.
|
||||||
|
fn a1454_buf_to_i16(buffer: &[u8; 4]) -> i16 {
|
||||||
|
let lower = buffer[3];
|
||||||
|
let mut upper = buffer[2];
|
||||||
|
// Fill in additional 1s if the 12 bit number is negative.
|
||||||
|
if (upper & 0b00001000) == 0b0001000 {
|
||||||
|
upper = upper | 0b11110000;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sensor_value_raw: u16 = lower.into();
|
||||||
|
sensor_value_raw |= (upper as u16) << 8;
|
||||||
|
let sensor_value: u16 = sensor_value_raw.into();
|
||||||
|
let sensor_value = sensor_value as i16;
|
||||||
|
sensor_value
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
info!("Setting up peripherals.");
|
||||||
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
|
let mut i2c1 = I2c::new(
|
||||||
|
p.I2C1,
|
||||||
|
p.PB8,
|
||||||
|
p.PB7,
|
||||||
|
Irqs,
|
||||||
|
p.DMA1_CH6,
|
||||||
|
p.DMA1_CH0,
|
||||||
|
Hertz(100_000),
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut i2c2 = I2c::new(
|
||||||
|
p.I2C2,
|
||||||
|
p.PB10,
|
||||||
|
p.PB11,
|
||||||
|
Irqs,
|
||||||
|
p.DMA1_CH7,
|
||||||
|
p.DMA1_CH3,
|
||||||
|
Hertz(100_000),
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut i2c3 = I2c::new(
|
||||||
|
p.I2C3,
|
||||||
|
p.PA8,
|
||||||
|
p.PC9,
|
||||||
|
Irqs,
|
||||||
|
p.DMA1_CH4,
|
||||||
|
p.DMA1_CH2,
|
||||||
|
Hertz(100_000),
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let a1454_read_sensor_command = [0x1F];
|
||||||
|
let mut i2c1_buffer: [u8; 4] = [0, 0, 0, 0];
|
||||||
|
let mut i2c2_buffer: [u8; 4] = [0, 0, 0, 0];
|
||||||
|
let mut i2c3_buffer: [u8; 4] = [0, 0, 0, 0];
|
||||||
|
loop {
|
||||||
|
// Blocking reads one after the other. Completes in about 2000us.
|
||||||
|
let blocking_read_start_us = Instant::now().as_micros();
|
||||||
|
match i2c1.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => error!("I2C Error: {:?}", e),
|
||||||
|
}
|
||||||
|
match i2c2.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => error!("I2C Error: {:?}", e),
|
||||||
|
}
|
||||||
|
match i2c3.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => error!("I2C Error: {:?}", e),
|
||||||
|
}
|
||||||
|
let blocking_read_total_us = Instant::now().as_micros() - blocking_read_start_us;
|
||||||
|
info!(
|
||||||
|
"Blocking reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}",
|
||||||
|
blocking_read_total_us,
|
||||||
|
a1454_buf_to_i16(&i2c1_buffer),
|
||||||
|
a1454_buf_to_i16(&i2c2_buffer),
|
||||||
|
a1454_buf_to_i16(&i2c3_buffer)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Async reads overlapping. Completes in about 1000us.
|
||||||
|
let async_read_start_us = Instant::now().as_micros();
|
||||||
|
|
||||||
|
let i2c1_result = i2c1.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer);
|
||||||
|
let i2c2_result = i2c2.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer);
|
||||||
|
let i2c3_result = i2c3.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer);
|
||||||
|
|
||||||
|
// Wait for all three transactions to finish, or any one of them to fail.
|
||||||
|
match try_join3(i2c1_result, i2c2_result, i2c3_result).await {
|
||||||
|
Ok(_) => {
|
||||||
|
let async_read_total_us = Instant::now().as_micros() - async_read_start_us;
|
||||||
|
info!(
|
||||||
|
"Async reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}",
|
||||||
|
async_read_total_us,
|
||||||
|
a1454_buf_to_i16(&i2c1_buffer),
|
||||||
|
a1454_buf_to_i16(&i2c2_buffer),
|
||||||
|
a1454_buf_to_i16(&i2c3_buffer)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => error!("I2C Error during async write-read: {}", e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user