//! # I2Cv1 //! //! This implementation is used for STM32F1, STM32F2, STM32F4, and STM32L1 devices. //! //! All other devices (as of 2023-12-28) use [`v2`](super::v2) instead. use core::future::poll_fn; use core::task::Poll; use embassy_embedded_hal::SetConfig; use embassy_futures::select::{Either, select}; use embassy_hal_internal::drop::OnDrop; use embedded_hal_1::i2c::Operation; use mode::Master; use super::*; use crate::mode::Mode as PeriMode; use crate::pac::i2c; // /!\ /!\ // /!\ Implementation note! /!\ // /!\ /!\ // // It's somewhat unclear whether using interrupts here in a *strictly* one-shot style is actually // what we want! If you are looking in this file because you are doing async I2C and your code is // just totally hanging (sometimes), maybe swing by this issue: // . // // There's some more details there, and we might have a fix for you. But please let us know if you // hit a case like this! pub unsafe fn on_interrupt() { let regs = T::info().regs; trace!("I2C interrupt triggered"); // 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); }); }); } impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { pub(crate) fn init(&mut self, config: Config) { self.info.regs.cr1().modify(|reg| { reg.set_pe(false); //reg.set_anfoff(false); }); // Errata: "Start cannot be generated after a misplaced Stop" // // > If a master generates a misplaced Stop on the bus (bus error) // > while the microcontroller I2C peripheral attempts to switch to // > Master mode by setting the START bit, the Start condition is // > not properly generated. // // This also can occur with falsely detected STOP events, for example // if the SDA line is shorted to low. // // The workaround for this is to trigger the SWRST line AFTER power is // enabled, AFTER PE is disabled and BEFORE making any other configuration. // // It COULD be possible to apply this workaround at runtime, instead of // only on initialization, however this would require detecting the timeout // or BUSY lockup condition, and re-configuring the peripheral after reset. // // This presents as an ~infinite hang on read or write, as the START condition // is never generated, meaning the start event is never generated. self.info.regs.cr1().modify(|reg| { reg.set_swrst(true); }); self.info.regs.cr1().modify(|reg| { reg.set_swrst(false); }); let timings = Timings::new(self.kernel_clock, config.frequency); self.info.regs.cr2().modify(|reg| { reg.set_freq(timings.freq); }); self.info.regs.ccr().modify(|reg| { reg.set_f_s(timings.mode.f_s()); reg.set_duty(timings.duty.duty()); reg.set_ccr(timings.ccr); }); self.info.regs.trise().modify(|reg| { reg.set_trise(timings.trise); }); self.info.regs.cr1().modify(|reg| { reg.set_pe(true); }); trace!("i2c v1 init complete"); } fn check_and_clear_error_flags(info: &'static Info) -> Result { // 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. let sr1 = info.regs.sr1().read(); if sr1.timeout() { info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_timeout(false); }); return Err(Error::Timeout); } if sr1.pecerr() { info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_pecerr(false); }); return Err(Error::Crc); } if sr1.ovr() { info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_ovr(false); }); return Err(Error::Overrun); } if sr1.af() { info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_af(false); }); return Err(Error::Nack); } if sr1.arlo() { info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_arlo(false); }); return Err(Error::Arbitration); } // The errata indicates that BERR may be incorrectly detected. It recommends ignoring and // clearing the BERR bit instead. if sr1.berr() { info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_berr(false); }); } Ok(sr1) } fn write_bytes( &mut self, address: u8, write_buffer: &[u8], timeout: Timeout, frame: FrameOptions, ) -> Result<(), Error> { if frame.send_start() { // Send a START condition self.info.regs.cr1().modify(|reg| { reg.set_start(true); }); // Wait until START condition was generated while !Self::check_and_clear_error_flags(self.info)?.start() { timeout.check()?; } // Check if we were the ones to generate START if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() { return Err(Error::Arbitration); } // Set up current address we're trying to talk to self.info.regs.dr().write(|reg| reg.set_dr(address << 1)); // Wait until address was sent // Wait for the address to be acknowledged // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. while !Self::check_and_clear_error_flags(self.info)?.addr() { timeout.check()?; } // Clear condition by reading SR2 let _ = self.info.regs.sr2().read(); } // Send bytes for c in write_buffer { self.send_byte(*c, timeout)?; } if frame.send_stop() { // Send a STOP condition self.info.regs.cr1().modify(|reg| reg.set_stop(true)); } // Fallthrough is success Ok(()) } fn send_byte(&self, byte: u8, timeout: Timeout) -> Result<(), Error> { // Wait until we're ready for sending while { // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. !Self::check_and_clear_error_flags(self.info)?.txe() } { timeout.check()?; } // Push out a byte of data self.info.regs.dr().write(|reg| reg.set_dr(byte)); // Wait until byte is transferred while { // Check for any potential error conditions. !Self::check_and_clear_error_flags(self.info)?.btf() } { timeout.check()?; } Ok(()) } fn recv_byte(&self, timeout: Timeout) -> Result { while { // Check for any potential error conditions. Self::check_and_clear_error_flags(self.info)?; !self.info.regs.sr1().read().rxne() } { timeout.check()?; } let value = self.info.regs.dr().read().dr(); Ok(value) } fn blocking_read_timeout( &mut self, address: u8, read_buffer: &mut [u8], timeout: Timeout, frame: FrameOptions, ) -> Result<(), Error> { let Some((last_byte, read_buffer)) = read_buffer.split_last_mut() else { return Err(Error::Overrun); }; if frame.send_start() { // Send a START condition and set ACK bit self.info.regs.cr1().modify(|reg| { reg.set_start(true); reg.set_ack(true); }); // Wait until START condition was generated while !Self::check_and_clear_error_flags(self.info)?.start() { timeout.check()?; } // Check if we were the ones to generate START if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() { return Err(Error::Arbitration); } // Set up current address we're trying to talk to self.info.regs.dr().write(|reg| reg.set_dr((address << 1) + 1)); // Wait until address was sent // Wait for the address to be acknowledged while !Self::check_and_clear_error_flags(self.info)?.addr() { timeout.check()?; } // Clear condition by reading SR2 let _ = self.info.regs.sr2().read(); } // Receive bytes into buffer for c in read_buffer { *c = self.recv_byte(timeout)?; } // Prepare to send NACK then STOP after next byte self.info.regs.cr1().modify(|reg| { if frame.send_nack() { reg.set_ack(false); } if frame.send_stop() { reg.set_stop(true); } }); // Receive last byte *last_byte = self.recv_byte(timeout)?; // Fallthrough is success Ok(()) } /// Blocking read. pub fn blocking_read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> { self.blocking_read_timeout(address, read_buffer, self.timeout(), FrameOptions::FirstAndLastFrame) } /// Blocking write. pub fn blocking_write(&mut self, address: u8, write_buffer: &[u8]) -> Result<(), Error> { self.write_bytes(address, write_buffer, self.timeout(), FrameOptions::FirstAndLastFrame)?; // Fallthrough is success Ok(()) } /// Blocking write, restart, read. pub fn blocking_write_read( &mut self, address: u8, write_buffer: &[u8], read_buffer: &mut [u8], ) -> Result<(), Error> { // Check empty read buffer before starting transaction. Otherwise, we would not generate the // stop condition below. if read_buffer.is_empty() { return Err(Error::Overrun); } let timeout = self.timeout(); self.write_bytes(address, write_buffer, timeout, FrameOptions::FirstFrame)?; self.blocking_read_timeout(address, read_buffer, timeout, FrameOptions::FirstAndLastFrame)?; Ok(()) } /// Blocking transaction with operations. /// /// Consecutive operations of same type are merged. See [transaction contract] for details. /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction pub fn blocking_transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { let timeout = self.timeout(); for (op, frame) in operation_frames(operations)? { match op { Operation::Read(read_buffer) => self.blocking_read_timeout(address, read_buffer, timeout, frame)?, Operation::Write(write_buffer) => self.write_bytes(address, write_buffer, timeout, frame)?, } } Ok(()) } /// Can be used by both blocking and async implementations #[inline] // pretty sure this should always be inlined fn enable_interrupts(info: &'static Info) { // The interrupt handler disables interrupts globally, so we need to re-enable them // This must be done in a critical section to avoid races critical_section::with(|_| { info.regs.cr2().modify(|w| { w.set_iterren(true); w.set_itevten(true); }); }); } /// Can be used by both blocking and async implementations fn clear_stop_flag(info: &'static Info) { trace!("I2C slave: clearing STOPF flag (v1 sequence)"); // v1 requires: READ SR1 then WRITE CR1 to clear STOPF let _ = info.regs.sr1().read(); info.regs.cr1().modify(|_| {}); // Dummy write to clear STOPF } } impl<'d, IM: MasterMode> I2c<'d, Async, IM> { async fn write_frame(&mut self, address: u8, write_buffer: &[u8], frame: FrameOptions) -> Result<(), Error> { self.info.regs.cr2().modify(|w| { // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for // reception. w.set_itbufen(false); // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 // register. w.set_dmaen(true); // Sending NACK is not necessary (nor possible) for write transfer. w.set_last(false); }); // Sentinel to disable transfer when an error occurs or future is canceled. // TODO: Generate STOP condition on cancel? let on_drop = OnDrop::new(|| { self.info.regs.cr2().modify(|w| { w.set_dmaen(false); w.set_iterren(false); w.set_itevten(false); }) }); if frame.send_start() { // Send a START condition self.info.regs.cr1().modify(|reg| { reg.set_start(true); }); // Wait until START condition was generated poll_fn(|cx| { self.state.waker.register(cx.waker()); match Self::check_and_clear_error_flags(self.info) { Err(e) => Poll::Ready(Err(e)), Ok(sr1) => { if sr1.start() { Poll::Ready(Ok(())) } else { // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(self.info); Poll::Pending } } } }) .await?; // Check if we were the ones to generate START if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() { return Err(Error::Arbitration); } // Set up current address we're trying to talk to self.info.regs.dr().write(|reg| reg.set_dr(address << 1)); // Wait for the address to be acknowledged poll_fn(|cx| { self.state.waker.register(cx.waker()); match Self::check_and_clear_error_flags(self.info) { Err(e) => Poll::Ready(Err(e)), Ok(sr1) => { if sr1.addr() { Poll::Ready(Ok(())) } else { // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(self.info); Poll::Pending } } } }) .await?; // Clear condition by reading SR2 self.info.regs.sr2().read(); } let dma_transfer = unsafe { // 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 = self.info.regs.dr().as_ptr() as *mut u8; self.tx_dma .as_mut() .unwrap() .write(write_buffer, dst, Default::default()) }; // Wait for bytes to be sent, or an error to occur. let poll_error = poll_fn(|cx| { self.state.waker.register(cx.waker()); match Self::check_and_clear_error_flags(self.info) { Err(e) => Poll::Ready(Err::<(), Error>(e)), Ok(_) => { // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(self.info); 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(()), }?; self.info.regs.cr2().modify(|w| { w.set_dmaen(false); }); if frame.send_stop() { // 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.” poll_fn(|cx| { self.state.waker.register(cx.waker()); match Self::check_and_clear_error_flags(self.info) { Err(e) => Poll::Ready(Err(e)), Ok(sr1) => { if sr1.btf() { Poll::Ready(Ok(())) } else { // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(self.info); Poll::Pending } } } }) .await?; self.info.regs.cr1().modify(|w| { w.set_stop(true); }); } drop(on_drop); // Fallthrough is success Ok(()) } /// Write. pub async fn write(&mut self, address: u8, write_buffer: &[u8]) -> Result<(), Error> { let _scoped_block_stop = self.info.rcc.block_stop(); self.write_frame(address, write_buffer, FrameOptions::FirstAndLastFrame) .await?; Ok(()) } /// Read. pub async fn read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> { let _scoped_block_stop = self.info.rcc.block_stop(); self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame) .await?; Ok(()) } async fn read_frame(&mut self, address: u8, read_buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> { if read_buffer.is_empty() { return Err(Error::Overrun); } // Some branches below depend on whether the buffer contains only a single byte. let single_byte = read_buffer.len() == 1; self.info.regs.cr2().modify(|w| { // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for // reception. w.set_itbufen(false); // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 // register. w.set_dmaen(true); // 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 generate a Stop condition in // the DMA Transfer Complete interrupt routine if enabled. w.set_last(frame.send_nack() && !single_byte); }); // Sentinel to disable transfer when an error occurs or future is canceled. // TODO: Generate STOP condition on cancel? let on_drop = OnDrop::new(|| { self.info.regs.cr2().modify(|w| { w.set_dmaen(false); w.set_iterren(false); w.set_itevten(false); }) }); if frame.send_start() { // Send a START condition and set ACK bit self.info.regs.cr1().modify(|reg| { reg.set_start(true); reg.set_ack(true); }); // Wait until START condition was generated poll_fn(|cx| { self.state.waker.register(cx.waker()); match Self::check_and_clear_error_flags(self.info) { Err(e) => Poll::Ready(Err(e)), Ok(sr1) => { if sr1.start() { Poll::Ready(Ok(())) } else { // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(self.info); Poll::Pending } } } }) .await?; // Check if we were the ones to generate START if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() { return Err(Error::Arbitration); } // Set up current address we're trying to talk to self.info.regs.dr().write(|reg| reg.set_dr((address << 1) + 1)); // Wait for the address to be acknowledged poll_fn(|cx| { self.state.waker.register(cx.waker()); match Self::check_and_clear_error_flags(self.info) { Err(e) => Poll::Ready(Err(e)), Ok(sr1) => { if sr1.addr() { Poll::Ready(Ok(())) } else { // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(self.info); Poll::Pending } } } }) .await?; // 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 frame.send_nack() && single_byte { self.info.regs.cr1().modify(|w| { w.set_ack(false); }); } // Clear condition by reading SR2 self.info.regs.sr2().read(); } else { // Before starting reception of single byte (but without START condition, i.e. in case // of merged operations), program NACK to emit at end of this byte. if frame.send_nack() && single_byte { self.info.regs.cr1().modify(|w| { w.set_ack(false); }); } } // 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 DMA Transfer Complete interrupt // routine. if frame.send_stop() && single_byte { self.info.regs.cr1().modify(|w| { w.set_stop(true); }); } let dma_transfer = unsafe { // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved // from this address from the memory after each RxE event. let src = self.info.regs.dr().as_ptr() as *mut u8; self.rx_dma.as_mut().unwrap().read(src, read_buffer, Default::default()) }; // Wait for bytes to be received, or an error to occur. let poll_error = poll_fn(|cx| { self.state.waker.register(cx.waker()); match Self::check_and_clear_error_flags(self.info) { Err(e) => Poll::Ready(Err::<(), Error>(e)), _ => { // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(self.info); Poll::Pending } } }); match select(dma_transfer, poll_error).await { Either::Second(Err(e)) => Err(e), _ => Ok(()), }?; self.info.regs.cr2().modify(|w| { w.set_dmaen(false); }); if frame.send_stop() && !single_byte { self.info.regs.cr1().modify(|w| { w.set_stop(true); }); } drop(on_drop); // Fallthrough is success Ok(()) } /// Write, restart, read. pub async fn write_read(&mut self, address: u8, write_buffer: &[u8], read_buffer: &mut [u8]) -> Result<(), Error> { let _scoped_block_stop = self.info.rcc.block_stop(); // Check empty read buffer before starting transaction. Otherwise, we would not generate the // stop condition below. if read_buffer.is_empty() { return Err(Error::Overrun); } self.write_frame(address, write_buffer, FrameOptions::FirstFrame) .await?; self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame) .await } /// Transaction with operations. /// /// Consecutive operations of same type are merged. See [transaction contract] for details. /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction pub async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { let _scoped_block_stop = self.info.rcc.block_stop(); for (op, frame) in operation_frames(operations)? { match op { Operation::Read(read_buffer) => self.read_frame(address, read_buffer, frame).await?, Operation::Write(write_buffer) => self.write_frame(address, write_buffer, frame).await?, } } Ok(()) } } enum Mode { Fast, Standard, } impl Mode { fn f_s(&self) -> i2c::vals::FS { match self { Mode::Fast => i2c::vals::FS::FAST, Mode::Standard => i2c::vals::FS::STANDARD, } } } enum Duty { Duty2_1, Duty16_9, } impl Duty { fn duty(&self) -> i2c::vals::Duty { match self { Duty::Duty2_1 => i2c::vals::Duty::DUTY2_1, Duty::Duty16_9 => i2c::vals::Duty::DUTY16_9, } } } /// Result of attempting to send a byte in slave transmitter mode #[derive(Debug, PartialEq)] enum TransmitResult { /// Byte sent and ACKed by master - continue transmission Acknowledged, /// Byte sent but NACKed by master - normal end of read transaction NotAcknowledged, /// STOP condition detected - master terminated transaction Stopped, /// RESTART condition detected - master starting new transaction Restarted, } /// Result of attempting to receive a byte in slave receiver mode #[derive(Debug, PartialEq)] enum ReceiveResult { /// Data byte successfully received Data(u8), /// STOP condition detected - end of write transaction Stopped, /// RESTART condition detected - master starting new transaction Restarted, } /// Enumeration of slave transaction termination conditions #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] enum SlaveTermination { /// STOP condition received - normal end of transaction Stop, /// RESTART condition received - master starting new transaction Restart, /// NACK received - normal end of read transaction Nack, } impl<'d, M: PeriMode> I2c<'d, M, Master> { /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster) pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> { let mut slave = I2c { info: self.info, state: self.state, kernel_clock: self.kernel_clock, tx_dma: self.tx_dma.take(), // Use take() to move ownership rx_dma: self.rx_dma.take(), // Use take() to move ownership #[cfg(feature = "time")] timeout: self.timeout, _phantom: PhantomData, _phantom2: PhantomData, _drop_guard: self._drop_guard, // Move the drop guard }; slave.init_slave(slave_addr_config); slave } } // Address configuration methods impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { /// Initialize slave mode with address configuration pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) { trace!("I2C slave: initializing with config={:?}", config); // Disable peripheral for configuration self.info.regs.cr1().modify(|reg| reg.set_pe(false)); // Configure slave addresses self.apply_address_configuration(config); // Enable peripheral with slave settings self.info.regs.cr1().modify(|reg| { reg.set_pe(true); reg.set_ack(true); // Enable acknowledgment for slave mode reg.set_nostretch(false); // Allow clock stretching for processing time }); trace!("I2C slave: initialization complete"); } /// Apply the complete address configuration for slave mode fn apply_address_configuration(&mut self, config: SlaveAddrConfig) { match config.addr { OwnAddresses::OA1(addr) => { self.configure_primary_address(addr); self.disable_secondary_address(); } OwnAddresses::OA2(oa2) => { self.configure_default_primary_address(); self.configure_secondary_address(oa2.addr); // v1 ignores mask } OwnAddresses::Both { oa1, oa2 } => { self.configure_primary_address(oa1); self.configure_secondary_address(oa2.addr); // v1 ignores mask } } // Configure general call detection if config.general_call { self.info.regs.cr1().modify(|w| w.set_engc(true)); } } /// Configure the primary address (OA1) register fn configure_primary_address(&mut self, addr: Address) { match addr { Address::SevenBit(addr) => { self.info.regs.oar1().write(|reg| { let hw_addr = (addr as u16) << 1; // Address in bits [7:1] reg.set_add(hw_addr); reg.set_addmode(i2c::vals::Addmode::BIT7); }); } Address::TenBit(addr) => { self.info.regs.oar1().write(|reg| { reg.set_add(addr); reg.set_addmode(i2c::vals::Addmode::BIT10); }); } } // Set required bit 14 as per reference manual self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14); } /// Configure the secondary address (OA2) register fn configure_secondary_address(&mut self, addr: u8) { self.info.regs.oar2().write(|reg| { reg.set_add2(addr); reg.set_endual(i2c::vals::Endual::DUAL); }); } /// Set a default primary address when using OA2-only mode fn configure_default_primary_address(&mut self) { self.info.regs.oar1().write(|reg| { reg.set_add(0); // Reserved address, safe to use reg.set_addmode(i2c::vals::Addmode::BIT7); }); self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14); } /// Disable secondary address when not needed fn disable_secondary_address(&mut self) { self.info.regs.oar2().write(|reg| { reg.set_endual(i2c::vals::Endual::SINGLE); }); } } impl<'d, M: PeriMode> I2c<'d, M, MultiMaster> { /// Listen for incoming I2C address match and return the command type /// /// This method blocks until the slave address is matched by a master. /// Returns the command type (Read/Write) and the matched address. pub fn blocking_listen(&mut self) -> Result { trace!("I2C slave: starting blocking listen for address match"); let result = self.blocking_listen_with_timeout(self.timeout()); trace!("I2C slave: blocking listen complete, result={:?}", result); result } /// Respond to a master read request by transmitting data /// /// Sends the provided data to the master. If the master requests more bytes /// than available, padding bytes (0x00) are sent until the master terminates /// the transaction with NACK. /// /// Returns the total number of bytes transmitted (including padding). pub fn blocking_respond_to_read(&mut self, data: &[u8]) -> Result { trace!("I2C slave: starting blocking respond_to_read, data_len={}", data.len()); if let Some(zero_length_result) = self.detect_zero_length_read(self.timeout())? { trace!("I2C slave: zero-length read detected"); return Ok(zero_length_result); } let result = self.transmit_to_master(data, self.timeout()); trace!("I2C slave: blocking respond_to_read complete, result={:?}", result); result } /// Respond to a master write request by receiving data /// /// Receives data from the master into the provided buffer. If the master /// sends more bytes than the buffer can hold, excess bytes are acknowledged /// but discarded. /// /// Returns the number of bytes stored in the buffer (not total received). pub fn blocking_respond_to_write(&mut self, buffer: &mut [u8]) -> Result { trace!( "I2C slave: starting blocking respond_to_write, buffer_len={}", buffer.len() ); let result = self.receive_from_master(buffer, self.timeout()); trace!("I2C slave: blocking respond_to_write complete, result={:?}", result); result } // Private implementation methods /// Wait for address match and determine transaction type fn blocking_listen_with_timeout(&mut self, timeout: Timeout) -> Result { // Ensure interrupts are disabled for blocking operation self.disable_i2c_interrupts(); // Wait for address match (ADDR flag) loop { let sr1 = Self::read_status_and_handle_errors(self.info)?; if sr1.addr() { // Address matched - read SR2 to get direction and clear ADDR flag let sr2 = self.info.regs.sr2().read(); let direction = if sr2.tra() { SlaveCommandKind::Read } else { SlaveCommandKind::Write }; // Use the static method instead of the instance method let matched_address = Self::decode_matched_address(sr2, self.info)?; trace!( "I2C slave: address matched, direction={:?}, addr={:?}", direction, matched_address ); return Ok(SlaveCommand { kind: direction, address: matched_address, }); } timeout.check()?; } } /// Transmit data to master in response to read request fn transmit_to_master(&mut self, data: &[u8], timeout: Timeout) -> Result { let mut bytes_transmitted = 0; let mut padding_count = 0; loop { let byte_to_send = if bytes_transmitted < data.len() { data[bytes_transmitted] } else { padding_count += 1; 0x00 // Send padding bytes when data is exhausted }; match self.transmit_byte(byte_to_send, timeout)? { TransmitResult::Acknowledged => { bytes_transmitted += 1; } TransmitResult::NotAcknowledged => { bytes_transmitted += 1; // Count the NACKed byte break; } TransmitResult::Stopped | TransmitResult::Restarted => { break; } } } if padding_count > 0 { trace!( "I2C slave: sent {} data bytes + {} padding bytes = {} total", data.len(), padding_count, bytes_transmitted ); } Ok(bytes_transmitted) } /// Receive data from master during write request fn receive_from_master(&mut self, buffer: &mut [u8], timeout: Timeout) -> Result { let mut bytes_stored = 0; // Receive bytes that fit in buffer while bytes_stored < buffer.len() { match self.receive_byte(timeout)? { ReceiveResult::Data(byte) => { buffer[bytes_stored] = byte; bytes_stored += 1; } ReceiveResult::Stopped | ReceiveResult::Restarted => { return Ok(bytes_stored); } } } // Handle buffer overflow by discarding excess bytes if bytes_stored == buffer.len() { trace!("I2C slave: buffer full, discarding excess bytes"); self.discard_excess_bytes(timeout)?; } Ok(bytes_stored) } /// Detect zero-length read pattern early /// /// Zero-length reads occur when a master sends START+ADDR+R followed immediately /// by NACK+STOP without wanting any data. This must be detected before attempting /// to transmit any bytes to avoid SDA line issues. fn detect_zero_length_read(&mut self, _timeout: Timeout) -> Result, Error> { // Quick check for immediate termination signals let sr1 = self.info.regs.sr1().read(); // Check for immediate NACK (fastest zero-length pattern) if sr1.af() { self.clear_acknowledge_failure(); return Ok(Some(0)); } // Check for immediate STOP (alternative zero-length pattern) if sr1.stopf() { Self::clear_stop_flag(self.info); return Ok(Some(0)); } // Give a brief window for master to send termination signals // This handles masters that have slight delays between address ACK and NACK const ZERO_LENGTH_DETECTION_CYCLES: u32 = 20; // ~5-10µs window for _ in 0..ZERO_LENGTH_DETECTION_CYCLES { let sr1 = self.info.regs.sr1().read(); // Immediate NACK indicates zero-length read if sr1.af() { self.clear_acknowledge_failure(); return Ok(Some(0)); } // Immediate STOP indicates zero-length read if sr1.stopf() { Self::clear_stop_flag(self.info); return Ok(Some(0)); } // If TXE becomes ready, master is waiting for data - not zero-length if sr1.txe() { return Ok(None); // Proceed with normal transmission } // If RESTART detected, handle as zero-length if sr1.addr() { return Ok(Some(0)); } } // No zero-length pattern detected within the window Ok(None) } /// Discard excess bytes when buffer is full fn discard_excess_bytes(&mut self, timeout: Timeout) -> Result<(), Error> { let mut discarded_count = 0; loop { match self.receive_byte(timeout)? { ReceiveResult::Data(_) => { discarded_count += 1; continue; } ReceiveResult::Stopped | ReceiveResult::Restarted => { if discarded_count > 0 { trace!("I2C slave: discarded {} excess bytes", discarded_count); } break; } } } Ok(()) } /// Send a single byte and wait for master's response fn transmit_byte(&mut self, byte: u8, timeout: Timeout) -> Result { // Wait for transmit buffer ready self.wait_for_transmit_ready(timeout)?; // Send the byte self.info.regs.dr().write(|w| w.set_dr(byte)); // Wait for transmission completion or master response self.wait_for_transmit_completion(timeout) } /// Wait until transmit buffer is ready (TXE flag set) fn wait_for_transmit_ready(&mut self, timeout: Timeout) -> Result<(), Error> { loop { let sr1 = Self::read_status_and_handle_errors(self.info)?; // Check for early termination conditions if let Some(result) = Self::check_early_termination(sr1) { return Err(self.handle_early_termination(result)); } if sr1.txe() { return Ok(()); // Ready to transmit } timeout.check()?; } } /// Wait for byte transmission completion or master response fn wait_for_transmit_completion(&mut self, timeout: Timeout) -> Result { loop { let sr1 = self.info.regs.sr1().read(); // Check flags in priority order if sr1.af() { self.clear_acknowledge_failure(); return Ok(TransmitResult::NotAcknowledged); } if sr1.btf() { return Ok(TransmitResult::Acknowledged); } if sr1.stopf() { Self::clear_stop_flag(self.info); return Ok(TransmitResult::Stopped); } if sr1.addr() { return Ok(TransmitResult::Restarted); } // Check for other error conditions self.check_for_hardware_errors(sr1)?; timeout.check()?; } } /// Receive a single byte or detect transaction termination fn receive_byte(&mut self, timeout: Timeout) -> Result { loop { let sr1 = Self::read_status_and_handle_errors(self.info)?; // Check for received data first (prioritize data over control signals) if sr1.rxne() { let byte = self.info.regs.dr().read().dr(); return Ok(ReceiveResult::Data(byte)); } // Check for transaction termination if sr1.addr() { return Ok(ReceiveResult::Restarted); } if sr1.stopf() { Self::clear_stop_flag(self.info); return Ok(ReceiveResult::Stopped); } timeout.check()?; } } /// Determine which slave address was matched based on SR2 flags fn decode_matched_address(sr2: i2c::regs::Sr2, info: &'static Info) -> Result { if sr2.gencall() { Ok(Address::SevenBit(0x00)) // General call address } else if sr2.dualf() { // OA2 (secondary address) was matched let oar2 = info.regs.oar2().read(); if oar2.endual() != i2c::vals::Endual::DUAL { return Err(Error::Bus); // Hardware inconsistency } Ok(Address::SevenBit(oar2.add2())) } else { // OA1 (primary address) was matched let oar1 = info.regs.oar1().read(); match oar1.addmode() { i2c::vals::Addmode::BIT7 => { let addr = (oar1.add() >> 1) as u8; Ok(Address::SevenBit(addr)) } i2c::vals::Addmode::BIT10 => Ok(Address::TenBit(oar1.add())), } } } // Helper methods for hardware interaction /// Read status register and handle I2C errors (except NACK in slave mode) fn read_status_and_handle_errors(info: &'static Info) -> Result { match Self::check_and_clear_error_flags(info) { Ok(sr1) => Ok(sr1), Err(Error::Nack) => { // In slave mode, NACK is normal protocol behavior, not an error Ok(info.regs.sr1().read()) } Err(other_error) => Err(other_error), } } /// Check for conditions that cause early termination of operations fn check_early_termination(sr1: i2c::regs::Sr1) -> Option { if sr1.stopf() { Some(TransmitResult::Stopped) } else if sr1.addr() { Some(TransmitResult::Restarted) } else if sr1.af() { Some(TransmitResult::NotAcknowledged) } else { None } } /// Convert early termination to appropriate error fn handle_early_termination(&mut self, result: TransmitResult) -> Error { match result { TransmitResult::Stopped => { Self::clear_stop_flag(self.info); Error::Bus // Unexpected STOP during setup } TransmitResult::Restarted => { Error::Bus // Unexpected RESTART during setup } TransmitResult::NotAcknowledged => { self.clear_acknowledge_failure(); Error::Bus // Unexpected NACK during setup } TransmitResult::Acknowledged => { unreachable!() // This should never be passed to this function } } } /// Check for hardware-level I2C errors during transmission fn check_for_hardware_errors(&self, sr1: i2c::regs::Sr1) -> Result<(), Error> { if sr1.timeout() || sr1.ovr() || sr1.arlo() || sr1.berr() { // Delegate to existing error handling Self::check_and_clear_error_flags(self.info)?; } Ok(()) } /// Disable I2C event and error interrupts for blocking operations fn disable_i2c_interrupts(&mut self) { self.info.regs.cr2().modify(|w| { w.set_itevten(false); w.set_iterren(false); }); } /// Clear the acknowledge failure flag fn clear_acknowledge_failure(&mut self) { self.info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_af(false); }); } /// Configure DMA settings for slave operations (shared between read/write) fn setup_slave_dma_base(&mut self) { self.info.regs.cr2().modify(|w| { w.set_itbufen(false); // Always disable buffer interrupts when using DMA w.set_dmaen(true); // Enable DMA requests w.set_last(false); // LAST bit not used in slave mode for v1 hardware }); } /// Disable DMA and interrupts in a critical section fn disable_dma_and_interrupts(info: &'static Info) { critical_section::with(|_| { info.regs.cr2().modify(|w| { w.set_dmaen(false); w.set_iterren(false); w.set_itevten(false); }); }); } /// Check for early termination conditions during slave operations /// Returns Some(result) if termination detected, None to continue fn check_slave_termination_conditions(sr1: i2c::regs::Sr1) -> Option { if sr1.stopf() { Some(SlaveTermination::Stop) } else if sr1.addr() { Some(SlaveTermination::Restart) } else if sr1.af() { Some(SlaveTermination::Nack) } else { None } } } impl<'d> I2c<'d, Async, MultiMaster> { /// Async listen for incoming I2C messages using interrupts /// /// Waits for a master to address this slave and returns the command type /// (Read/Write) and the matched address. This method will suspend until /// an address match occurs. pub async fn listen(&mut self) -> Result { let _scoped_block_stop = self.info.rcc.block_stop(); trace!("I2C slave: starting async listen for address match"); let state = self.state; let info = self.info; Self::enable_interrupts(info); let on_drop = OnDrop::new(|| { Self::disable_dma_and_interrupts(info); }); let result = poll_fn(|cx| { state.waker.register(cx.waker()); match Self::check_and_clear_error_flags(info) { Err(e) => { error!("I2C slave: error during listen: {:?}", e); Poll::Ready(Err(e)) } Ok(sr1) => { if sr1.addr() { let sr2 = info.regs.sr2().read(); let direction = if sr2.tra() { SlaveCommandKind::Read } else { SlaveCommandKind::Write }; let matched_address = match Self::decode_matched_address(sr2, info) { Ok(addr) => { trace!("I2C slave: address matched, direction={:?}, addr={:?}", direction, addr); addr } Err(e) => { error!("I2C slave: failed to decode matched address: {:?}", e); return Poll::Ready(Err(e)); } }; Poll::Ready(Ok(SlaveCommand { kind: direction, address: matched_address, })) } else { Self::enable_interrupts(info); Poll::Pending } } } }) .await; drop(on_drop); trace!("I2C slave: listen complete, result={:?}", result); result } /// Async respond to write command using RX DMA /// /// Receives data from the master into the provided buffer using DMA. /// If the master sends more bytes than the buffer can hold, excess bytes /// are acknowledged but discarded to prevent interrupt flooding. /// /// Returns the number of bytes stored in the buffer (not total received). pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result { let _scoped_block_stop = self.info.rcc.block_stop(); trace!("I2C slave: starting respond_to_write, buffer_len={}", buffer.len()); if buffer.is_empty() { warn!("I2C slave: respond_to_write called with empty buffer"); return Err(Error::Overrun); } let state = self.state; let info = self.info; self.setup_slave_dma_base(); let on_drop = OnDrop::new(|| { Self::disable_dma_and_interrupts(info); }); info.regs.sr2().read(); let result = self.execute_slave_receive_transfer(buffer, state, info).await; drop(on_drop); trace!("I2C slave: respond_to_write complete, result={:?}", result); result } /// Async respond to read command using TX DMA /// /// Transmits data to the master using DMA. If the master requests more bytes /// than available in the data buffer, padding bytes (0x00) are sent until /// the master terminates the transaction with NACK, STOP, or RESTART. /// /// Returns the total number of bytes transmitted (data + padding). pub async fn respond_to_read(&mut self, data: &[u8]) -> Result { let _scoped_block_stop = self.info.rcc.block_stop(); trace!("I2C slave: starting respond_to_read, data_len={}", data.len()); if data.is_empty() { warn!("I2C slave: respond_to_read called with empty data"); return Err(Error::Overrun); } let state = self.state; let info = self.info; self.setup_slave_dma_base(); let on_drop = OnDrop::new(|| { Self::disable_dma_and_interrupts(info); }); info.regs.sr2().read(); let result = self.execute_slave_transmit_transfer(data, state, info).await; drop(on_drop); trace!("I2C slave: respond_to_read complete, result={:?}", result); result } // === Private Transfer Execution Methods === /// Execute complete slave receive transfer with excess byte handling async fn execute_slave_receive_transfer( &mut self, buffer: &mut [u8], state: &'static State, info: &'static Info, ) -> Result { let dma_transfer = unsafe { let src = info.regs.dr().as_ptr() as *mut u8; self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) }; let i2c_monitor = Self::create_termination_monitor(state, info, &[SlaveTermination::Stop, SlaveTermination::Restart]); match select(dma_transfer, i2c_monitor).await { Either::Second(Err(e)) => { error!("I2C slave: error during receive transfer: {:?}", e); Self::disable_dma_and_interrupts(info); Err(e) } Either::First(_) => { trace!("I2C slave: DMA receive completed, handling excess bytes"); Self::disable_dma_and_interrupts(info); self.handle_excess_bytes(state, info).await?; Ok(buffer.len()) } Either::Second(Ok(termination)) => { trace!("I2C slave: receive terminated by I2C event: {:?}", termination); Self::disable_dma_and_interrupts(info); Ok(buffer.len()) } } } /// Execute complete slave transmit transfer with padding byte handling async fn execute_slave_transmit_transfer( &mut self, data: &[u8], state: &'static State, info: &'static Info, ) -> Result { let dma_transfer = unsafe { let dst = info.regs.dr().as_ptr() as *mut u8; self.tx_dma.as_mut().unwrap().write(data, dst, Default::default()) }; let i2c_monitor = Self::create_termination_monitor( state, info, &[ SlaveTermination::Stop, SlaveTermination::Restart, SlaveTermination::Nack, ], ); match select(dma_transfer, i2c_monitor).await { Either::Second(Err(e)) => { error!("I2C slave: error during transmit transfer: {:?}", e); Self::disable_dma_and_interrupts(info); Err(e) } Either::First(_) => { trace!("I2C slave: DMA transmit completed, handling padding bytes"); Self::disable_dma_and_interrupts(info); let padding_count = self.handle_padding_bytes(state, info).await?; let total_bytes = data.len() + padding_count; trace!( "I2C slave: sent {} data bytes + {} padding bytes = {} total", data.len(), padding_count, total_bytes ); Ok(total_bytes) } Either::Second(Ok(termination)) => { trace!("I2C slave: transmit terminated by I2C event: {:?}", termination); Self::disable_dma_and_interrupts(info); Ok(data.len()) } } } /// Create a future that monitors for specific slave termination conditions fn create_termination_monitor( state: &'static State, info: &'static Info, allowed_terminations: &'static [SlaveTermination], ) -> impl Future> { poll_fn(move |cx| { state.waker.register(cx.waker()); match Self::check_and_clear_error_flags(info) { Err(Error::Nack) if allowed_terminations.contains(&SlaveTermination::Nack) => { Poll::Ready(Ok(SlaveTermination::Nack)) } Err(e) => Poll::Ready(Err(e)), Ok(sr1) => { if let Some(termination) = Self::check_slave_termination_conditions(sr1) { if allowed_terminations.contains(&termination) { // Handle the specific termination condition match termination { SlaveTermination::Stop => Self::clear_stop_flag(info), SlaveTermination::Nack => { info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_af(false); }); } SlaveTermination::Restart => { // ADDR flag will be handled by next listen() call } } Poll::Ready(Ok(termination)) } else { // Unexpected termination condition Poll::Ready(Err(Error::Bus)) } } else { Self::enable_interrupts(info); Poll::Pending } } } }) } /// Handle excess bytes after DMA buffer is full /// /// Reads and discards bytes until transaction termination to prevent interrupt flooding async fn handle_excess_bytes(&mut self, state: &'static State, info: &'static Info) -> Result<(), Error> { let mut discarded_count = 0; poll_fn(|cx| { state.waker.register(cx.waker()); match Self::check_and_clear_error_flags(info) { Err(e) => { error!("I2C slave: error while discarding excess bytes: {:?}", e); Poll::Ready(Err(e)) } Ok(sr1) => { if let Some(termination) = Self::check_slave_termination_conditions(sr1) { match termination { SlaveTermination::Stop => Self::clear_stop_flag(info), SlaveTermination::Restart => {} SlaveTermination::Nack => unreachable!("NACK not expected during receive"), } if discarded_count > 0 { trace!("I2C slave: discarded {} excess bytes", discarded_count); } return Poll::Ready(Ok(())); } if sr1.rxne() { let _discarded_byte = info.regs.dr().read().dr(); discarded_count += 1; Self::enable_interrupts(info); return Poll::Pending; } Self::enable_interrupts(info); Poll::Pending } } }) .await } /// Handle padding bytes after DMA data is exhausted /// /// Sends 0x00 bytes until transaction termination to prevent interrupt flooding async fn handle_padding_bytes(&mut self, state: &'static State, info: &'static Info) -> Result { let mut padding_count = 0; poll_fn(|cx| { state.waker.register(cx.waker()); match Self::check_and_clear_error_flags(info) { Err(Error::Nack) => Poll::Ready(Ok(padding_count)), Err(e) => { error!("I2C slave: error while sending padding bytes: {:?}", e); Poll::Ready(Err(e)) } Ok(sr1) => { if let Some(termination) = Self::check_slave_termination_conditions(sr1) { match termination { SlaveTermination::Stop => Self::clear_stop_flag(info), SlaveTermination::Restart => {} SlaveTermination::Nack => { info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_af(false); }); } } return Poll::Ready(Ok(padding_count)); } if sr1.txe() { info.regs.dr().write(|w| w.set_dr(0x00)); padding_count += 1; Self::enable_interrupts(info); return Poll::Pending; } Self::enable_interrupts(info); Poll::Pending } } }) .await } } /// Timing configuration for I2C v1 hardware /// /// This struct encapsulates the complex timing calculations required for STM32 I2C v1 /// peripherals, which use three separate registers (CR2.FREQ, CCR, TRISE) instead of /// the unified TIMINGR register found in v2 hardware. struct Timings { freq: u8, // APB frequency in MHz for CR2.FREQ register mode: Mode, // Standard or Fast mode selection trise: u8, // Rise time compensation value ccr: u16, // Clock control register value duty: Duty, // Fast mode duty cycle selection } impl Timings { fn new(i2cclk: Hertz, frequency: Hertz) -> Self { // Calculate settings for I2C speed modes let frequency = frequency.0; let clock = i2cclk.0; let freq = clock / 1_000_000; assert!((2..=50).contains(&freq)); // Configure bus frequency into I2C peripheral let trise = if frequency <= 100_000 { freq + 1 } else { (freq * 300) / 1000 + 1 }; let mut ccr; let duty; let mode; // I2C clock control calculation if frequency <= 100_000 { duty = Duty::Duty2_1; mode = Mode::Standard; ccr = { let ccr = clock / (frequency * 2); if ccr < 4 { 4 } else { ccr } }; } else { const DUTYCYCLE: u8 = 0; mode = Mode::Fast; if DUTYCYCLE == 0 { duty = Duty::Duty2_1; ccr = clock / (frequency * 3); ccr = if ccr < 1 { 1 } else { ccr }; } else { duty = Duty::Duty16_9; ccr = clock / (frequency * 25); ccr = if ccr < 1 { 1 } else { ccr }; } } Self { freq: freq as u8, trise: trise as u8, ccr: ccr as u16, duty, mode, } } } impl<'d, M: PeriMode> SetConfig for I2c<'d, M, Master> { type Config = Hertz; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { let timings = Timings::new(self.kernel_clock, *config); self.info.regs.cr2().modify(|reg| { reg.set_freq(timings.freq); }); self.info.regs.ccr().modify(|reg| { reg.set_f_s(timings.mode.f_s()); reg.set_duty(timings.duty.duty()); reg.set_ccr(timings.ccr); }); self.info.regs.trise().modify(|reg| { reg.set_trise(timings.trise); }); Ok(()) } }