//! # General Purpose Input/Output (GPIO) //! //! ## Overview //! //! Each pin can be used as a general-purpose I/O, or be connected to one or //! more internal peripheral signals. #![cfg_attr( soc_etm, doc = "The GPIO pins also provide tasks and events via the ETM interconnect system. For more information, see the [etm] module." )] #![doc = ""] //! ## Working with pins //! //! After initializing the HAL, you can access the individual pins using the //! [`crate::Peripherals`] struct. These pins can then be used as general //! purpose digital IO using pin drivers, or they can be passed to peripherals //! (such as SPI, UART, I2C, etc.), or can be [`GpioPin::split`] //! into peripheral signals for advanced use. //! //! Pin drivers can be created using [`Flex::new`], [`Input::new`], //! [`Output::new`] and [`OutputOpenDrain::new`]. If you need the pin drivers to //! carry the type of the pin, you can use the [`Flex::new_typed`], //! [`Input::new_typed`], [`Output::new_typed`], and //! [`OutputOpenDrain::new_typed`] functions. //! //! Each pin is a different type initially. Internally, `esp-hal` will often //! erase their types automatically, but they can also be converted into //! [`AnyPin`] manually by calling [`Pin::degrade`]. //! //! The [`Io`] struct can also be used to configure the interrupt handler for //! GPIO interrupts. For more information, see the //! [`Io::set_interrupt_handler`]. //! //! ## GPIO interconnect //! //! Sometimes you may want to connect peripherals together without using //! external hardware. The [`interconnect`] module provides tools to achieve //! this using GPIO pins. //! //! To obtain peripheral signals, use the [`GpioPin::split`] method to split a //! pin into an input and output signal. Alternatively, you may use //! [`Flex::split`], [`Flex::into_peripheral_output`], //! [`Flex::peripheral_input`], and similar methods to split a pin driver into //! an input and output signal. You can then pass these signals to the //! peripheral drivers similar to how you would pass a pin. //! //! ### GPIO interconnect example //! //! See the [Inverting TX and RX Pins] example of the UART documentation. //! //! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/ //! [Inverting TX and RX Pins]: crate::uart#inverting-rx-and-tx-pins use portable_atomic::{AtomicPtr, Ordering}; use procmacros::ram; #[cfg(any(lp_io, rtc_cntl))] use crate::peripherals::gpio::{handle_rtcio, handle_rtcio_with_resistors}; pub use crate::soc::gpio::*; use crate::{ interrupt::{self, InterruptHandler, Priority}, peripheral::{Peripheral, PeripheralRef}, peripherals::{ gpio::{handle_gpio_input, handle_gpio_output, AnyPinInner}, Interrupt, GPIO, IO_MUX, }, private::{self, Sealed}, InterruptConfigurable, DEFAULT_INTERRUPT_HANDLER, }; pub mod interconnect; mod placeholder; pub use placeholder::NoPin; #[cfg(soc_etm)] pub mod etm; #[cfg(lp_io)] pub mod lp_io; #[cfg(all(rtc_io, not(esp32)))] pub mod rtc_io; /// Convenience constant for `Option::None` pin static USER_INTERRUPT_HANDLER: CFnPtr = CFnPtr::new(); struct CFnPtr(AtomicPtr<()>); impl CFnPtr { pub const fn new() -> Self { Self(AtomicPtr::new(core::ptr::null_mut())) } pub fn store(&self, f: extern "C" fn()) { self.0.store(f as *mut (), Ordering::Relaxed); } pub fn call(&self) { let ptr = self.0.load(Ordering::Relaxed); if !ptr.is_null() { unsafe { (core::mem::transmute::<*mut (), extern "C" fn()>(ptr))() }; } } } /// Event used to trigger interrupts. #[derive(Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Event { /// Interrupts trigger on rising pin edge. RisingEdge = 1, /// Interrupts trigger on falling pin edge. FallingEdge = 2, /// Interrupts trigger on either rising or falling pin edges. AnyEdge = 3, /// Interrupts trigger on low level LowLevel = 4, /// Interrupts trigger on high level HighLevel = 5, } impl From for Event { fn from(value: WakeEvent) -> Self { match value { WakeEvent::LowLevel => Event::LowLevel, WakeEvent::HighLevel => Event::HighLevel, } } } /// Event used to wake up from light sleep. #[derive(Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum WakeEvent { /// Wake on low level LowLevel = 4, /// Wake on high level HighLevel = 5, } /// Digital input or output level. /// /// `Level` can be used to control a GPIO output, and it can act as a peripheral /// signal and be connected to peripheral inputs and outputs. /// /// When connected to a peripheral /// input, the peripheral will read the corresponding level from that signal. /// /// When connected to a peripheral output, the level will be ignored. #[derive(Debug, Eq, PartialEq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Level { /// Low Low, /// High High, } impl Sealed for Level {} impl core::ops::Not for Level { type Output = Self; fn not(self) -> Self { match self { Self::Low => Self::High, Self::High => Self::Low, } } } impl From for Level { fn from(val: bool) -> Self { match val { true => Self::High, false => Self::Low, } } } impl From for bool { fn from(level: Level) -> bool { match level { Level::Low => false, Level::High => true, } } } /// Pull setting for an input. #[derive(Debug, Eq, PartialEq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Pull { /// No pull None, /// Pull up Up, /// Pull down Down, } /// Drive strength (values are approximates) #[derive(Debug, Eq, PartialEq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DriveStrength { /// Drive strength of approximately 5mA. I5mA = 0, /// Drive strength of approximately 10mA. I10mA = 1, /// Drive strength of approximately 20mA. I20mA = 2, /// Drive strength of approximately 40mA. I40mA = 3, } /// Alternate functions /// /// GPIO pins can be configured for various functions, such as GPIO /// or being directly connected to a peripheral's signal like UART, SPI, etc. /// The `AlternateFunction` enum allows selecting one of several functions that /// a pin can perform, rather than using it as a general-purpose input or /// output. /// /// The different variants correspond to different functionality depending on /// the chip and the specific pin. For more information, refer to your chip's #[doc = crate::trm_markdown_link!("iomuxgpio")] #[derive(Debug, Eq, PartialEq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AlternateFunction { /// Alternate function 0. Function0 = 0, /// Alternate function 1. Function1 = 1, /// Alternate function 2. Function2 = 2, /// Alternate function 3. Function3 = 3, /// Alternate function 4. Function4 = 4, /// Alternate function 5. Function5 = 5, } impl TryFrom for AlternateFunction { type Error = (); fn try_from(value: usize) -> Result { match value { 0 => Ok(AlternateFunction::Function0), 1 => Ok(AlternateFunction::Function1), 2 => Ok(AlternateFunction::Function2), 3 => Ok(AlternateFunction::Function3), 4 => Ok(AlternateFunction::Function4), 5 => Ok(AlternateFunction::Function5), _ => Err(()), } } } /// RTC function #[derive(Debug, Eq, PartialEq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum RtcFunction { /// RTC mode. Rtc = 0, /// Digital mode. Digital = 1, } /// Trait implemented by RTC pins pub trait RtcPin: Pin { /// RTC number of the pin #[cfg(xtensa)] fn rtc_number(&self) -> u8; /// Configure the pin #[cfg(any(xtensa, esp32c6))] fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: RtcFunction); /// Enable or disable PAD_HOLD fn rtcio_pad_hold(&mut self, enable: bool); /// # Safety /// /// The `level` argument needs to be a valid setting for the /// `rtc_cntl.gpio_wakeup.gpio_pinX_int_type`. #[cfg(any(esp32c3, esp32c2, esp32c6))] unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8); } /// Trait implemented by RTC pins which supporting internal pull-up / pull-down /// resistors. pub trait RtcPinWithResistors: RtcPin { /// Enable/disable the internal pull-up resistor fn rtcio_pullup(&mut self, enable: bool); /// Enable/disable the internal pull-down resistor fn rtcio_pulldown(&mut self, enable: bool); } /// Common trait implemented by pins pub trait Pin: Sealed { /// GPIO number fn number(&self) -> u8; #[doc(hidden)] fn mask(&self) -> u32 { 1 << (self.number() % 32) } /// Type-erase (degrade) this pin into an [`AnyPin`]. /// /// This converts pin singletons (`GpioPin<0>`, …), which are all different /// types, into the same type. It is useful for creating arrays of pins, /// or avoiding generics. /// /// ## Example /// /// ```rust, no_run #[doc = crate::before_snippet!()] /// use esp_hal::gpio::{AnyPin, Pin, Output, Level}; /// use esp_hal::delay::Delay; /// /// fn toggle_pins(pins: [AnyPin; 2], delay: &mut Delay) { /// let [red, blue] = pins; /// let mut red = Output::new(red, Level::High); /// let mut blue = Output::new(blue, Level::Low); /// /// loop { /// red.toggle(); /// blue.toggle(); /// delay.delay_millis(500); /// } /// } /// /// let pins: [AnyPin; 2] = [ /// peripherals.GPIO5.degrade(), /// peripherals.GPIO6.degrade(), /// ]; /// /// let mut delay = Delay::new(); /// toggle_pins(pins, &mut delay); /// # } /// ``` fn degrade(self) -> AnyPin where Self: Sized, { self.degrade_pin(private::Internal) } #[doc(hidden)] fn degrade_pin(&self, _: private::Internal) -> AnyPin; /// Enable/disable sleep-mode #[doc(hidden)] fn sleep_mode(&mut self, on: bool, _: private::Internal) { io_mux_reg(self.number()).modify(|_, w| w.slp_sel().bit(on)); } /// Configure the alternate function #[doc(hidden)] fn set_alternate_function(&mut self, alternate: AlternateFunction, _: private::Internal) { io_mux_reg(self.number()).modify(|_, w| unsafe { w.mcu_sel().bits(alternate as u8) }); } /// Enable or disable the GPIO pin output buffer. #[doc(hidden)] fn enable_output(&self, enable: bool, _: private::Internal) { self.gpio_bank(private::Internal) .write_out_en(self.mask(), enable); } /// Enable input for the pin #[doc(hidden)] fn enable_input(&mut self, on: bool, _: private::Internal) { io_mux_reg(self.number()).modify(|_, w| w.fun_ie().bit(on)); } #[doc(hidden)] fn output_signals(&self, _: private::Internal) -> &[(AlternateFunction, OutputSignal)]; #[doc(hidden)] fn input_signals(&self, _: private::Internal) -> &[(AlternateFunction, InputSignal)]; #[doc(hidden)] fn gpio_bank(&self, _: private::Internal) -> GpioRegisterAccess; #[doc(hidden)] fn pull_direction(&self, pull: Pull, _: private::Internal) { let pull_up = pull == Pull::Up; let pull_down = pull == Pull::Down; #[cfg(esp32)] crate::soc::gpio::errata36(self.degrade_pin(private::Internal), pull_up, pull_down); io_mux_reg(self.number()).modify(|_, w| { w.fun_wpd().bit(pull_down); w.fun_wpu().bit(pull_up) }); } } /// Trait implemented by pins which can be used as inputs. pub trait InputPin: Pin + Into + 'static { /// Init as input with the given pull-up/pull-down #[doc(hidden)] fn init_input(&self, pull: Pull, _: private::Internal) { self.pull_direction(pull, private::Internal); #[cfg(usb_device)] disable_usb_pads(self.number()); io_mux_reg(self.number()).modify(|_, w| unsafe { w.mcu_sel().bits(GPIO_FUNCTION as u8); w.fun_ie().set_bit(); w.slp_sel().clear_bit() }); } /// The current state of the input #[doc(hidden)] fn is_input_high(&self, _: private::Internal) -> bool { self.gpio_bank(private::Internal).read_input() & self.mask() != 0 } } /// Trait implemented by pins which can be used as outputs. pub trait OutputPin: Pin + Into + 'static { /// Set up as output #[doc(hidden)] fn init_output( &mut self, alternate: AlternateFunction, open_drain: bool, input_enable: Option, _: private::Internal, ) { self.enable_output(true, private::Internal); let gpio = unsafe { GPIO::steal() }; gpio.pin(self.number() as usize) .modify(|_, w| w.pad_driver().bit(open_drain)); gpio.func_out_sel_cfg(self.number() as usize) .modify(|_, w| unsafe { w.out_sel().bits(OutputSignal::GPIO as OutputSignalType) }); #[cfg(any(esp32c3, esp32s3))] disable_usb_pads(self.number()); io_mux_reg(self.number()).modify(|_, w| unsafe { w.mcu_sel().bits(alternate as u8); if let Some(input_enable) = input_enable { w.fun_ie().bit(input_enable); } w.fun_drv().bits(DriveStrength::I20mA as u8); w.slp_sel().clear_bit() }); } /// Configure open-drain mode #[doc(hidden)] fn set_to_open_drain_output(&mut self, _: private::Internal) { self.init_output(GPIO_FUNCTION, true, Some(true), private::Internal); } /// Configure output mode #[doc(hidden)] fn set_to_push_pull_output(&mut self, _: private::Internal) { self.init_output(GPIO_FUNCTION, false, None, private::Internal); } /// Set the pin's level to high or low #[doc(hidden)] fn set_output_high(&mut self, high: bool, _: private::Internal) { self.gpio_bank(private::Internal) .write_output(self.mask(), high); } /// Configure the [DriveStrength] of the pin #[doc(hidden)] fn set_drive_strength(&mut self, strength: DriveStrength, _: private::Internal) { io_mux_reg(self.number()).modify(|_, w| unsafe { w.fun_drv().bits(strength as u8) }); } /// Enable/disable open-drain mode #[doc(hidden)] fn enable_open_drain(&mut self, on: bool, _: private::Internal) { unsafe { GPIO::steal() } .pin(self.number() as usize) .modify(|_, w| w.pad_driver().bit(on)); } /// Configure internal pull-up resistor in sleep mode #[doc(hidden)] fn internal_pull_up_in_sleep_mode(&mut self, on: bool, _: private::Internal) { io_mux_reg(self.number()).modify(|_, w| w.mcu_wpu().bit(on)); } /// Configure internal pull-down resistor in sleep mode #[doc(hidden)] fn internal_pull_down_in_sleep_mode(&mut self, on: bool, _: private::Internal) { io_mux_reg(self.number()).modify(|_, w| w.mcu_wpd().bit(on)); } /// Is the output set to high #[doc(hidden)] fn is_set_high(&self, _: private::Internal) -> bool { self.gpio_bank(private::Internal).read_output() & self.mask() != 0 } } /// Trait implemented by pins which can be used as analog pins pub trait AnalogPin: Pin { /// Configure the pin for analog operation #[doc(hidden)] fn set_analog(&self, _: private::Internal); } /// Trait implemented by pins which can be used as Touchpad pins pub trait TouchPin: Pin { /// Configure the pin for analog operation #[doc(hidden)] fn set_touch(&self, _: private::Internal); /// Reads the pin's touch measurement register #[doc(hidden)] fn touch_measurement(&self, _: private::Internal) -> u16; /// Maps the pin nr to the touch pad nr #[doc(hidden)] fn touch_nr(&self, _: private::Internal) -> u8; /// Set a pins touch threshold for interrupts. #[doc(hidden)] fn set_threshold(&self, threshold: u16, _: private::Internal); } #[doc(hidden)] #[derive(Clone, Copy)] pub enum GpioRegisterAccess { Bank0, #[cfg(any(esp32, esp32s2, esp32s3))] Bank1, } impl From for GpioRegisterAccess { fn from(_gpio_num: usize) -> Self { #[cfg(any(esp32, esp32s2, esp32s3))] if _gpio_num >= 32 { return GpioRegisterAccess::Bank1; } GpioRegisterAccess::Bank0 } } impl GpioRegisterAccess { fn write_out_en(self, word: u32, enable: bool) { if enable { self.write_out_en_set(word); } else { self.write_out_en_clear(word); } } fn write_out_en_clear(self, word: u32) { match self { Self::Bank0 => Bank0GpioRegisterAccess::write_out_en_clear(word), #[cfg(any(esp32, esp32s2, esp32s3))] Self::Bank1 => Bank1GpioRegisterAccess::write_out_en_clear(word), } } fn write_out_en_set(self, word: u32) { match self { Self::Bank0 => Bank0GpioRegisterAccess::write_out_en_set(word), #[cfg(any(esp32, esp32s2, esp32s3))] Self::Bank1 => Bank1GpioRegisterAccess::write_out_en_set(word), } } fn read_input(self) -> u32 { match self { Self::Bank0 => Bank0GpioRegisterAccess::read_input(), #[cfg(any(esp32, esp32s2, esp32s3))] Self::Bank1 => Bank1GpioRegisterAccess::read_input(), } } fn read_output(self) -> u32 { match self { Self::Bank0 => Bank0GpioRegisterAccess::read_output(), #[cfg(any(esp32, esp32s2, esp32s3))] Self::Bank1 => Bank1GpioRegisterAccess::read_output(), } } fn read_interrupt_status(self) -> u32 { match self { Self::Bank0 => Bank0GpioRegisterAccess::read_interrupt_status(), #[cfg(any(esp32, esp32s2, esp32s3))] Self::Bank1 => Bank1GpioRegisterAccess::read_interrupt_status(), } } fn write_interrupt_status_clear(self, word: u32) { match self { Self::Bank0 => Bank0GpioRegisterAccess::write_interrupt_status_clear(word), #[cfg(any(esp32, esp32s2, esp32s3))] Self::Bank1 => Bank1GpioRegisterAccess::write_interrupt_status_clear(word), } } fn write_output(self, word: u32, set: bool) { if set { self.write_output_set(word); } else { self.write_output_clear(word); } } fn write_output_set(self, word: u32) { match self { Self::Bank0 => Bank0GpioRegisterAccess::write_output_set(word), #[cfg(any(esp32, esp32s2, esp32s3))] Self::Bank1 => Bank1GpioRegisterAccess::write_output_set(word), } } fn write_output_clear(self, word: u32) { match self { Self::Bank0 => Bank0GpioRegisterAccess::write_output_clear(word), #[cfg(any(esp32, esp32s2, esp32s3))] Self::Bank1 => Bank1GpioRegisterAccess::write_output_clear(word), } } } struct Bank0GpioRegisterAccess; impl Bank0GpioRegisterAccess { fn write_out_en_clear(word: u32) { unsafe { GPIO::steal() } .enable_w1tc() .write(|w| unsafe { w.bits(word) }); } fn write_out_en_set(word: u32) { unsafe { GPIO::steal() } .enable_w1ts() .write(|w| unsafe { w.bits(word) }); } fn read_input() -> u32 { unsafe { GPIO::steal() }.in_().read().bits() } fn read_output() -> u32 { unsafe { GPIO::steal() }.out().read().bits() } fn read_interrupt_status() -> u32 { unsafe { GPIO::steal() }.status().read().bits() } fn write_interrupt_status_clear(word: u32) { unsafe { GPIO::steal() } .status_w1tc() .write(|w| unsafe { w.bits(word) }); } fn write_output_set(word: u32) { unsafe { GPIO::steal() } .out_w1ts() .write(|w| unsafe { w.bits(word) }); } fn write_output_clear(word: u32) { unsafe { GPIO::steal() } .out_w1tc() .write(|w| unsafe { w.bits(word) }); } } #[cfg(any(esp32, esp32s2, esp32s3))] struct Bank1GpioRegisterAccess; #[cfg(any(esp32, esp32s2, esp32s3))] impl Bank1GpioRegisterAccess { fn write_out_en_clear(word: u32) { unsafe { GPIO::steal() } .enable1_w1tc() .write(|w| unsafe { w.bits(word) }); } fn write_out_en_set(word: u32) { unsafe { GPIO::steal() } .enable1_w1ts() .write(|w| unsafe { w.bits(word) }); } fn read_input() -> u32 { unsafe { GPIO::steal() }.in1().read().bits() } fn read_output() -> u32 { unsafe { GPIO::steal() }.out1().read().bits() } fn read_interrupt_status() -> u32 { unsafe { GPIO::steal() }.status1().read().bits() } fn write_interrupt_status_clear(word: u32) { unsafe { GPIO::steal() } .status1_w1tc() .write(|w| unsafe { w.bits(word) }); } fn write_output_set(word: u32) { unsafe { GPIO::steal() } .out1_w1ts() .write(|w| unsafe { w.bits(word) }); } fn write_output_clear(word: u32) { unsafe { GPIO::steal() } .out1_w1tc() .write(|w| unsafe { w.bits(word) }); } } /// GPIO pin pub struct GpioPin; /// Type-erased GPIO pin pub struct AnyPin(pub(crate) AnyPinInner); impl GpioPin where Self: Pin, { /// Create a pin out of thin air. /// /// # Safety /// /// Ensure that only one instance of a pin exists at one time. pub unsafe fn steal() -> Self { Self } /// Split the pin into an input and output signal. /// /// Peripheral signals allow connecting peripherals together without using /// external hardware. pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { ( interconnect::InputSignal::new(self.degrade_pin(private::Internal)), interconnect::OutputSignal::new(self.degrade_pin(private::Internal)), ) } } /// Workaround to make D+ and D- work on the ESP32-C3 and ESP32-S3, which by /// default are assigned to the `USB_SERIAL_JTAG` peripheral. #[cfg(usb_device)] fn disable_usb_pads(gpionum: u8) { cfg_if::cfg_if! { if #[cfg(esp32c3)] { let pins = [18, 19]; } else if #[cfg(esp32c6)] { let pins = [12, 13]; } else if #[cfg(esp32h2)] { let pins = [26, 27]; } else if #[cfg(esp32s3)] { let pins = [19, 20]; } else { compile_error!("Please define USB pins for this chip"); } } if pins.contains(&gpionum) { unsafe { crate::peripherals::USB_DEVICE::steal() } .conf0() .modify(|_, w| { w.usb_pad_enable().clear_bit(); w.dm_pullup().clear_bit(); w.dm_pulldown().clear_bit(); w.dp_pullup().clear_bit(); w.dp_pulldown().clear_bit() }); } } impl Peripheral for GpioPin where Self: Pin, { type P = GpioPin; unsafe fn clone_unchecked(&self) -> Self::P { core::ptr::read(self as *const _) } } impl private::Sealed for GpioPin {} pub(crate) fn bind_default_interrupt_handler() { // We first check if a handler is set in the vector table. if let Some(handler) = interrupt::bound_handler(Interrupt::GPIO) { let handler = handler as *const unsafe extern "C" fn(); // We only allow binding the default handler if nothing else is bound. // This prevents silently overwriting RTIC's interrupt handler, if using GPIO. if !core::ptr::eq(handler, DEFAULT_INTERRUPT_HANDLER.handler() as _) { // The user has configured an interrupt handler they wish to use. info!("Not using default GPIO interrupt handler: already bound in vector table"); return; } } // The vector table doesn't contain a custom entry.Still, the // peripheral interrupt may already be bound to something else. if interrupt::bound_cpu_interrupt_for(crate::Cpu::current(), Interrupt::GPIO).is_some() { info!("Not using default GPIO interrupt handler: peripheral interrupt already in use"); return; } unsafe { interrupt::bind_interrupt(Interrupt::GPIO, default_gpio_interrupt_handler) }; // By default, we use lowest priority unwrap!(interrupt::enable(Interrupt::GPIO, Priority::min())); } /// General Purpose Input/Output driver pub struct Io { _io_mux: IO_MUX, } impl Io { /// Initialize the I/O driver. pub fn new(_io_mux: IO_MUX) -> Self { Io { _io_mux } } /// Set the interrupt priority for GPIO interrupts. pub fn set_interrupt_priority(&self, prio: Priority) { unwrap!(interrupt::enable(Interrupt::GPIO, prio)); } } impl crate::private::Sealed for Io {} impl InterruptConfigurable for Io { /// Install the given interrupt handler replacing any previously set /// handler. /// /// ⚠️ Be careful when using this together with the async API: /// /// - The async driver will disable any interrupts whose status is not /// cleared by the user handler. /// - Clearing the interrupt status in the user handler will prevent the /// async driver from detecting the interrupt, silently disabling the /// corresponding pin's async API. /// - You will not be notified if you make a mistake. fn set_interrupt_handler(&mut self, handler: InterruptHandler) { for core in crate::Cpu::other() { crate::interrupt::disable(core, Interrupt::GPIO); } self.set_interrupt_priority(handler.priority()); unsafe { interrupt::bind_interrupt(Interrupt::GPIO, user_gpio_interrupt_handler) }; USER_INTERRUPT_HANDLER.store(handler.handler()); } } #[ram] extern "C" fn user_gpio_interrupt_handler() { USER_INTERRUPT_HANDLER.call(); default_gpio_interrupt_handler(); } #[ram] extern "C" fn default_gpio_interrupt_handler() { handle_pin_interrupts(on_pin_irq); } #[ram] fn on_pin_irq(pin_nr: u8) { // FIXME: async handlers signal completion by disabling the interrupt, but this // conflicts with user handlers. set_int_enable(pin_nr, 0, 0, false); asynch::PIN_WAKERS[pin_nr as usize].wake(); // wake task } #[doc(hidden)] #[macro_export] macro_rules! if_output_pin { // Base case: not an Output pin, substitute the else branch ({ $($then:tt)* } else { $($else:tt)* }) => { $($else)* }; // First is an Output pin, skip checking and substitute the then branch (Output $(, $other:ident)* { $($then:tt)* } else { $($else:tt)* }) => { $($then)* }; // First is not an Output pin, check the rest ($not:ident $(, $other:ident)* { $($then:tt)* } else { $($else:tt)* }) => { $crate::if_output_pin!($($other),* { $($then)* } else { $($else)* }) }; } #[doc(hidden)] #[macro_export] macro_rules! if_rtcio_pin { // Base case: not an RtcIo pin, substitute the else branch ({ $($then:tt)* } else { $($else:tt)* }) => { $($else)* }; // First is an RtcIo pin, skip checking and substitute the then branch (RtcIo $(, $other:ident)* { $($then:tt)* } else { $($else:tt)* }) => { $($then)* }; (RtcIoInput $(, $other:ident)* { $($then:tt)* } else { $($else:tt)* }) => { $($then)* }; // First is not an RtcIo pin, check the rest ($not:ident $(, $other:ident)* { $($then:tt)* } else { $($else:tt)* }) => { $crate::if_rtcio_pin!($($other),* { $($then)* } else { $($else)* }) }; } #[doc(hidden)] #[macro_export] macro_rules! io_type { (Input, $gpionum:literal) => { impl $crate::gpio::InputPin for $crate::gpio::GpioPin<$gpionum> {} }; (Output, $gpionum:literal) => { impl $crate::gpio::OutputPin for $crate::gpio::GpioPin<$gpionum> {} }; (Analog, $gpionum:literal) => { // FIXME: the implementation shouldn't be in the GPIO module #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2))] impl $crate::gpio::AnalogPin for $crate::gpio::GpioPin<$gpionum> { /// Configures the pin for analog mode. fn set_analog(&self, _: $crate::private::Internal) { use $crate::peripherals::GPIO; $crate::gpio::io_mux_reg($gpionum).modify(|_, w| unsafe { w.mcu_sel().bits(1); w.fun_ie().clear_bit(); w.fun_wpu().clear_bit(); w.fun_wpd().clear_bit() }); unsafe { GPIO::steal() } .enable_w1tc() .write(|w| unsafe { w.bits(1 << $gpionum) }); } } }; ($other:ident, $gpionum:literal) => { // TODO }; } #[doc(hidden)] #[macro_export] macro_rules! gpio { ( $( ($gpionum:literal, [$($type:tt),*] $( ( $( $af_input_num:literal => $af_input_signal:ident )* ) ( $( $af_output_num:literal => $af_output_signal:ident )* ) )? ) )+ ) => { paste::paste! { $( $( $crate::io_type!($type, $gpionum); )* impl $crate::gpio::Pin for $crate::gpio::GpioPin<$gpionum> { fn number(&self) -> u8 { $gpionum } fn degrade_pin(&self, _: $crate::private::Internal) -> $crate::gpio::AnyPin { $crate::gpio::AnyPin(AnyPinInner::[< Gpio $gpionum >](unsafe { Self::steal() })) } fn gpio_bank(&self, _: $crate::private::Internal) -> $crate::gpio::GpioRegisterAccess { $crate::gpio::GpioRegisterAccess::from($gpionum) } fn output_signals(&self, _: $crate::private::Internal) -> &[($crate::gpio::AlternateFunction, $crate::gpio::OutputSignal)] { &[ $( $( ( $crate::gpio::AlternateFunction::[< Function $af_output_num >], $crate::gpio::OutputSignal::$af_output_signal ), )* )? ] } fn input_signals(&self, _: $crate::private::Internal) -> &[($crate::gpio::AlternateFunction, $crate::gpio::InputSignal)] { &[ $( $( ( $crate::gpio::AlternateFunction::[< Function $af_input_num >], $crate::gpio::InputSignal::$af_input_signal ), )* )? ] } } impl From<$crate::gpio::GpioPin<$gpionum>> for $crate::gpio::AnyPin { fn from(pin: $crate::gpio::GpioPin<$gpionum>) -> Self { $crate::gpio::Pin::degrade(pin) } } )+ pub(crate) enum AnyPinInner { $( []($crate::gpio::GpioPin<$gpionum>), )+ } impl $crate::peripheral::Peripheral for $crate::gpio::AnyPin { type P = $crate::gpio::AnyPin; unsafe fn clone_unchecked(&self) -> Self { match self.0 { $(AnyPinInner::[](_) => { Self(AnyPinInner::[< Gpio $gpionum >](unsafe { $crate::gpio::GpioPin::steal() })) })+ } } } // These macros call the code block on the actually contained GPIO pin. #[doc(hidden)] macro_rules! handle_gpio_output { ($this:expr, $inner:ident, $code:tt) => { match $this { $( AnyPinInner::[]($inner) => $crate::if_output_pin!($($type),* { $code } else {{ let _ = $inner; panic!("Unsupported") }}), )+ } } } #[doc(hidden)] macro_rules! handle_gpio_input { ($this:expr, $inner:ident, $code:tt) => { match $this { $( AnyPinInner::[]($inner) => $code )+ } } } pub(crate) use handle_gpio_output; pub(crate) use handle_gpio_input; cfg_if::cfg_if! { if #[cfg(any(lp_io, rtc_cntl))] { #[doc(hidden)] macro_rules! handle_rtcio { ($this:expr, $inner:ident, $code:tt) => { match $this { $( AnyPinInner::[]($inner) => $crate::if_rtcio_pin!($($type),* { $code } else {{ let _ = $inner; panic!("Unsupported") }}), )+ } } } #[doc(hidden)] macro_rules! handle_rtcio_with_resistors { ($this:expr, $inner:ident, $code:tt) => { match $this { $( AnyPinInner::[]($inner) => $crate::if_rtcio_pin!($($type),* { $crate::if_output_pin!($($type),* { $code } else {{ let _ = $inner; panic!("Unsupported") }}) } else {{ let _ = $inner; panic!("Unsupported") }}), )+ } } } pub(crate) use handle_rtcio; pub(crate) use handle_rtcio_with_resistors; } } } }; } /// Push-pull digital output. /// /// This driver configures the GPIO pin to be a push-pull output driver. /// Push-pull means that the driver actively sets the output voltage level /// for both high and low logical [`Level`]s. pub struct Output<'d, P = AnyPin> { pin: Flex<'d, P>, } impl

