fix: make RP2040 I2C slave aware of serial writes
In general, this commit allows the I2C slave to recognize when writes are occuring in serial - and seperate them out into independant commands. In order to achieve this it: * Corrects some setup parameters for the I2C state machine. - Enables clock stretching - Enables recommendations from the datasheet * Participates in clock stretching. This is done by leaving the `R_RD_REQ` register of `IC_INTR_STAT` set until the slave has responded with some bytes (in `respond_to_write`). * Recognizes `FIRST_DATA_BYTE` of `IC_DATA_CMD`, which indicates to a slave-receiver than the current byte is part of a new transaction. * Uses a state machine heavily inspired by the one in rp-rs as an implementation detail. This is no more correct than the existing approach, but makes the code significantly easier to reason about. * Two additional errors were added, both indicating that the provided buffer is too small for the message from the master-sender. As a convenience, they have the same assosciated data so that matching can be performed in the event that the call site handles messages in a consistent way.
This commit is contained in:
		@@ -4,6 +4,7 @@ use core::task::Poll;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use embassy_hal_internal::into_ref;
 | 
					use embassy_hal_internal::into_ref;
 | 
				
			||||||
use pac::i2c;
 | 
					use pac::i2c;
 | 
				
			||||||
 | 
					use pac::i2c::vals::Speed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::i2c::{
 | 
					use crate::i2c::{
 | 
				
			||||||
    i2c_reserved_addr, set_up_i2c_pin, AbortReason, Instance, InterruptHandler, SclPin, SdaPin, FIFO_SIZE,
 | 
					    i2c_reserved_addr, set_up_i2c_pin, AbortReason, Instance, InterruptHandler, SclPin, SdaPin, FIFO_SIZE,
 | 
				
			||||||
@@ -20,6 +21,16 @@ pub enum Error {
 | 
				
			|||||||
    Abort(AbortReason),
 | 
					    Abort(AbortReason),
 | 
				
			||||||
    /// User passed in a response buffer that was 0 length
 | 
					    /// User passed in a response buffer that was 0 length
 | 
				
			||||||
    InvalidResponseBufferLength,
 | 
					    InvalidResponseBufferLength,
 | 
				
			||||||
 | 
					    /// The response buffer length was too short to contain the message
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// The length parameter will always be the length of the buffer, and is
 | 
				
			||||||
 | 
					    /// provided as a convenience for matching alongside `Command::Write`.
 | 
				
			||||||
 | 
					    PartialWrite(usize),
 | 
				
			||||||
 | 
					    /// The response buffer length was too short to contain the message
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// The length parameter will always be the length of the buffer, and is
 | 
				
			||||||
 | 
					    /// provided as a convenience for matching alongside `Command::GeneralCall`.
 | 
				
			||||||
 | 
					    PartialGeneralCall(usize),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Received command
 | 
					/// Received command
 | 
				
			||||||
@@ -63,7 +74,29 @@ impl Default for Config {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default, Copy, Clone)]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
				
			||||||
 | 
					enum State {
 | 
				
			||||||
 | 
					    #[default]
 | 
				
			||||||
 | 
					    Idle,
 | 
				
			||||||
 | 
					    Active,
 | 
				
			||||||
 | 
					    Read,
 | 
				
			||||||
 | 
					    Write,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Copy, Clone)]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
				
			||||||
 | 
					enum I2cSlaveEvent {
 | 
				
			||||||
 | 
					    Start,
 | 
				
			||||||
 | 
					    Restart,
 | 
				
			||||||
 | 
					    DataRequested,
 | 
				
			||||||
 | 
					    DataTransmitted,
 | 
				
			||||||
 | 
					    Stop,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct I2cSlave<'d, T: Instance> {
 | 
					pub struct I2cSlave<'d, T: Instance> {
 | 
				
			||||||
 | 
					    state: State,
 | 
				
			||||||
 | 
					    pending_byte: Option<u8>,
 | 
				
			||||||
    phantom: PhantomData<&'d mut T>,
 | 
					    phantom: PhantomData<&'d mut T>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -93,6 +126,15 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
 | 
				
			|||||||
            w.set_master_mode(false);
 | 
					            w.set_master_mode(false);
 | 
				
			||||||
            w.set_ic_slave_disable(false);
 | 
					            w.set_ic_slave_disable(false);
 | 
				
			||||||
            w.set_tx_empty_ctrl(true);
 | 
					            w.set_tx_empty_ctrl(true);
 | 
				
			||||||
 | 
					            w.set_rx_fifo_full_hld_ctrl(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // This typically makes no sense for a slave, but it is used to
 | 
				
			||||||
 | 
					            // tune spike suppression, according to the datasheet.
 | 
				
			||||||
 | 
					            w.set_speed(Speed::FAST);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Generate stop interrupts for general calls (and other devices
 | 
				
			||||||
 | 
					            // on the bus).
 | 
				
			||||||
 | 
					            w.set_stop_det_ifaddressed(false);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Set FIFO watermarks to 1 to make things simpler. This is encoded
 | 
					        // Set FIFO watermarks to 1 to make things simpler. This is encoded
 | 
				
			||||||
@@ -112,11 +154,21 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
 | 
				
			|||||||
        p.ic_enable().write(|w| w.set_enable(true));
 | 
					        p.ic_enable().write(|w| w.set_enable(true));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // mask everything initially
 | 
					        // mask everything initially
 | 
				
			||||||
        p.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0));
 | 
					        Self::set_intr_mask(|_| {});
 | 
				
			||||||
        T::Interrupt::unpend();
 | 
					        T::Interrupt::unpend();
 | 
				
			||||||
        unsafe { T::Interrupt::enable() };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Self { phantom: PhantomData }
 | 
					        Self {
 | 
				
			||||||
 | 
					            state: State::Idle,
 | 
				
			||||||
 | 
					            pending_byte: None,
 | 
				
			||||||
 | 
					            phantom: PhantomData,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn set_intr_mask(f: impl FnOnce(&mut i2c::regs::IcIntrMask)) {
 | 
				
			||||||
 | 
					        let mut value = i2c::regs::IcIntrMask(0);
 | 
				
			||||||
 | 
					        f(&mut value);
 | 
				
			||||||
 | 
					        T::regs().ic_intr_mask().write_value(value);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Calls `f` to check if we are ready or not.
 | 
					    /// Calls `f` to check if we are ready or not.
 | 
				
			||||||
@@ -128,13 +180,14 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
 | 
				
			|||||||
        G: FnMut(&mut Self),
 | 
					        G: FnMut(&mut Self),
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        future::poll_fn(|cx| {
 | 
					        future::poll_fn(|cx| {
 | 
				
			||||||
            let r = f(self);
 | 
					            T::Interrupt::disable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            trace!("intr p: {:013b}", T::regs().ic_raw_intr_stat().read().0);
 | 
					            let r = f(self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if r.is_pending() {
 | 
					            if r.is_pending() {
 | 
				
			||||||
                T::waker().register(cx.waker());
 | 
					                T::waker().register(cx.waker());
 | 
				
			||||||
                g(self);
 | 
					                g(self);
 | 
				
			||||||
 | 
					                unsafe { T::Interrupt::enable() };
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            r
 | 
					            r
 | 
				
			||||||
@@ -143,14 +196,36 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[inline(always)]
 | 
					    #[inline(always)]
 | 
				
			||||||
    fn drain_fifo(&mut self, buffer: &mut [u8], offset: usize) -> usize {
 | 
					    fn drain_fifo(&mut self, buffer: &mut [u8], offset: &mut usize) {
 | 
				
			||||||
        let p = T::regs();
 | 
					        let p = T::regs();
 | 
				
			||||||
        let len = p.ic_rxflr().read().rxflr() as usize;
 | 
					
 | 
				
			||||||
        let end = offset + len;
 | 
					        for b in &mut buffer[*offset..] {
 | 
				
			||||||
        for i in offset..end {
 | 
					            if let Some(pending) = self.pending_byte.take() {
 | 
				
			||||||
            buffer[i] = p.ic_data_cmd().read().dat();
 | 
					                *b = pending;
 | 
				
			||||||
 | 
					                *offset += 1;
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let status = p.ic_status().read();
 | 
				
			||||||
 | 
					            if !status.rfne() {
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let dat = p.ic_data_cmd().read();
 | 
				
			||||||
 | 
					            if *offset != 0 && dat.first_data_byte() {
 | 
				
			||||||
 | 
					                // The RP2040 state machine will keep placing bytes into the
 | 
				
			||||||
 | 
					                // FIFO, even if they are part of a subsequent write transaction.
 | 
				
			||||||
 | 
					                //
 | 
				
			||||||
 | 
					                // Unfortunately merely reading ic_data_cmd will consume that
 | 
				
			||||||
 | 
					                // byte, the first byte of the next transaction, so we need
 | 
				
			||||||
 | 
					                // to store it elsewhere
 | 
				
			||||||
 | 
					                self.pending_byte = Some(dat.dat());
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            *b = dat.dat();
 | 
				
			||||||
 | 
					            *offset += 1;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[inline(always)]
 | 
					    #[inline(always)]
 | 
				
			||||||
@@ -161,53 +236,139 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn rx_not_empty(&self) -> bool {
 | 
				
			||||||
 | 
					        let p = T::regs();
 | 
				
			||||||
 | 
					        self.pending_byte.is_some() || p.ic_status().read().rfne()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    async fn next_event(&mut self) -> I2cSlaveEvent {
 | 
				
			||||||
 | 
					        self.wait_on(
 | 
				
			||||||
 | 
					            |me| {
 | 
				
			||||||
 | 
					                let p = T::regs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let i_stat = p.ic_raw_intr_stat().read();
 | 
				
			||||||
 | 
					                p.ic_clr_activity().read();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                match me.state {
 | 
				
			||||||
 | 
					                    State::Idle if me.pending_byte.is_some() => {
 | 
				
			||||||
 | 
					                        // 2. Continue emulating the end of a transaction, this
 | 
				
			||||||
 | 
					                        // is the start of the next transaction.
 | 
				
			||||||
 | 
					                        me.state = State::Active;
 | 
				
			||||||
 | 
					                        Poll::Ready(I2cSlaveEvent::Start)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    State::Idle if i_stat.start_det() => {
 | 
				
			||||||
 | 
					                        p.ic_clr_start_det().read();
 | 
				
			||||||
 | 
					                        me.state = State::Active;
 | 
				
			||||||
 | 
					                        Poll::Ready(I2cSlaveEvent::Start)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    State::Active if me.rx_not_empty() => {
 | 
				
			||||||
 | 
					                        // 3. Will catch normal starts as well as emulated
 | 
				
			||||||
 | 
					                        // transactions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Reduce interrupt noise.
 | 
				
			||||||
 | 
					                        p.ic_rx_tl().write(|w| w.set_rx_tl(11));
 | 
				
			||||||
 | 
					                        me.state = State::Write;
 | 
				
			||||||
 | 
					                        Poll::Ready(I2cSlaveEvent::DataTransmitted)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    State::Active if i_stat.rd_req() => {
 | 
				
			||||||
 | 
					                        // We intentionally don't reset rd_req here, because
 | 
				
			||||||
 | 
					                        // resetting it will stop stretching the clock. Instead
 | 
				
			||||||
 | 
					                        // it gets reset in respond_to_read.
 | 
				
			||||||
 | 
					                        me.state = State::Read;
 | 
				
			||||||
 | 
					                        Poll::Ready(I2cSlaveEvent::DataRequested)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    State::Read if i_stat.rd_req() => Poll::Ready(I2cSlaveEvent::DataRequested),
 | 
				
			||||||
 | 
					                    State::Read if i_stat.restart_det() => {
 | 
				
			||||||
 | 
					                        p.ic_clr_restart_det().read();
 | 
				
			||||||
 | 
					                        me.state = State::Active;
 | 
				
			||||||
 | 
					                        Poll::Ready(I2cSlaveEvent::Restart)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    State::Write if me.pending_byte.is_some() => {
 | 
				
			||||||
 | 
					                        me.state = State::Idle;
 | 
				
			||||||
 | 
					                        // 1. Start emulating the end of a transaction.
 | 
				
			||||||
 | 
					                        // We know it is the end because it is not valid to
 | 
				
			||||||
 | 
					                        // issue a restart between transmissions in the same
 | 
				
			||||||
 | 
					                        // direction
 | 
				
			||||||
 | 
					                        Poll::Ready(I2cSlaveEvent::Stop)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    State::Write if me.rx_not_empty() => Poll::Ready(I2cSlaveEvent::DataTransmitted),
 | 
				
			||||||
 | 
					                    State::Write if i_stat.restart_det() => {
 | 
				
			||||||
 | 
					                        p.ic_clr_restart_det().read();
 | 
				
			||||||
 | 
					                        me.state = State::Active;
 | 
				
			||||||
 | 
					                        Poll::Ready(I2cSlaveEvent::Restart)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    State::Idle if i_stat.stop_det() => {
 | 
				
			||||||
 | 
					                        // Probably another device on the bus.
 | 
				
			||||||
 | 
					                        p.ic_clr_stop_det().read();
 | 
				
			||||||
 | 
					                        Poll::Pending
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    _ if i_stat.stop_det() => {
 | 
				
			||||||
 | 
					                        p.ic_clr_stop_det().read();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // The bus is going idle here, make sure the interrupt
 | 
				
			||||||
 | 
					                        // occurs as soon as possible if a write occurs.
 | 
				
			||||||
 | 
					                        p.ic_rx_tl().write(|w| w.set_rx_tl(0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        me.state = State::Idle;
 | 
				
			||||||
 | 
					                        Poll::Ready(I2cSlaveEvent::Stop)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    _ => Poll::Pending,
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            |_me| {
 | 
				
			||||||
 | 
					                Self::set_intr_mask(|w| {
 | 
				
			||||||
 | 
					                    w.set_m_start_det(true);
 | 
				
			||||||
 | 
					                    w.set_m_stop_det(true);
 | 
				
			||||||
 | 
					                    w.set_m_restart_det(true);
 | 
				
			||||||
 | 
					                    w.set_m_rd_req(true);
 | 
				
			||||||
 | 
					                    w.set_m_rx_full(true);
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Wait asynchronously for commands from an I2C master.
 | 
					    /// Wait asynchronously for commands from an I2C master.
 | 
				
			||||||
    /// `buffer` is provided in case master does a 'write' and is unused for 'read'.
 | 
					    /// `buffer` is provided in case master does a 'write' and is unused for 'read'.
 | 
				
			||||||
    pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
 | 
					    pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
 | 
				
			||||||
        let p = T::regs();
 | 
					        let p = T::regs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        p.ic_clr_intr().read();
 | 
					        let mut offset = 0;
 | 
				
			||||||
        // set rx fifo watermark to 1 byte
 | 
					        loop {
 | 
				
			||||||
        p.ic_rx_tl().write(|w| w.set_rx_tl(0));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut len = 0;
 | 
					 | 
				
			||||||
        let ret = self
 | 
					 | 
				
			||||||
            .wait_on(
 | 
					 | 
				
			||||||
                |me| {
 | 
					 | 
				
			||||||
            let stat = p.ic_raw_intr_stat().read();
 | 
					            let stat = p.ic_raw_intr_stat().read();
 | 
				
			||||||
                    if p.ic_rxflr().read().rxflr() > 0 {
 | 
					            let evt = self.next_event().await;
 | 
				
			||||||
                        len = me.drain_fifo(buffer, len);
 | 
					            match evt {
 | 
				
			||||||
                        // we're recieving data, set rx fifo watermark to 12 bytes to reduce interrupt noise
 | 
					                I2cSlaveEvent::Start | I2cSlaveEvent::Restart => {}
 | 
				
			||||||
                        p.ic_rx_tl().write(|w| w.set_rx_tl(11));
 | 
					                I2cSlaveEvent::DataRequested if offset == 0 => {
 | 
				
			||||||
 | 
					                    return Ok(Command::Read);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                I2cSlaveEvent::DataRequested => {
 | 
				
			||||||
                    if stat.restart_det() && stat.rd_req() {
 | 
					                    return Ok(Command::WriteRead(offset));
 | 
				
			||||||
                        Poll::Ready(Ok(Command::WriteRead(len)))
 | 
					                }
 | 
				
			||||||
                    } else if stat.gen_call() && stat.stop_det() && len > 0 {
 | 
					                I2cSlaveEvent::DataTransmitted if offset == buffer.len() => {
 | 
				
			||||||
                        Poll::Ready(Ok(Command::GeneralCall(len)))
 | 
					                    if stat.gen_call() {
 | 
				
			||||||
                    } else if stat.stop_det() {
 | 
					                        p.ic_clr_gen_call().read();
 | 
				
			||||||
                        Poll::Ready(Ok(Command::Write(len)))
 | 
					                        return Err(Error::PartialGeneralCall(offset));
 | 
				
			||||||
                    } else if stat.rd_req() {
 | 
					 | 
				
			||||||
                        Poll::Ready(Ok(Command::Read))
 | 
					 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        Poll::Pending
 | 
					                        return Err(Error::PartialWrite(offset));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                I2cSlaveEvent::DataTransmitted => {
 | 
				
			||||||
 | 
					                    self.drain_fifo(buffer, &mut offset);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                I2cSlaveEvent::Stop if offset == 0 => {}
 | 
				
			||||||
 | 
					                I2cSlaveEvent::Stop => {
 | 
				
			||||||
 | 
					                    if stat.gen_call() {
 | 
				
			||||||
 | 
					                        p.ic_clr_gen_call().read();
 | 
				
			||||||
 | 
					                        return Ok(Command::GeneralCall(offset));
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        return Ok(Command::Write(offset));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                |_me| {
 | 
					 | 
				
			||||||
                    p.ic_intr_mask().modify(|w| {
 | 
					 | 
				
			||||||
                        w.set_m_stop_det(true);
 | 
					 | 
				
			||||||
                        w.set_m_restart_det(true);
 | 
					 | 
				
			||||||
                        w.set_m_gen_call(true);
 | 
					 | 
				
			||||||
                        w.set_m_rd_req(true);
 | 
					 | 
				
			||||||
                        w.set_m_rx_full(true);
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            .await;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        p.ic_clr_intr().read();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ret
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Respond to an I2C master READ command, asynchronously.
 | 
					    /// Respond to an I2C master READ command, asynchronously.
 | 
				
			||||||
@@ -220,8 +381,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        let mut chunks = buffer.chunks(FIFO_SIZE as usize);
 | 
					        let mut chunks = buffer.chunks(FIFO_SIZE as usize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let ret = self
 | 
					        self.wait_on(
 | 
				
			||||||
            .wait_on(
 | 
					 | 
				
			||||||
            |me| {
 | 
					            |me| {
 | 
				
			||||||
                if let Err(abort_reason) = me.read_and_clear_abort_reason() {
 | 
					                if let Err(abort_reason) = me.read_and_clear_abort_reason() {
 | 
				
			||||||
                    if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason {
 | 
					                    if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason {
 | 
				
			||||||
@@ -231,14 +391,21 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let stat = p.ic_raw_intr_stat().read();
 | 
				
			||||||
 | 
					                p.ic_clr_activity().read();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if let Some(chunk) = chunks.next() {
 | 
					                if let Some(chunk) = chunks.next() {
 | 
				
			||||||
                    me.write_to_fifo(chunk);
 | 
					                    me.write_to_fifo(chunk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Stop stretching the clk
 | 
				
			||||||
 | 
					                    p.ic_clr_rd_req().read();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Poll::Pending
 | 
					                    Poll::Pending
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                        let stat = p.ic_raw_intr_stat().read();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if stat.rx_done() && stat.stop_det() {
 | 
					                    if stat.rx_done() && stat.stop_det() {
 | 
				
			||||||
 | 
					                        p.ic_clr_rx_done().read();
 | 
				
			||||||
 | 
					                        p.ic_clr_stop_det().read();
 | 
				
			||||||
 | 
					                        me.state = State::Idle;
 | 
				
			||||||
                        Poll::Ready(Ok(ReadStatus::Done))
 | 
					                        Poll::Ready(Ok(ReadStatus::Done))
 | 
				
			||||||
                    } else if stat.rd_req() {
 | 
					                    } else if stat.rd_req() {
 | 
				
			||||||
                        Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
 | 
					                        Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
 | 
				
			||||||
@@ -249,6 +416,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
            |_me| {
 | 
					            |_me| {
 | 
				
			||||||
                p.ic_intr_mask().modify(|w| {
 | 
					                p.ic_intr_mask().modify(|w| {
 | 
				
			||||||
 | 
					                    w.set_m_rd_req(true);
 | 
				
			||||||
                    w.set_m_stop_det(true);
 | 
					                    w.set_m_stop_det(true);
 | 
				
			||||||
                    w.set_m_rx_done(true);
 | 
					                    w.set_m_rx_done(true);
 | 
				
			||||||
                    w.set_m_tx_empty(true);
 | 
					                    w.set_m_tx_empty(true);
 | 
				
			||||||
@@ -256,11 +424,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
 | 
				
			|||||||
                })
 | 
					                })
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
            .await;
 | 
					        .await
 | 
				
			||||||
 | 
					 | 
				
			||||||
        p.ic_clr_intr().read();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ret
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Respond to reads with the fill byte until the controller stops asking
 | 
					    /// Respond to reads with the fill byte until the controller stops asking
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user