//! Inter-Integrated-Circuit (I2C) #![macro_use] #[cfg_attr(i2c_v1, path = "v1.rs")] #[cfg_attr(i2c_v2, path = "v2.rs")] mod _version; use core::future::Future; use core::marker::PhantomData; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; use crate::dma::NoDma; use crate::gpio::sealed::AFType; use crate::gpio::Pull; use crate::interrupt::typelevel::Interrupt; use crate::time::Hertz; use crate::{interrupt, peripherals}; /// I2C error. #[derive(Debug, PartialEq, Eq)] #[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, } /// I2C config #[non_exhaustive] #[derive(Copy, Clone)] pub struct Config { /// Enable internal pullup on SDA. /// /// Using external pullup resistors is recommended for I2C. If you do /// have external pullups you should not enable this. pub sda_pullup: bool, /// Enable internal pullup on SCL. /// /// Using external pullup resistors is recommended for I2C. If you do /// have external pullups you should not enable this. pub scl_pullup: bool, /// Timeout. #[cfg(feature = "time")] pub timeout: embassy_time::Duration, } impl Default for Config { fn default() -> Self { Self { sda_pullup: false, scl_pullup: false, #[cfg(feature = "time")] timeout: embassy_time::Duration::from_millis(1000), } } } /// I2C driver. pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> { _peri: PeripheralRef<'d, T>, #[allow(dead_code)] tx_dma: PeripheralRef<'d, TXDMA>, #[allow(dead_code)] rx_dma: PeripheralRef<'d, RXDMA>, #[cfg(feature = "time")] timeout: Duration, } impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { /// Create a new I2C driver. pub fn new( peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, _irq: impl interrupt::typelevel::Binding> + interrupt::typelevel::Binding> + 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, freq: Hertz, config: Config, ) -> Self { into_ref!(peri, scl, sda, tx_dma, rx_dma); T::enable_and_reset(); scl.set_as_af_pull( scl.af_num(), AFType::OutputOpenDrain, match config.scl_pullup { true => Pull::Up, false => Pull::None, }, ); sda.set_as_af_pull( sda.af_num(), AFType::OutputOpenDrain, match config.sda_pullup { true => Pull::Up, false => Pull::None, }, ); unsafe { T::EventInterrupt::enable() }; unsafe { T::ErrorInterrupt::enable() }; let mut this = Self { _peri: peri, tx_dma, rx_dma, #[cfg(feature = "time")] timeout: config.timeout, }; this.init(freq, config); this } 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::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 } } pub(crate) mod sealed { use super::*; pub struct State { #[allow(unused)] pub waker: AtomicWaker, } impl State { pub const fn new() -> Self { Self { waker: AtomicWaker::new(), } } } pub trait Instance: crate::rcc::RccPeripheral { fn regs() -> crate::pac::i2c::I2c; fn state() -> &'static State; } } /// I2C peripheral instance pub trait Instance: sealed::Instance + 'static { /// Event interrupt for this instance type EventInterrupt: interrupt::typelevel::Interrupt; /// Error interrupt for this instance type ErrorInterrupt: interrupt::typelevel::Interrupt; } pin_trait!(SclPin, Instance); pin_trait!(SdaPin, Instance); 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) => { impl sealed::Instance for peripherals::$inst { fn regs() -> crate::pac::i2c::I2c { crate::pac::$inst } fn state() -> &'static sealed::State { static STATE: sealed::State = sealed::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, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { type Error = Error; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } } impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { type Error = Error; fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { self.blocking_write(address, write) } } impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> { 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, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for I2c<'d, T, TXDMA, RXDMA> { type Error = Error; } impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> { 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> { todo!(); } } impl<'d, T: Instance, TXDMA: TxDma, RXDMA: RxDma> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> { 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> { let _ = address; let _ = operations; todo!() } }