private::Sealed for Output<'_, P> {} impl<'d, P> Peripheral for Output<'d, P> { type P = Flex<'d, P>; unsafe fn clone_unchecked(&self) -> Self::P { self.pin.clone_unchecked() } } impl<'d> Output<'d> { /// Creates a new, type-erased GPIO output driver. /// /// The `initial_output` parameter sets the initial output level of the pin. /// /// ## Example /// /// The following example configures `GPIO5` to pulse a LED once. The /// example assumes that the LED is connected such that it is on when /// the pin is low. /// /// ```rust, no_run #[doc = crate::before_snippet!()] /// use esp_hal::gpio::{Level, Output}; /// use esp_hal::delay::Delay; /// /// fn blink_once(led: &mut Output<'_>, delay: &mut Delay) { /// led.set_low(); /// delay.delay_millis(500); /// led.set_high(); /// } /// /// let mut led = Output::new(peripherals.GPIO5, Level::High); /// let mut delay = Delay::new(); /// /// blink_once(&mut led, &mut delay); /// # } /// ``` #[inline] pub fn new(pin: impl Peripheral

+ 'd, initial_output: Level) -> Self { Self::new_typed(pin.map_into(), initial_output) } } impl<'d, P> Output<'d, P> where P: OutputPin, { /// Creates a new, typed GPIO output driver. /// /// The `initial_output` parameter sets the initial output level of the pin. /// /// This constructor is useful when you want to limit which GPIO pin can be /// used for a particular function. /// /// ## Example /// /// The following example configures `GPIO5` to pulse a LED once. The /// example assumes that the LED is connected such that it is on when /// the pin is low. /// /// ```rust, no_run #[doc = crate::before_snippet!()] /// use esp_hal::gpio::{GpioPin, Level, Output}; /// use esp_hal::delay::Delay; /// /// fn blink_once(led: &mut Output<'_, GpioPin<5>>, delay: &mut Delay) { /// led.set_low(); /// delay.delay_millis(500); /// led.set_high(); /// } /// /// let mut led = Output::new_typed(peripherals.GPIO5, Level::High); /// let mut delay = Delay::new(); /// /// blink_once(&mut led, &mut delay); /// # } /// ``` #[inline] pub fn new_typed(pin: impl Peripheral

+ 'd, initial_output: Level) -> Self { let mut pin = Flex::new_typed(pin); pin.set_level(initial_output); pin.set_as_output(); Self { pin } } /// Split the pin into an input and output signal. /// /// Peripheral signals allow connecting peripherals together without using /// external hardware. pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { self.pin.split() } /// Returns a peripheral [input][interconnect::InputSignal] connected to /// this pin. /// /// The input signal can be passed to peripherals in place of an input pin. #[inline] pub fn peripheral_input(&self) -> interconnect::InputSignal { self.pin.peripheral_input() } /// Turns the pin object into a peripheral /// [output][interconnect::OutputSignal]. /// /// The output signal can be passed to peripherals in place of an output /// pin. #[inline] pub fn into_peripheral_output(self) -> interconnect::OutputSignal { self.pin.into_peripheral_output() } /// Set the output as high. #[inline] pub fn set_high(&mut self) { self.set_level(Level::High) } /// Set the output as low. #[inline] pub fn set_low(&mut self) { self.set_level(Level::Low) } /// Set the output level. #[inline] pub fn set_level(&mut self, level: Level) { self.pin.set_level(level) } /// Is the output pin set as high? #[inline] pub fn is_set_high(&self) -> bool { self.output_level() == Level::High } /// Is the output pin set as low? #[inline] pub fn is_set_low(&self) -> bool { self.output_level() == Level::Low } /// What level output is set to #[inline] pub fn output_level(&self) -> Level { self.pin.output_level() } /// Toggle pin output #[inline] pub fn toggle(&mut self) { self.pin.toggle(); } /// Configure the [DriveStrength] of the pin #[inline] pub fn set_drive_strength(&mut self, strength: DriveStrength) { self.pin.set_drive_strength(strength); } } /// Digital input. /// /// This driver configures the GPIO pin to be an input. Input drivers read the /// voltage of their pins and convert it to a logical [`Level`]. pub struct Input<'d, P = AnyPin> { pin: Flex<'d, P>, } impl

private::Sealed for Input<'_, P> {} impl<'d, P> Peripheral for Input<'d, P> { type P = Flex<'d, P>; unsafe fn clone_unchecked(&self) -> Self::P { self.pin.clone_unchecked() } } impl<'d> Input<'d> { /// Creates a new, type-erased GPIO input. /// /// The `pull` parameter configures internal pull-up or pull-down /// resistors. /// /// ## Example /// /// The following example configures `GPIO5` to read a button press. The /// example assumes that the button is connected such that the pin is low /// when the button is pressed. /// /// ```rust, no_run #[doc = crate::before_snippet!()] /// use esp_hal::gpio::{Level, Input, Pull}; /// use esp_hal::delay::Delay; /// /// fn print_when_pressed(button: &mut Input<'_>, delay: &mut Delay) { /// let mut was_pressed = false; /// loop { /// let is_pressed = button.is_low(); /// if is_pressed && !was_pressed { /// println!("Button pressed!"); /// } /// was_pressed = is_pressed; /// delay.delay_millis(100); /// } /// } /// /// let mut button = Input::new( /// peripherals.GPIO5, /// Pull::Up, /// ); /// let mut delay = Delay::new(); /// /// print_when_pressed(&mut button, &mut delay); /// # } /// ``` #[inline] pub fn new(pin: impl Peripheral

+ 'd, pull: Pull) -> Self { Self::new_typed(pin.map_into(), pull) } } impl<'d, P> Input<'d, P> where P: InputPin, { /// Creates a new, typed GPIO input. /// /// The `pull` parameter configures internal pull-up or pull-down /// resistors. /// /// This constructor is useful when you want to limit which GPIO pin can be /// used for a particular function. /// /// ## Example /// /// The following example configures `GPIO5` to read a button press. The /// example assumes that the button is connected such that the pin is low /// when the button is pressed. /// /// ```rust, no_run #[doc = crate::before_snippet!()] /// use esp_hal::gpio::{GpioPin, Level, Input, Pull}; /// use esp_hal::delay::Delay; /// /// fn print_when_pressed( /// button: &mut Input<'_, GpioPin<5>>, /// delay: &mut Delay, /// ) { /// let mut was_pressed = false; /// loop { /// let is_pressed = button.is_low(); /// if is_pressed && !was_pressed { /// println!("Button pressed!"); /// } /// was_pressed = is_pressed; /// delay.delay_millis(100); /// } /// } /// /// let mut button = Input::new_typed( /// peripherals.GPIO5, /// Pull::Up, /// ); /// let mut delay = Delay::new(); /// /// print_when_pressed(&mut button, &mut delay); /// # } /// ``` #[inline] pub fn new_typed(pin: impl Peripheral

+ 'd, pull: Pull) -> Self { let mut pin = Flex::new_typed(pin); pin.set_as_input(pull); Self { pin } } /// Returns a peripheral [input][interconnect::InputSignal] connected to /// this pin. /// /// The input signal can be passed to peripherals in place of an input pin. #[inline] pub fn peripheral_input(&self) -> interconnect::InputSignal { self.pin.peripheral_input() } /// Get whether the pin input level is high. #[inline] pub fn is_high(&self) -> bool { self.level() == Level::High } /// Get whether the pin input level is low. #[inline] pub fn is_low(&self) -> bool { self.level() == Level::Low } /// Get the current pin input level. #[inline] pub fn level(&self) -> Level { self.pin.level() } /// Listen for interrupts #[inline] pub fn listen(&mut self, event: Event) { self.pin.listen(event); } /// Stop listening for interrupts #[inline] pub fn unlisten(&mut self) { self.pin.unlisten(); } /// Clear the interrupt status bit for this Pin #[inline] pub fn clear_interrupt(&mut self) { self.pin.clear_interrupt(); } /// Checks if the interrupt status bit for this Pin is set #[inline] pub fn is_interrupt_set(&self) -> bool { self.pin.is_interrupt_set() } /// Enable as a wake-up source. /// /// This will unlisten for interrupts #[inline] pub fn wakeup_enable(&mut self, enable: bool, event: WakeEvent) { self.pin.wakeup_enable(enable, event); } } impl

Input<'_, P> where P: InputPin + OutputPin, { /// Split the pin into an input and output signal. /// /// Peripheral signals allow connecting peripherals together without using /// external hardware. pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { self.pin.split() } /// Turns the pin object into a peripheral /// [output][interconnect::OutputSignal]. /// /// The output signal can be passed to peripherals in place of an output /// pin. #[inline] pub fn into_peripheral_output(self) -> interconnect::OutputSignal { self.pin.into_peripheral_output() } } /// Open drain digital output. /// /// This driver configures the GPIO pin to be an open drain output driver. /// Open drain means that the driver actively pulls the output voltage level low /// for the low logical [`Level`], but leaves the high level floating, which is /// then determined by external hardware, or internal pull-up/pull-down /// resistors. pub struct OutputOpenDrain<'d, P = AnyPin> { pin: Flex<'d, P>, } impl

private::Sealed for OutputOpenDrain<'_, P> {} impl<'d, P> Peripheral for OutputOpenDrain<'d, P> { type P = Flex<'d, P>; unsafe fn clone_unchecked(&self) -> Self::P { self.pin.clone_unchecked() } } impl<'d> OutputOpenDrain<'d> { /// Creates a new, type-erased GPIO output driver. /// /// The `initial_output` parameter sets the initial output level of the pin. /// The `pull` parameter configures internal pull-up or pull-down /// resistors. /// /// ## Example /// /// The following example configures `GPIO5` to pulse a LED once. The /// example assumes that the LED is connected such that it is on when /// the pin is low. /// /// ```rust, no_run #[doc = crate::before_snippet!()] /// use esp_hal::gpio::{Level, OutputOpenDrain, Pull}; /// use esp_hal::delay::Delay; /// /// fn blink_once(led: &mut OutputOpenDrain<'_>, delay: &mut Delay) { /// led.set_low(); /// delay.delay_millis(500); /// led.set_high(); /// } /// /// let mut led = OutputOpenDrain::new( /// peripherals.GPIO5, /// Level::High, /// Pull::Up, /// ); /// let mut delay = Delay::new(); /// /// blink_once(&mut led, &mut delay); /// # } /// ``` #[inline] pub fn new( pin: impl Peripheral

+ 'd, initial_output: Level, pull: Pull, ) -> Self { Self::new_typed(pin.map_into(), initial_output, pull) } } impl<'d, P> OutputOpenDrain<'d, P> where P: InputPin + OutputPin, { /// Creates a new, typed GPIO output driver. /// /// The `initial_output` parameter sets the initial output level of the pin. /// The `pull` parameter configures internal pull-up or pull-down /// resistors. /// /// ## Example /// /// The following example configures `GPIO5` to pulse a LED once. The /// example assumes that the LED is connected such that it is on when /// the pin is low. /// /// ```rust, no_run #[doc = crate::before_snippet!()] /// use esp_hal::gpio::{GpioPin, Level, OutputOpenDrain, Pull}; /// use esp_hal::delay::Delay; /// /// fn blink_once( /// led: &mut OutputOpenDrain<'_, GpioPin<5>>, /// delay: &mut Delay, /// ) { /// led.set_low(); /// delay.delay_millis(500); /// led.set_high(); /// } /// /// let mut led = OutputOpenDrain::new_typed( /// peripherals.GPIO5, /// Level::High, /// Pull::Up, /// ); /// let mut delay = Delay::new(); /// /// blink_once(&mut led, &mut delay); /// # } /// ``` #[inline] pub fn new_typed(pin: impl Peripheral

+ 'd, initial_output: Level, pull: Pull) -> Self { let mut pin = Flex::new_typed(pin); pin.set_level(initial_output); pin.set_as_open_drain(pull); Self { pin } } /// Split the pin into an input and output signal. /// /// Peripheral signals allow connecting peripherals together without using /// external hardware. pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { self.pin.split() } /// Returns a peripheral [input][interconnect::InputSignal] connected to /// this pin. /// /// The input signal can be passed to peripherals in place of an input pin. #[inline] pub fn peripheral_input(&self) -> interconnect::InputSignal { self.pin.peripheral_input() } /// Turns the pin object into a peripheral /// [output][interconnect::OutputSignal]. /// /// The output signal can be passed to peripherals in place of an output /// pin. #[inline] pub fn into_peripheral_output(self) -> interconnect::OutputSignal { self.pin.into_peripheral_output() } /// Get whether the pin input level is high. #[inline] pub fn is_high(&self) -> bool { self.level() == Level::High } /// Get whether the pin input level is low. #[inline] pub fn is_low(&self) -> bool { self.level() == Level::Low } /// Get the current pin input level. #[inline] pub fn level(&self) -> Level { self.pin.level() } /// Listen for interrupts #[inline] pub fn listen(&mut self, event: Event) { self.pin.listen(event); } /// Clear the interrupt status bit for this Pin #[inline] pub fn clear_interrupt(&mut self) { self.pin.clear_interrupt(); } /// Set the output as high. #[inline] pub fn set_high(&mut self) { self.set_level(Level::High); } /// Set the output as low. #[inline] pub fn set_low(&mut self) { self.set_level(Level::Low); } /// Set the output level. #[inline] pub fn set_level(&mut self, level: Level) { self.pin.set_level(level); } /// Is the output pin set as high? #[inline] pub fn is_set_high(&self) -> bool { self.output_level() == Level::High } /// Is the output pin set as low? #[inline] pub fn is_set_low(&self) -> bool { self.output_level() == Level::Low } /// What level output is set to #[inline] pub fn output_level(&self) -> Level { self.pin.output_level() } /// Toggle pin output #[inline] pub fn toggle(&mut self) { self.pin.toggle() } /// Configure the [DriveStrength] of the pin pub fn set_drive_strength(&mut self, strength: DriveStrength) { self.pin.set_drive_strength(strength); } } /// Flexible pin driver. /// /// This driver allows changing the pin mode between input and output. pub struct Flex<'d, P = AnyPin> { pin: PeripheralRef<'d, P>, } impl

private::Sealed for Flex<'_, P> {} impl

Peripheral for Flex<'_, P> { type P = Self; unsafe fn clone_unchecked(&self) -> Self::P { Self { pin: PeripheralRef::new(core::ptr::read(&*self.pin as *const P)), } } } impl<'d> Flex<'d> { /// Create flexible pin driver for a [Pin]. /// No mode change happens. #[inline] pub fn new(pin: impl Peripheral

> + 'd) -> Self { Self::new_typed(pin.map_into()) } } impl<'d, P> Flex<'d, P> where P: Pin, { /// Create flexible pin driver for a [Pin]. /// No mode change happens. #[inline] pub fn new_typed(pin: impl Peripheral

+ 'd) -> Self { crate::into_ref!(pin); Self { pin } } /// Returns a peripheral [input][interconnect::InputSignal] connected to /// this pin. /// /// The input signal can be passed to peripherals in place of an input pin. #[inline] pub fn peripheral_input(&self) -> interconnect::InputSignal { self.pin.degrade_pin(private::Internal).split().0 } } impl

Flex<'_, P> where P: InputPin, { /// Set the GPIO to input mode. pub fn set_as_input(&mut self, pull: Pull) { self.pin.init_input(pull, private::Internal); self.pin.enable_output(false, private::Internal); } /// Get whether the pin input level is high. #[inline] pub fn is_high(&self) -> bool { self.level() == Level::High } /// Get whether the pin input level is low. #[inline] pub fn is_low(&self) -> bool { self.level() == Level::Low } /// Get the current pin input level. #[inline] pub fn level(&self) -> Level { self.pin.is_input_high(private::Internal).into() } fn listen_with_options( &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"); } _ => {} } } set_int_enable( self.pin.number(), gpio_intr_enable(int_enable, nmi_enable), event as u8, wake_up_from_light_sleep, ) } /// Listen for interrupts #[inline] pub fn listen(&mut self, event: Event) { self.listen_with_options(event, true, false, false) } /// Stop listening for interrupts pub fn unlisten(&mut self) { set_int_enable(self.pin.number(), 0, 0, false); } /// Clear the interrupt status bit for this Pin #[inline] pub fn clear_interrupt(&mut self) { self.pin .gpio_bank(private::Internal) .write_interrupt_status_clear(1 << (self.pin.number() % 32)); } /// Checks if the interrupt status bit for this Pin is set #[inline] pub fn is_interrupt_set(&self) -> bool { self.pin .gpio_bank(private::Internal) .read_interrupt_status() & 1 << (self.pin.number() % 32) != 0 } /// Enable as a wake-up source. /// /// This will unlisten for interrupts #[inline] pub fn wakeup_enable(&mut self, enable: bool, event: WakeEvent) { self.listen_with_options(event.into(), false, false, enable); } } impl

Flex<'_, P> where P: OutputPin, { /// Set the GPIO to output mode. pub fn set_as_output(&mut self) { self.pin.set_to_push_pull_output(private::Internal); } /// Set the output as high. #[inline] pub fn set_high(&mut self) { self.set_level(Level::High) } /// Set the output as low. #[inline] pub fn set_low(&mut self) { self.set_level(Level::Low) } /// Set the output level. #[inline] pub fn set_level(&mut self, level: Level) { self.pin.set_output_high(level.into(), private::Internal); } /// Is the output pin set as high? #[inline] pub fn is_set_high(&self) -> bool { self.output_level() == Level::High } /// Is the output pin set as low? #[inline] pub fn is_set_low(&self) -> bool { self.output_level() == Level::Low } /// What level output is set to #[inline] pub fn output_level(&self) -> Level { self.pin.is_set_high(private::Internal).into() } /// Toggle pin output #[inline] pub fn toggle(&mut self) { let level = !self.output_level(); self.set_level(level); } /// Configure the [DriveStrength] of the pin #[inline] pub fn set_drive_strength(&mut self, strength: DriveStrength) { self.pin.set_drive_strength(strength, private::Internal); } /// Set the GPIO to open-drain mode. pub fn set_as_open_drain(&mut self, pull: Pull) { self.pin.set_to_open_drain_output(private::Internal); self.pin.pull_direction(pull, private::Internal); } /// Split the pin into an input and output signal. /// /// Peripheral signals allow connecting peripherals together without using /// external hardware. pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { self.pin.degrade_pin(private::Internal).split() } /// Turns the pin object into a peripheral /// [output][interconnect::OutputSignal]. /// /// The output signal can be passed to peripherals in place of an output /// pin. #[inline] pub fn into_peripheral_output(self) -> interconnect::OutputSignal { self.split().1 } } // Unfortunate implementation details responsible for: // - making pin drivers work with the peripheral signal system // - making the pin drivers work with the sleep API impl Pin for Flex<'_, P> { delegate::delegate! { to self.pin { fn number(&self) -> u8; fn degrade_pin(&self, _internal: private::Internal) -> AnyPin; fn output_signals(&self, _internal: private::Internal) -> &[(AlternateFunction, OutputSignal)]; fn input_signals(&self, _internal: private::Internal) -> &[(AlternateFunction, InputSignal)]; fn gpio_bank(&self, _internal: private::Internal) -> GpioRegisterAccess; } } } impl RtcPin for Flex<'_, P> { delegate::delegate! { to self.pin { #[cfg(xtensa)] fn rtc_number(&self) -> u8; #[cfg(any(xtensa, esp32c6))] fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: RtcFunction); fn rtcio_pad_hold(&mut self, enable: bool); #[cfg(any(esp32c3, esp32c2, esp32c6))] unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8); } } } impl RtcPinWithResistors for Flex<'_, P> { delegate::delegate! { to self.pin { fn rtcio_pullup(&mut self, enable: bool); fn rtcio_pulldown(&mut self, enable: bool); } } } pub(crate) mod internal { use super::*; impl private::Sealed for AnyPin {} impl AnyPin { /// Split the pin into an input and output signal. /// /// Peripheral signals allow connecting peripherals together without /// using external hardware. #[inline] pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { handle_gpio_input!(self.0, target, { target.split() }) } } impl Pin for AnyPin { fn number(&self) -> u8 { handle_gpio_input!(&self.0, target, { Pin::number(target) }) } fn degrade_pin(&self, _: private::Internal) -> AnyPin { unsafe { self.clone_unchecked() } } fn sleep_mode(&mut self, on: bool, _: private::Internal) { handle_gpio_input!(&mut self.0, target, { Pin::sleep_mode(target, on, private::Internal) }) } fn set_alternate_function(&mut self, alternate: AlternateFunction, _: private::Internal) { handle_gpio_input!(&mut self.0, target, { Pin::set_alternate_function(target, alternate, private::Internal) }) } fn output_signals(&self, _: private::Internal) -> &[(AlternateFunction, OutputSignal)] { handle_gpio_output!(&self.0, target, { Pin::output_signals(target, private::Internal) }) } fn input_signals(&self, _: private::Internal) -> &[(AlternateFunction, InputSignal)] { handle_gpio_input!(&self.0, target, { Pin::input_signals(target, private::Internal) }) } fn gpio_bank(&self, _: private::Internal) -> GpioRegisterAccess { handle_gpio_input!(&self.0, target, { Pin::gpio_bank(target, private::Internal) }) } } impl InputPin for AnyPin {} // Need to forward these one by one because not all pins support all functions. impl OutputPin for AnyPin { fn init_output( &mut self, alternate: AlternateFunction, open_drain: bool, input_enable: Option, _: private::Internal, ) { handle_gpio_output!(&mut self.0, target, { OutputPin::init_output( target, alternate, open_drain, input_enable, private::Internal, ) }) } fn set_to_open_drain_output(&mut self, _: private::Internal) { handle_gpio_output!(&mut self.0, target, { OutputPin::set_to_open_drain_output(target, private::Internal) }) } fn set_to_push_pull_output(&mut self, _: private::Internal) { handle_gpio_output!(&mut self.0, target, { OutputPin::set_to_push_pull_output(target, private::Internal) }) } fn set_output_high(&mut self, high: bool, _: private::Internal) { handle_gpio_output!(&mut self.0, target, { OutputPin::set_output_high(target, high, private::Internal) }) } fn set_drive_strength(&mut self, strength: DriveStrength, _: private::Internal) { handle_gpio_output!(&mut self.0, target, { OutputPin::set_drive_strength(target, strength, private::Internal) }) } fn enable_open_drain(&mut self, on: bool, _: private::Internal) { handle_gpio_output!(&mut self.0, target, { OutputPin::enable_open_drain(target, on, private::Internal) }) } fn internal_pull_up_in_sleep_mode(&mut self, on: bool, _: private::Internal) { handle_gpio_output!(&mut self.0, target, { OutputPin::internal_pull_up_in_sleep_mode(target, on, private::Internal) }) } fn internal_pull_down_in_sleep_mode(&mut self, on: bool, _: private::Internal) { handle_gpio_output!(&mut self.0, target, { OutputPin::internal_pull_down_in_sleep_mode(target, on, private::Internal) }) } fn is_set_high(&self, _: private::Internal) -> bool { handle_gpio_output!(&self.0, target, { OutputPin::is_set_high(target, private::Internal) }) } } #[cfg(any(xtensa, esp32c2, esp32c3, esp32c6))] impl RtcPin for AnyPin { #[cfg(xtensa)] #[allow(unused_braces)] // False positive :/ fn rtc_number(&self) -> u8 { handle_rtcio!(&self.0, target, { RtcPin::rtc_number(target) }) } #[cfg(any(xtensa, esp32c6))] fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: RtcFunction) { handle_rtcio!(&mut self.0, target, { RtcPin::rtc_set_config(target, input_enable, mux, func) }) } fn rtcio_pad_hold(&mut self, enable: bool) { handle_rtcio!(&mut self.0, target, { RtcPin::rtcio_pad_hold(target, enable) }) } #[cfg(any(esp32c2, esp32c3, esp32c6))] unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8) { handle_rtcio!(&mut self.0, target, { RtcPin::apply_wakeup(target, wakeup, level) }) } } #[cfg(any(esp32c2, esp32c3, esp32c6, xtensa))] impl RtcPinWithResistors for AnyPin { fn rtcio_pullup(&mut self, enable: bool) { handle_rtcio_with_resistors!(&mut self.0, target, { RtcPinWithResistors::rtcio_pullup(target, enable) }) } fn rtcio_pulldown(&mut self, enable: bool) { handle_rtcio_with_resistors!(&mut self.0, target, { RtcPinWithResistors::rtcio_pulldown(target, enable) }) } } } fn is_listening(pin_num: u8) -> bool { let bits = unsafe { GPIO::steal() } .pin(pin_num as usize) .read() .int_ena() .bits(); bits != 0 } fn set_int_enable(gpio_num: u8, int_ena: u8, int_type: u8, wake_up_from_light_sleep: bool) { unsafe { GPIO::steal() } .pin(gpio_num as usize) .modify(|_, w| unsafe { w.int_ena().bits(int_ena); w.int_type().bits(int_type); w.wakeup_enable().bit(wake_up_from_light_sleep) }); } #[ram] fn handle_pin_interrupts(handle: impl Fn(u8)) { let intrs_bank0 = InterruptStatusRegisterAccess::Bank0.interrupt_status_read(); #[cfg(any(esp32, esp32s2, esp32s3))] let intrs_bank1 = InterruptStatusRegisterAccess::Bank1.interrupt_status_read(); let mut intr_bits = intrs_bank0; while intr_bits != 0 { let pin_nr = intr_bits.trailing_zeros(); handle(pin_nr as u8); intr_bits -= 1 << pin_nr; } // clear interrupt bits Bank0GpioRegisterAccess::write_interrupt_status_clear(intrs_bank0); #[cfg(any(esp32, esp32s2, esp32s3))] { let mut intr_bits = intrs_bank1; while intr_bits != 0 { let pin_nr = intr_bits.trailing_zeros(); handle(pin_nr as u8 + 32); intr_bits -= 1 << pin_nr; } Bank1GpioRegisterAccess::write_interrupt_status_clear(intrs_bank1); } } mod asynch { use core::task::{Context, Poll}; use embassy_sync::waitqueue::AtomicWaker; use super::*; #[ram] pub(super) static PIN_WAKERS: [AtomicWaker; NUM_PINS] = [const { AtomicWaker::new() }; NUM_PINS]; impl

Flex<'_, P> where P: InputPin, { async fn wait_for(&mut self, event: Event) { self.listen(event); PinFuture::new(self.pin.number()).await } /// Wait until the pin is high. If it is already high, return /// immediately. pub async fn wait_for_high(&mut self) { self.wait_for(Event::HighLevel).await } /// Wait until the pin is low. If it is already low, return immediately. pub async fn wait_for_low(&mut self) { self.wait_for(Event::LowLevel).await } /// Wait for the pin to undergo a transition from low to high. pub async fn wait_for_rising_edge(&mut self) { self.wait_for(Event::RisingEdge).await } /// Wait for the pin to undergo a transition from high to low. pub async fn wait_for_falling_edge(&mut self) { self.wait_for(Event::FallingEdge).await } /// Wait for the pin to undergo any transition, i.e low to high OR high /// to low. pub async fn wait_for_any_edge(&mut self) { self.wait_for(Event::AnyEdge).await } } impl

Input<'_, P> where P: InputPin, { /// Wait until the pin is high. If it is already high, return /// immediately. pub async fn wait_for_high(&mut self) { self.pin.wait_for_high().await } /// Wait until the pin is low. If it is already low, return immediately. pub async fn wait_for_low(&mut self) { self.pin.wait_for_low().await } /// Wait for the pin to undergo a transition from low to high. pub async fn wait_for_rising_edge(&mut self) { self.pin.wait_for_rising_edge().await } /// Wait for the pin to undergo a transition from high to low. pub async fn wait_for_falling_edge(&mut self) { self.pin.wait_for_falling_edge().await } /// Wait for the pin to undergo any transition, i.e low to high OR high /// to low. pub async fn wait_for_any_edge(&mut self) { self.pin.wait_for_any_edge().await } } #[must_use = "futures do nothing unless you `.await` or poll them"] struct PinFuture { pin_num: u8, } impl PinFuture { fn new(pin_num: u8) -> Self { Self { pin_num } } } impl core::future::Future for PinFuture { type Output = (); fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { PIN_WAKERS[self.pin_num as usize].register(cx.waker()); // if pin is no longer listening its been triggered // therefore the future has resolved if !is_listening(self.pin_num) { Poll::Ready(()) } else { Poll::Pending } } } } mod embedded_hal_02_impls { use embedded_hal_02::digital::v2 as digital; use super::*; impl

digital::InputPin for Input<'_, P> where P: InputPin, { type Error = core::convert::Infallible; fn is_high(&self) -> Result { Ok(self.pin.is_high()) } fn is_low(&self) -> Result { Ok(self.pin.is_low()) } } impl

digital::OutputPin for Output<'_, P> where P: OutputPin, { type Error = core::convert::Infallible; fn set_high(&mut self) -> Result<(), Self::Error> { self.pin.set_high(); Ok(()) } fn set_low(&mut self) -> Result<(), Self::Error> { self.pin.set_low(); Ok(()) } } impl

digital::StatefulOutputPin for Output<'_, P> where P: OutputPin, { fn is_set_high(&self) -> Result { Ok(self.is_set_high()) } fn is_set_low(&self) -> Result { Ok(self.is_set_low()) } } impl

digital::ToggleableOutputPin for Output<'_, P> where P: OutputPin, { type Error = core::convert::Infallible; fn toggle(&mut self) -> Result<(), Self::Error> { self.toggle(); Ok(()) } } impl

digital::InputPin for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { type Error = core::convert::Infallible; fn is_high(&self) -> Result { Ok(self.pin.is_high()) } fn is_low(&self) -> Result { Ok(self.pin.is_low()) } } impl

digital::OutputPin for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { type Error = core::convert::Infallible; fn set_high(&mut self) -> Result<(), Self::Error> { self.set_high(); Ok(()) } fn set_low(&mut self) -> Result<(), Self::Error> { self.set_low(); Ok(()) } } impl

digital::StatefulOutputPin for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { fn is_set_high(&self) -> Result { Ok(self.is_set_high()) } fn is_set_low(&self) -> Result { Ok(self.is_set_low()) } } impl

digital::ToggleableOutputPin for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { type Error = core::convert::Infallible; fn toggle(&mut self) -> Result<(), Self::Error> { self.toggle(); Ok(()) } } impl

digital::InputPin for Flex<'_, P> where P: InputPin + OutputPin, { type Error = core::convert::Infallible; fn is_high(&self) -> Result { Ok(self.is_high()) } fn is_low(&self) -> Result { Ok(self.is_low()) } } impl

digital::OutputPin for Flex<'_, P> where P: InputPin + OutputPin, { type Error = core::convert::Infallible; fn set_high(&mut self) -> Result<(), Self::Error> { self.pin.set_output_high(true, private::Internal); Ok(()) } fn set_low(&mut self) -> Result<(), Self::Error> { self.pin.set_output_high(false, private::Internal); Ok(()) } } impl

digital::StatefulOutputPin for Flex<'_, P> where P: InputPin + OutputPin, { fn is_set_high(&self) -> Result { Ok(self.is_set_high()) } fn is_set_low(&self) -> Result { Ok(self.is_set_low()) } } impl

digital::ToggleableOutputPin for Flex<'_, P> where P: InputPin + OutputPin, { type Error = core::convert::Infallible; fn toggle(&mut self) -> Result<(), Self::Error> { self.toggle(); Ok(()) } } } mod embedded_hal_impls { use embedded_hal::digital; use super::*; impl

digital::ErrorType for Input<'_, P> where P: InputPin, { type Error = core::convert::Infallible; } impl

digital::InputPin for Input<'_, P> where P: InputPin, { fn is_high(&mut self) -> Result { Ok(Self::is_high(self)) } fn is_low(&mut self) -> Result { Ok(Self::is_low(self)) } } impl

digital::ErrorType for Output<'_, P> where P: OutputPin, { type Error = core::convert::Infallible; } impl

digital::OutputPin for Output<'_, P> where P: OutputPin, { fn set_low(&mut self) -> Result<(), Self::Error> { Self::set_low(self); Ok(()) } fn set_high(&mut self) -> Result<(), Self::Error> { Self::set_high(self); Ok(()) } } impl

digital::StatefulOutputPin for Output<'_, P> where P: OutputPin, { fn is_set_high(&mut self) -> Result { Ok(Self::is_set_high(self)) } fn is_set_low(&mut self) -> Result { Ok(Self::is_set_low(self)) } } impl

digital::InputPin for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { fn is_high(&mut self) -> Result { Ok(Self::is_high(self)) } fn is_low(&mut self) -> Result { Ok(Self::is_low(self)) } } impl

digital::ErrorType for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { type Error = core::convert::Infallible; } impl

digital::OutputPin for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { fn set_low(&mut self) -> Result<(), Self::Error> { Self::set_low(self); Ok(()) } fn set_high(&mut self) -> Result<(), Self::Error> { Self::set_high(self); Ok(()) } } impl

digital::StatefulOutputPin for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { fn is_set_high(&mut self) -> Result { Ok(Self::is_set_high(self)) } fn is_set_low(&mut self) -> Result { Ok(Self::is_set_low(self)) } } impl

digital::InputPin for Flex<'_, P> where P: InputPin, { fn is_high(&mut self) -> Result { Ok(Self::is_high(self)) } fn is_low(&mut self) -> Result { Ok(Self::is_low(self)) } } impl

digital::ErrorType for Flex<'_, P> { type Error = core::convert::Infallible; } impl

digital::OutputPin for Flex<'_, P> where P: OutputPin, { fn set_low(&mut self) -> Result<(), Self::Error> { Self::set_low(self); Ok(()) } fn set_high(&mut self) -> Result<(), Self::Error> { Self::set_high(self); Ok(()) } } impl

digital::StatefulOutputPin for Flex<'_, P> where P: OutputPin, { fn is_set_high(&mut self) -> Result { Ok(Self::is_set_high(self)) } fn is_set_low(&mut self) -> Result { Ok(Self::is_set_low(self)) } } } mod embedded_hal_async_impls { use embedded_hal_async::digital::Wait; use super::*; impl

Wait for Flex<'_, P> where P: InputPin, { async fn wait_for_high(&mut self) -> Result<(), Self::Error> { Self::wait_for_high(self).await; Ok(()) } async fn wait_for_low(&mut self) -> Result<(), Self::Error> { Self::wait_for_low(self).await; Ok(()) } async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { Self::wait_for_rising_edge(self).await; Ok(()) } async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { Self::wait_for_falling_edge(self).await; Ok(()) } async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { Self::wait_for_any_edge(self).await; Ok(()) } } impl

Wait for Input<'_, P> where P: InputPin, { async fn wait_for_high(&mut self) -> Result<(), Self::Error> { Self::wait_for_high(self).await; Ok(()) } async fn wait_for_low(&mut self) -> Result<(), Self::Error> { Self::wait_for_low(self).await; Ok(()) } async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { Self::wait_for_rising_edge(self).await; Ok(()) } async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { Self::wait_for_falling_edge(self).await; Ok(()) } async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { Self::wait_for_any_edge(self).await; Ok(()) } } }