#![cfg_attr(docsrs, procmacros::doc_replace(
"etm_availability" => {
cfg(etm_driver_supported) => "The GPIO pins also provide tasks and events via the ETM interconnect system. For more information, see the [etm] module."
}
))]
crate::unstable_module! {
pub mod interconnect;
#[cfg(etm_driver_supported)]
pub mod etm;
#[cfg(soc_has_lp_io)]
pub mod lp_io;
#[cfg(all(soc_has_rtc_io, not(esp32)))]
pub mod rtc_io;
#[cfg(dedicated_gpio_driver_supported)]
pub mod dedicated;
}
use interconnect::PeripheralOutput;
mod asynch;
mod embedded_hal_impls;
pub(crate) mod interrupt;
use interrupt::*;
mod placeholder;
use core::fmt::Display;
use esp_sync::RawMutex;
pub use placeholder::NoPin;
use portable_atomic::AtomicU32;
use strum::EnumCount;
use crate::{
asynch::AtomicWaker,
interrupt::{InterruptHandler, Priority},
peripherals::{GPIO, IO_MUX, Interrupt},
private::{self, Sealed},
};
define_io_mux_signals!();
pub(crate) static GPIO_LOCK: RawMutex = RawMutex::new();
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct PinGuard {
pin: u8,
}
impl crate::private::Sealed for PinGuard {}
impl PinGuard {
fn new(pin: AnyPin<'_>) -> Self {
Self { pin: pin.number() }
}
pub(crate) const fn new_unconnected() -> Self {
Self { pin: u8::MAX }
}
#[allow(unused)]
pub(crate) fn pin_number(&self) -> Option<u8> {
if self.pin == u8::MAX {
None
} else {
Some(self.pin)
}
}
}
impl Drop for PinGuard {
fn drop(&mut self) {
if self.pin != u8::MAX {
let pin = unsafe { AnyPin::steal(self.pin) };
pin.disconnect_from_peripheral_output();
}
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum Event {
RisingEdge = 1,
FallingEdge = 2,
AnyEdge = 3,
LowLevel = 4,
HighLevel = 5,
}
impl From<WakeEvent> for Event {
fn from(value: WakeEvent) -> Self {
match value {
WakeEvent::LowLevel => Event::LowLevel,
WakeEvent::HighLevel => Event::HighLevel,
}
}
}
#[instability::unstable]
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum WakeEvent {
LowLevel = 4,
HighLevel = 5,
}
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Level {
Low,
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 Level {
pub(crate) const fn const_from(val: bool) -> Self {
match val {
true => Self::High,
false => Self::Low,
}
}
pub(crate) const fn const_into(self) -> bool {
match self {
Level::Low => false,
Level::High => true,
}
}
}
impl From<bool> for Level {
fn from(val: bool) -> Self {
Self::const_from(val)
}
}
impl From<Level> for bool {
fn from(level: Level) -> bool {
level.const_into()
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
#[non_exhaustive]
pub enum WakeConfigError {
EdgeTriggeringNotSupported,
}
impl Display for WakeConfigError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
WakeConfigError::EdgeTriggeringNotSupported => {
write!(
f,
"Edge triggering is not supported for wake-up from light sleep"
)
}
}
}
}
impl core::error::Error for WakeConfigError {}
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Pull {
None,
Up,
Down,
}
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DriveStrength {
_5mA = 0,
_10mA = 1,
_20mA = 2,
_40mA = 3,
}
#[doc(hidden)]
#[doc = crate::trm_markdown_link!("iomuxgpio")]
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AlternateFunction {
_0 = 0,
_1 = 1,
_2 = 2,
_3 = 3,
_4 = 4,
_5 = 5,
}
impl AlternateFunction {
const GPIO: Self = match Self::const_try_from(property!("gpio.gpio_function")) {
Ok(func) => func,
Err(_) => ::core::panic!("Invalid GPIO function"),
};
const fn const_try_from(value: usize) -> Result<Self, ()> {
match value {
0 => Ok(Self::_0),
1 => Ok(Self::_1),
2 => Ok(Self::_2),
3 => Ok(Self::_3),
4 => Ok(Self::_4),
5 => Ok(Self::_5),
_ => Err(()),
}
}
}
impl TryFrom<usize> for AlternateFunction {
type Error = ();
fn try_from(value: usize) -> Result<Self, Self::Error> {
Self::const_try_from(value)
}
}
#[instability::unstable]
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(not(any(esp32h2, esp32c5, esp32c61)))]
pub enum RtcFunction {
Rtc = 0,
Digital = 1,
#[cfg(soc_has_rtc_i2c)]
I2c = 3,
}
#[instability::unstable]
#[cfg(not(any(esp32c5, esp32c61)))]
pub trait RtcPin: Pin {
#[cfg(any(xtensa, esp32h2))]
fn rtc_number(&self) -> u8;
#[cfg(any(xtensa, esp32c6))]
#[doc(hidden)]
fn rtc_set_config(&self, input_enable: bool, mux: bool, func: RtcFunction);
#[doc(hidden)]
fn rtcio_pad_hold(&self, enable: bool);
#[cfg(any(esp32c3, esp32c2, esp32c6))]
#[doc(hidden)]
unsafe fn apply_wakeup(&self, wakeup: bool, level: u8);
}
#[instability::unstable]
#[cfg(not(any(esp32c5, esp32c61)))]
pub trait RtcPinWithResistors: RtcPin {
#[cfg(not(esp32h2))]
#[doc(hidden)]
fn rtcio_pullup(&self, enable: bool);
#[cfg(not(esp32h2))]
#[doc(hidden)]
fn rtcio_pulldown(&self, enable: bool);
}
pub trait Pin: Sealed {
fn number(&self) -> u8;
#[procmacros::doc_replace]
fn degrade<'d>(self) -> AnyPin<'d>
where
Self: Sized + 'd,
{
unsafe { AnyPin::steal(self.number()) }
}
#[doc(hidden)]
fn output_signals(&self, _: private::Internal) -> &'static [(AlternateFunction, OutputSignal)];
#[doc(hidden)]
fn input_signals(&self, _: private::Internal) -> &'static [(AlternateFunction, InputSignal)];
}
pub trait InputPin: Pin {
#[doc(hidden)]
fn waker(&self) -> &'static AtomicWaker;
}
pub trait OutputPin: Pin {}
#[instability::unstable]
pub trait AnalogPin: Pin {
#[doc(hidden)]
fn set_analog(&self, _: private::Internal);
}
#[cfg(touch)]
#[instability::unstable]
pub trait TouchPin: Pin {
#[doc(hidden)]
fn set_touch(&self, _: private::Internal);
#[doc(hidden)]
fn touch_measurement(&self, _: private::Internal) -> u16;
#[doc(hidden)]
fn touch_nr(&self, _: private::Internal) -> u8;
#[doc(hidden)]
fn set_threshold(&self, threshold: u16, _: private::Internal);
}
#[doc(hidden)]
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash, EnumCount)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum GpioBank {
_0,
#[cfg(gpio_has_bank_1)]
_1,
}
impl GpioBank {
fn async_operations(self) -> &'static AtomicU32 {
static FLAGS: [AtomicU32; GpioBank::COUNT] = [const { AtomicU32::new(0) }; GpioBank::COUNT];
&FLAGS[self as usize]
}
fn offset(self) -> u8 {
match self {
Self::_0 => 0,
#[cfg(gpio_has_bank_1)]
Self::_1 => 32,
}
}
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::_0 => GPIO::regs()
.enable_w1tc()
.write(|w| unsafe { w.bits(word) }),
#[cfg(gpio_has_bank_1)]
Self::_1 => GPIO::regs()
.enable1_w1tc()
.write(|w| unsafe { w.bits(word) }),
};
}
fn write_out_en_set(self, word: u32) {
match self {
Self::_0 => GPIO::regs()
.enable_w1ts()
.write(|w| unsafe { w.bits(word) }),
#[cfg(gpio_has_bank_1)]
Self::_1 => GPIO::regs()
.enable1_w1ts()
.write(|w| unsafe { w.bits(word) }),
};
}
fn read_input(self) -> u32 {
match self {
Self::_0 => GPIO::regs().in_().read().bits(),
#[cfg(gpio_has_bank_1)]
Self::_1 => GPIO::regs().in1().read().bits(),
}
}
fn read_output(self) -> u32 {
match self {
Self::_0 => GPIO::regs().out().read().bits(),
#[cfg(gpio_has_bank_1)]
Self::_1 => GPIO::regs().out1().read().bits(),
}
}
fn read_interrupt_status(self) -> u32 {
match self {
Self::_0 => GPIO::regs().status().read().bits(),
#[cfg(gpio_has_bank_1)]
Self::_1 => GPIO::regs().status1().read().bits(),
}
}
fn write_interrupt_status_clear(self, word: u32) {
match self {
Self::_0 => GPIO::regs()
.status_w1tc()
.write(|w| unsafe { w.bits(word) }),
#[cfg(gpio_has_bank_1)]
Self::_1 => GPIO::regs()
.status1_w1tc()
.write(|w| unsafe { w.bits(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::_0 => GPIO::regs().out_w1ts().write(|w| unsafe { w.bits(word) }),
#[cfg(gpio_has_bank_1)]
Self::_1 => GPIO::regs().out1_w1ts().write(|w| unsafe { w.bits(word) }),
};
}
fn write_output_clear(self, word: u32) {
match self {
Self::_0 => GPIO::regs().out_w1tc().write(|w| unsafe { w.bits(word) }),
#[cfg(gpio_has_bank_1)]
Self::_1 => GPIO::regs().out1_w1tc().write(|w| unsafe { w.bits(word) }),
};
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AnyPin<'lt> {
pub(crate) pin: u8,
pub(crate) _lifetime: core::marker::PhantomData<&'lt mut ()>,
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct Io<'d> {
_io_mux: IO_MUX<'d>,
}
impl<'d> Io<'d> {
#[instability::unstable]
pub fn new(_io_mux: IO_MUX<'d>) -> Self {
Io { _io_mux }
}
#[instability::unstable]
pub fn set_interrupt_priority(&self, prio: Priority) {
interrupt::set_interrupt_priority(Interrupt::GPIO, prio);
}
#[cfg_attr(
not(multi_core),
doc = "Registers an interrupt handler for all GPIO pins."
)]
#[cfg_attr(
multi_core,
doc = "Registers an interrupt handler for all GPIO pins. Enables the interrupt on the current core."
)]
#[doc = ""]
#[instability::unstable]
pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
for core in crate::system::Cpu::other() {
crate::interrupt::disable(core, Interrupt::GPIO);
}
USER_INTERRUPT_HANDLER.store(handler.handler().callback());
crate::interrupt::bind_handler(
Interrupt::GPIO,
InterruptHandler::new(user_gpio_interrupt_handler, handler.priority()),
);
}
}
impl crate::private::Sealed for Io<'_> {}
#[instability::unstable]
impl crate::interrupt::InterruptConfigurable for Io<'_> {
fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
self.set_interrupt_handler(handler);
}
}
for_each_analog_function! {
(($_ch:ident, ADCn_CHm, $_n:literal, $_m:literal), $gpio:ident) => {
#[instability::unstable]
impl $crate::gpio::AnalogPin for crate::peripherals::$gpio<'_> {
#[cfg(riscv)]
fn set_analog(&self, _: private::Internal) {
io_mux_reg(self.number()).modify(|_, w| unsafe {
w.mcu_sel().bits(1);
w.fun_ie().clear_bit();
w.fun_wpu().clear_bit();
w.fun_wpd().clear_bit()
});
GPIO::regs()
.enable_w1tc()
.write(|w| unsafe { w.bits(1 << self.number()) });
}
#[cfg(not(riscv))]
fn set_analog(&self, _: private::Internal) {
self.set_analog_impl();
}
}
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DriveMode {
PushPull,
#[cfg_attr(
feature = "unstable",
doc = "\n\nEnable the input related functionality by using [Output::into_flex] and enabling input via [Flex::set_input_enable]"
)]
OpenDrain,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, procmacros::BuilderLite)]
#[non_exhaustive]
pub struct OutputConfig {
drive_mode: DriveMode,
drive_strength: DriveStrength,
pull: Pull,
}
impl Default for OutputConfig {
fn default() -> Self {
Self {
drive_mode: DriveMode::PushPull,
drive_strength: DriveStrength::_20mA,
pull: Pull::None,
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Output<'d> {
pin: Flex<'d>,
}
impl private::Sealed for Output<'_> {}
impl private::Sealed for &mut Output<'_> {}
impl<'d> Output<'d> {
#[procmacros::doc_replace]
#[inline]
pub fn new(pin: impl OutputPin + 'd, initial_level: Level, config: OutputConfig) -> Self {
let mut this = Self {
pin: Flex::new(pin),
};
this.set_level(initial_level);
this.apply_config(&config);
this.pin.pin.set_output_enable(true);
this
}
#[procmacros::doc_replace]
#[inline]
#[instability::unstable]
pub fn into_peripheral_output(self) -> interconnect::OutputSignal<'d> {
self.pin.into_peripheral_output()
}
#[procmacros::doc_replace]
#[inline]
pub fn apply_config(&mut self, config: &OutputConfig) {
self.pin.apply_output_config(config)
}
#[procmacros::doc_replace]
#[inline]
pub fn set_high(&mut self) {
self.set_level(Level::High)
}
#[procmacros::doc_replace]
#[inline]
pub fn set_low(&mut self) {
self.set_level(Level::Low)
}
#[procmacros::doc_replace]
#[inline]
pub fn set_level(&mut self, level: Level) {
self.pin.set_level(level)
}
#[procmacros::doc_replace]
#[inline]
pub fn is_set_high(&self) -> bool {
self.output_level() == Level::High
}
#[procmacros::doc_replace]
#[inline]
pub fn is_set_low(&self) -> bool {
self.output_level() == Level::Low
}
#[procmacros::doc_replace]
#[inline]
pub fn output_level(&self) -> Level {
self.pin.output_level()
}
#[procmacros::doc_replace]
#[inline]
pub fn toggle(&mut self) {
self.pin.toggle();
}
#[inline]
#[instability::unstable]
pub fn into_flex(self) -> Flex<'d> {
self.pin
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, procmacros::BuilderLite)]
#[non_exhaustive]
pub struct InputConfig {
pull: Pull,
}
impl Default for InputConfig {
fn default() -> Self {
Self { pull: Pull::None }
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Input<'d> {
pin: Flex<'d>,
}
impl private::Sealed for Input<'_> {}
impl private::Sealed for &mut Input<'_> {}
impl<'d> Input<'d> {
#[procmacros::doc_replace]
#[inline]
pub fn new(pin: impl InputPin + 'd, config: InputConfig) -> Self {
let mut pin = Flex::new(pin);
pin.set_output_enable(false);
pin.set_input_enable(true);
pin.apply_input_config(&config);
Self { pin }
}
#[procmacros::doc_replace]
#[inline]
#[instability::unstable]
pub fn peripheral_input(&self) -> interconnect::InputSignal<'d> {
self.pin.peripheral_input()
}
#[procmacros::doc_replace]
#[inline]
pub fn is_high(&self) -> bool {
self.level() == Level::High
}
#[procmacros::doc_replace]
#[inline]
pub fn is_low(&self) -> bool {
self.level() == Level::Low
}
#[procmacros::doc_replace]
#[inline]
pub fn level(&self) -> Level {
self.pin.level()
}
#[procmacros::doc_replace]
pub fn apply_config(&mut self, config: &InputConfig) {
self.pin.apply_input_config(config)
}
#[procmacros::doc_replace]
#[inline]
#[instability::unstable]
pub fn listen(&mut self, event: Event) {
self.pin.listen(event);
}
#[inline]
#[instability::unstable]
pub fn unlisten(&mut self) {
self.pin.unlisten();
}
#[inline]
#[instability::unstable]
pub fn clear_interrupt(&mut self) {
self.pin.clear_interrupt();
}
#[inline]
#[instability::unstable]
pub fn is_interrupt_set(&self) -> bool {
self.pin.is_interrupt_set()
}
#[instability::unstable]
#[inline]
pub fn wakeup_enable(&mut self, enable: bool, event: WakeEvent) -> Result<(), WakeConfigError> {
self.pin.wakeup_enable(enable, event)
}
#[inline]
#[instability::unstable]
pub fn into_flex(self) -> Flex<'d> {
self.pin
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct Flex<'d> {
pin: AnyPin<'d>,
}
impl private::Sealed for Flex<'_> {}
impl private::Sealed for &mut Flex<'_> {}
impl<'d> Flex<'d> {
#[inline]
#[instability::unstable]
pub fn new(pin: impl Pin + 'd) -> Self {
let pin = pin.degrade();
pin.init_gpio();
Self { pin }
}
#[inline]
#[instability::unstable]
pub fn apply_input_config(&mut self, config: &InputConfig) {
self.pin.apply_input_config(config);
}
#[inline]
#[instability::unstable]
pub fn set_input_enable(&mut self, enable_input: bool) {
self.pin.set_input_enable(enable_input);
}
#[inline]
#[instability::unstable]
pub fn is_high(&self) -> bool {
self.level() == Level::High
}
#[inline]
#[instability::unstable]
pub fn is_low(&self) -> bool {
self.level() == Level::Low
}
#[inline]
#[instability::unstable]
pub fn level(&self) -> Level {
self.pin.is_input_high().into()
}
#[inline]
#[instability::unstable]
pub fn listen(&mut self, event: Event) {
unwrap!(self.pin.listen_with_options(event, true, false, false));
}
#[inline]
#[instability::unstable]
pub fn unlisten(&mut self) {
GPIO_LOCK.lock(|| {
set_int_enable(self.pin.number(), Some(0), 0, false);
});
}
fn unlisten_and_clear(&mut self) {
GPIO_LOCK.lock(|| {
set_int_enable(self.pin.number(), Some(0), 0, false);
self.clear_interrupt();
});
}
#[inline]
#[instability::unstable]
pub fn is_listening(&self) -> bool {
is_int_enabled(self.pin.number())
}
#[inline]
#[instability::unstable]
pub fn clear_interrupt(&mut self) {
self.pin
.bank()
.write_interrupt_status_clear(self.pin.mask());
}
#[inline]
#[instability::unstable]
pub fn is_interrupt_set(&self) -> bool {
self.pin.bank().read_interrupt_status() & self.pin.mask() != 0
}
#[inline]
#[instability::unstable]
pub fn wakeup_enable(&mut self, enable: bool, event: WakeEvent) -> Result<(), WakeConfigError> {
self.pin
.listen_with_options(event.into(), false, false, enable)
}
#[inline]
#[instability::unstable]
pub fn apply_output_config(&mut self, config: &OutputConfig) {
self.pin.apply_output_config(config);
}
#[inline]
#[instability::unstable]
pub fn set_output_enable(&mut self, enable_output: bool) {
self.pin.set_output_enable(enable_output);
}
#[inline]
#[instability::unstable]
pub fn set_high(&mut self) {
self.set_level(Level::High)
}
#[inline]
#[instability::unstable]
pub fn set_low(&mut self) {
self.set_level(Level::Low)
}
#[inline]
#[instability::unstable]
pub fn set_level(&mut self, level: Level) {
self.pin.set_output_high(level.into());
}
#[inline]
#[instability::unstable]
pub fn is_set_high(&self) -> bool {
self.output_level() == Level::High
}
#[inline]
#[instability::unstable]
pub fn is_set_low(&self) -> bool {
self.output_level() == Level::Low
}
#[inline]
#[instability::unstable]
pub fn output_level(&self) -> Level {
self.pin.is_set_high().into()
}
#[inline]
#[instability::unstable]
pub fn toggle(&mut self) {
let level = self.output_level();
self.set_level(!level);
}
#[procmacros::doc_replace]
#[inline]
#[instability::unstable]
pub fn peripheral_input(&self) -> interconnect::InputSignal<'d> {
self.pin.set_input_enable(true);
unsafe {
self.pin.clone_unchecked().split_no_init().0.freeze()
}
}
#[procmacros::doc_replace]
#[inline]
#[instability::unstable]
pub fn split(
self,
) -> (
interconnect::InputSignal<'d>,
interconnect::OutputSignal<'d>,
) {
let input = self.peripheral_input();
let output = self.into_peripheral_output();
(input, output)
}
#[inline]
#[instability::unstable]
pub unsafe fn split_into_drivers(self) -> (Input<'d>, Output<'d>) {
self.pin.set_input_enable(true);
let input = Input {
pin: Flex {
pin: unsafe { self.pin.clone_unchecked() },
},
};
let output = Output { pin: self };
(input, output)
}
#[procmacros::doc_replace]
#[inline]
#[instability::unstable]
pub fn into_peripheral_output(self) -> interconnect::OutputSignal<'d> {
unsafe {
self.pin.split_no_init().1.freeze()
}
}
}
impl private::Sealed for AnyPin<'_> {}
impl<'lt> AnyPin<'lt> {
fn bank(&self) -> GpioBank {
#[cfg(gpio_has_bank_1)]
if self.number() >= 32 {
return GpioBank::_1;
}
GpioBank::_0
}
pub(crate) fn disable_usb_pads(&self) {
#[cfg(soc_has_usb_device)]
{
fn disable_usb_pads(_gpionum: u8) {
crate::peripherals::USB_DEVICE::regs()
.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()
});
}
macro_rules! disable_usb_pads {
($gpio:ident) => {
if self.number() == crate::peripherals::$gpio::NUMBER {
disable_usb_pads(crate::peripherals::$gpio::NUMBER);
}
};
}
for_each_analog_function! {
(USB_DM, $gpio:ident) => { disable_usb_pads!($gpio) };
(USB_DP, $gpio:ident) => { disable_usb_pads!($gpio) };
}
}
}
#[inline]
pub(crate) fn init_gpio(&self) {
self.set_output_enable(false);
self.disable_usb_pads();
GPIO::regs()
.func_out_sel_cfg(self.number() as usize)
.write(|w| unsafe { w.out_sel().bits(OutputSignal::GPIO as _) });
io_mux_reg(self.number()).modify(|_, w| unsafe {
w.mcu_sel().bits(AlternateFunction::GPIO as u8);
w.fun_ie().clear_bit();
w.slp_sel().clear_bit()
});
}
#[procmacros::doc_replace]
#[inline]
#[instability::unstable]
pub unsafe fn split(
self,
) -> (
interconnect::InputSignal<'lt>,
interconnect::OutputSignal<'lt>,
) {
assert!(self.is_output());
self.init_gpio();
self.set_input_enable(true);
let (input, output) = unsafe { self.split_no_init() };
let output = output.with_gpio_matrix_forced(true);
(input, output)
}
#[inline]
#[instability::unstable]
pub unsafe fn into_input_signal(self) -> interconnect::InputSignal<'lt> {
self.init_gpio();
self.set_input_enable(true);
let (input, _) = unsafe { self.split_no_init() };
input
}
#[inline]
#[instability::unstable]
pub fn into_output_signal(self) -> interconnect::OutputSignal<'lt> {
assert!(self.is_output());
self.init_gpio();
let (_, output) = unsafe { self.split_no_init() };
output
}
unsafe fn split_no_init(
self,
) -> (
interconnect::InputSignal<'lt>,
interconnect::OutputSignal<'lt>,
) {
let input = interconnect::InputSignal::new(unsafe { self.clone_unchecked() });
let output = interconnect::OutputSignal::new(self);
let input = input.with_gpio_matrix_forced(true);
(input, output)
}
#[inline]
pub(crate) fn set_alternate_function(&self, alternate: AlternateFunction) {
io_mux_reg(self.number()).modify(|_, w| unsafe { w.mcu_sel().bits(alternate as u8) });
}
#[inline]
pub(crate) fn set_output_enable(&self, enable: bool) {
assert!(self.is_output() || !enable);
self.bank().write_out_en(self.mask(), enable);
}
#[inline]
pub(crate) fn set_input_enable(&self, on: bool) {
io_mux_reg(self.number()).modify(|_, w| w.fun_ie().bit(on));
}
#[inline]
pub(crate) fn apply_input_config(&self, config: &InputConfig) {
let pull_up = config.pull == Pull::Up;
let pull_down = config.pull == Pull::Down;
#[cfg(esp32)]
crate::soc::gpio::errata36(unsafe { self.clone_unchecked() }, pull_up, pull_down);
io_mux_reg(self.number()).modify(|_, w| {
w.fun_wpd().bit(pull_down);
w.fun_wpu().bit(pull_up)
});
}
fn clear_interrupt(&self) {
self.bank().write_interrupt_status_clear(self.mask());
}
fn with_gpio_lock<F, R>(&self, f: F) -> R
where
F: FnOnce() -> R,
{
if is_int_enabled(self.number()) {
GPIO_LOCK.lock(f)
} else {
f()
}
}
fn listen_with_options(
&self,
event: Event,
int_enable: bool,
nmi_enable: bool,
wake_up_from_light_sleep: bool,
) -> Result<(), WakeConfigError> {
fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 {
cfg_if::cfg_if! {
if #[cfg(esp32)] {
match crate::system::Cpu::current() {
crate::system::Cpu::AppCpu => int_enable as u8 | ((nmi_enable as u8) << 1),
crate::system::Cpu::ProCpu => ((int_enable as u8) << 2) | ((nmi_enable as u8) << 3),
}
} else {
int_enable as u8 | ((nmi_enable as u8) << 1)
}
}
}
if wake_up_from_light_sleep {
match event {
Event::AnyEdge | Event::RisingEdge | Event::FallingEdge => {
return Err(WakeConfigError::EdgeTriggeringNotSupported);
}
_ => {}
}
}
self.with_gpio_lock(|| {
self.clear_interrupt();
set_int_enable(
self.number(),
Some(gpio_intr_enable(int_enable, nmi_enable)),
event as u8,
wake_up_from_light_sleep,
);
});
Ok(())
}
#[inline]
fn apply_output_config(&self, config: &OutputConfig) {
let pull_up = config.pull == Pull::Up;
let pull_down = config.pull == Pull::Down;
#[cfg(esp32)]
crate::soc::gpio::errata36(unsafe { self.clone_unchecked() }, pull_up, pull_down);
io_mux_reg(self.number()).modify(|_, w| {
unsafe { w.fun_drv().bits(config.drive_strength as u8) };
w.fun_wpu().bit(pull_up);
w.fun_wpd().bit(pull_down);
w
});
self.with_gpio_lock(|| {
GPIO::regs().pin(self.number() as usize).modify(|_, w| {
w.pad_driver()
.bit(config.drive_mode == DriveMode::OpenDrain)
});
});
}
#[inline]
fn mask(&self) -> u32 {
1 << (self.number() % 32)
}
#[inline]
pub(crate) fn is_input_high(&self) -> bool {
self.bank().read_input() & self.mask() != 0
}
#[inline]
pub(crate) fn set_output_high(&self, high: bool) {
self.bank().write_output(self.mask(), high);
}
#[inline]
pub(crate) fn is_set_high(&self) -> bool {
self.bank().read_output() & self.mask() != 0
}
}
impl Pin for AnyPin<'_> {
#[inline(always)]
fn number(&self) -> u8 {
self.pin
}
fn output_signals(
&self,
private: private::Internal,
) -> &'static [(AlternateFunction, OutputSignal)] {
for_each_gpio! {
(all $( ($n:literal, $gpio:ident $in_afs:tt $out_afs:tt ($input:tt [$($is_output:ident)?]) ) ),* ) => {
match self.number() {
$($(
$n => {
crate::ignore!($is_output);
let inner = unsafe { crate::peripherals::$gpio::steal() };
return Pin::output_signals(&inner, private);
}
)?)*
other => panic!("Pin {} is not an OutputPin", other)
}
};
}
}
fn input_signals(
&self,
private: private::Internal,
) -> &'static [(AlternateFunction, InputSignal)] {
for_each_gpio! {
(all $( ($n:literal, $gpio:ident $in_afs:tt $out_afs:tt ([$($is_input:ident)?] $output:tt) ) ),* ) => {
match self.number() {
$($(
$n => {
crate::ignore!($is_input);
let inner = unsafe { crate::peripherals::$gpio::steal() };
return Pin::input_signals(&inner, private);
}
)?)*
other => panic!("Pin {} is not an InputPin", other)
}
};
}
}
}
impl InputPin for AnyPin<'_> {
fn waker(&self) -> &'static AtomicWaker {
for_each_gpio! {
(all $( ($n:literal, $gpio:ident $in_afs:tt $out_afs:tt ([$($is_input:ident)?] $output:tt) ) ),* ) => {
match self.number() {
$($(
$n => {
crate::ignore!($is_input);
let inner = unsafe { crate::peripherals::$gpio::steal() };
return InputPin::waker(&inner);
}
)?)*
other => panic!("Pin {} is not an InputPin", other)
}
};
}
}
}
impl OutputPin for AnyPin<'_> {}
for_each_gpio! {
($n:literal, $gpio:ident $($_rest:tt)*) => {
impl<'lt> TryFrom<AnyPin<'lt>> for crate::peripherals::$gpio<'lt> {
type Error = AnyPin<'lt>;
fn try_from(any_pin: AnyPin<'lt>) -> Result<Self, Self::Error> {
if any_pin.number() == $n {
Ok(unsafe { Self::steal() })
} else {
Err(any_pin)
}
}
}
};
}
impl AnyPin<'_> {
#[procmacros::doc_replace]
#[inline]
pub fn downcast<P: Pin>(self) -> Result<P, Self>
where
Self: TryInto<P, Error = Self>,
{
self.try_into()
}
#[procmacros::doc_replace]
pub unsafe fn steal(pin: u8) -> Self {
for_each_gpio! {
(all $( ($n:literal $($any:tt)*) ),*) => { const PINS: &[u8] = &[ $($n),* ]; };
};
assert!(PINS.contains(&pin), "Pin {} does not exist", pin);
Self {
pin,
_lifetime: core::marker::PhantomData,
}
}
#[procmacros::doc_replace]
pub unsafe fn clone_unchecked(&self) -> Self {
Self {
pin: self.pin,
_lifetime: core::marker::PhantomData,
}
}
#[procmacros::doc_replace]
pub fn reborrow(&mut self) -> AnyPin<'_> {
unsafe { self.clone_unchecked() }
}
pub(crate) fn is_output(&self) -> bool {
for_each_gpio! {
(all $( ($n:literal, $gpio:ident $in_afs:tt $out_afs:tt ($input:tt [$($is_output:ident)?]) ) ),* ) => {
return match self.number() {
$($(
$n => {
crate::ignore!($is_output);
true
}
)?)*
other => false,
};
};
}
}
}
#[cold]
#[allow(unused)]
fn pin_does_not_support_function(pin: u8, function: &str) {
panic!("Pin {} is not an {}", pin, function)
}
#[cfg(not(any(esp32c5, esp32c61)))]
macro_rules! for_each_rtcio_pin {
(@impl $ident:ident, $target:ident, $gpio:ident, $code:tt) => {
if $ident.number() == $crate::peripherals::$gpio::NUMBER {
#[allow(unused_mut)]
let mut $target = unsafe { $crate::peripherals::$gpio::steal() };
return $code;
}
};
(($ident:ident, $target:ident) => $code:tt;) => {
for_each_lp_function! {
(($_sig:ident, RTC_GPIOn, $_n:literal), $gpio:ident) => {
for_each_rtcio_pin!(@impl $ident, $target, $gpio, $code)
};
(($_sig:ident, LP_GPIOn, $_n:literal), $gpio:ident) => {
for_each_rtcio_pin!(@impl $ident, $target, $gpio, $code)
};
}
unreachable!();
};
}
#[cfg(not(any(esp32h2, esp32c5, esp32c61)))]
macro_rules! for_each_rtcio_output_pin {
(@impl $ident:ident, $target:ident, $gpio:ident, $code:tt, $kind:literal) => {
if $ident.number() == $crate::peripherals::$gpio::NUMBER {
for_each_gpio! {
($n:tt, $gpio $in_afs:tt $out_afs:tt ($input:tt [Output])) => {
#[allow(unused_mut)]
let mut $target = unsafe { $crate::peripherals::$gpio::steal() };
return $code;
};
($n:tt, $gpio $in_afs:tt $out_afs:tt ($input:tt [])) => {
pin_does_not_support_function($crate::peripherals::$gpio::NUMBER, $kind)
};
}
}
};
(($ident:ident, $target:ident) => $code:tt;) => {
for_each_lp_function! {
(($_sig:ident, RTC_GPIOn, $_n:literal), $gpio:ident) => {
for_each_rtcio_output_pin!(@impl $ident, $target, $gpio, $code, "RTC_IO output")
};
(($_sig:ident, LP_GPIOn, $_n:literal), $gpio:ident) => {
for_each_rtcio_output_pin!(@impl $ident, $target, $gpio, $code, "LP_IO output")
};
}
unreachable!();
};
}
#[cfg(not(any(esp32c5, esp32c61)))]
impl RtcPin for AnyPin<'_> {
#[cfg(any(xtensa, esp32h2))]
fn rtc_number(&self) -> u8 {
for_each_rtcio_pin! {
(self, target) => { RtcPin::rtc_number(&target) };
}
}
#[cfg(any(xtensa, esp32c6))]
fn rtc_set_config(&self, input_enable: bool, mux: bool, func: RtcFunction) {
for_each_rtcio_pin! {
(self, target) => { RtcPin::rtc_set_config(&target, input_enable, mux, func) };
}
}
fn rtcio_pad_hold(&self, enable: bool) {
for_each_rtcio_pin! {
(self, target) => { RtcPin::rtcio_pad_hold(&target, enable) };
}
}
#[cfg(any(esp32c2, esp32c3, esp32c6))]
unsafe fn apply_wakeup(&self, wakeup: bool, level: u8) {
for_each_rtcio_pin! {
(self, target) => { unsafe { RtcPin::apply_wakeup(&target, wakeup, level) } };
}
}
}
#[cfg(not(any(esp32c5, esp32c61)))]
impl RtcPinWithResistors for AnyPin<'_> {
#[cfg(not(esp32h2))]
fn rtcio_pullup(&self, enable: bool) {
for_each_rtcio_output_pin! {
(self, target) => { RtcPinWithResistors::rtcio_pullup(&target, enable) };
}
}
#[cfg(not(esp32h2))]
fn rtcio_pulldown(&self, enable: bool) {
for_each_rtcio_output_pin! {
(self, target) => { RtcPinWithResistors::rtcio_pulldown(&target, enable) };
}
}
}
fn set_int_enable(gpio_num: u8, int_ena: Option<u8>, int_type: u8, wake_up_from_light_sleep: bool) {
GPIO::regs().pin(gpio_num as usize).modify(|_, w| unsafe {
if let Some(int_ena) = int_ena {
w.int_ena().bits(int_ena);
}
w.int_type().bits(int_type);
w.wakeup_enable().bit(wake_up_from_light_sleep)
});
}
fn is_int_enabled(gpio_num: u8) -> bool {
GPIO::regs().pin(gpio_num as usize).read().int_ena().bits() != 0
}
for_each_gpio! {
($n:literal, $gpio:ident $af_ins:tt $af_outs:tt ([Input] $output:tt)) => {
impl InputPin for crate::peripherals::$gpio<'_> {
#[doc(hidden)]
#[inline]
fn waker(&self) -> &'static $crate::asynch::AtomicWaker {
static WAKER: $crate::asynch::AtomicWaker = $crate::asynch::AtomicWaker::new();
&WAKER
}
}
};
}
for_each_gpio! {
($n:literal, $gpio:ident $af_ins:tt $af_outs:tt ($input:tt [Output])) => {
impl OutputPin for crate::peripherals::$gpio<'_> {}
};
}
for_each_gpio! {
($n:literal, $gpio:ident ($( $af_input_num:ident => $af_input_signal:ident )*) ($( $af_output_num:ident => $af_output_signal:ident )*) $attrs:tt) => {
impl<'d> crate::peripherals::$gpio<'d> {
#[allow(unused)]
pub(crate) const NUMBER: u8 = $n;
#[procmacros::doc_replace]
#[instability::unstable]
pub unsafe fn split(self) -> (interconnect::InputSignal<'d>, interconnect::OutputSignal<'d>) {
unsafe { self.degrade().split() }
}
}
impl Pin for crate::peripherals::$gpio<'_> {
#[inline(always)]
fn number(&self) -> u8 {
$n
}
fn output_signals(&self, _: crate::private::Internal) -> &'static [(AlternateFunction, OutputSignal)] {
&[$(
(AlternateFunction::$af_output_num, OutputSignal::$af_output_signal),
)*]
}
fn input_signals(&self, _: crate::private::Internal) -> &'static [(AlternateFunction, InputSignal)] {
&[$(
(AlternateFunction::$af_input_num, InputSignal::$af_input_signal),
)*]
}
}
impl<'lt> From<crate::peripherals::$gpio<'lt>> for AnyPin<'lt> {
fn from(pin: crate::peripherals::$gpio<'lt>) -> Self {
Pin::degrade(pin)
}
}
};
}
define_io_mux_reg!();