Introduce frame options to control start/stop conditions

This commit is contained in:
Sebastian Goll 2024-03-20 02:10:45 +01:00
parent 7bf4710f3f
commit 7c08616c02

View file

@ -41,6 +41,68 @@ pub unsafe fn on_interrupt<T: Instance>() {
});
}
/// Frame type in I2C transaction.
///
/// This tells each method what kind of framing to use, to generate a (repeated) start condition (ST
/// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an
/// ACK or NACK after the last byte received.
///
/// For write operations, the following options are identical because they differ only in the (N)ACK
/// treatment relevant for read operations:
///
/// - `FirstFrame` and `FirstAndNextFrame`
/// - `NextFrame` and `LastFrameNoStop`
///
/// Abbreviations used below:
///
/// - `ST` = start condition
/// - `SR` = repeated start condition
/// - `SP` = stop condition
#[derive(Copy, Clone)]
enum FrameOptions {
/// `[ST/SR]+[NACK]+[SP]` First frame (of this type) in operation and last frame overall in this
/// transaction.
FirstAndLastFrame,
/// `[ST/SR]+[NACK]` First frame of this type in transaction, last frame in a read operation but
/// not the last frame overall.
FirstFrame,
/// `[ST/SR]+[ACK]` First frame of this type in transaction, neither last frame overall nor last
/// frame in a read operation.
FirstAndNextFrame,
/// `[ACK]` Middle frame in a read operation (neither first nor last).
NextFrame,
/// `[NACK]+[SP]` Last frame overall in this transaction but not the first frame.
LastFrame,
/// `[NACK]` Last frame in a read operation but not last frame overall in this transaction.
LastFrameNoStop,
}
impl FrameOptions {
/// Sends start or repeated start condition before transfer.
fn send_start(self) -> bool {
match self {
Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true,
Self::NextFrame | Self::LastFrame | Self::LastFrameNoStop => false,
}
}
/// Sends stop condition after transfer.
fn send_stop(self) -> bool {
match self {
Self::FirstAndLastFrame | Self::LastFrame => true,
Self::FirstFrame | Self::FirstAndNextFrame | Self::NextFrame | Self::LastFrameNoStop => false,
}
}
/// Sends NACK after last byte received, indicating end of read operation.
fn send_nack(self) -> bool {
match self {
Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true,
Self::FirstAndNextFrame | Self::NextFrame => false,
}
}
}
impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
pub(crate) fn init(&mut self, freq: Hertz, _config: Config) {
T::regs().cr1().modify(|reg| {
@ -124,7 +186,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
Ok(sr1)
}
fn write_bytes(&mut self, addr: u8, bytes: &[u8], timeout: Timeout) -> Result<(), Error> {
fn write_bytes(&mut self, addr: u8, bytes: &[u8], timeout: Timeout, frame: FrameOptions) -> Result<(), Error> {
if frame.send_start() {
// Send a START condition
T::regs().cr1().modify(|reg| {
@ -158,12 +221,22 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// Clear condition by reading SR2
let _ = T::regs().sr2().read();
}
// Send bytes
for c in bytes {
self.send_byte(*c, timeout)?;
}
if frame.send_stop() {
// 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() {
timeout.check()?;
}
}
// Fallthrough is success
Ok(())
}
@ -205,8 +278,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
Ok(value)
}
fn blocking_read_timeout(&mut self, addr: u8, buffer: &mut [u8], timeout: Timeout) -> Result<(), Error> {
if let Some((last, buffer)) = buffer.split_last_mut() {
fn blocking_read_timeout(
&mut self,
addr: u8,
buffer: &mut [u8],
timeout: Timeout,
frame: FrameOptions,
) -> Result<(), Error> {
let Some((last, buffer)) = buffer.split_last_mut() else {
return Err(Error::Overrun);
};
if frame.send_start() {
// Send a START condition and set ACK bit
T::regs().cr1().modify(|reg| {
reg.set_start(true);
@ -237,6 +320,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// Clear condition by reading SR2
let _ = T::regs().sr2().read();
}
// Receive bytes into buffer
for c in buffer {
@ -245,41 +329,36 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// Prepare to send NACK then STOP after next byte
T::regs().cr1().modify(|reg| {
if frame.send_nack() {
reg.set_ack(false);
}
if frame.send_stop() {
reg.set_stop(true);
}
});
// Receive last byte
*last = self.recv_byte(timeout)?;
if frame.send_stop() {
// Wait for the STOP to be sent.
while T::regs().cr1().read().stop() {
timeout.check()?;
}
}
// Fallthrough is success
Ok(())
} else {
Err(Error::Overrun)
}
}
/// Blocking read.
pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> {
self.blocking_read_timeout(addr, read, self.timeout())
self.blocking_read_timeout(addr, read, self.timeout(), FrameOptions::FirstAndLastFrame)
}
/// Blocking write.
pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> {
let timeout = self.timeout();
self.write_bytes(addr, write, 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() {
timeout.check()?;
}
self.write_bytes(addr, write, self.timeout(), FrameOptions::FirstAndLastFrame)?;
// Fallthrough is success
Ok(())
@ -289,8 +368,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
let timeout = self.timeout();
self.write_bytes(addr, write, timeout)?;
self.blocking_read_timeout(addr, read, timeout)?;
self.write_bytes(addr, write, timeout, FrameOptions::FirstFrame)?;
self.blocking_read_timeout(addr, read, timeout, FrameOptions::FirstAndLastFrame)?;
Ok(())
}