//! General Purpose I/Os //! //! To get access to the pins, you first need to convert them into a HAL //! designed struct from the pac struct `GPIO` and `IO_MUX` using `IO::new`. //! //! ```no_run //! let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); //! let mut led = io.pins.gpio5.into_push_pull_output(); //! ``` use core::{convert::Infallible, marker::PhantomData}; use crate::peripherals::{GPIO, IO_MUX}; pub use crate::soc::gpio::*; pub(crate) use crate::{analog, gpio}; #[derive(Copy, Clone)] pub enum Event { RisingEdge = 1, FallingEdge = 2, AnyEdge = 3, LowLevel = 4, HighLevel = 5, } pub struct Unknown {} pub struct Input { _mode: PhantomData, } pub struct RTCInput { _mode: PhantomData, } pub struct Floating; pub struct PullDown; pub struct PullUp; pub struct Output { _mode: PhantomData, } pub struct RTCOutput { _mode: PhantomData, } pub struct OpenDrain; pub struct PushPull; pub struct Analog; pub struct Alternate { _mode: PhantomData, } #[doc(hidden)] pub struct AF0; #[doc(hidden)] pub struct AF1; #[doc(hidden)] pub struct AF2; pub enum DriveStrength { I5mA = 0, I10mA = 1, I20mA = 2, I40mA = 3, } #[derive(PartialEq)] pub enum AlternateFunction { Function0 = 0, Function1 = 1, Function2 = 2, Function3 = 3, Function4 = 4, Function5 = 5, } pub trait RTCPin {} pub trait AnalogPin {} pub trait Pin { fn number(&self) -> u8; fn sleep_mode(&mut self, on: bool) -> &mut Self; fn set_alternate_function(&mut self, alternate: AlternateFunction) -> &mut Self; fn listen(&mut self, event: Event) { self.listen_with_options(event, true, false, false) } fn is_listening(&self) -> bool; fn listen_with_options( &mut self, event: Event, int_enable: bool, nmi_enable: bool, wake_up_from_light_sleep: bool, ); fn unlisten(&mut self); fn clear_interrupt(&mut self); fn is_pcore_interrupt_set(&self) -> bool; fn is_pcore_non_maskable_interrupt_set(&self) -> bool; fn is_acore_interrupt_set(&self) -> bool; fn is_acore_non_maskable_interrupt_set(&self) -> bool; fn enable_hold(&mut self, on: bool); } pub trait InputPin: Pin { fn set_to_input(&mut self) -> &mut Self; fn enable_input(&mut self, on: bool) -> &mut Self; fn enable_input_in_sleep_mode(&mut self, on: bool) -> &mut Self; fn is_input_high(&self) -> bool; fn connect_input_to_peripheral(&mut self, signal: InputSignal) -> &mut Self { self.connect_input_to_peripheral_with_options(signal, false, false) } fn connect_input_to_peripheral_with_options( &mut self, signal: InputSignal, invert: bool, force_via_gpio_mux: bool, ) -> &mut Self; /// Remove a connected `signal` from this input pin. /// /// Clears the entry in the GPIO matrix / IO mux that associates this input /// pin with the given [input `signal`](`InputSignal`). Any other /// connected signals remain intact. fn disconnect_input_from_peripheral(&mut self, signal: InputSignal) -> &mut Self; } pub trait OutputPin: Pin { fn set_to_open_drain_output(&mut self) -> &mut Self; fn set_to_push_pull_output(&mut self) -> &mut Self; fn enable_output(&mut self, on: bool) -> &mut Self; fn set_output_high(&mut self, on: bool) -> &mut Self; fn set_drive_strength(&mut self, strength: DriveStrength) -> &mut Self; fn enable_open_drain(&mut self, on: bool) -> &mut Self; fn enable_output_in_sleep_mode(&mut self, on: bool) -> &mut Self; fn internal_pull_up_in_sleep_mode(&mut self, on: bool) -> &mut Self; fn internal_pull_down_in_sleep_mode(&mut self, on: bool) -> &mut Self; fn connect_peripheral_to_output(&mut self, signal: OutputSignal) -> &mut Self { self.connect_peripheral_to_output_with_options(signal, false, false, false, false) } fn connect_peripheral_to_output_with_options( &mut self, signal: OutputSignal, invert: bool, invert_enable: bool, enable_from_gpio: bool, force_via_gpio_mux: bool, ) -> &mut Self; /// Remove this output pin from a connected [signal](`InputSignal`). /// /// Clears the entry in the GPIO matrix / IO mux that associates this output /// pin with a previously connected [signal](`InputSignal`). Any other /// outputs connected to the signal remain intact. fn disconnect_peripheral_from_output(&mut self) -> &mut Self; fn internal_pull_up(&mut self, on: bool) -> &mut Self; fn internal_pull_down(&mut self, on: bool) -> &mut Self; } #[doc(hidden)] pub struct SingleCoreInteruptStatusRegisterAccessBank0; #[doc(hidden)] pub struct DualCoreInteruptStatusRegisterAccessBank0; #[doc(hidden)] pub struct SingleCoreInteruptStatusRegisterAccessBank1; #[doc(hidden)] pub struct DualCoreInteruptStatusRegisterAccessBank1; #[doc(hidden)] pub trait InteruptStatusRegisterAccess { fn pro_cpu_interrupt_status_read() -> u32; fn pro_cpu_nmi_status_read() -> u32; fn app_cpu_interrupt_status_read() -> u32; fn app_cpu_nmi_status_read() -> u32; } impl InteruptStatusRegisterAccess for SingleCoreInteruptStatusRegisterAccessBank0 { fn pro_cpu_interrupt_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_int.read().bits() } fn pro_cpu_nmi_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_nmi_int.read().bits() } fn app_cpu_interrupt_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_int.read().bits() } fn app_cpu_nmi_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_nmi_int.read().bits() } } #[cfg(any(esp32, esp32s2, esp32s3))] impl InteruptStatusRegisterAccess for SingleCoreInteruptStatusRegisterAccessBank1 { fn pro_cpu_interrupt_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_int1.read().bits() } fn pro_cpu_nmi_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_nmi_int1.read().bits() } fn app_cpu_interrupt_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_int1.read().bits() } fn app_cpu_nmi_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_nmi_int1.read().bits() } } // ESP32S3 is a dual-core chip however pro cpu and app cpu shares the same // interrupt enable bit see // https://github.com/espressif/esp-idf/blob/c04803e88b871a4044da152dfb3699cf47354d18/components/hal/esp32s3/include/hal/gpio_ll.h#L32 // Treating it as SingleCore in the gpio macro makes this work. #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32s2, esp32s3)))] impl InteruptStatusRegisterAccess for DualCoreInteruptStatusRegisterAccessBank0 { fn pro_cpu_interrupt_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_int.read().bits() } fn pro_cpu_nmi_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_nmi_int.read().bits() } fn app_cpu_interrupt_status_read() -> u32 { unsafe { &*GPIO::PTR }.acpu_int.read().bits() } fn app_cpu_nmi_status_read() -> u32 { unsafe { &*GPIO::PTR }.acpu_nmi_int.read().bits() } } // ESP32S3 is a dual-core chip however pro cpu and app cpu shares the same // interrupt enable bit see // https://github.com/espressif/esp-idf/blob/c04803e88b871a4044da152dfb3699cf47354d18/components/hal/esp32s3/include/hal/gpio_ll.h#L32 // Treating it as SingleCore in the gpio macro makes this work. #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32s2, esp32s3)))] impl InteruptStatusRegisterAccess for DualCoreInteruptStatusRegisterAccessBank1 { fn pro_cpu_interrupt_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_int1.read().bits() } fn pro_cpu_nmi_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_nmi_int1.read().bits() } fn app_cpu_interrupt_status_read() -> u32 { unsafe { &*GPIO::PTR }.acpu_int1.read().bits() } fn app_cpu_nmi_status_read() -> u32 { unsafe { &*GPIO::PTR }.acpu_nmi_int1.read().bits() } } #[doc(hidden)] pub trait InterruptStatusRegisters where RegisterAccess: InteruptStatusRegisterAccess, { fn pro_cpu_interrupt_status_read(&self) -> u32 { RegisterAccess::pro_cpu_interrupt_status_read() } fn pro_cpu_nmi_status_read(&self) -> u32 { RegisterAccess::pro_cpu_nmi_status_read() } fn app_cpu_interrupt_status_read(&self) -> u32 { RegisterAccess::app_cpu_interrupt_status_read() } fn app_cpu_nmi_status_read(&self) -> u32 { RegisterAccess::app_cpu_nmi_status_read() } } #[doc(hidden)] pub trait GpioSignal { fn output_signals() -> [Option; 6]; fn input_signals() -> [Option; 6]; } #[doc(hidden)] pub struct Bank0GpioRegisterAccess; #[doc(hidden)] pub struct Bank1GpioRegisterAccess; #[doc(hidden)] pub trait BankGpioRegisterAccess { fn write_out_en_clear(word: u32); fn write_out_en_set(word: u32); fn read_input() -> u32; fn read_output() -> u32; fn write_interrupt_status_clear(word: u32); fn write_output_set(word: u32); fn write_output_clear(word: u32); fn set_output_signal(gpio_num: u8, signal: u32) { let gpio = unsafe { &*crate::peripherals::GPIO::PTR }; gpio.func_out_sel_cfg[gpio_num as usize] .modify(|_, w| unsafe { w.out_sel().bits(signal as OutputSignalType) }); } fn configure_out_sel(gpio_num: u8, signal: u32, invert: bool, oen: bool, oen_inv: bool) { let gpio = unsafe { &*crate::peripherals::GPIO::PTR }; gpio.func_out_sel_cfg[gpio_num as usize].modify(|_, w| unsafe { w.out_sel() .bits(signal as OutputSignalType) .inv_sel() .bit(invert) .oen_sel() .bit(oen) .oen_inv_sel() .bit(oen_inv) }); } fn set_signal_to_level(signal: u32, high: bool) { let gpio = unsafe { &*crate::peripherals::GPIO::PTR }; gpio.func_in_sel_cfg[signal as usize].modify(|_, w| unsafe { w.sel() .set_bit() .in_inv_sel() .bit(false) .in_sel() .bits(if high { ONE_INPUT } else { ZERO_INPUT }) }); } fn clear_func_in_sel(signal: u32) { let gpio = unsafe { &*crate::peripherals::GPIO::PTR }; gpio.func_in_sel_cfg[signal as usize].modify(|_, w| w.sel().clear_bit()); } fn set_int_enable(gpio_num: u8, int_ena: u32, int_type: u8, wake_up_from_light_sleep: bool) { let gpio = unsafe { &*crate::peripherals::GPIO::PTR }; gpio.pin[gpio_num as usize].modify(|_, w| unsafe { w.int_ena() .bits(int_ena as u8) .int_type() .bits(int_type as u8) .wakeup_enable() .bit(wake_up_from_light_sleep) }); } fn set_open_drain(&self, gpio_num: u8, open_drain: bool) { let gpio = unsafe { &*crate::peripherals::GPIO::PTR }; gpio.pin[gpio_num as usize].modify(|_, w| w.pad_driver().bit(open_drain)); } } impl BankGpioRegisterAccess for Bank0GpioRegisterAccess { fn write_out_en_clear(word: u32) { unsafe { &*GPIO::PTR } .enable_w1tc .write(|w| unsafe { w.bits(word) }); } fn write_out_en_set(word: u32) { unsafe { &*GPIO::PTR } .enable_w1ts .write(|w| unsafe { w.bits(word) }); } fn read_input() -> u32 { unsafe { &*GPIO::PTR }.in_.read().bits() } fn read_output() -> u32 { unsafe { &*GPIO::PTR }.out.read().bits() } fn write_interrupt_status_clear(word: u32) { unsafe { &*GPIO::PTR } .status_w1tc .write(|w| unsafe { w.bits(word) }); } fn write_output_set(word: u32) { unsafe { &*GPIO::PTR } .out_w1ts .write(|w| unsafe { w.bits(word) }); } fn write_output_clear(word: u32) { unsafe { &*GPIO::PTR } .out_w1tc .write(|w| unsafe { w.bits(word) }); } } #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] impl BankGpioRegisterAccess for Bank1GpioRegisterAccess { fn write_out_en_clear(word: u32) { unsafe { &*GPIO::PTR } .enable1_w1tc .write(|w| unsafe { w.bits(word) }); } fn write_out_en_set(word: u32) { unsafe { &*GPIO::PTR } .enable1_w1ts .write(|w| unsafe { w.bits(word) }); } fn read_input() -> u32 { unsafe { &*GPIO::PTR }.in1.read().bits() } fn read_output() -> u32 { unsafe { &*GPIO::PTR }.out1.read().bits() } fn write_interrupt_status_clear(word: u32) { unsafe { &*GPIO::PTR } .status1_w1tc .write(|w| unsafe { w.bits(word) }); } fn write_output_set(word: u32) { unsafe { &*GPIO::PTR } .out1_w1ts .write(|w| unsafe { w.bits(word) }); } fn write_output_clear(word: u32) { unsafe { &*GPIO::PTR } .out1_w1tc .write(|w| unsafe { w.bits(word) }); } } pub fn connect_low_to_peripheral(signal: InputSignal) { unsafe { &*GPIO::PTR }.func_in_sel_cfg[signal as usize].modify(|_, w| unsafe { w.sel() .set_bit() .in_inv_sel() .bit(false) .in_sel() .bits(ZERO_INPUT) }); } pub fn connect_high_to_peripheral(signal: InputSignal) { unsafe { &*GPIO::PTR }.func_in_sel_cfg[signal as usize].modify(|_, w| unsafe { w.sel() .set_bit() .in_inv_sel() .bit(false) .in_sel() .bits(ONE_INPUT) }); } #[doc(hidden)] pub trait PinType {} #[doc(hidden)] pub trait IsOutputPin: PinType {} #[doc(hidden)] pub trait IsInputPin: PinType {} #[doc(hidden)] pub trait IsAnalogPin: PinType {} #[doc(hidden)] pub struct InputOutputPinType; #[doc(hidden)] pub struct InputOnlyPinType; #[doc(hidden)] pub struct InputOutputAnalogPinType; #[doc(hidden)] pub struct InputOnlyAnalogPinType; impl PinType for InputOutputPinType {} impl IsOutputPin for InputOutputPinType {} impl IsInputPin for InputOutputPinType {} impl PinType for InputOnlyPinType {} impl IsInputPin for InputOnlyPinType {} impl PinType for InputOutputAnalogPinType {} impl IsOutputPin for InputOutputAnalogPinType {} impl IsInputPin for InputOutputAnalogPinType {} impl IsAnalogPin for InputOutputAnalogPinType {} impl PinType for InputOnlyAnalogPinType {} impl IsInputPin for InputOnlyAnalogPinType {} impl IsAnalogPin for InputOnlyAnalogPinType {} pub struct GpioPin where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: PinType, SIG: GpioSignal, { _mode: PhantomData, _pintype: PhantomData, _reg_access: PhantomData, _ira: PhantomData, _signals: PhantomData, } impl embedded_hal::digital::v2::InputPin for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: PinType, SIG: GpioSignal, { type Error = Infallible; fn is_high(&self) -> Result { Ok(RA::read_input() & (1 << (GPIONUM % 32)) != 0) } fn is_low(&self) -> Result { Ok(!self.is_high()?) } } impl embedded_hal::digital::v2::InputPin for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: PinType, SIG: GpioSignal, { type Error = Infallible; fn is_high(&self) -> Result { Ok(RA::read_input() & (1 << (GPIONUM % 32)) != 0) } fn is_low(&self) -> Result { Ok(!self.is_high()?) } } #[cfg(feature = "eh1")] impl embedded_hal_1::digital::ErrorType for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: PinType, SIG: GpioSignal, { type Error = Infallible; } #[cfg(feature = "eh1")] impl embedded_hal_1::digital::InputPin for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: PinType, SIG: GpioSignal, { fn is_high(&self) -> Result { Ok(RA::read_input() & (1 << (GPIONUM % 32)) != 0) } fn is_low(&self) -> Result { Ok(!self.is_high()?) } } impl GpioPin where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: PinType, SIG: GpioSignal, { pub(crate) fn new() -> Self { Self { _mode: PhantomData, _pintype: PhantomData, _reg_access: PhantomData, _ira: PhantomData, _signals: PhantomData, } } fn init_input(&self, pull_down: bool, pull_up: bool) { let gpio = unsafe { &*GPIO::PTR }; RA::write_out_en_clear(1 << (GPIONUM % 32)); gpio.func_out_sel_cfg[GPIONUM as usize] .modify(|_, w| unsafe { w.out_sel().bits(OutputSignal::GPIO as OutputSignalType) }); #[cfg(esp32)] crate::soc::gpio::errata36(GPIONUM, pull_up, pull_down); get_io_mux_reg(GPIONUM).modify(|_, w| unsafe { w.mcu_sel() .bits(GPIO_FUNCTION as u8) .fun_ie() .set_bit() .fun_wpd() .bit(pull_down) .fun_wpu() .bit(pull_up) .slp_sel() .clear_bit() }); } pub fn into_floating_input(self) -> GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> { self.init_input(false, false); GpioPin { _mode: PhantomData, _pintype: PhantomData, _reg_access: PhantomData, _ira: PhantomData, _signals: PhantomData, } } pub fn into_pull_up_input(self) -> GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> { self.init_input(false, true); GpioPin { _mode: PhantomData, _pintype: PhantomData, _reg_access: PhantomData, _ira: PhantomData, _signals: PhantomData, } } pub fn into_pull_down_input(self) -> GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> { self.init_input(true, false); GpioPin { _mode: PhantomData, _pintype: PhantomData, _reg_access: PhantomData, _ira: PhantomData, _signals: PhantomData, } } } impl InputPin for GpioPin where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: PinType, SIG: GpioSignal, { fn set_to_input(&mut self) -> &mut Self { self.init_input(false, false); self } fn enable_input(&mut self, on: bool) -> &mut Self { get_io_mux_reg(GPIONUM).modify(|_, w| w.fun_ie().bit(on)); self } fn enable_input_in_sleep_mode(&mut self, on: bool) -> &mut Self { get_io_mux_reg(GPIONUM).modify(|_, w| w.mcu_ie().bit(on)); self } fn is_input_high(&self) -> bool { RA::read_input() & (1 << (GPIONUM % 32)) != 0 } fn connect_input_to_peripheral_with_options( &mut self, signal: InputSignal, invert: bool, force_via_gpio_mux: bool, ) -> &mut Self { let af = if force_via_gpio_mux { GPIO_FUNCTION } else { let mut res = GPIO_FUNCTION; for (i, input_signal) in SIG::input_signals().iter().enumerate() { if let Some(input_signal) = input_signal { if *input_signal == signal { res = match i { 0 => AlternateFunction::Function0, 1 => AlternateFunction::Function1, 2 => AlternateFunction::Function2, 3 => AlternateFunction::Function3, 4 => AlternateFunction::Function4, 5 => AlternateFunction::Function5, _ => unreachable!(), }; break; } } } res }; if af == GPIO_FUNCTION && signal as usize > INPUT_SIGNAL_MAX as usize { panic!("Cannot connect GPIO to this peripheral"); } self.set_alternate_function(af); if (signal as usize) <= INPUT_SIGNAL_MAX as usize { unsafe { &*GPIO::PTR }.func_in_sel_cfg[signal as usize].modify(|_, w| unsafe { w.sel() .set_bit() .in_inv_sel() .bit(invert) .in_sel() .bits(GPIONUM) }); } self } fn disconnect_input_from_peripheral(&mut self, signal: InputSignal) -> &mut Self { self.set_alternate_function(GPIO_FUNCTION); unsafe { &*GPIO::PTR }.func_in_sel_cfg[signal as usize].modify(|_, w| w.sel().clear_bit()); self } } impl Pin for GpioPin where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: PinType, SIG: GpioSignal, { fn number(&self) -> u8 { GPIONUM } fn sleep_mode(&mut self, on: bool) -> &mut Self { get_io_mux_reg(GPIONUM).modify(|_, w| w.slp_sel().bit(on)); self } fn set_alternate_function(&mut self, alternate: AlternateFunction) -> &mut Self { get_io_mux_reg(GPIONUM).modify(|_, w| unsafe { w.mcu_sel().bits(alternate as u8) }); self } fn listen_with_options( &mut self, event: Event, int_enable: bool, nmi_enable: bool, wake_up_from_light_sleep: bool, ) { if wake_up_from_light_sleep { match event { Event::AnyEdge | Event::RisingEdge | Event::FallingEdge => { panic!("Edge triggering is not supported for wake-up from light sleep"); } _ => {} } } unsafe { (&*GPIO::PTR).pin[GPIONUM as usize].modify(|_, w| { w.int_ena() .bits(gpio_intr_enable(int_enable, nmi_enable)) .int_type() .bits(event as u8) .wakeup_enable() .bit(wake_up_from_light_sleep) }); } } fn is_listening(&self) -> bool { let bits = unsafe { &*GPIO::PTR }.pin[GPIONUM as usize] .read() .int_ena() .bits(); bits != 0 } fn unlisten(&mut self) { unsafe { (&*GPIO::PTR).pin[GPIONUM as usize] .modify(|_, w| w.int_ena().bits(0).int_type().bits(0).int_ena().bits(0)); } } fn clear_interrupt(&mut self) { RA::write_interrupt_status_clear(1 << (GPIONUM % 32)); } fn is_pcore_interrupt_set(&self) -> bool { (IRA::pro_cpu_interrupt_status_read() & (1 << (GPIONUM % 32))) != 0 } fn is_pcore_non_maskable_interrupt_set(&self) -> bool { (IRA::pro_cpu_nmi_status_read() & (1 << (GPIONUM % 32))) != 0 } fn is_acore_interrupt_set(&self) -> bool { (IRA::app_cpu_interrupt_status_read() & (1 << (GPIONUM % 32))) != 0 } fn is_acore_non_maskable_interrupt_set(&self) -> bool { (IRA::app_cpu_nmi_status_read() & (1 << (GPIONUM % 32))) != 0 } fn enable_hold(&mut self, _on: bool) { todo!(); } } impl embedded_hal::digital::v2::OutputPin for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { type Error = Infallible; fn set_high(&mut self) -> Result<(), Self::Error> { RA::write_output_set(1 << (GPIONUM % 32)); Ok(()) } fn set_low(&mut self) -> Result<(), Self::Error> { RA::write_output_clear(1 << (GPIONUM % 32)); Ok(()) } } impl embedded_hal::digital::v2::StatefulOutputPin for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { fn is_set_high(&self) -> Result { Ok(RA::read_output() & (1 << (GPIONUM % 32)) != 0) } fn is_set_low(&self) -> Result { Ok(!self.is_set_high()?) } } impl embedded_hal::digital::v2::ToggleableOutputPin for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { type Error = Infallible; fn toggle(&mut self) -> Result<(), Self::Error> { use embedded_hal::digital::v2::{OutputPin as _, StatefulOutputPin as _}; if self.is_set_high()? { Ok(self.set_low()?) } else { Ok(self.set_high()?) } } } #[cfg(feature = "eh1")] impl embedded_hal_1::digital::ErrorType for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { type Error = Infallible; } #[cfg(feature = "eh1")] impl embedded_hal_1::digital::OutputPin for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { fn set_low(&mut self) -> Result<(), Self::Error> { RA::write_output_clear(1 << (GPIONUM % 32)); Ok(()) } fn set_high(&mut self) -> Result<(), Self::Error> { RA::write_output_set(1 << (GPIONUM % 32)); Ok(()) } } #[cfg(feature = "eh1")] impl embedded_hal_1::digital::StatefulOutputPin for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { fn is_set_high(&self) -> Result { Ok(RA::read_output() & (1 << (GPIONUM % 32)) != 0) } fn is_set_low(&self) -> Result { Ok(!self.is_set_high()?) } } #[cfg(feature = "eh1")] impl embedded_hal_1::digital::ToggleableOutputPin for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { fn toggle(&mut self) -> Result<(), Self::Error> { use embedded_hal_1::digital::{OutputPin as _, StatefulOutputPin as _}; if self.is_set_high()? { Ok(self.set_low()?) } else { Ok(self.set_high()?) } } } impl crate::peripheral::Peripheral for GpioPin where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: PinType, SIG: GpioSignal, { type P = GpioPin; unsafe fn clone_unchecked(&mut self) -> Self::P { core::ptr::read(self as *const _) } } impl crate::peripheral::Peripheral for &mut GpioPin where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: PinType, SIG: GpioSignal, { type P = GpioPin; unsafe fn clone_unchecked(&mut self) -> Self::P { core::ptr::read(*self as *const _) } } impl crate::peripheral::sealed::Sealed for GpioPin where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: PinType, SIG: GpioSignal, { } impl crate::peripheral::sealed::Sealed for &mut GpioPin where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: PinType, SIG: GpioSignal, { } impl From> for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { fn from( pin: GpioPin, ) -> GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> { pin.into_floating_input() } } impl From> for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { fn from( pin: GpioPin, ) -> GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> { pin.into_pull_up_input() } } impl From> for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsInputPin, SIG: GpioSignal, { fn from( pin: GpioPin, ) -> GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> { pin.into_pull_down_input() } } impl From> for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { fn from( pin: GpioPin, ) -> GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> { pin.into_push_pull_output() } } impl From> for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { fn from( pin: GpioPin, ) -> GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> { pin.into_open_drain_output() } } impl From> for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { fn from( pin: GpioPin, ) -> GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> { pin.into_alternate_1() } } impl From> for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { fn from( pin: GpioPin, ) -> GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> { pin.into_alternate_2() } } impl GpioPin where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { fn init_output(&self, alternate: AlternateFunction, open_drain: bool) { let gpio = unsafe { &*GPIO::PTR }; RA::write_out_en_set(1 << (GPIONUM % 32)); gpio.pin[GPIONUM as usize].modify(|_, w| w.pad_driver().bit(open_drain)); gpio.func_out_sel_cfg[GPIONUM as usize] .modify(|_, w| unsafe { w.out_sel().bits(OutputSignal::GPIO as OutputSignalType) }); get_io_mux_reg(GPIONUM).modify(|_, w| unsafe { w.mcu_sel() .bits(alternate as u8) .fun_ie() .bit(open_drain) .fun_wpd() .clear_bit() .fun_wpu() .clear_bit() .fun_drv() .bits(DriveStrength::I20mA as u8) .slp_sel() .clear_bit() }); } pub fn into_push_pull_output( self, ) -> GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> { self.init_output(GPIO_FUNCTION, false); GpioPin { _mode: PhantomData, _pintype: PhantomData, _reg_access: PhantomData, _ira: PhantomData, _signals: PhantomData, } } pub fn into_open_drain_output( self, ) -> GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> { self.init_output(GPIO_FUNCTION, true); GpioPin { _mode: PhantomData, _pintype: PhantomData, _reg_access: PhantomData, _ira: PhantomData, _signals: PhantomData, } } pub fn into_alternate_1(self) -> GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> { self.init_output(AlternateFunction::Function1, false); GpioPin { _mode: PhantomData, _pintype: PhantomData, _reg_access: PhantomData, _ira: PhantomData, _signals: PhantomData, } } pub fn into_alternate_2(self) -> GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> { self.init_output(AlternateFunction::Function2, false); GpioPin { _mode: PhantomData, _pintype: PhantomData, _reg_access: PhantomData, _ira: PhantomData, _signals: PhantomData, } } } impl OutputPin for GpioPin where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsOutputPin, SIG: GpioSignal, { fn set_to_open_drain_output(&mut self) -> &mut Self { self.init_output(GPIO_FUNCTION, true); self } fn set_to_push_pull_output(&mut self) -> &mut Self { self.init_output(GPIO_FUNCTION, false); self } fn enable_output(&mut self, on: bool) -> &mut Self { if on { RA::write_out_en_set(1 << (GPIONUM % 32)); } else { RA::write_out_en_clear(1 << (GPIONUM % 32)); } self } fn set_output_high(&mut self, high: bool) -> &mut Self { if high { RA::write_output_set(1 << (GPIONUM % 32)); } else { RA::write_output_clear(1 << (GPIONUM % 32)); } self } fn set_drive_strength(&mut self, strength: DriveStrength) -> &mut Self { get_io_mux_reg(GPIONUM).modify(|_, w| unsafe { w.fun_drv().bits(strength as u8) }); self } fn enable_open_drain(&mut self, on: bool) -> &mut Self { unsafe { &*GPIO::PTR }.pin[GPIONUM as usize].modify(|_, w| w.pad_driver().bit(on)); self } fn internal_pull_up_in_sleep_mode(&mut self, on: bool) -> &mut Self { get_io_mux_reg(GPIONUM).modify(|_, w| w.mcu_wpu().bit(on)); self } fn internal_pull_down_in_sleep_mode(&mut self, on: bool) -> &mut Self { get_io_mux_reg(GPIONUM).modify(|_, w| w.mcu_wpd().bit(on)); self } fn enable_output_in_sleep_mode(&mut self, on: bool) -> &mut Self { get_io_mux_reg(GPIONUM).modify(|_, w| w.mcu_oe().bit(on)); self } fn connect_peripheral_to_output_with_options( &mut self, signal: OutputSignal, invert: bool, invert_enable: bool, enable_from_gpio: bool, force_via_gpio_mux: bool, ) -> &mut Self { let af = if force_via_gpio_mux { GPIO_FUNCTION } else { let mut res = GPIO_FUNCTION; for (i, output_signal) in SIG::output_signals().iter().enumerate() { if let Some(output_signal) = output_signal { if *output_signal == signal { res = match i { 0 => AlternateFunction::Function0, 1 => AlternateFunction::Function1, 2 => AlternateFunction::Function2, 3 => AlternateFunction::Function3, 4 => AlternateFunction::Function4, 5 => AlternateFunction::Function5, _ => unreachable!(), }; break; } } } res }; if af == GPIO_FUNCTION && signal as usize > OUTPUT_SIGNAL_MAX as usize { panic!("Cannot connect this peripheral to GPIO"); } self.set_alternate_function(af); let clipped_signal = if signal as usize <= OUTPUT_SIGNAL_MAX as usize { signal as OutputSignalType } else { OUTPUT_SIGNAL_MAX }; unsafe { &*GPIO::PTR }.func_out_sel_cfg[GPIONUM as usize].modify(|_, w| unsafe { w.out_sel() .bits(clipped_signal) .inv_sel() .bit(invert) .oen_sel() .bit(enable_from_gpio) .oen_inv_sel() .bit(invert_enable) }); self } fn disconnect_peripheral_from_output(&mut self) -> &mut Self { self.set_alternate_function(GPIO_FUNCTION); unsafe { &*GPIO::PTR }.func_out_sel_cfg[GPIONUM as usize] .modify(|_, w| unsafe { w.out_sel().bits(OutputSignal::GPIO as OutputSignalType) }); self } fn internal_pull_up(&mut self, on: bool) -> &mut Self { get_io_mux_reg(GPIONUM).modify(|_, w| w.fun_wpu().bit(on)); self } fn internal_pull_down(&mut self, on: bool) -> &mut Self { get_io_mux_reg(GPIONUM).modify(|_, w| w.fun_wpd().bit(on)); self } } impl GpioPin where RA: BankGpioRegisterAccess, IRA: InteruptStatusRegisterAccess, PINTYPE: IsAnalogPin, SIG: GpioSignal, { pub fn into_analog(self) -> GpioPin { crate::soc::gpio::internal_into_analog(GPIONUM); GpioPin { _mode: PhantomData, _pintype: PhantomData, _reg_access: PhantomData, _ira: PhantomData, _signals: PhantomData, } } } impl embedded_hal::digital::v2::InputPin for AnyPin> { type Error = core::convert::Infallible; fn is_high(&self) -> Result { let inner = &self.inner; handle_gpio_input!(inner, target, { target.is_high() }) } fn is_low(&self) -> Result { let inner = &self.inner; handle_gpio_input!(inner, target, { target.is_low() }) } } #[cfg(feature = "eh1")] impl embedded_hal_1::digital::ErrorType for AnyPin> { type Error = Infallible; } #[cfg(feature = "eh1")] impl embedded_hal_1::digital::InputPin for AnyPin> { fn is_high(&self) -> Result { let inner = &self.inner; handle_gpio_input!(inner, target, { target.is_high() }) } fn is_low(&self) -> Result { let inner = &self.inner; handle_gpio_input!(inner, target, { target.is_low() }) } } impl embedded_hal::digital::v2::OutputPin for AnyPin> { type Error = Infallible; fn set_low(&mut self) -> Result<(), Self::Error> { let inner = &mut self.inner; handle_gpio_output!(inner, target, { target.set_low() }) } fn set_high(&mut self) -> Result<(), Self::Error> { let inner = &mut self.inner; handle_gpio_output!(inner, target, { target.set_high() }) } } impl embedded_hal::digital::v2::StatefulOutputPin for AnyPin> { fn is_set_high(&self) -> Result { let inner = &self.inner; handle_gpio_output!(inner, target, { target.is_set_high() }) } fn is_set_low(&self) -> Result { let inner = &self.inner; handle_gpio_output!(inner, target, { target.is_set_low() }) } } impl embedded_hal::digital::v2::ToggleableOutputPin for AnyPin> { type Error = Infallible; fn toggle(&mut self) -> Result<(), Self::Error> { let inner = &mut self.inner; handle_gpio_output!(inner, target, { target.toggle() }) } } #[cfg(feature = "eh1")] impl embedded_hal_1::digital::ErrorType for AnyPin> { type Error = Infallible; } #[cfg(feature = "eh1")] impl embedded_hal_1::digital::OutputPin for AnyPin> { fn set_low(&mut self) -> Result<(), Self::Error> { let inner = &mut self.inner; handle_gpio_output!(inner, target, { target.set_low() }) } fn set_high(&mut self) -> Result<(), Self::Error> { let inner = &mut self.inner; handle_gpio_output!(inner, target, { target.set_high() }) } } #[cfg(feature = "eh1")] impl embedded_hal_1::digital::StatefulOutputPin for AnyPin> { fn is_set_high(&self) -> Result { let inner = &self.inner; handle_gpio_output!(inner, target, { target.is_set_high() }) } fn is_set_low(&self) -> Result { let inner = &self.inner; handle_gpio_output!(inner, target, { target.is_set_low() }) } } #[cfg(feature = "eh1")] impl embedded_hal_1::digital::ToggleableOutputPin for AnyPin> { fn toggle(&mut self) -> Result<(), Self::Error> { let inner = &mut self.inner; handle_gpio_output!(inner, target, { target.toggle() }) } } #[cfg(feature = "async")] impl embedded_hal_async::digital::Wait for AnyPin> { async fn wait_for_high(&mut self) -> Result<(), Self::Error> { let inner = &mut self.inner; handle_gpio_input!(inner, target, { target.wait_for_high().await }) } async fn wait_for_low(&mut self) -> Result<(), Self::Error> { let inner = &mut self.inner; handle_gpio_input!(inner, target, { target.wait_for_low().await }) } async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { let inner = &mut self.inner; handle_gpio_input!(inner, target, { target.wait_for_rising_edge().await }) } async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { let inner = &mut self.inner; handle_gpio_input!(inner, target, { target.wait_for_falling_edge().await }) } async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { let inner = &mut self.inner; handle_gpio_input!(inner, target, { target.wait_for_any_edge().await }) } } pub struct IO { _io_mux: IO_MUX, pub pins: Pins, } impl IO { pub fn new(gpio: GPIO, io_mux: IO_MUX) -> Self { let pins = gpio.split(); let io = IO { _io_mux: io_mux, pins, }; io } } #[doc(hidden)] #[macro_export] macro_rules! gpio { ( $cores:ident, $( ($gpionum:literal, $bank:literal, $type:ident $( ( $( $af_input_num:literal => $af_input_signal:ident )* ) ( $( $af_output_num:literal => $af_output_signal:ident )* ) )? ) )+ ) => { #[doc(hidden)] pub trait GpioExt { type Parts; fn split(self) -> Self::Parts; } paste!{ impl GpioExt for GPIO { type Parts = Pins; fn split(self) -> Self::Parts { Pins { $( [< gpio $gpionum >]: { GpioPin::new() }, )+ } } } $( pub struct [] {} impl crate::gpio::GpioSignal for [] { fn output_signals() -> [Option; 6]{ #[allow(unused_mut)] let mut output_signals = [None,None,None,None,None,None]; $( $( output_signals[ $af_output_num ] = Some( OutputSignal::$af_output_signal ); )* )? output_signals } fn input_signals() -> [Option; 6] { #[allow(unused_mut)] let mut input_signals = [None,None,None,None,None,None]; $( $( input_signals[ $af_input_num ] = Some( InputSignal::$af_input_signal ); )* )? input_signals } } )+ pub struct Pins { $( pub [< gpio $gpionum >] : GpioPin], $crate::gpio::[< $cores CoreInteruptStatusRegisterAccessBank $bank >], [< $type PinType >], [], $gpionum>, )+ } $( pub type [] = GpioPin], $crate::gpio::[< $cores CoreInteruptStatusRegisterAccessBank $bank >], [< $type PinType >], [], $gpionum>; )+ pub(crate) enum ErasedPin { $( []([]), )+ } pub struct AnyPin { pub(crate) inner: ErasedPin } $( impl From< [] > for AnyPin { fn from(value: []) -> Self { AnyPin { inner: ErasedPin::[](value) } } } impl [] { pub fn degrade(self) -> AnyPin { AnyPin { inner: ErasedPin::[](self) } } } impl TryInto<[]> for AnyPin { type Error = (); fn try_into(self) -> Result<[], Self::Error> { match self.inner { ErasedPin::[](gpio) => Ok(gpio), _ => Err(()), } } } )+ procmacros::make_gpio_enum_dispatch_macro!( handle_gpio_output { InputOutputAnalog, InputOutput, } { $( $type,$gpionum )+ } ); procmacros::make_gpio_enum_dispatch_macro!( handle_gpio_input { InputOutputAnalog, InputOutput, InputOnlyAnalog } { $( $type,$gpionum )+ } ); } }; } // Following code enables `into_analog` #[doc(hidden)] pub fn enable_iomux_clk_gate() { #[cfg(esp32s2)] { use crate::peripherals::SENS; let sensors = unsafe { &*SENS::ptr() }; sensors .sar_io_mux_conf .modify(|_, w| w.iomux_clk_gate_en().set_bit()); } } #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32s2)))] #[doc(hidden)] #[macro_export] macro_rules! analog { ( $( ( $pin_num:expr, $rtc_pin:expr, $pin_reg:expr, $mux_sel:ident, $fun_sel:ident, $fun_ie:ident $(, $rue:ident, $rde:ident)? ) )+ ) => { pub(crate) fn internal_into_analog(pin: u8) { use crate::peripherals::RTCIO; let rtcio = unsafe{ &*RTCIO::ptr() }; $crate::gpio::enable_iomux_clk_gate(); match pin { $( $pin_num => { // disable input paste! { rtcio.$pin_reg.modify(|_,w| w.$fun_ie().bit(false)); // disable output rtcio.enable_w1tc.write(|w| unsafe { w.enable_w1tc().bits(1 << $rtc_pin) }); // disable open drain rtcio.pin[$rtc_pin].modify(|_,w| w.pad_driver().bit(false)); rtcio.$pin_reg.modify(|_,w| { w.$fun_ie().clear_bit(); // Connect pin to analog / RTC module instead of standard GPIO w.$mux_sel().set_bit(); // Select function "RTC function 1" (GPIO) for analog use unsafe { w.$fun_sel().bits(0b00) } }); // Disable pull-up and pull-down resistors on the pin, if it has them $( rtcio.$pin_reg.modify(|_,w| { w .$rue().bit(false) .$rde().bit(false) }); )? } } )+ _ => unreachable!(), } } } } #[cfg(esp32s2)] #[doc(hidden)] #[macro_export] macro_rules! analog { ( $( ( $pin_num:expr, $rtc_pin:expr, $pin_reg:expr, $mux_sel:ident, $fun_sel:ident, $fun_ie:ident $(, $rue:ident, $rde:ident)? ) )+ ) => { pub(crate) fn internal_into_analog(pin: u8) { use crate::peripherals::RTCIO; let rtcio = unsafe{ &*RTCIO::ptr() }; $crate::gpio::enable_iomux_clk_gate(); match pin { $( $pin_num => { paste!{ use $crate::gpio::[< esp32s2_get_rtc_pad_ $pin_reg>]; let rtc_pad = [< esp32s2_get_rtc_pad_ $pin_reg>](); } // disable input rtc_pad.modify(|_,w| w.$fun_ie().bit(false)); // disable output rtcio.enable_w1tc.write(|w| unsafe { w.enable_w1tc().bits(1 << $rtc_pin) }); // disable open drain rtcio.pin[$rtc_pin].modify(|_,w| w.pad_driver().bit(false)); rtc_pad.modify(|_,w| { w.$fun_ie().clear_bit(); // Connect pin to analog / RTC module instead of standard GPIO w.$mux_sel().set_bit(); // Select function "RTC function 1" (GPIO) for analog use unsafe { w.$fun_sel().bits(0b00) } }); // Disable pull-up and pull-down resistors on the pin, if it has them $( rtc_pad.modify(|_,w| { w .$rue().bit(false) .$rde().bit(false) }); )? } )+ _ => unreachable!(), } } } } #[cfg(any(esp32c2, esp32c3, esp32c6))] #[doc(hidden)] #[macro_export] macro_rules! analog { ( $($pin_num:literal)+ ) => { pub(crate) fn internal_into_analog(pin: u8) { use crate::peripherals::IO_MUX; use crate::peripherals::GPIO; let io_mux = unsafe{ &*IO_MUX::PTR }; let gpio = unsafe{ &*GPIO::PTR }; match pin { $( $pin_num => { io_mux.gpio[$pin_num].modify(|_,w| unsafe { w.mcu_sel().bits(1) .fun_ie().clear_bit() .fun_wpu().clear_bit() .fun_wpd().clear_bit() }); gpio.enable_w1tc.write(|w| unsafe { w.bits(1 << $pin_num) }); } )+ _ => unreachable!() } } } } #[cfg(feature = "async")] mod asynch { use core::task::{Context, Poll}; use embassy_sync::waitqueue::AtomicWaker; use embedded_hal_async::digital::Wait; use super::*; use crate::prelude::*; #[allow(clippy::declare_interior_mutable_const)] const NEW_AW: AtomicWaker = AtomicWaker::new(); static PIN_WAKERS: [AtomicWaker; NUM_PINS] = [NEW_AW; NUM_PINS]; impl Wait for GpioPin, RA, IRA, PINTYPE, SIG, GPIONUM> where RA: BankGpioRegisterAccess, PINTYPE: IsInputPin, IRA: InteruptStatusRegisterAccess, SIG: GpioSignal, { async fn wait_for_high(&mut self) -> Result<(), Self::Error> { PinFuture::new(self, Event::HighLevel).await } async fn wait_for_low(&mut self) -> Result<(), Self::Error> { PinFuture::new(self, Event::LowLevel).await } async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { PinFuture::new(self, Event::RisingEdge).await } async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { PinFuture::new(self, Event::FallingEdge).await } async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { PinFuture::new(self, Event::AnyEdge).await } } pub struct PinFuture<'a, P> { pin: &'a mut P, } impl<'a, P> PinFuture<'a, P> where P: crate::gpio::Pin + embedded_hal_1::digital::ErrorType, { pub fn new(pin: &'a mut P, event: Event) -> Self { pin.listen(event); Self { pin } } } impl<'a, P> core::future::Future for PinFuture<'a, P> where P: crate::gpio::Pin + embedded_hal_1::digital::ErrorType, { type Output = Result<(), P::Error>; fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { PIN_WAKERS[self.pin.number() as usize].register(cx.waker()); // if pin is no longer listening its been triggered // therefore the future has resolved if !self.pin.is_listening() { Poll::Ready(Ok(())) } else { Poll::Pending } } } #[interrupt] unsafe fn GPIO() { // TODO how to handle dual core reg access // we need to check which core the interrupt is currently firing on // and only fire interrupts registered for that core type Bank0 = SingleCoreInteruptStatusRegisterAccessBank0; #[cfg(any(esp32, esp32s2, esp32s3))] type Bank1 = SingleCoreInteruptStatusRegisterAccessBank1; let mut intrs = Bank0::pro_cpu_interrupt_status_read() as u64; #[cfg(any(esp32, esp32s2, esp32s3))] { intrs |= (Bank1::pro_cpu_interrupt_status_read() as u64) << 32; } // clear interrupts Bank0GpioRegisterAccess::write_interrupt_status_clear(!0); #[cfg(any(esp32, esp32s2, esp32s3))] Bank1GpioRegisterAccess::write_interrupt_status_clear(!0); while intrs != 0 { let pin_nr = intrs.trailing_zeros(); cfg_if::cfg_if! { if #[cfg(any(esp32, esp32s2, esp32s3))] { if pin_nr < 32 { Bank0GpioRegisterAccess::set_int_enable(pin_nr as u8, 0, 0, false); } else { Bank1GpioRegisterAccess::set_int_enable(pin_nr as u8, 0, 0, false); } } else { Bank0GpioRegisterAccess::set_int_enable(pin_nr as u8, 0, 0, false); } } PIN_WAKERS[pin_nr as usize].wake(); // wake task intrs &= !(1 << pin_nr); } } }