diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 474c619bb..76631ea13 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -46,6 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - DMA channels can/have to be explicitly created for async or blocking drivers, added `set_interrupt_handler` to DMA channels, SPI, I2S, PARL_IO, don't enable interrupts on startup for DMA, I2S, PARL_IO, GPIO (#1300) - UART: Rework `change_baud` so it is possible to set baud rate even after instantiation (#1350) - Runtime ISR binding for SHA,ECC and RSA (#1354) +- Runtime ISR binding for I2C (#1376) ### Removed diff --git a/esp-hal/src/embassy/mod.rs b/esp-hal/src/embassy/mod.rs index 883f4f5e1..fb5bb0624 100644 --- a/esp-hal/src/embassy/mod.rs +++ b/esp-hal/src/embassy/mod.rs @@ -111,17 +111,12 @@ pub fn init(clocks: &Clocks, td: time_driver::TimerType) { // only enable interrupts if the async feature is present #[cfg(feature = "async")] { - #[cfg(rmt)] - crate::interrupt::enable(Interrupt::RMT, Priority::min()).unwrap(); - #[cfg(usb_device)] crate::interrupt::enable(Interrupt::USB_DEVICE, Priority::min()).unwrap(); - crate::interrupt::enable(Interrupt::I2C_EXT0, Priority::min()).unwrap(); - crate::interrupt::enable(Interrupt::GPIO, Priority::min()).unwrap(); - #[cfg(twai0)] crate::interrupt::enable(Interrupt::TWAI0, Priority::min()).unwrap(); + #[cfg(twai1)] crate::interrupt::enable(Interrupt::TWAI1, Priority::min()).unwrap(); } diff --git a/esp-hal/src/i2c.rs b/esp-hal/src/i2c.rs index 3fb7991c6..06027a57c 100644 --- a/esp-hal/src/i2c.rs +++ b/esp-hal/src/i2c.rs @@ -32,11 +32,14 @@ //! } //! ``` +use core::marker::PhantomData; + use fugit::HertzU32; use crate::{ clock::Clocks, gpio::{InputPin, InputSignal, OutputPin, OutputSignal}, + interrupt::InterruptHandler, peripheral::{Peripheral, PeripheralRef}, peripherals::i2c0::{RegisterBlock, COMD}, system::PeripheralClockControl, @@ -183,12 +186,13 @@ enum Opcode { } /// I2C peripheral container (I2C) -pub struct I2C<'d, T> { +pub struct I2C<'d, T, DM: crate::Mode> { peripheral: PeripheralRef<'d, T>, + phantom: PhantomData, } #[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::blocking::i2c::Read for I2C<'_, T> +impl embedded_hal_02::blocking::i2c::Read for I2C<'_, T, DM> where T: Instance, { @@ -200,7 +204,7 @@ where } #[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::blocking::i2c::Write for I2C<'_, T> +impl embedded_hal_02::blocking::i2c::Write for I2C<'_, T, DM> where T: Instance, { @@ -212,7 +216,7 @@ where } #[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::blocking::i2c::WriteRead for I2C<'_, T> +impl embedded_hal_02::blocking::i2c::WriteRead for I2C<'_, T, DM> where T: Instance, { @@ -229,12 +233,12 @@ where } #[cfg(feature = "embedded-hal")] -impl embedded_hal::i2c::ErrorType for I2C<'_, T> { +impl embedded_hal::i2c::ErrorType for I2C<'_, T, DM> { type Error = Error; } #[cfg(feature = "embedded-hal")] -impl embedded_hal::i2c::I2c for I2C<'_, T> +impl embedded_hal::i2c::I2c for I2C<'_, T, DM> where T: Instance, { @@ -264,33 +268,18 @@ where } } -impl<'d, T> I2C<'d, T> +impl<'d, T, DM: crate::Mode> I2C<'d, T, DM> where T: Instance, { - /// Create a new I2C instance - /// This will enable the peripheral but the peripheral won't get - /// automatically disabled when this gets dropped. - pub fn new( - i2c: impl Peripheral

+ 'd, - sda: impl Peripheral

+ 'd, - scl: impl Peripheral

