Refactor async I2C transfers to use frame options

This commit is contained in:
Sebastian Goll 2024-03-20 20:11:59 +01:00
parent 746ded94b1
commit 0885c102d3

View file

@ -459,7 +459,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}); });
} }
async fn write_with_stop(&mut self, address: u8, write: &[u8], send_stop: bool) -> Result<(), Error> async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error>
where where
TXDMA: crate::i2c::TxDma<T>, TXDMA: crate::i2c::TxDma<T>,
{ {
@ -487,74 +487,76 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}) })
}); });
Self::enable_interrupts();
// Send a START condition
T::regs().cr1().modify(|reg| {
reg.set_start(true);
});
let state = T::state(); let state = T::state();
// Wait until START condition was generated if frame.send_start() {
poll_fn(|cx| { // Send a START condition
state.waker.register(cx.waker()); Self::enable_interrupts();
T::regs().cr1().modify(|reg| {
reg.set_start(true);
});
match Self::check_and_clear_error_flags() { // Wait until START condition was generated
Err(e) => Poll::Ready(Err(e)), poll_fn(|cx| {
Ok(sr1) => { state.waker.register(cx.waker());
if sr1.start() {
Poll::Ready(Ok(())) match Self::check_and_clear_error_flags() {
} else { Err(e) => Poll::Ready(Err(e)),
Poll::Pending Ok(sr1) => {
if sr1.start() {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
} }
} }
} })
}) .await?;
.await?;
// 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
Self::enable_interrupts(); Self::enable_interrupts();
poll_fn(|cx| { poll_fn(|cx| {
state.waker.register(cx.waker()); state.waker.register(cx.waker());
match Self::check_and_clear_error_flags() { match Self::check_and_clear_error_flags() {
Err(e) => Poll::Ready(Err(e)), Err(e) => Poll::Ready(Err(e)),
Ok(_) => { Ok(_) => {
let sr2 = T::regs().sr2().read(); let sr2 = T::regs().sr2().read();
if !sr2.msl() && !sr2.busy() { if !sr2.msl() && !sr2.busy() {
Poll::Pending Poll::Pending
} else { } else {
Poll::Ready(Ok(())) Poll::Ready(Ok(()))
}
} }
} }
} })
}) .await?;
.await?;
// Set up current address, we're trying to talk to // Set up current address, we're trying to talk to
Self::enable_interrupts(); Self::enable_interrupts();
T::regs().dr().write(|reg| reg.set_dr(address << 1)); T::regs().dr().write(|reg| reg.set_dr(address << 1));
poll_fn(|cx| { poll_fn(|cx| {
state.waker.register(cx.waker()); state.waker.register(cx.waker());
match Self::check_and_clear_error_flags() { match Self::check_and_clear_error_flags() {
Err(e) => Poll::Ready(Err(e)), Err(e) => Poll::Ready(Err(e)),
Ok(sr1) => { Ok(sr1) => {
if sr1.addr() { if sr1.addr() {
// Clear the ADDR condition by reading SR2. // Clear the ADDR condition by reading SR2.
T::regs().sr2().read(); T::regs().sr2().read();
Poll::Ready(Ok(())) Poll::Ready(Ok(()))
} else { } else {
// If we need to go around, then re-enable the interrupts, otherwise nothing // If we need to go around, then re-enable the interrupts, otherwise nothing
// can wake us up and we'll hang. // can wake us up and we'll hang.
Self::enable_interrupts(); Self::enable_interrupts();
Poll::Pending Poll::Pending
}
} }
} }
} })
}) .await?;
.await?; }
Self::enable_interrupts(); Self::enable_interrupts();
let poll_error = poll_fn(|cx| { let poll_error = poll_fn(|cx| {
state.waker.register(cx.waker()); state.waker.register(cx.waker());
@ -591,7 +593,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
Err(e) => Poll::Ready(Err(e)), Err(e) => Poll::Ready(Err(e)),
Ok(sr1) => { Ok(sr1) => {
if sr1.btf() { if sr1.btf() {
if send_stop { if frame.send_stop() {
T::regs().cr1().modify(|w| { T::regs().cr1().modify(|w| {
w.set_stop(true); w.set_stop(true);
}); });
@ -606,6 +608,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}) })
.await?; .await?;
if frame.send_stop() {
// 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?;
}
drop(on_drop); drop(on_drop);
// Fallthrough is success // Fallthrough is success
@ -617,26 +634,24 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
where where
TXDMA: crate::i2c::TxDma<T>, TXDMA: crate::i2c::TxDma<T>,
{ {
self.write_with_stop(address, write, true).await?; self.write_frame(address, write, FrameOptions::FirstAndLastFrame)
.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(()) Ok(())
} }
/// Read. /// Read.
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
RXDMA: crate::i2c::RxDma<T>,
{
self.read_frame(address, buffer, FrameOptions::FirstAndLastFrame)
.await?;
Ok(())
}
async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error>
where where
RXDMA: crate::i2c::RxDma<T>, RXDMA: crate::i2c::RxDma<T>,
{ {
@ -667,98 +682,99 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}) })
}); });
Self::enable_interrupts(); if frame.send_start() {
// Send a START condition and set ACK bit
Self::enable_interrupts();
T::regs().cr1().modify(|reg| {
reg.set_start(true);
reg.set_ack(true);
});
// Send a START condition and set ACK bit // Wait until START condition was generated
T::regs().cr1().modify(|reg| { poll_fn(|cx| {
reg.set_start(true); state.waker.register(cx.waker());
reg.set_ack(true);
});
// Wait until START condition was generated match Self::check_and_clear_error_flags() {
poll_fn(|cx| { Err(e) => Poll::Ready(Err(e)),
state.waker.register(cx.waker()); Ok(sr1) => {
if sr1.start() {
match Self::check_and_clear_error_flags() { Poll::Ready(Ok(()))
Err(e) => Poll::Ready(Err(e)), } else {
Ok(sr1) => { Poll::Pending
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.
if buffer_len == 1 {
T::regs().cr1().modify(|w| {
w.set_ack(false);
});
} }
Poll::Ready(Ok(()))
} else {
Poll::Pending
} }
} }
} })
}) .await?;
.await?;
// Clear ADDR condition by reading SR2 // Also wait until signalled we're master and everything is waiting for us
T::regs().sr2().read(); 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.
if buffer_len == 1 && frame.send_nack() {
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 // 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 // user can program the STOP condition either after clearing ADDR flag, or in the
// DMA Transfer Complete interrupt routine. // DMA Transfer Complete interrupt routine.
if buffer_len == 1 { if buffer_len == 1 && frame.send_stop() {
T::regs().cr1().modify(|w| { T::regs().cr1().modify(|w| {
w.set_stop(true); w.set_stop(true);
}); });
} else { } else if buffer_len != 1 && frame.send_nack() {
// If, in the I2C_CR2 register, the LAST bit is set, I2C // 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 // 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. // generate a Stop condition in the DMA Transfer Complete interrupt routine if enabled.
T::regs().cr2().modify(|w| { T::regs().cr2().modify(|w| {
w.set_last(true); w.set_last(true);
}) });
} }
// Wait for bytes to be received, or an error to occur. // Wait for bytes to be received, or an error to occur.
@ -777,18 +793,27 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
_ => Ok(()), _ => Ok(()),
}?; }?;
// Wait for the STOP to be sent (STOP bit cleared). if frame.send_stop() {
Self::enable_interrupts(); if buffer_len != 1 {
poll_fn(|cx| { T::regs().cr1().modify(|w| {
state.waker.register(cx.waker()); w.set_stop(true);
// 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?; // 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); drop(on_drop);
// Fallthrough is success // Fallthrough is success
@ -801,8 +826,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
RXDMA: crate::i2c::RxDma<T>, RXDMA: crate::i2c::RxDma<T>,
TXDMA: crate::i2c::TxDma<T>, TXDMA: crate::i2c::TxDma<T>,
{ {
self.write_with_stop(address, write, false).await?; self.write_frame(address, write, FrameOptions::FirstFrame).await?;
self.read(address, read).await self.read_frame(address, read, FrameOptions::FirstAndLastFrame).await
} }
} }