Introduce frame options to control start/stop conditions
This commit is contained in:
parent
7bf4710f3f
commit
7c08616c02
1 changed files with 138 additions and 59 deletions
|
@ -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(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue