//! Inter-Integrated-Circuit (I2C) #![macro_use] #[cfg_attr(i2c_v1, path = "v1.rs")] #[cfg_attr(any(i2c_v2, i2c_v3), path = "v2.rs")] mod _version; mod config; use core::future::Future; use core::iter; use core::marker::PhantomData; pub use config::*; use embassy_hal_internal::Peri; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; use mode::MasterMode; pub use mode::{Master, MultiMaster}; use crate::dma::ChannelAndRequest; use crate::gpio::{AnyPin, SealedPin as _}; use crate::interrupt::typelevel::Interrupt; use crate::mode::{Async, Blocking, Mode}; use crate::rcc::{RccInfo, SealedRccPeripheral}; use crate::time::Hertz; use crate::{interrupt, peripherals}; /// I2C error. #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { /// Bus error Bus, /// Arbitration lost Arbitration, /// ACK not received (either to the address or to a data byte) Nack, /// Timeout Timeout, /// CRC error Crc, /// Overrun error Overrun, /// Zero-length transfers are not allowed. ZeroLengthTransfer, } impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let message = match self { Self::Bus => "Bus Error", Self::Arbitration => "Arbitration Lost", Self::Nack => "ACK Not Received", Self::Timeout => "Request Timed Out", Self::Crc => "CRC Mismatch", Self::Overrun => "Buffer Overrun", Self::ZeroLengthTransfer => "Zero-Length Transfers are not allowed", }; write!(f, "{}", message) } } impl core::error::Error for Error {} /// I2C modes pub mod mode { trait SealedMode {} /// Trait for I2C master operations. #[allow(private_bounds)] pub trait MasterMode: SealedMode {} /// Mode allowing for I2C master operations. pub struct Master; /// Mode allowing for I2C master and slave operations. pub struct MultiMaster; impl SealedMode for Master {} impl MasterMode for Master {} impl SealedMode for MultiMaster {} impl MasterMode for MultiMaster {} } #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// The command kind to the slave from the master pub enum SlaveCommandKind { /// Write to the slave Write, /// Read from the slave Read, } #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// The command kind to the slave from the master and the address that the slave matched pub struct SlaveCommand { /// The kind of command pub kind: SlaveCommandKind, /// The address that the slave matched pub address: Address, } #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// The status of the slave send operation pub enum SendStatus { /// The slave send operation is done, all bytes have been sent and the master is not requesting more Done, /// The slave send operation is done, but there are leftover bytes that the master did not read LeftoverBytes(usize), } struct I2CDropGuard<'d> { info: &'static Info, scl: Option>, sda: Option>, } impl<'d> Drop for I2CDropGuard<'d> { fn drop(&mut self) { if let Some(x) = self.scl.as_ref() { x.set_as_disconnected() } if let Some(x) = self.sda.as_ref() { x.set_as_disconnected() } self.info.rcc.disable_without_stop(); } } /// I2C driver. pub struct I2c<'d, M: Mode, IM: MasterMode> { info: &'static Info, state: &'static State, kernel_clock: Hertz, tx_dma: Option>, rx_dma: Option>, #[cfg(feature = "time")] timeout: Duration, _phantom: PhantomData, _phantom2: PhantomData, _drop_guard: I2CDropGuard<'d>, } impl<'d> I2c<'d, Async, Master> { /// Create a new I2C driver. pub fn new( peri: Peri<'d, T>, scl: Peri<'d, if_afio!(impl SclPin)>, sda: Peri<'d, if_afio!(impl SdaPin)>, _irq: impl interrupt::typelevel::Binding> + interrupt::typelevel::Binding> + 'd, tx_dma: Peri<'d, impl TxDma>, rx_dma: Peri<'d, impl RxDma>, config: Config, ) -> Self { Self::new_inner( peri, new_pin!(scl, config.scl_af()), new_pin!(sda, config.sda_af()), new_dma!(tx_dma), new_dma!(rx_dma), config, ) } } impl<'d> I2c<'d, Blocking, Master> { /// Create a new blocking I2C driver. pub fn new_blocking( peri: Peri<'d, T>, scl: Peri<'d, if_afio!(impl SclPin)>, sda: Peri<'d, if_afio!(impl SdaPin)>, config: Config, ) -> Self { Self::new_inner( peri, new_pin!(scl, config.scl_af()), new_pin!(sda, config.sda_af()), None, None, config, ) } } impl<'d, M: Mode> I2c<'d, M, Master> { /// Create a new I2C driver. fn new_inner( _peri: Peri<'d, T>, scl: Option>, sda: Option>, tx_dma: Option>, rx_dma: Option>, config: Config, ) -> Self { unsafe { T::EventInterrupt::enable() }; unsafe { T::ErrorInterrupt::enable() }; let mut this = Self { info: T::info(), state: T::state(), kernel_clock: T::frequency(), tx_dma, rx_dma, #[cfg(feature = "time")] timeout: config.timeout, _phantom: PhantomData, _phantom2: PhantomData, _drop_guard: I2CDropGuard { info: T::info(), scl, sda, }, }; this.enable_and_init(config); this } fn enable_and_init(&mut self, config: Config) { self.info.rcc.enable_and_reset_without_stop(); self.init(config); } } impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { fn timeout(&self) -> Timeout { Timeout { #[cfg(feature = "time")] deadline: Instant::now() + self.timeout, } } } #[derive(Copy, Clone)] struct Timeout { #[cfg(feature = "time")] deadline: Instant, } #[allow(dead_code)] impl Timeout { #[inline] fn check(self) -> Result<(), Error> { #[cfg(feature = "time")] if Instant::now() > self.deadline { return Err(Error::Timeout); } Ok(()) } #[inline] fn with(self, fut: impl Future>) -> impl Future> { #[cfg(feature = "time")] { use futures_util::FutureExt; embassy_futures::select::select(embassy_time::Timer::at(self.deadline), fut).map(|r| match r { embassy_futures::select::Either::First(_) => Err(Error::Timeout), embassy_futures::select::Either::Second(r) => r, }) } #[cfg(not(feature = "time"))] fut } } struct State { #[allow(unused)] waker: AtomicWaker, } impl State { const fn new() -> Self { Self { waker: AtomicWaker::new(), } } } struct Info { regs: crate::pac::i2c::I2c, rcc: RccInfo, } peri_trait!( irqs: [EventInterrupt, ErrorInterrupt], ); pin_trait!(SclPin, Instance, @A); pin_trait!(SdaPin, Instance, @A); dma_trait!(RxDma, Instance); dma_trait!(TxDma, Instance); /// Event interrupt handler. pub struct EventInterruptHandler { _phantom: PhantomData, } impl interrupt::typelevel::Handler for EventInterruptHandler { unsafe fn on_interrupt() { _version::on_interrupt::() } } /// Error interrupt handler. pub struct ErrorInterruptHandler { _phantom: PhantomData, } impl interrupt::typelevel::Handler for ErrorInterruptHandler { unsafe fn on_interrupt() { _version::on_interrupt::() } } foreach_peripheral!( (i2c, $inst:ident) => { #[allow(private_interfaces)] impl SealedInstance for peripherals::$inst { fn info() -> &'static Info { static INFO: Info = Info{ regs: crate::pac::$inst, rcc: crate::peripherals::$inst::RCC_INFO, }; &INFO } fn state() -> &'static State { static STATE: State = State::new(); &STATE } } impl Instance for peripherals::$inst { type EventInterrupt = crate::_generated::peripheral_interrupts::$inst::EV; type ErrorInterrupt = crate::_generated::peripheral_interrupts::$inst::ER; } }; ); impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M, IM> { type Error = Error; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } } impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M, IM> { type Error = Error; fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { self.blocking_write(address, write) } } impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M, IM> { type Error = Error; fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { self.blocking_write_read(address, write, read) } } impl embedded_hal_1::i2c::Error for Error { fn kind(&self) -> embedded_hal_1::i2c::ErrorKind { match *self { Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus, Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, Self::Nack => { embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown) } Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other, Self::Crc => embedded_hal_1::i2c::ErrorKind::Other, Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun, Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other, } } } impl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::ErrorType for I2c<'d, M, IM> { type Error = Error; } impl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::I2c for I2c<'d, M, IM> { fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, read) } fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { self.blocking_write(address, write) } fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { self.blocking_write_read(address, write, read) } fn transaction( &mut self, address: u8, operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { self.blocking_transaction(address, operations) } } impl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> { async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.read(address, read).await } async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { self.write(address, write).await } async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { self.write_read(address, write, read).await } async fn transaction( &mut self, address: u8, operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { self.transaction(address, operations).await } } /// Frame type in I2C transaction. /// /// This tells each method what kind of frame 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` behave identically for writes /// - `NextFrame` and `LastFrameNoStop` behave identically for writes /// /// Abbreviations used below: /// /// - `ST` = start condition /// - `SR` = repeated start condition /// - `SP` = stop condition /// - `ACK`/`NACK` = last byte in read operation #[derive(Copy, Clone)] #[allow(dead_code)] enum FrameOptions { /// `[ST/SR]+[NACK]+[SP]` First frame (of this type) in transaction and also last frame overall. 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, } #[allow(dead_code)] impl FrameOptions { /// Returns true if a start or repeated start condition should be generated before this operation. fn send_start(self) -> bool { match self { Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true, Self::NextFrame | Self::LastFrame | Self::LastFrameNoStop => false, } } /// Returns true if a stop condition should be generated after this operation. fn send_stop(self) -> bool { match self { Self::FirstAndLastFrame | Self::LastFrame => true, Self::FirstFrame | Self::FirstAndNextFrame | Self::NextFrame | Self::LastFrameNoStop => false, } } /// Returns true if NACK should be sent after the last byte received in a read operation. /// /// This signals the end of a read sequence and releases the bus for the master's /// next transmission (or stop condition). fn send_nack(self) -> bool { match self { Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true, Self::FirstAndNextFrame | Self::NextFrame => false, } } } /// Analyzes I2C transaction operations and assigns appropriate frame to each. /// /// This function processes a sequence of I2C operations and determines the correct /// frame configuration for each operation to ensure proper I2C protocol compliance. /// It handles the complex logic of: /// /// - Generating start conditions for the first operation of each type (read/write) /// - Generating stop conditions for the final operation in the entire transaction /// - Managing ACK/NACK behavior for read operations, including merging consecutive reads /// - Ensuring proper bus handoff between different operation types /// /// **Transaction Contract Compliance:** /// The frame assignments ensure compliance with the embedded-hal I2C transaction contract, /// where consecutive operations of the same type are logically merged while maintaining /// proper protocol boundaries. /// /// **Error Handling:** /// Returns an error if any read operation has an empty buffer, as this would create /// an invalid I2C transaction that could halt mid-execution. /// /// # Arguments /// * `operations` - Mutable slice of I2C operations from embedded-hal /// /// # Returns /// An iterator over (operation, frame) pairs, or an error if the transaction is invalid /// #[allow(dead_code)] fn operation_frames<'a, 'b: 'a>( operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], ) -> Result, FrameOptions)>, Error> { use embedded_hal_1::i2c::Operation::{Read, Write}; // Validate that no read operations have empty buffers before starting the transaction. // Empty read operations would risk halting with an error mid-transaction. // // Note: We could theoretically allow empty read operations within consecutive read // sequences as long as the final merged read has at least one byte, but this would // complicate the logic significantly and create error-prone edge cases. if operations.iter().any(|op| match op { Read(read) => read.is_empty(), Write(_) => false, }) { return Err(Error::Overrun); } let mut operations = operations.iter_mut().peekable(); let mut next_first_operation = true; Ok(iter::from_fn(move || { let current_op = operations.next()?; // Determine if this is the first operation of its type (read or write) let is_first_of_type = next_first_operation; let next_op = operations.peek(); // Compute the appropriate frame based on three key properties: // // 1. **Start Condition**: Generate (repeated) start for first operation of each type // 2. **Stop Condition**: Generate stop for the final operation in the entire transaction // 3. **ACK/NACK for Reads**: For read operations, send ACK if more reads follow in the // sequence, or NACK for the final read in a sequence (before write or transaction end) // // The third property is checked for all operations since the resulting frame // configurations are identical for write operations regardless of ACK/NACK treatment. let frame = match (is_first_of_type, next_op) { // First operation of type, and it's also the final operation overall (true, None) => FrameOptions::FirstAndLastFrame, // First operation of type, next operation is also a read (continue read sequence) (true, Some(Read(_))) => FrameOptions::FirstAndNextFrame, // First operation of type, next operation is write (end current sequence) (true, Some(Write(_))) => FrameOptions::FirstFrame, // Continuation operation, and it's the final operation overall (false, None) => FrameOptions::LastFrame, // Continuation operation, next operation is also a read (continue read sequence) (false, Some(Read(_))) => FrameOptions::NextFrame, // Continuation operation, next operation is write (end current sequence, no stop) (false, Some(Write(_))) => FrameOptions::LastFrameNoStop, }; // Pre-calculate whether the next operation will be the first of its type. // This is done here because we consume `current_op` as the iterator value // and cannot access it in the next iteration. next_first_operation = match (¤t_op, next_op) { // No next operation (_, None) => false, // Operation type changes: next will be first of its type (Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true, // Operation type continues: next will not be first of its type (Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false, }; Some((current_op, frame)) })) }