351 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			351 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! 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<P = T> + 'd,
 | |
|         scl: impl Peripheral<P = impl SclPin<T>> + 'd,
 | |
|         sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
 | |
|         _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>>
 | |
|             + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>>
 | |
|             + 'd,
 | |
|         tx_dma: impl Peripheral<P = TXDMA> + 'd,
 | |
|         rx_dma: impl Peripheral<P = RXDMA> + '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 {
 | |
|     #[cfg(not(feature = "time"))]
 | |
|     #[inline]
 | |
|     fn check(self) -> Result<(), Error> {
 | |
|         Ok(())
 | |
|     }
 | |
| 
 | |
|     #[cfg(feature = "time")]
 | |
|     #[inline]
 | |
|     fn check(self) -> Result<(), Error> {
 | |
|         if Instant::now() > self.deadline {
 | |
|             Err(Error::Timeout)
 | |
|         } else {
 | |
|             Ok(())
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #[cfg(not(feature = "time"))]
 | |
|     #[inline]
 | |
|     fn with<R>(self, fut: impl Future<Output = Result<R, Error>>) -> impl Future<Output = Result<R, Error>> {
 | |
|         fut
 | |
|     }
 | |
| 
 | |
|     #[cfg(feature = "time")]
 | |
|     #[inline]
 | |
|     fn with<R>(self, fut: impl Future<Output = Result<R, Error>>) -> impl Future<Output = Result<R, Error>> {
 | |
|         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,
 | |
|         })
 | |
|     }
 | |
| }
 | |
| 
 | |
| 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<T: Instance> {
 | |
|     _phantom: PhantomData<T>,
 | |
| }
 | |
| 
 | |
| impl<T: Instance> interrupt::typelevel::Handler<T::EventInterrupt> for EventInterruptHandler<T> {
 | |
|     unsafe fn on_interrupt() {
 | |
|         _version::on_interrupt::<T>()
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Error interrupt handler.
 | |
| pub struct ErrorInterruptHandler<T: Instance> {
 | |
|     _phantom: PhantomData<T>,
 | |
| }
 | |
| 
 | |
| impl<T: Instance> interrupt::typelevel::Handler<T::ErrorInterrupt> for ErrorInterruptHandler<T> {
 | |
|     unsafe fn on_interrupt() {
 | |
|         _version::on_interrupt::<T>()
 | |
|     }
 | |
| }
 | |
| 
 | |
| 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<T>, RXDMA: RxDma<T>> 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!()
 | |
|     }
 | |
| }
 |