diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 65434ceca..d04000a1d 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -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] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index a7dac5f9d..4aae58229 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -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); diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index dde1a5040..19346d707 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -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!() + } + } +} diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index ab59f5ab9..b62ee8246 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -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, diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 40bcaa9bc..8c20e1c54 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -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,25 +34,18 @@ pub fn no_timeout_fn() -> impl Fn() -> Result<(), Error> { move || Ok(()) } -/// Interrupt handler. -pub struct InterruptHandler<T: Instance> { - _phantom: PhantomData<T>, -} +pub unsafe fn on_interrupt<T: Instance>() { + let regs = T::regs(); + let isr = regs.isr().read(); -impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { - unsafe fn on_interrupt() { - let regs = T::regs(); - let isr = regs.isr().read(); - - if isr.tcr() || isr.tc() { - T::state().waker.wake(); - } - // The flag can only be cleared by writting to nbytes, we won't do that here, so disable - // the interrupt - critical_section::with(|_| { - regs.cr1().modify(|w| w.set_tcie(false)); - }); + if isr.tcr() || isr.tc() { + T::state().waker.wake(); } + // The flag can only be cleared by writting to nbytes, we won't do that here, so disable + // the interrupt + critical_section::with(|_| { + regs.cr1().modify(|w| w.set_tcie(false)); + }); } #[non_exhaustive] @@ -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 = (); diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs index 032bd97ee..4f4adde28 100644 --- a/examples/stm32f4/src/bin/i2c.rs +++ b/examples/stm32f4/src/bin/i2c.rs @@ -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] diff --git a/examples/stm32f4/src/bin/i2c_async.rs b/examples/stm32f4/src/bin/i2c_async.rs new file mode 100644 index 000000000..9f59e4d41 --- /dev/null +++ b/examples/stm32f4/src/bin/i2c_async.rs @@ -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), + } + } +} diff --git a/examples/stm32f4/src/bin/i2c_comparison.rs b/examples/stm32f4/src/bin/i2c_comparison.rs new file mode 100644 index 000000000..6d23c0ed8 --- /dev/null +++ b/examples/stm32f4/src/bin/i2c_comparison.rs @@ -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), + }; + } +} diff --git a/examples/stm32h5/src/bin/i2c.rs b/examples/stm32h5/src/bin/i2c.rs index 8b1662f39..31783a2bf 100644 --- a/examples/stm32h5/src/bin/i2c.rs +++ b/examples/stm32h5/src/bin/i2c.rs @@ -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] diff --git a/examples/stm32h7/src/bin/camera.rs b/examples/stm32h7/src/bin/camera.rs index 23ece1c38..489fb03dd 100644 --- a/examples/stm32h7/src/bin/camera.rs +++ b/examples/stm32h7/src/bin/camera.rs @@ -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>; }); diff --git a/examples/stm32h7/src/bin/i2c.rs b/examples/stm32h7/src/bin/i2c.rs index 9aa0ca08b..aea21ec6f 100644 --- a/examples/stm32h7/src/bin/i2c.rs +++ b/examples/stm32h7/src/bin/i2c.rs @@ -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] diff --git a/examples/stm32l4/src/bin/i2c.rs b/examples/stm32l4/src/bin/i2c.rs index d0060d20c..07dc12e8c 100644 --- a/examples/stm32l4/src/bin/i2c.rs +++ b/examples/stm32l4/src/bin/i2c.rs @@ -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] diff --git a/examples/stm32l4/src/bin/i2c_blocking_async.rs b/examples/stm32l4/src/bin/i2c_blocking_async.rs index eca59087b..60a4e2eb3 100644 --- a/examples/stm32l4/src/bin/i2c_blocking_async.rs +++ b/examples/stm32l4/src/bin/i2c_blocking_async.rs @@ -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] diff --git a/examples/stm32l4/src/bin/i2c_dma.rs b/examples/stm32l4/src/bin/i2c_dma.rs index cf6f3da67..4c2c224a6 100644 --- a/examples/stm32l4/src/bin/i2c_dma.rs +++ b/examples/stm32l4/src/bin/i2c_dma.rs @@ -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] diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index 3a7e5370c..4826e0bed 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -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>; });