+ 'd, - frequency: HertzU32, - clocks: &Clocks, - ) -> Self { - Self::new_with_timeout(i2c, sda, scl, frequency, clocks, None) - } - - /// Create a new I2C instance with a custom timeout value. - /// This will enable the peripheral but the peripheral won't get - /// automatically disabled when this gets dropped. - pub fn new_with_timeout( + fn new_internal( i2c: impl Peripheral

+ 'd, sda: impl Peripheral

+ 'd, scl: impl Peripheral

+ 'd, frequency: HertzU32, clocks: &Clocks, timeout: Option, + isr: Option, ) -> Self { crate::into_ref!(i2c, sda, scl); @@ -301,7 +290,10 @@ where _ => unreachable!(), // will never happen }); - let mut i2c = I2C { peripheral: i2c }; + let mut i2c = I2C { + peripheral: i2c, + phantom: PhantomData, + }; // avoid SCL/SDA going low during configuration scl.set_output_high(true); @@ -321,10 +313,89 @@ where i2c.peripheral.setup(frequency, clocks, timeout); + if let Some(interrupt) = isr { + unsafe { + crate::interrupt::bind_interrupt(T::interrupt(), interrupt.handler()); + crate::interrupt::enable(T::interrupt(), interrupt.priority()).unwrap(); + } + } + i2c } +} + +impl<'d, T> I2C<'d, T, crate::Blocking> +where + T: Instance, +{ + /// Create a new I2C instance + /// This will enable the peripheral but the peripheral won't get + /// automatically disabled when this gets dropped. + pub fn new( + i2c: impl Peripheral

+ 'd, + sda: impl Peripheral

+ 'd, + scl: impl Peripheral

+ 'd, + frequency: HertzU32, + clocks: &Clocks, + isr: Option, + ) -> Self { + Self::new_with_timeout(i2c, sda, scl, frequency, clocks, None, isr) + } + + /// Create a new I2C instance with a custom timeout value. + /// This will enable the peripheral but the peripheral won't get + /// automatically disabled when this gets dropped. + pub fn new_with_timeout( + i2c: impl Peripheral

+ 'd, + sda: impl Peripheral

+ 'd, + scl: impl Peripheral

+ 'd, + frequency: HertzU32, + clocks: &Clocks, + timeout: Option, + isr: Option, + ) -> Self { + Self::new_internal(i2c, sda, scl, frequency, clocks, timeout, isr) + } +} + +#[cfg(feature = "async")] +impl<'d, T> I2C<'d, T, crate::Async> +where + T: Instance, +{ + /// Create a new I2C instance + /// This will enable the peripheral but the peripheral won't get + /// automatically disabled when this gets dropped. + pub fn new_async( + i2c: impl Peripheral

+ 'd, + sda: impl Peripheral

+ 'd, + scl: impl Peripheral

+ 'd, + frequency: HertzU32, + clocks: &Clocks, + ) -> Self { + Self::new_with_timeout_async(i2c, sda, scl, frequency, clocks, None) + } + + /// Create a new I2C instance with a custom timeout value. + /// This will enable the peripheral but the peripheral won't get + /// automatically disabled when this gets dropped. + pub fn new_with_timeout_async( + i2c: impl Peripheral

+ 'd, + sda: impl Peripheral

+ 'd, + scl: impl Peripheral

