Merge pull request #2200 from barnabywalters/asynci2cv1
Implemented async I2C for v1
This commit is contained in:
		
						commit
						f5c9e3baa6
					
				@ -58,7 +58,7 @@ rand_core = "0.6.3"
 | 
			
		||||
sdio-host = "0.5.0"
 | 
			
		||||
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
 | 
			
		||||
critical-section = "1.1"
 | 
			
		||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b" }
 | 
			
		||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f6d1ffc1a25f208b5cd6b1024bff246592da1949" }
 | 
			
		||||
vcell = "0.1.3"
 | 
			
		||||
bxcan = "0.7.0"
 | 
			
		||||
nb = "1.0.0"
 | 
			
		||||
@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
 | 
			
		||||
[build-dependencies]
 | 
			
		||||
proc-macro2 = "1.0.36"
 | 
			
		||||
quote = "1.0.15"
 | 
			
		||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b", default-features = false, features = ["metadata"]}
 | 
			
		||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f6d1ffc1a25f208b5cd6b1024bff246592da1949", default-features = false, features = ["metadata"]}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[features]
 | 
			
		||||
 | 
			
		||||
@ -1138,6 +1138,23 @@ fn main() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ========
 | 
			
		||||
    // Write peripheral_interrupts module.
 | 
			
		||||
    let mut mt = TokenStream::new();
 | 
			
		||||
    for p in METADATA.peripherals {
 | 
			
		||||
        let mut pt = TokenStream::new();
 | 
			
		||||
 | 
			
		||||
        for irq in p.interrupts {
 | 
			
		||||
            let iname = format_ident!("{}", irq.interrupt);
 | 
			
		||||
            let sname = format_ident!("{}", irq.signal);
 | 
			
		||||
            pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let pname = format_ident!("{}", p.name);
 | 
			
		||||
        mt.extend(quote!(pub mod #pname { #pt }));
 | 
			
		||||
    }
 | 
			
		||||
    g.extend(quote!(#[allow(non_camel_case_types)] pub mod peripheral_interrupts { #mt }));
 | 
			
		||||
 | 
			
		||||
    // ========
 | 
			
		||||
    // Write foreach_foo! macrotables
 | 
			
		||||
 | 
			
		||||
@ -1296,6 +1313,9 @@ fn main() {
 | 
			
		||||
 | 
			
		||||
    let mut m = String::new();
 | 
			
		||||
 | 
			
		||||
    // DO NOT ADD more macros like these.
 | 
			
		||||
    // These turned to be a bad idea!
 | 
			
		||||
    // Instead, make build.rs generate the final code.
 | 
			
		||||
    make_table(&mut m, "foreach_flash_region", &flash_regions_table);
 | 
			
		||||
    make_table(&mut m, "foreach_interrupt", &interrupts_table);
 | 
			
		||||
    make_table(&mut m, "foreach_peripheral", &peripherals_table);
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,14 @@
 | 
			
		||||
#![macro_use]
 | 
			
		||||
 | 
			
		||||
use core::marker::PhantomData;
 | 
			
		||||
 | 
			
		||||
use crate::interrupt;
 | 
			
		||||
 | 
			
		||||
#[cfg_attr(i2c_v1, path = "v1.rs")]
 | 
			
		||||
#[cfg_attr(i2c_v2, path = "v2.rs")]
 | 
			
		||||
mod _version;
 | 
			
		||||
pub use _version::*;
 | 
			
		||||
use embassy_sync::waitqueue::AtomicWaker;
 | 
			
		||||
 | 
			
		||||
use crate::peripherals;
 | 
			
		||||
 | 
			
		||||
@ -23,6 +26,20 @@ pub enum Error {
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
@ -30,7 +47,8 @@ pub(crate) mod sealed {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait Instance: sealed::Instance + 'static {
 | 
			
		||||
    type Interrupt: interrupt::typelevel::Interrupt;
 | 
			
		||||
    type EventInterrupt: interrupt::typelevel::Interrupt;
 | 
			
		||||
    type ErrorInterrupt: interrupt::typelevel::Interrupt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pin_trait!(SclPin, Instance);
 | 
			
		||||
@ -38,21 +56,148 @@ pin_trait!(SdaPin, Instance);
 | 
			
		||||
dma_trait!(RxDma, Instance);
 | 
			
		||||
dma_trait!(TxDma, Instance);
 | 
			
		||||
 | 
			
		||||
foreach_interrupt!(
 | 
			
		||||
    ($inst:ident, i2c, $block:ident, EV, $irq:ident) => {
 | 
			
		||||
/// 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>()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 State {
 | 
			
		||||
                static STATE: State = State::new();
 | 
			
		||||
            fn state() -> &'static sealed::State {
 | 
			
		||||
                static STATE: sealed::State = sealed::State::new();
 | 
			
		||||
                &STATE
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl Instance for peripherals::$inst {
 | 
			
		||||
            type Interrupt = crate::interrupt::typelevel::$irq;
 | 
			
		||||
            type EventInterrupt = crate::_generated::peripheral_interrupts::$inst::EV;
 | 
			
		||||
            type ErrorInterrupt = crate::_generated::peripheral_interrupts::$inst::ER;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
mod eh02 {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "unstable-traits")]
 | 
			
		||||
mod eh1 {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::dma::NoDma;
 | 
			
		||||
 | 
			
		||||
    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!();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
 | 
			
		||||
mod eha {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    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!()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,23 +1,33 @@
 | 
			
		||||
use core::future::poll_fn;
 | 
			
		||||
use core::marker::PhantomData;
 | 
			
		||||
use core::task::Poll;
 | 
			
		||||
 | 
			
		||||
use embassy_embedded_hal::SetConfig;
 | 
			
		||||
use embassy_futures::select::{select, Either};
 | 
			
		||||
use embassy_hal_internal::drop::OnDrop;
 | 
			
		||||
use embassy_hal_internal::{into_ref, PeripheralRef};
 | 
			
		||||
 | 
			
		||||
use crate::dma::NoDma;
 | 
			
		||||
use super::*;
 | 
			
		||||
use crate::dma::{NoDma, Transfer};
 | 
			
		||||
use crate::gpio::sealed::AFType;
 | 
			
		||||
use crate::gpio::Pull;
 | 
			
		||||
use crate::i2c::{Error, Instance, SclPin, SdaPin};
 | 
			
		||||
use crate::interrupt::typelevel::Interrupt;
 | 
			
		||||
use crate::pac::i2c;
 | 
			
		||||
use crate::time::Hertz;
 | 
			
		||||
use crate::{interrupt, Peripheral};
 | 
			
		||||
 | 
			
		||||
/// Interrupt handler.
 | 
			
		||||
pub struct InterruptHandler<T: Instance> {
 | 
			
		||||
    _phantom: PhantomData<T>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
 | 
			
		||||
    unsafe fn on_interrupt() {}
 | 
			
		||||
pub unsafe fn on_interrupt<T: Instance>() {
 | 
			
		||||
    let regs = T::regs();
 | 
			
		||||
    // 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);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[non_exhaustive]
 | 
			
		||||
@ -27,14 +37,6 @@ pub struct Config {
 | 
			
		||||
    pub scl_pullup: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct State {}
 | 
			
		||||
 | 
			
		||||
impl State {
 | 
			
		||||
    pub(crate) const fn new() -> Self {
 | 
			
		||||
        Self {}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
 | 
			
		||||
    phantom: PhantomData<&'d mut T>,
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
@ -48,7 +50,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
        _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::Interrupt, InterruptHandler<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,
 | 
			
		||||
@ -98,6 +102,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
            reg.set_pe(true);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        unsafe { T::EventInterrupt::enable() };
 | 
			
		||||
        unsafe { T::ErrorInterrupt::enable() };
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            phantom: PhantomData,
 | 
			
		||||
            tx_dma,
 | 
			
		||||
@ -105,40 +112,58 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn check_and_clear_error_flags(&self) -> Result<i2c::regs::Sr1, Error> {
 | 
			
		||||
    fn check_and_clear_error_flags() -> Result<i2c::regs::Sr1, Error> {
 | 
			
		||||
        // 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 = T::regs().sr1().read();
 | 
			
		||||
 | 
			
		||||
        if sr1.timeout() {
 | 
			
		||||
            T::regs().sr1().modify(|reg| reg.set_timeout(false));
 | 
			
		||||
            T::regs().sr1().write(|reg| {
 | 
			
		||||
                reg.0 = !0;
 | 
			
		||||
                reg.set_timeout(false);
 | 
			
		||||
            });
 | 
			
		||||
            return Err(Error::Timeout);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if sr1.pecerr() {
 | 
			
		||||
            T::regs().sr1().modify(|reg| reg.set_pecerr(false));
 | 
			
		||||
            T::regs().sr1().write(|reg| {
 | 
			
		||||
                reg.0 = !0;
 | 
			
		||||
                reg.set_pecerr(false);
 | 
			
		||||
            });
 | 
			
		||||
            return Err(Error::Crc);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if sr1.ovr() {
 | 
			
		||||
            T::regs().sr1().modify(|reg| reg.set_ovr(false));
 | 
			
		||||
            T::regs().sr1().write(|reg| {
 | 
			
		||||
                reg.0 = !0;
 | 
			
		||||
                reg.set_ovr(false);
 | 
			
		||||
            });
 | 
			
		||||
            return Err(Error::Overrun);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if sr1.af() {
 | 
			
		||||
            T::regs().sr1().modify(|reg| reg.set_af(false));
 | 
			
		||||
            T::regs().sr1().write(|reg| {
 | 
			
		||||
                reg.0 = !0;
 | 
			
		||||
                reg.set_af(false);
 | 
			
		||||
            });
 | 
			
		||||
            return Err(Error::Nack);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if sr1.arlo() {
 | 
			
		||||
            T::regs().sr1().modify(|reg| reg.set_arlo(false));
 | 
			
		||||
            T::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() {
 | 
			
		||||
            T::regs().sr1().modify(|reg| reg.set_berr(false));
 | 
			
		||||
            T::regs().sr1().write(|reg| {
 | 
			
		||||
                reg.0 = !0;
 | 
			
		||||
                reg.set_berr(false);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(sr1)
 | 
			
		||||
@ -157,13 +182,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Wait until START condition was generated
 | 
			
		||||
        while !self.check_and_clear_error_flags()?.start() {
 | 
			
		||||
        while !Self::check_and_clear_error_flags()?.start() {
 | 
			
		||||
            check_timeout()?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Also wait until signalled we're master and everything is waiting for us
 | 
			
		||||
        while {
 | 
			
		||||
            self.check_and_clear_error_flags()?;
 | 
			
		||||
            Self::check_and_clear_error_flags()?;
 | 
			
		||||
 | 
			
		||||
            let sr2 = T::regs().sr2().read();
 | 
			
		||||
            !sr2.msl() && !sr2.busy()
 | 
			
		||||
@ -177,7 +202,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
        // 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()?.addr() {
 | 
			
		||||
        while !Self::check_and_clear_error_flags()?.addr() {
 | 
			
		||||
            check_timeout()?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -197,7 +222,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
        // 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()?.txe()
 | 
			
		||||
            !Self::check_and_clear_error_flags()?.txe()
 | 
			
		||||
        } {
 | 
			
		||||
            check_timeout()?;
 | 
			
		||||
        }
 | 
			
		||||
@ -208,7 +233,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
        // Wait until byte is transferred
 | 
			
		||||
        while {
 | 
			
		||||
            // Check for any potential error conditions.
 | 
			
		||||
            !self.check_and_clear_error_flags()?.btf()
 | 
			
		||||
            !Self::check_and_clear_error_flags()?.btf()
 | 
			
		||||
        } {
 | 
			
		||||
            check_timeout()?;
 | 
			
		||||
        }
 | 
			
		||||
@ -219,7 +244,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
    fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> {
 | 
			
		||||
        while {
 | 
			
		||||
            // Check for any potential error conditions.
 | 
			
		||||
            self.check_and_clear_error_flags()?;
 | 
			
		||||
            Self::check_and_clear_error_flags()?;
 | 
			
		||||
 | 
			
		||||
            !T::regs().sr1().read().rxne()
 | 
			
		||||
        } {
 | 
			
		||||
@ -244,7 +269,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // Wait until START condition was generated
 | 
			
		||||
            while !self.check_and_clear_error_flags()?.start() {
 | 
			
		||||
            while !Self::check_and_clear_error_flags()?.start() {
 | 
			
		||||
                check_timeout()?;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -261,7 +286,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
 | 
			
		||||
            // Wait until address was sent
 | 
			
		||||
            // Wait for the address to be acknowledged
 | 
			
		||||
            while !self.check_and_clear_error_flags()?.addr() {
 | 
			
		||||
            while !Self::check_and_clear_error_flags()?.addr() {
 | 
			
		||||
                check_timeout()?;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -336,6 +361,356 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
    pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
 | 
			
		||||
        self.blocking_write_read_timeout(addr, write, read, || Ok(()))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Async
 | 
			
		||||
 | 
			
		||||
    #[inline] // pretty sure this should always be inlined
 | 
			
		||||
    fn enable_interrupts() -> () {
 | 
			
		||||
        T::regs().cr2().modify(|w| {
 | 
			
		||||
            w.set_iterren(true);
 | 
			
		||||
            w.set_itevten(true);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn write_with_stop(&mut self, address: u8, write: &[u8], send_stop: bool) -> Result<(), Error>
 | 
			
		||||
    where
 | 
			
		||||
        TXDMA: crate::i2c::TxDma<T>,
 | 
			
		||||
    {
 | 
			
		||||
        let dma_transfer = unsafe {
 | 
			
		||||
            let regs = T::regs();
 | 
			
		||||
            regs.cr2().modify(|w| {
 | 
			
		||||
                // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register.
 | 
			
		||||
                w.set_dmaen(true);
 | 
			
		||||
                w.set_itbufen(false);
 | 
			
		||||
            });
 | 
			
		||||
            // 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 = regs.dr().as_ptr() as *mut u8;
 | 
			
		||||
 | 
			
		||||
            let ch = &mut self.tx_dma;
 | 
			
		||||
            let request = ch.request();
 | 
			
		||||
            Transfer::new_write(ch, request, write, dst, Default::default())
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let on_drop = OnDrop::new(|| {
 | 
			
		||||
            let regs = T::regs();
 | 
			
		||||
            regs.cr2().modify(|w| {
 | 
			
		||||
                w.set_dmaen(false);
 | 
			
		||||
                w.set_iterren(false);
 | 
			
		||||
                w.set_itevten(false);
 | 
			
		||||
            })
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        Self::enable_interrupts();
 | 
			
		||||
 | 
			
		||||
        // Send a START condition
 | 
			
		||||
        T::regs().cr1().modify(|reg| {
 | 
			
		||||
            reg.set_start(true);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let state = T::state();
 | 
			
		||||
 | 
			
		||||
        // Wait until START condition was generated
 | 
			
		||||
        poll_fn(|cx| {
 | 
			
		||||
            state.waker.register(cx.waker());
 | 
			
		||||
 | 
			
		||||
            match Self::check_and_clear_error_flags() {
 | 
			
		||||
                Err(e) => Poll::Ready(Err(e)),
 | 
			
		||||
                Ok(sr1) => {
 | 
			
		||||
                    if sr1.start() {
 | 
			
		||||
                        Poll::Ready(Ok(()))
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Poll::Pending
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
        // Also wait until signalled we're master and everything is waiting for us
 | 
			
		||||
        Self::enable_interrupts();
 | 
			
		||||
        poll_fn(|cx| {
 | 
			
		||||
            state.waker.register(cx.waker());
 | 
			
		||||
 | 
			
		||||
            match Self::check_and_clear_error_flags() {
 | 
			
		||||
                Err(e) => Poll::Ready(Err(e)),
 | 
			
		||||
                Ok(_) => {
 | 
			
		||||
                    let sr2 = T::regs().sr2().read();
 | 
			
		||||
                    if !sr2.msl() && !sr2.busy() {
 | 
			
		||||
                        Poll::Pending
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Poll::Ready(Ok(()))
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
        // Set up current address, we're trying to talk to
 | 
			
		||||
        Self::enable_interrupts();
 | 
			
		||||
        T::regs().dr().write(|reg| reg.set_dr(address << 1));
 | 
			
		||||
 | 
			
		||||
        poll_fn(|cx| {
 | 
			
		||||
            state.waker.register(cx.waker());
 | 
			
		||||
            match Self::check_and_clear_error_flags() {
 | 
			
		||||
                Err(e) => Poll::Ready(Err(e)),
 | 
			
		||||
                Ok(sr1) => {
 | 
			
		||||
                    if sr1.addr() {
 | 
			
		||||
                        // Clear the ADDR condition by reading SR2.
 | 
			
		||||
                        T::regs().sr2().read();
 | 
			
		||||
                        Poll::Ready(Ok(()))
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Poll::Pending
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .await?;
 | 
			
		||||
        Self::enable_interrupts();
 | 
			
		||||
        let poll_error = poll_fn(|cx| {
 | 
			
		||||
            state.waker.register(cx.waker());
 | 
			
		||||
 | 
			
		||||
            match Self::check_and_clear_error_flags() {
 | 
			
		||||
                // Unclear why the Err turbofish is necessary here? The compiler didn’t require it in the other
 | 
			
		||||
                // identical poll_fn check_and_clear matches.
 | 
			
		||||
                Err(e) => Poll::Ready(Err::<T, Error>(e)),
 | 
			
		||||
                Ok(_) => 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(()),
 | 
			
		||||
        }?;
 | 
			
		||||
 | 
			
		||||
        // 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.”
 | 
			
		||||
 | 
			
		||||
        // TODO: If this has to be done “in the interrupt routine after the EOT interrupt”, where to put it?
 | 
			
		||||
        T::regs().cr2().modify(|w| {
 | 
			
		||||
            w.set_dmaen(false);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        Self::enable_interrupts();
 | 
			
		||||
        poll_fn(|cx| {
 | 
			
		||||
            state.waker.register(cx.waker());
 | 
			
		||||
 | 
			
		||||
            match Self::check_and_clear_error_flags() {
 | 
			
		||||
                Err(e) => Poll::Ready(Err(e)),
 | 
			
		||||
                Ok(sr1) => {
 | 
			
		||||
                    if sr1.btf() {
 | 
			
		||||
                        if send_stop {
 | 
			
		||||
                            T::regs().cr1().modify(|w| {
 | 
			
		||||
                                w.set_stop(true);
 | 
			
		||||
                            });
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        Poll::Ready(Ok(()))
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Poll::Pending
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
        drop(on_drop);
 | 
			
		||||
 | 
			
		||||
        // Fallthrough is success
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error>
 | 
			
		||||
    where
 | 
			
		||||
        TXDMA: crate::i2c::TxDma<T>,
 | 
			
		||||
    {
 | 
			
		||||
        self.write_with_stop(address, write, true).await?;
 | 
			
		||||
 | 
			
		||||
        // Wait for STOP condition to transmit.
 | 
			
		||||
        Self::enable_interrupts();
 | 
			
		||||
        poll_fn(|cx| {
 | 
			
		||||
            T::state().waker.register(cx.waker());
 | 
			
		||||
            // TODO: error interrupts are enabled here, should we additional check for and return errors?
 | 
			
		||||
            if T::regs().cr1().read().stop() {
 | 
			
		||||
                Poll::Pending
 | 
			
		||||
            } else {
 | 
			
		||||
                Poll::Ready(Ok(()))
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error>
 | 
			
		||||
    where
 | 
			
		||||
        RXDMA: crate::i2c::RxDma<T>,
 | 
			
		||||
    {
 | 
			
		||||
        let state = T::state();
 | 
			
		||||
        let buffer_len = buffer.len();
 | 
			
		||||
 | 
			
		||||
        let dma_transfer = unsafe {
 | 
			
		||||
            let regs = T::regs();
 | 
			
		||||
            regs.cr2().modify(|w| {
 | 
			
		||||
                // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register.
 | 
			
		||||
                w.set_itbufen(false);
 | 
			
		||||
                w.set_dmaen(true);
 | 
			
		||||
            });
 | 
			
		||||
            // 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 src = regs.dr().as_ptr() as *mut u8;
 | 
			
		||||
 | 
			
		||||
            let ch = &mut self.rx_dma;
 | 
			
		||||
            let request = ch.request();
 | 
			
		||||
            Transfer::new_read(ch, request, src, buffer, Default::default())
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let on_drop = OnDrop::new(|| {
 | 
			
		||||
            let regs = T::regs();
 | 
			
		||||
            regs.cr2().modify(|w| {
 | 
			
		||||
                w.set_dmaen(false);
 | 
			
		||||
                w.set_iterren(false);
 | 
			
		||||
                w.set_itevten(false);
 | 
			
		||||
            })
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        Self::enable_interrupts();
 | 
			
		||||
 | 
			
		||||
        // Send a START condition and set ACK bit
 | 
			
		||||
        T::regs().cr1().modify(|reg| {
 | 
			
		||||
            reg.set_start(true);
 | 
			
		||||
            reg.set_ack(true);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Wait until START condition was generated
 | 
			
		||||
        poll_fn(|cx| {
 | 
			
		||||
            state.waker.register(cx.waker());
 | 
			
		||||
 | 
			
		||||
            match Self::check_and_clear_error_flags() {
 | 
			
		||||
                Err(e) => Poll::Ready(Err(e)),
 | 
			
		||||
                Ok(sr1) => {
 | 
			
		||||
                    if sr1.start() {
 | 
			
		||||
                        Poll::Ready(Ok(()))
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Poll::Pending
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
        // Also wait until signalled we're master and everything is waiting for us
 | 
			
		||||
        Self::enable_interrupts();
 | 
			
		||||
        poll_fn(|cx| {
 | 
			
		||||
            state.waker.register(cx.waker());
 | 
			
		||||
 | 
			
		||||
            // blocking read didn’t have a check_and_clear call here, but blocking write did so
 | 
			
		||||
            // I’m adding it here in case that was an oversight.
 | 
			
		||||
            match Self::check_and_clear_error_flags() {
 | 
			
		||||
                Err(e) => Poll::Ready(Err(e)),
 | 
			
		||||
                Ok(_) => {
 | 
			
		||||
                    let sr2 = T::regs().sr2().read();
 | 
			
		||||
                    if !sr2.msl() && !sr2.busy() {
 | 
			
		||||
                        Poll::Pending
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Poll::Ready(Ok(()))
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
        // Set up current address, we're trying to talk to
 | 
			
		||||
        T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1));
 | 
			
		||||
 | 
			
		||||
        // Wait for the address to be acknowledged
 | 
			
		||||
 | 
			
		||||
        Self::enable_interrupts();
 | 
			
		||||
        poll_fn(|cx| {
 | 
			
		||||
            state.waker.register(cx.waker());
 | 
			
		||||
 | 
			
		||||
            match Self::check_and_clear_error_flags() {
 | 
			
		||||
                Err(e) => Poll::Ready(Err(e)),
 | 
			
		||||
                Ok(sr1) => {
 | 
			
		||||
                    if sr1.addr() {
 | 
			
		||||
                        // 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 buffer_len == 1 {
 | 
			
		||||
                            T::regs().cr1().modify(|w| {
 | 
			
		||||
                                w.set_ack(false);
 | 
			
		||||
                            });
 | 
			
		||||
                        }
 | 
			
		||||
                        Poll::Ready(Ok(()))
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Poll::Pending
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
        // Clear ADDR condition by reading SR2
 | 
			
		||||
        T::regs().sr2().read();
 | 
			
		||||
 | 
			
		||||
        // 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 buffer_len == 1 {
 | 
			
		||||
            T::regs().cr1().modify(|w| {
 | 
			
		||||
                w.set_stop(true);
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            // 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.
 | 
			
		||||
            T::regs().cr2().modify(|w| {
 | 
			
		||||
                w.set_last(true);
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Wait for bytes to be received, or an error to occur.
 | 
			
		||||
        Self::enable_interrupts();
 | 
			
		||||
        let poll_error = poll_fn(|cx| {
 | 
			
		||||
            state.waker.register(cx.waker());
 | 
			
		||||
 | 
			
		||||
            match Self::check_and_clear_error_flags() {
 | 
			
		||||
                Err(e) => Poll::Ready(Err::<T, Error>(e)),
 | 
			
		||||
                _ => Poll::Pending,
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        match select(dma_transfer, poll_error).await {
 | 
			
		||||
            Either::Second(Err(e)) => Err(e),
 | 
			
		||||
            _ => Ok(()),
 | 
			
		||||
        }?;
 | 
			
		||||
 | 
			
		||||
        // Wait for the STOP to be sent (STOP bit cleared).
 | 
			
		||||
        Self::enable_interrupts();
 | 
			
		||||
        poll_fn(|cx| {
 | 
			
		||||
            state.waker.register(cx.waker());
 | 
			
		||||
            // TODO: error interrupts are enabled here, should we additional check for and return errors?
 | 
			
		||||
            if T::regs().cr1().read().stop() {
 | 
			
		||||
                Poll::Pending
 | 
			
		||||
            } else {
 | 
			
		||||
                Poll::Ready(Ok(()))
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .await?;
 | 
			
		||||
        drop(on_drop);
 | 
			
		||||
 | 
			
		||||
        // Fallthrough is success
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error>
 | 
			
		||||
    where
 | 
			
		||||
        RXDMA: crate::i2c::RxDma<T>,
 | 
			
		||||
        TXDMA: crate::i2c::TxDma<T>,
 | 
			
		||||
    {
 | 
			
		||||
        self.write_with_stop(address, write, false).await?;
 | 
			
		||||
        self.read(address, read).await
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
@ -344,77 +719,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {
 | 
			
		||||
    type Error = Error;
 | 
			
		||||
 | 
			
		||||
    fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> {
 | 
			
		||||
        self.blocking_read(addr, read)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> {
 | 
			
		||||
    type Error = Error;
 | 
			
		||||
 | 
			
		||||
    fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> {
 | 
			
		||||
        self.blocking_write(addr, write)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> {
 | 
			
		||||
    type Error = Error;
 | 
			
		||||
 | 
			
		||||
    fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
 | 
			
		||||
        self.blocking_write_read(addr, write, read)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "unstable-traits")]
 | 
			
		||||
mod eh1 {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    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> embedded_hal_1::i2c::ErrorType for I2c<'d, T> {
 | 
			
		||||
        type Error = Error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T> {
 | 
			
		||||
        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!();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum Mode {
 | 
			
		||||
    Fast,
 | 
			
		||||
    Standard,
 | 
			
		||||
 | 
			
		||||
@ -1,19 +1,17 @@
 | 
			
		||||
use core::cmp;
 | 
			
		||||
use core::future::poll_fn;
 | 
			
		||||
use core::marker::PhantomData;
 | 
			
		||||
use core::task::Poll;
 | 
			
		||||
 | 
			
		||||
use embassy_embedded_hal::SetConfig;
 | 
			
		||||
use embassy_hal_internal::drop::OnDrop;
 | 
			
		||||
use embassy_hal_internal::{into_ref, PeripheralRef};
 | 
			
		||||
use embassy_sync::waitqueue::AtomicWaker;
 | 
			
		||||
#[cfg(feature = "time")]
 | 
			
		||||
use embassy_time::{Duration, Instant};
 | 
			
		||||
 | 
			
		||||
use super::*;
 | 
			
		||||
use crate::dma::{NoDma, Transfer};
 | 
			
		||||
use crate::gpio::sealed::AFType;
 | 
			
		||||
use crate::gpio::Pull;
 | 
			
		||||
use crate::i2c::{Error, Instance, SclPin, SdaPin};
 | 
			
		||||
use crate::interrupt::typelevel::Interrupt;
 | 
			
		||||
use crate::pac::i2c;
 | 
			
		||||
use crate::time::Hertz;
 | 
			
		||||
@ -36,13 +34,7 @@ pub fn no_timeout_fn() -> impl Fn() -> Result<(), Error> {
 | 
			
		||||
    move || Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Interrupt handler.
 | 
			
		||||
pub struct InterruptHandler<T: Instance> {
 | 
			
		||||
    _phantom: PhantomData<T>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
 | 
			
		||||
    unsafe fn on_interrupt() {
 | 
			
		||||
pub unsafe fn on_interrupt<T: Instance>() {
 | 
			
		||||
    let regs = T::regs();
 | 
			
		||||
    let isr = regs.isr().read();
 | 
			
		||||
 | 
			
		||||
@ -55,7 +47,6 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
 | 
			
		||||
        regs.cr1().modify(|w| w.set_tcie(false));
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[non_exhaustive]
 | 
			
		||||
#[derive(Copy, Clone)]
 | 
			
		||||
@ -77,18 +68,6 @@ impl Default for Config {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct State {
 | 
			
		||||
    waker: AtomicWaker,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl State {
 | 
			
		||||
    pub(crate) const fn new() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            waker: AtomicWaker::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
 | 
			
		||||
    _peri: PeripheralRef<'d, T>,
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
@ -104,7 +83,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
        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::Interrupt, InterruptHandler<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,
 | 
			
		||||
@ -150,8 +131,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
            reg.set_pe(true);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        T::Interrupt::unpend();
 | 
			
		||||
        unsafe { T::Interrupt::enable() };
 | 
			
		||||
        unsafe { T::EventInterrupt::enable() };
 | 
			
		||||
        unsafe { T::ErrorInterrupt::enable() };
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            _peri: peri,
 | 
			
		||||
@ -987,35 +968,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "time")]
 | 
			
		||||
mod eh02 {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// I2C Stop Configuration
 | 
			
		||||
///
 | 
			
		||||
/// Peripheral options for generating the STOP condition
 | 
			
		||||
@ -1140,83 +1092,6 @@ impl Timings {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "unstable-traits")]
 | 
			
		||||
mod eh1 {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    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!();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
 | 
			
		||||
mod eha {
 | 
			
		||||
    use super::super::{RxDma, TxDma};
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    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!()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, T: Instance> SetConfig for I2c<'d, T> {
 | 
			
		||||
    type Config = Hertz;
 | 
			
		||||
    type ConfigError = ();
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,8 @@ const ADDRESS: u8 = 0x5F;
 | 
			
		||||
const WHOAMI: u8 = 0x0F;
 | 
			
		||||
 | 
			
		||||
bind_interrupts!(struct Irqs {
 | 
			
		||||
    I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
 | 
			
		||||
    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
 | 
			
		||||
    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::main]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										62
									
								
								examples/stm32f4/src/bin/i2c_async.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								examples/stm32f4/src/bin/i2c_async.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,62 @@
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![no_main]
 | 
			
		||||
#![feature(type_alias_impl_trait)]
 | 
			
		||||
 | 
			
		||||
// Example originally designed for stm32f411ceu6 reading an A1454 hall effect sensor on I2C1
 | 
			
		||||
// DMA peripherals changed to compile for stm32f429zi, for the CI.
 | 
			
		||||
 | 
			
		||||
use defmt::*;
 | 
			
		||||
use embassy_executor::Spawner;
 | 
			
		||||
use embassy_stm32::i2c::I2c;
 | 
			
		||||
use embassy_stm32::time::Hertz;
 | 
			
		||||
use embassy_stm32::{bind_interrupts, i2c, peripherals};
 | 
			
		||||
use {defmt_rtt as _, panic_probe as _};
 | 
			
		||||
 | 
			
		||||
const ADDRESS: u8 = 96;
 | 
			
		||||
 | 
			
		||||
bind_interrupts!(struct Irqs {
 | 
			
		||||
    I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
 | 
			
		||||
    I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::main]
 | 
			
		||||
async fn main(_spawner: Spawner) {
 | 
			
		||||
    info!("Hello world!");
 | 
			
		||||
    let p = embassy_stm32::init(Default::default());
 | 
			
		||||
 | 
			
		||||
    let mut i2c = I2c::new(
 | 
			
		||||
        p.I2C1,
 | 
			
		||||
        p.PB8,
 | 
			
		||||
        p.PB7,
 | 
			
		||||
        Irqs,
 | 
			
		||||
        p.DMA1_CH6,
 | 
			
		||||
        p.DMA1_CH0,
 | 
			
		||||
        Hertz(100_000),
 | 
			
		||||
        Default::default(),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    loop {
 | 
			
		||||
        let a1454_read_sensor_command = [0x1F];
 | 
			
		||||
        let mut sensor_data_buffer: [u8; 4] = [0, 0, 0, 0];
 | 
			
		||||
 | 
			
		||||
        match i2c
 | 
			
		||||
            .write_read(ADDRESS, &a1454_read_sensor_command, &mut sensor_data_buffer)
 | 
			
		||||
            .await
 | 
			
		||||
        {
 | 
			
		||||
            Ok(()) => {
 | 
			
		||||
                // Convert 12-bit signed integer into 16-bit signed integer.
 | 
			
		||||
                // Is the 12 bit number negative?
 | 
			
		||||
                if (sensor_data_buffer[2] & 0b00001000) == 0b0001000 {
 | 
			
		||||
                    sensor_data_buffer[2] = sensor_data_buffer[2] | 0b11110000;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let mut sensor_value_raw: u16 = sensor_data_buffer[3].into();
 | 
			
		||||
                sensor_value_raw |= (sensor_data_buffer[2] as u16) << 8;
 | 
			
		||||
                let sensor_value: u16 = sensor_value_raw.into();
 | 
			
		||||
                let sensor_value = sensor_value as i16;
 | 
			
		||||
                info!("Data: {}", sensor_value);
 | 
			
		||||
            }
 | 
			
		||||
            Err(e) => error!("I2C Error during read: {:?}", e),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										135
									
								
								examples/stm32f4/src/bin/i2c_comparison.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								examples/stm32f4/src/bin/i2c_comparison.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,135 @@
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![no_main]
 | 
			
		||||
#![feature(type_alias_impl_trait)]
 | 
			
		||||
 | 
			
		||||
// Example originally designed for stm32f411ceu6 with three A1454 hall effect sensors, connected to I2C1, 2 and 3
 | 
			
		||||
// on the pins referenced in the peripheral definitions.
 | 
			
		||||
// Pins and DMA peripherals changed to compile for stm32f429zi, to work with the CI.
 | 
			
		||||
// MUST be compiled in release mode to see actual performance, otherwise the async transactions take 2x
 | 
			
		||||
// as long to complete as the blocking ones!
 | 
			
		||||
 | 
			
		||||
use defmt::*;
 | 
			
		||||
use embassy_executor::Spawner;
 | 
			
		||||
use embassy_stm32::i2c::I2c;
 | 
			
		||||
use embassy_stm32::time::Hertz;
 | 
			
		||||
use embassy_stm32::{bind_interrupts, i2c, peripherals};
 | 
			
		||||
use embassy_time::Instant;
 | 
			
		||||
use futures::future::try_join3;
 | 
			
		||||
use {defmt_rtt as _, panic_probe as _};
 | 
			
		||||
 | 
			
		||||
const ADDRESS: u8 = 96;
 | 
			
		||||
 | 
			
		||||
bind_interrupts!(struct Irqs {
 | 
			
		||||
    I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
 | 
			
		||||
    I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
 | 
			
		||||
    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
 | 
			
		||||
    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
 | 
			
		||||
    I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>;
 | 
			
		||||
    I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
/// Convert 12-bit signed integer within a 4 byte long buffer into 16-bit signed integer.
 | 
			
		||||
fn a1454_buf_to_i16(buffer: &[u8; 4]) -> i16 {
 | 
			
		||||
    let lower = buffer[3];
 | 
			
		||||
    let mut upper = buffer[2];
 | 
			
		||||
    // Fill in additional 1s if the 12 bit number is negative.
 | 
			
		||||
    if (upper & 0b00001000) == 0b0001000 {
 | 
			
		||||
        upper = upper | 0b11110000;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let mut sensor_value_raw: u16 = lower.into();
 | 
			
		||||
    sensor_value_raw |= (upper as u16) << 8;
 | 
			
		||||
    let sensor_value: u16 = sensor_value_raw.into();
 | 
			
		||||
    let sensor_value = sensor_value as i16;
 | 
			
		||||
    sensor_value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::main]
 | 
			
		||||
async fn main(_spawner: Spawner) {
 | 
			
		||||
    info!("Setting up peripherals.");
 | 
			
		||||
    let p = embassy_stm32::init(Default::default());
 | 
			
		||||
 | 
			
		||||
    let mut i2c1 = I2c::new(
 | 
			
		||||
        p.I2C1,
 | 
			
		||||
        p.PB8,
 | 
			
		||||
        p.PB7,
 | 
			
		||||
        Irqs,
 | 
			
		||||
        p.DMA1_CH6,
 | 
			
		||||
        p.DMA1_CH0,
 | 
			
		||||
        Hertz(100_000),
 | 
			
		||||
        Default::default(),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let mut i2c2 = I2c::new(
 | 
			
		||||
        p.I2C2,
 | 
			
		||||
        p.PB10,
 | 
			
		||||
        p.PB11,
 | 
			
		||||
        Irqs,
 | 
			
		||||
        p.DMA1_CH7,
 | 
			
		||||
        p.DMA1_CH3,
 | 
			
		||||
        Hertz(100_000),
 | 
			
		||||
        Default::default(),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let mut i2c3 = I2c::new(
 | 
			
		||||
        p.I2C3,
 | 
			
		||||
        p.PA8,
 | 
			
		||||
        p.PC9,
 | 
			
		||||
        Irqs,
 | 
			
		||||
        p.DMA1_CH4,
 | 
			
		||||
        p.DMA1_CH2,
 | 
			
		||||
        Hertz(100_000),
 | 
			
		||||
        Default::default(),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let a1454_read_sensor_command = [0x1F];
 | 
			
		||||
    let mut i2c1_buffer: [u8; 4] = [0, 0, 0, 0];
 | 
			
		||||
    let mut i2c2_buffer: [u8; 4] = [0, 0, 0, 0];
 | 
			
		||||
    let mut i2c3_buffer: [u8; 4] = [0, 0, 0, 0];
 | 
			
		||||
    loop {
 | 
			
		||||
        // Blocking reads one after the other. Completes in about 2000us.
 | 
			
		||||
        let blocking_read_start_us = Instant::now().as_micros();
 | 
			
		||||
        match i2c1.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer) {
 | 
			
		||||
            Ok(()) => {}
 | 
			
		||||
            Err(e) => error!("I2C Error: {:?}", e),
 | 
			
		||||
        }
 | 
			
		||||
        match i2c2.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer) {
 | 
			
		||||
            Ok(()) => {}
 | 
			
		||||
            Err(e) => error!("I2C Error: {:?}", e),
 | 
			
		||||
        }
 | 
			
		||||
        match i2c3.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer) {
 | 
			
		||||
            Ok(()) => {}
 | 
			
		||||
            Err(e) => error!("I2C Error: {:?}", e),
 | 
			
		||||
        }
 | 
			
		||||
        let blocking_read_total_us = Instant::now().as_micros() - blocking_read_start_us;
 | 
			
		||||
        info!(
 | 
			
		||||
            "Blocking reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}",
 | 
			
		||||
            blocking_read_total_us,
 | 
			
		||||
            a1454_buf_to_i16(&i2c1_buffer),
 | 
			
		||||
            a1454_buf_to_i16(&i2c2_buffer),
 | 
			
		||||
            a1454_buf_to_i16(&i2c3_buffer)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Async reads overlapping. Completes in about 1000us.
 | 
			
		||||
        let async_read_start_us = Instant::now().as_micros();
 | 
			
		||||
 | 
			
		||||
        let i2c1_result = i2c1.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer);
 | 
			
		||||
        let i2c2_result = i2c2.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer);
 | 
			
		||||
        let i2c3_result = i2c3.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer);
 | 
			
		||||
 | 
			
		||||
        // Wait for all three transactions to finish, or any one of them to fail.
 | 
			
		||||
        match try_join3(i2c1_result, i2c2_result, i2c3_result).await {
 | 
			
		||||
            Ok(_) => {
 | 
			
		||||
                let async_read_total_us = Instant::now().as_micros() - async_read_start_us;
 | 
			
		||||
                info!(
 | 
			
		||||
                    "Async reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}",
 | 
			
		||||
                    async_read_total_us,
 | 
			
		||||
                    a1454_buf_to_i16(&i2c1_buffer),
 | 
			
		||||
                    a1454_buf_to_i16(&i2c2_buffer),
 | 
			
		||||
                    a1454_buf_to_i16(&i2c3_buffer)
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            Err(e) => error!("I2C Error during async write-read: {}", e),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
 | 
			
		||||
const WHOAMI: u8 = 0x0F;
 | 
			
		||||
 | 
			
		||||
bind_interrupts!(struct Irqs {
 | 
			
		||||
    I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
 | 
			
		||||
    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
 | 
			
		||||
    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::main]
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,8 @@ const HEIGHT: usize = 100;
 | 
			
		||||
static mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2];
 | 
			
		||||
 | 
			
		||||
bind_interrupts!(struct Irqs {
 | 
			
		||||
    I2C1_EV => i2c::InterruptHandler<peripherals::I2C1>;
 | 
			
		||||
    I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
 | 
			
		||||
    I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
 | 
			
		||||
    DCMI => dcmi::InterruptHandler<peripherals::DCMI>;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
 | 
			
		||||
const WHOAMI: u8 = 0x0F;
 | 
			
		||||
 | 
			
		||||
bind_interrupts!(struct Irqs {
 | 
			
		||||
    I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
 | 
			
		||||
    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
 | 
			
		||||
    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::main]
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,8 @@ const ADDRESS: u8 = 0x5F;
 | 
			
		||||
const WHOAMI: u8 = 0x0F;
 | 
			
		||||
 | 
			
		||||
bind_interrupts!(struct Irqs {
 | 
			
		||||
    I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
 | 
			
		||||
    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
 | 
			
		||||
    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::main]
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,8 @@ const ADDRESS: u8 = 0x5F;
 | 
			
		||||
const WHOAMI: u8 = 0x0F;
 | 
			
		||||
 | 
			
		||||
bind_interrupts!(struct Irqs {
 | 
			
		||||
    I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
 | 
			
		||||
    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
 | 
			
		||||
    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::main]
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
 | 
			
		||||
const WHOAMI: u8 = 0x0F;
 | 
			
		||||
 | 
			
		||||
bind_interrupts!(struct Irqs {
 | 
			
		||||
    I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
 | 
			
		||||
    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
 | 
			
		||||
    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::main]
 | 
			
		||||
 | 
			
		||||
@ -40,7 +40,8 @@ use static_cell::make_static;
 | 
			
		||||
use {embassy_stm32 as hal, panic_probe as _};
 | 
			
		||||
 | 
			
		||||
bind_interrupts!(struct Irqs {
 | 
			
		||||
    I2C3_EV => i2c::InterruptHandler<peripherals::I2C3>;
 | 
			
		||||
    I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>;
 | 
			
		||||
    I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>;
 | 
			
		||||
    RNG => rng::InterruptHandler<peripherals::RNG>;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user