stm32/i2c: WIP async i2cv1

This commit is contained in:
Barnaby Walters 2023-11-18 01:44:13 +01:00 committed by Dario Nieuwenhuis
parent 838a97c186
commit 54123de7bd

View File

@ -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,18 +364,310 @@ 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);
});
}
pub async fn write(&mut self, address: u8, write: &[u8]) -> 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
T::regs().dr().write(|reg| reg.set_dr(address << 1));
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() {
// 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 didnt 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() {
T::regs().cr1().modify(|w| {
w.set_stop(true);
});
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
}
}
})
.await?;
// Wait for STOP condition to transmit.
Self::enable_interrupts();
poll_fn(|cx| {
state.waker.register(cx.waker());
if T::regs().cr1().read().stop() {
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
})
.await?;
drop(on_drop);
// Fallthrough is success
Ok(())
} }
pub async fn read(&mut self, _address: u8, _buffer: &mut [u8]) -> Result<(), Error> 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 didnt have a check_and_clear call here, but blocking write did so
// Im 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. 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_ack(false);
});
}
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
}
}
})
.await?;
// Clear condition by reading SR2
T::regs().sr2().read();
// 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(()),
};
// v1 blocking waits for STOP to be written, the manual says to write the STOP bit yourself.
// what to do…
// Wait for the STOP to be sent.
// while T::regs().cr1().read().stop() {
// check_timeout()?;
// }
// Fallthrough is success
Ok(())
} }
pub async fn write_read(&mut self, _address: u8, _write: &[u8], _read: &mut [u8]) -> Result<(), Error> pub async fn write_read(&mut self, _address: u8, _write: &[u8], _read: &mut [u8]) -> Result<(), Error>