+ 'd, + frequency: HertzU32, + clocks: &Clocks, + timeout: Option, + ) -> Self { + let handler = Some(match T::I2C_NUMBER { + 0 => asynch::i2c0_handler, + #[cfg(i2c1)] + 1 => asynch::i2c1_handler, + _ => panic!("Unexpected I2C peripheral"), + }); + Self::new_internal(i2c, sda, scl, frequency, clocks, timeout, handler) + } - #[cfg(feature = "async")] pub(crate) fn inner(&self) -> &T { &self.peripheral } @@ -341,7 +412,7 @@ mod asynch { use embassy_futures::select::select; use embassy_sync::waitqueue::AtomicWaker; use embedded_hal::i2c::Operation; - use procmacros::interrupt; + use procmacros::handler; use super::*; @@ -418,7 +489,7 @@ mod asynch { } } - impl I2C<'_, T> + impl I2C<'_, T, crate::Async> where T: Instance, { @@ -571,7 +642,7 @@ mod asynch { } } - impl<'d, T> embedded_hal_async::i2c::I2c for I2C<'d, T> + impl<'d, T> embedded_hal_async::i2c::I2c for I2C<'d, T, crate::Async> where T: Instance, { @@ -604,8 +675,8 @@ mod asynch { } } - #[interrupt] - fn I2C_EXT0() { + #[handler] + pub(super) fn i2c0_handler() { unsafe { &*crate::peripherals::I2C0::PTR } .int_ena() .modify(|_, w| w.end_detect().clear_bit().trans_complete().clear_bit()); @@ -619,8 +690,8 @@ mod asynch { } #[cfg(i2c1)] - #[interrupt] - fn I2C_EXT1() { + #[handler] + pub(super) fn i2c1_handler() { unsafe { &*crate::peripherals::I2C1::PTR } .int_ena() .modify(|_, w| w.end_detect().clear_bit().trans_complete().clear_bit()); @@ -636,6 +707,10 @@ mod asynch { /// I2C Peripheral Instance pub trait Instance: crate::private::Sealed { + const I2C_NUMBER: usize; + + fn interrupt() -> crate::peripherals::Interrupt; + fn scl_output_signal(&self) -> OutputSignal; fn scl_input_signal(&self) -> InputSignal; fn sda_output_signal(&self) -> OutputSignal; @@ -1581,6 +1656,8 @@ fn write_fifo(register_block: &RegisterBlock, data: u8) { } impl Instance for crate::peripherals::I2C0 { + const I2C_NUMBER: usize = 0; + #[inline(always)] fn scl_output_signal(&self) -> OutputSignal { OutputSignal::I2CEXT0_SCL @@ -1609,10 +1686,16 @@ impl Instance for crate::peripherals::I2C0 { fn i2c_number(&self) -> usize { 0 } + + fn interrupt() -> crate::peripherals::Interrupt { + crate::peripherals::Interrupt::I2C_EXT0 + } } #[cfg(i2c1)] impl Instance for crate::peripherals::I2C1 { + const I2C_NUMBER: usize = 1; + #[inline(always)] fn scl_output_signal(&self) -> OutputSignal { OutputSignal::I2CEXT1_SCL @@ -1641,6 +1724,10 @@ impl Instance for crate::peripherals::I2C1 { fn i2c_number(&self) -> usize { 1 } + + fn interrupt() -> crate::peripherals::Interrupt { + crate::peripherals::Interrupt::I2C_EXT1 + } } #[cfg(lp_i2c0)] diff --git a/examples/src/bin/embassy_i2c.rs b/examples/src/bin/embassy_i2c.rs index 2d8b9ca73..9932bc3bc 100644 --- a/examples/src/bin/embassy_i2c.rs +++ b/examples/src/bin/embassy_i2c.rs @@ -42,7 +42,7 @@ async fn main(_spawner: Spawner) { let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); - let i2c0 = I2C::new( + let i2c0 = I2C::new_async( peripherals.I2C0, io.pins.gpio4, io.pins.gpio5, diff --git a/examples/src/bin/i2c_bmp180_calibration_data.rs b/examples/src/bin/i2c_bmp180_calibration_data.rs index a21893adc..90fcaddcc 100644 --- a/examples/src/bin/i2c_bmp180_calibration_data.rs +++ b/examples/src/bin/i2c_bmp180_calibration_data.rs @@ -33,6 +33,7 @@ fn main() -> ! { io.pins.gpio5, 100.kHz(), &clocks, + None, ); loop { diff --git a/examples/src/bin/i2c_display.rs b/examples/src/bin/i2c_display.rs index 1a42544bb..22761568a 100644 --- a/examples/src/bin/i2c_display.rs +++ b/examples/src/bin/i2c_display.rs @@ -50,6 +50,7 @@ fn main() -> ! { io.pins.gpio5, 100.kHz(), &clocks, + None, ); // Initialize display