diff --git a/esp-hal-procmacros/src/lib.rs b/esp-hal-procmacros/src/lib.rs
index 0f010570bb0..ab14c8da486 100644
--- a/esp-hal-procmacros/src/lib.rs
+++ b/esp-hal-procmacros/src/lib.rs
@@ -620,7 +620,7 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream {
let imports = quote! {
use #hal_crate::lp_core::LpCore;
use #hal_crate::lp_core::LpCoreWakeupSource;
- use #hal_crate::gpio::lp_gpio::LowPowerPin;
+ use #hal_crate::gpio::lp_io::LowPowerPin;
use #hal_crate::gpio::*;
use #hal_crate::uart::lp_uart::LpUart;
use #hal_crate::i2c::lp_i2c::LpI2c;
diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md
index eeaf1c1a742..049cfaaea46 100644
--- a/esp-hal/CHANGELOG.md
+++ b/esp-hal/CHANGELOG.md
@@ -54,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `UsbSerialJtag` can be created in async or blocking mode. The blocking constructor takes an optional interrupt handler argument (#1377)
- SYSTIMER and TIMG instances can now be created in async or blocking mode (#1348)
- Runtime ISR binding for TWAI (#1384)
+- ESP32-C6: The `gpio::lp_gpio` module has been renamed to `gpio::lp_io` to match the peripheral name (#1397)
- Runtime ISR binding for assist_debug (#1395)
- Runtime ISR binding for software interrupts, software interrupts are split now, interrupt-executor takes the software interrupt to use, interrupt-executor is easier to use (#1398)
diff --git a/esp-hal/src/gpio/etm.rs b/esp-hal/src/gpio/etm.rs
new file mode 100644
index 00000000000..853bab09b0c
--- /dev/null
+++ b/esp-hal/src/gpio/etm.rs
@@ -0,0 +1,322 @@
+//! # Event Task Matrix Function
+//!
+//! ## Overview
+//!
+//! GPIO supports ETM function, that is, the ETM task of GPIO can be
+//! triggered by the ETM event of any peripheral, or the ETM task of any
+//! peripheral can be triggered by the ETM event of GPIO.
+//!
+//! The GPIO ETM provides eight task channels. The ETM tasks that each task
+//! channel can receive are:
+//! - SET: GPIO goes high when triggered
+//! - CLEAR: GPIO goes low when triggered
+//! - TOGGLE: GPIO toggle level when triggered.
+//!
+//! GPIO has eight event channels, and the ETM events that each event
+//! channel can generate are:
+//! - RISE_EDGE: Indicates that the output signal of the corresponding GPIO has
+//! a rising edge
+//! - FALL_EDGE: Indicates that the output signal of the corresponding GPIO has
+//! a falling edge
+//! - ANY_EDGE: Indicates that the output signal of the corresponding GPIO is
+//! reversed
+//!
+//! ## Example
+//! ```no_run
+//! let led_task = gpio_ext.channel0_task.toggle(&mut led);
+//! let button_event = gpio_ext.channel0_event.falling_edge(button);
+//! ```
+
+use crate::peripheral::{Peripheral, PeripheralRef};
+
+/// All the GPIO ETM channels
+#[non_exhaustive]
+pub struct GpioEtmChannels<'d> {
+ _gpio_sd: PeripheralRef<'d, crate::peripherals::GPIO_SD>,
+ pub channel0_task: GpioEtmTaskChannel<0>,
+ pub channel0_event: GpioEtmEventChannel<0>,
+ pub channel1_task: GpioEtmTaskChannel<1>,
+ pub channel1_event: GpioEtmEventChannel<1>,
+ pub channel2_task: GpioEtmTaskChannel<2>,
+ pub channel2_event: GpioEtmEventChannel<2>,
+ pub channel3_task: GpioEtmTaskChannel<3>,
+ pub channel3_event: GpioEtmEventChannel<3>,
+ pub channel4_task: GpioEtmTaskChannel<4>,
+ pub channel4_event: GpioEtmEventChannel<4>,
+ pub channel5_task: GpioEtmTaskChannel<5>,
+ pub channel5_event: GpioEtmEventChannel<5>,
+ pub channel6_task: GpioEtmTaskChannel<6>,
+ pub channel6_event: GpioEtmEventChannel<6>,
+ pub channel7_task: GpioEtmTaskChannel<7>,
+ pub channel7_event: GpioEtmEventChannel<7>,
+}
+
+impl<'d> GpioEtmChannels<'d> {
+ pub fn new(peripheral: impl Peripheral
+ 'd) -> Self {
+ crate::into_ref!(peripheral);
+
+ Self {
+ _gpio_sd: peripheral,
+ channel0_task: GpioEtmTaskChannel {},
+ channel0_event: GpioEtmEventChannel {},
+ channel1_task: GpioEtmTaskChannel {},
+ channel1_event: GpioEtmEventChannel {},
+ channel2_task: GpioEtmTaskChannel {},
+ channel2_event: GpioEtmEventChannel {},
+ channel3_task: GpioEtmTaskChannel {},
+ channel3_event: GpioEtmEventChannel {},
+ channel4_task: GpioEtmTaskChannel {},
+ channel4_event: GpioEtmEventChannel {},
+ channel5_task: GpioEtmTaskChannel {},
+ channel5_event: GpioEtmEventChannel {},
+ channel6_task: GpioEtmTaskChannel {},
+ channel6_event: GpioEtmEventChannel {},
+ channel7_task: GpioEtmTaskChannel {},
+ channel7_event: GpioEtmEventChannel {},
+ }
+ }
+}
+
+/// An ETM controlled GPIO event
+pub struct GpioEtmEventChannel {}
+
+impl GpioEtmEventChannel {
+ /// Trigger at rising edge
+ pub fn rising_edge<'d, PIN>(
+ self,
+ pin: impl Peripheral + 'd,
+ ) -> GpioEtmEventChannelRising<'d, PIN, C>
+ where
+ PIN: super::Pin,
+ {
+ crate::into_ref!(pin);
+ enable_event_channel(C, pin.number());
+ GpioEtmEventChannelRising { _pin: pin }
+ }
+
+ /// Trigger at falling edge
+ pub fn falling_edge<'d, PIN>(
+ self,
+ pin: impl Peripheral
+ 'd,
+ ) -> GpioEtmEventChannelFalling<'d, PIN, C>
+ where
+ PIN: super::Pin,
+ {
+ crate::into_ref!(pin);
+ enable_event_channel(C, pin.number());
+ GpioEtmEventChannelFalling { _pin: pin }
+ }
+
+ /// Trigger at any edge
+ pub fn any_edge<'d, PIN>(
+ self,
+ pin: impl Peripheral
+ 'd,
+ ) -> GpioEtmEventChannelAny<'d, PIN, C>
+ where
+ PIN: super::Pin,
+ {
+ crate::into_ref!(pin);
+ enable_event_channel(C, pin.number());
+ GpioEtmEventChannelAny { _pin: pin }
+ }
+}
+
+/// Event for rising edge
+#[non_exhaustive]
+pub struct GpioEtmEventChannelRising<'d, PIN, const C: u8>
+where
+ PIN: super::Pin,
+{
+ _pin: PeripheralRef<'d, PIN>,
+}
+
+impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmEventChannelRising<'d, PIN, C> where
+ PIN: super::Pin
+{
+}
+
+impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelRising<'d, PIN, C>
+where
+ PIN: super::Pin,
+{
+ fn id(&self) -> u8 {
+ 1 + C
+ }
+}
+
+/// Event for falling edge
+#[non_exhaustive]
+pub struct GpioEtmEventChannelFalling<'d, PIN, const C: u8>
+where
+ PIN: super::Pin,
+{
+ _pin: PeripheralRef<'d, PIN>,
+}
+
+impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmEventChannelFalling<'d, PIN, C> where
+ PIN: super::Pin
+{
+}
+
+impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelFalling<'d, PIN, C>
+where
+ PIN: super::Pin,
+{
+ fn id(&self) -> u8 {
+ 9 + C
+ }
+}
+
+/// Event for any edge
+#[non_exhaustive]
+pub struct GpioEtmEventChannelAny<'d, PIN, const C: u8>
+where
+ PIN: super::Pin,
+{
+ _pin: PeripheralRef<'d, PIN>,
+}
+
+impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmEventChannelAny<'d, PIN, C> where
+ PIN: super::Pin
+{
+}
+
+impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelAny<'d, PIN, C>
+where
+ PIN: super::Pin,
+{
+ fn id(&self) -> u8 {
+ 17 + C
+ }
+}
+
+/// An ETM controlled GPIO task
+pub struct GpioEtmTaskChannel {}
+
+impl GpioEtmTaskChannel {
+ // In theory we could have multiple pins assigned to the same task. Not sure how
+ // useful that would be. If we want to support it, the easiest would be
+ // to offer additional functions like `set2`, `set3` etc. where the
+ // number is the pin-count
+
+ /// Task to set a high level
+ pub fn set<'d, PIN>(self, pin: impl Peripheral + 'd) -> GpioEtmTaskSet<'d, PIN, C>
+ where
+ PIN: super::Pin,
+ {
+ crate::into_ref!(pin);
+ enable_task_channel(C, pin.number());
+ GpioEtmTaskSet { _pin: pin }
+ }
+
+ /// Task to set a low level
+ pub fn clear<'d, PIN>(self, pin: impl Peripheral
+ 'd) -> GpioEtmTaskClear<'d, PIN, C>
+ where
+ PIN: super::Pin,
+ {
+ crate::into_ref!(pin);
+ enable_task_channel(C, pin.number());
+ GpioEtmTaskClear { _pin: pin }
+ }
+
+ /// Task to toggle the level
+ pub fn toggle<'d, PIN>(
+ self,
+ pin: impl Peripheral
+ 'd,
+ ) -> GpioEtmTaskToggle<'d, PIN, C>
+ where
+ PIN: super::Pin,
+ {
+ crate::into_ref!(pin);
+ enable_task_channel(C, pin.number());
+ GpioEtmTaskToggle { _pin: pin }
+ }
+}
+
+/// Task for set operation
+#[non_exhaustive]
+pub struct GpioEtmTaskSet<'d, PIN, const C: u8>
+where
+ PIN: super::Pin,
+{
+ _pin: PeripheralRef<'d, PIN>,
+}
+
+impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmTaskSet<'d, PIN, C> where
+ PIN: super::Pin
+{
+}
+
+impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskSet<'d, PIN, C>
+where
+ PIN: super::Pin,
+{
+ fn id(&self) -> u8 {
+ 1 + C
+ }
+}
+
+/// Task for clear operation
+#[non_exhaustive]
+pub struct GpioEtmTaskClear<'d, PIN, const C: u8> {
+ _pin: PeripheralRef<'d, PIN>,
+}
+
+impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmTaskClear<'d, PIN, C> where
+ PIN: super::Pin
+{
+}
+
+impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskClear<'d, PIN, C>
+where
+ PIN: super::Pin,
+{
+ fn id(&self) -> u8 {
+ 9 + C
+ }
+}
+
+/// Task for toggle operation
+#[non_exhaustive]
+pub struct GpioEtmTaskToggle<'d, PIN, const C: u8> {
+ _pin: PeripheralRef<'d, PIN>,
+}
+
+impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmTaskToggle<'d, PIN, C> where
+ PIN: super::Pin
+{
+}
+
+impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskToggle<'d, PIN, C>
+where
+ PIN: super::Pin,
+{
+ fn id(&self) -> u8 {
+ 17 + C
+ }
+}
+
+fn enable_task_channel(channel: u8, pin: u8) {
+ let gpio_sd = unsafe { crate::peripherals::GPIO_SD::steal() };
+ let ptr = unsafe { gpio_sd.etm_task_p0_cfg().as_ptr().add(pin as usize / 4) };
+ let shift = 8 * (pin as usize % 4);
+ // bit 0 = en, bit 1-3 = channel
+ unsafe {
+ ptr.write_volatile(
+ ptr.read_volatile() & !(0xf << shift) | 1 << shift | (channel as u32) << (shift + 1),
+ );
+ }
+}
+
+fn enable_event_channel(channel: u8, pin: u8) {
+ let gpio_sd = unsafe { crate::peripherals::GPIO_SD::steal() };
+ gpio_sd
+ .etm_event_ch_cfg(channel as usize)
+ .modify(|_, w| w.etm_ch0_event_en().clear_bit());
+ gpio_sd
+ .etm_event_ch_cfg(channel as usize)
+ .modify(|_, w| w.etm_ch0_event_sel().variant(pin));
+ gpio_sd
+ .etm_event_ch_cfg(channel as usize)
+ .modify(|_, w| w.etm_ch0_event_en().set_bit());
+}
diff --git a/esp-hal/src/gpio/lp_io.rs b/esp-hal/src/gpio/lp_io.rs
new file mode 100644
index 00000000000..e8bd21029d2
--- /dev/null
+++ b/esp-hal/src/gpio/lp_io.rs
@@ -0,0 +1,250 @@
+//! Low Power IO (LP_IO)
+//!
+//! # Overview
+//!
+//! The hardware provides a couple of GPIO pins with low power (LP)
+//! capabilities and analog functions. These pins can be controlled by
+//! either IO MUX or LP IO MUX.
+//!
+//! If controlled by LP IO MUX, these pins will bypass IO MUX and GPIO
+//! matrix for the use by ULP and peripherals in LP system.
+//!
+//! When configured as LP GPIOs, the pins can still be controlled by ULP or
+//! the peripherals in LP system during chip Deep-sleep, and wake up the
+//! chip from Deep-sleep.
+//!
+//! # Example
+//! ```no_run
+//! let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
+//! // configure GPIO 1 as LP output pin
+//! let lp_pin = io.pins.gpio1.into_low_power().into_push_pull_output();
+//! ```
+
+use core::marker::PhantomData;
+
+#[cfg(esp32c6)]
+use super::OpenDrain;
+use super::{Floating, Input, Output, PullDown, PullUp, PushPull, Unknown};
+
+/// A GPIO pin configured for low power operation
+pub struct LowPowerPin {
+ pub(crate) private: PhantomData,
+}
+
+impl LowPowerPin {
+ #[doc(hidden)]
+ pub fn output_enable(&self, enable: bool) {
+ let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR };
+ if enable {
+ lp_io
+ .out_enable_w1ts()
+ .write(|w| w.enable_w1ts().variant(1 << PIN));
+ } else {
+ lp_io
+ .out_enable_w1tc()
+ .write(|w| w.enable_w1tc().variant(1 << PIN));
+ }
+ }
+
+ fn input_enable(&self, enable: bool) {
+ get_pin_reg(PIN).modify(|_, w| w.fun_ie().bit(enable));
+ }
+
+ fn pullup_enable(&self, enable: bool) {
+ get_pin_reg(PIN).modify(|_, w| w.fun_wpu().bit(enable));
+ }
+
+ fn pulldown_enable(&self, enable: bool) {
+ get_pin_reg(PIN).modify(|_, w| w.fun_wpd().bit(enable));
+ }
+
+ #[doc(hidden)]
+ pub fn set_level(&mut self, level: bool) {
+ let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR };
+ if level {
+ lp_io
+ .out_data_w1ts()
+ .write(|w| w.out_data_w1ts().variant(1 << PIN));
+ } else {
+ lp_io
+ .out_data_w1tc()
+ .write(|w| w.out_data_w1tc().variant(1 << PIN));
+ }
+ }
+
+ #[doc(hidden)]
+ pub fn get_level(&self) -> bool {
+ let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR };
+ (lp_io.in_().read().data_next().bits() & 1 << PIN) != 0
+ }
+
+ /// Configures the pin as an input with the internal pull-up resistor
+ /// enabled.
+ pub fn into_pull_up_input(self) -> LowPowerPin , PIN> {
+ self.input_enable(true);
+ self.pullup_enable(true);
+ self.pulldown_enable(false);
+ LowPowerPin {
+ private: PhantomData,
+ }
+ }
+
+ /// Configures the pin as an input with the internal pull-down resistor
+ /// enabled.
+ pub fn into_pull_down_input(self) -> LowPowerPin , PIN> {
+ self.input_enable(true);
+ self.pullup_enable(false);
+ self.pulldown_enable(true);
+ LowPowerPin {
+ private: PhantomData,
+ }
+ }
+
+ /// Configures the pin as a floating input pin.
+ pub fn into_floating_input(self) -> LowPowerPin , PIN> {
+ self.input_enable(true);
+ self.pullup_enable(false);
+ self.pulldown_enable(false);
+ LowPowerPin {
+ private: PhantomData,
+ }
+ }
+
+ /// Configures the pin as a push-pull output pin.
+ pub fn into_push_pull_output(self) -> LowPowerPin, PIN> {
+ self.output_enable(true);
+ LowPowerPin {
+ private: PhantomData,
+ }
+ }
+
+ /// Configures the pin as an open-drain output pin.
+ pub fn into_open_drain_output(self) -> LowPowerPin {
+ use crate::peripherals::GPIO;
+
+ let gpio = unsafe { &*GPIO::PTR };
+
+ gpio.pin(PIN as usize)
+ .modify(|_, w| w.pad_driver().bit(true));
+ self.pulldown_enable(false);
+ self.into_pull_up_input().into_push_pull_output();
+
+ LowPowerPin {
+ private: PhantomData,
+ }
+ }
+}
+
+pub(crate) fn init_low_power_pin(pin: u8) {
+ let lp_aon = unsafe { &*crate::peripherals::LP_AON::PTR };
+
+ lp_aon
+ .gpio_mux()
+ .modify(|r, w| w.sel().variant(r.sel().bits() | 1 << pin));
+
+ get_pin_reg(pin).modify(|_, w| w.mcu_sel().variant(0));
+}
+
+#[inline(always)]
+fn get_pin_reg(pin: u8) -> &'static crate::peripherals::lp_io::GPIO0 {
+ // ideally we should change the SVD and make the GPIOx registers into an
+ // array
+ unsafe {
+ let lp_io = &*crate::peripherals::LP_IO::PTR;
+ let pin_ptr = (lp_io.gpio0().as_ptr()).add(pin as usize);
+
+ &*(pin_ptr as *const esp32c6::generic::Reg)
+ }
+}
+
+/// Configures a pin for use as a low power pin
+pub trait IntoLowPowerPin {
+ fn into_low_power(self) -> LowPowerPin;
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! lp_gpio {
+ (
+ $($gpionum:literal)+
+ ) => {
+ paste::paste!{
+ $(
+ impl $crate::gpio::lp_io::IntoLowPowerPin<$gpionum> for GpioPin {
+ fn into_low_power(self) -> $crate::gpio::lp_io::LowPowerPin {
+ $crate::gpio::lp_io::init_low_power_pin($gpionum);
+ $crate::gpio::lp_io::LowPowerPin {
+ private: core::marker::PhantomData,
+ }
+ }
+ }
+
+ impl $crate::gpio::RTCPin for GpioPin {
+ unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8) {
+ let lp_io = &*$crate::peripherals::LP_IO::ptr();
+ lp_io.[< pin $gpionum >]().modify(|_, w| {
+ w.wakeup_enable().bit(wakeup).int_type().bits(level)
+ });
+ }
+
+ fn rtcio_pad_hold(&mut self, enable: bool) {
+ let mask = 1 << $gpionum;
+ unsafe {
+ let lp_aon = &*$crate::peripherals::LP_AON::ptr();
+
+ lp_aon.gpio_hold0().modify(|r, w| {
+ if enable {
+ w.gpio_hold0().bits(r.gpio_hold0().bits() | mask)
+ } else {
+ w.gpio_hold0().bits(r.gpio_hold0().bits() & !mask)
+ }
+ });
+ }
+ }
+
+ /// Set the LP properties of the pin. If `mux` is true then then pin is
+ /// routed to LP_IO, when false it is routed to IO_MUX.
+ fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: $crate::gpio::RtcFunction) {
+ let mask = 1 << $gpionum;
+ unsafe {
+ // Select LP_IO
+ let lp_aon = &*$crate::peripherals::LP_AON::ptr();
+ lp_aon
+ .gpio_mux()
+ .modify(|r, w| {
+ if mux {
+ w.sel().bits(r.sel().bits() | mask)
+ } else {
+ w.sel().bits(r.sel().bits() & !mask)
+ }
+ });
+
+ // Configure input, function and select normal operation registers
+ let lp_io = &*$crate::peripherals::LP_IO::ptr();
+ lp_io.[< gpio $gpionum >]().modify(|_, w| {
+ w
+ .slp_sel().bit(false)
+ .fun_ie().bit(input_enable)
+ .mcu_sel().bits(func as u8)
+ });
+ }
+ }
+ }
+
+ impl $crate::gpio::RTCPinWithResistors for GpioPin {
+ fn rtcio_pullup(&mut self, enable: bool) {
+ let lp_io = unsafe { &*$crate::peripherals::LP_IO::ptr() };
+ lp_io.[< gpio $gpionum >]().modify(|_, w| w.fun_wpu().bit(enable));
+ }
+
+ fn rtcio_pulldown(&mut self, enable: bool) {
+ let lp_io = unsafe { &*$crate::peripherals::LP_IO::ptr() };
+ lp_io.[< gpio $gpionum >]().modify(|_, w| w.fun_wpd().bit(enable));
+ }
+ }
+ )+
+ }
+ }
+}
+
+pub(crate) use lp_gpio;
diff --git a/esp-hal/src/gpio.rs b/esp-hal/src/gpio/mod.rs
similarity index 75%
rename from esp-hal/src/gpio.rs
rename to esp-hal/src/gpio/mod.rs
index faae26d0f32..53ebadec5f2 100644
--- a/esp-hal/src/gpio.rs
+++ b/esp-hal/src/gpio/mod.rs
@@ -37,6 +37,13 @@ use crate::{
peripherals::{GPIO, IO_MUX},
};
+#[cfg(soc_etm)]
+pub mod etm;
+#[cfg(lp_io)]
+pub mod lp_io;
+#[cfg(all(rtc_io, not(esp32)))]
+pub mod rtc_io;
+
/// Convenience type-alias for a no-pin / don't care - pin
pub type NoPinType = Gpio0;
@@ -2247,780 +2254,6 @@ macro_rules! analog {
}
}
-#[cfg(soc_etm)]
-pub mod etm {
- //! # Event Task Matrix Function
- //!
- //! ## Overview
- //!
- //! GPIO supports ETM function, that is, the ETM task of GPIO can be
- //! triggered by the ETM event of any peripheral, or the ETM task of any
- //! peripheral can be triggered by the ETM event of GPIO.
- //!
- //! The GPIO ETM provides eight task channels. The ETM tasks that each task
- //! channel can receive are:
- //! - SET: GPIO goes high when triggered
- //! - CLEAR: GPIO goes low when triggered
- //! - TOGGLE: GPIO toggle level when triggered.
- //!
- //! GPIO has eight event channels, and the ETM events that each event
- //! channel can generate are:
- //! - RISE_EDGE: Indicates that the output signal of the corresponding GPIO
- //! has a rising edge
- //! - FALL_EDGE: Indicates that the output signal of the corresponding GPIO
- //! has a falling edge
- //! - ANY_EDGE: Indicates that the output signal of the corresponding GPIO
- //! is reversed
- //!
- //! ## Example
- //! ```no_run
- //! let led_task = gpio_ext.channel0_task.toggle(&mut led);
- //! let button_event = gpio_ext.channel0_event.falling_edge(button);
- //! ```
-
- use crate::peripheral::{Peripheral, PeripheralRef};
-
- /// All the GPIO ETM channels
- #[non_exhaustive]
- pub struct GpioEtmChannels<'d> {
- _gpio_sd: PeripheralRef<'d, crate::peripherals::GPIO_SD>,
- pub channel0_task: GpioEtmTaskChannel<0>,
- pub channel0_event: GpioEtmEventChannel<0>,
- pub channel1_task: GpioEtmTaskChannel<1>,
- pub channel1_event: GpioEtmEventChannel<1>,
- pub channel2_task: GpioEtmTaskChannel<2>,
- pub channel2_event: GpioEtmEventChannel<2>,
- pub channel3_task: GpioEtmTaskChannel<3>,
- pub channel3_event: GpioEtmEventChannel<3>,
- pub channel4_task: GpioEtmTaskChannel<4>,
- pub channel4_event: GpioEtmEventChannel<4>,
- pub channel5_task: GpioEtmTaskChannel<5>,
- pub channel5_event: GpioEtmEventChannel<5>,
- pub channel6_task: GpioEtmTaskChannel<6>,
- pub channel6_event: GpioEtmEventChannel<6>,
- pub channel7_task: GpioEtmTaskChannel<7>,
- pub channel7_event: GpioEtmEventChannel<7>,
- }
-
- impl<'d> GpioEtmChannels<'d> {
- pub fn new(peripheral: impl Peripheral + 'd) -> Self {
- crate::into_ref!(peripheral);
-
- Self {
- _gpio_sd: peripheral,
- channel0_task: GpioEtmTaskChannel {},
- channel0_event: GpioEtmEventChannel {},
- channel1_task: GpioEtmTaskChannel {},
- channel1_event: GpioEtmEventChannel {},
- channel2_task: GpioEtmTaskChannel {},
- channel2_event: GpioEtmEventChannel {},
- channel3_task: GpioEtmTaskChannel {},
- channel3_event: GpioEtmEventChannel {},
- channel4_task: GpioEtmTaskChannel {},
- channel4_event: GpioEtmEventChannel {},
- channel5_task: GpioEtmTaskChannel {},
- channel5_event: GpioEtmEventChannel {},
- channel6_task: GpioEtmTaskChannel {},
- channel6_event: GpioEtmEventChannel {},
- channel7_task: GpioEtmTaskChannel {},
- channel7_event: GpioEtmEventChannel {},
- }
- }
- }
-
- /// An ETM controlled GPIO event
- pub struct GpioEtmEventChannel {}
-
- impl GpioEtmEventChannel {
- /// Trigger at rising edge
- pub fn rising_edge<'d, PIN>(
- self,
- pin: impl Peripheral + 'd,
- ) -> GpioEtmEventChannelRising<'d, PIN, C>
- where
- PIN: super::Pin,
- {
- crate::into_ref!(pin);
- enable_event_channel(C, pin.number());
- GpioEtmEventChannelRising { _pin: pin }
- }
-
- /// Trigger at falling edge
- pub fn falling_edge<'d, PIN>(
- self,
- pin: impl Peripheral
+ 'd,
- ) -> GpioEtmEventChannelFalling<'d, PIN, C>
- where
- PIN: super::Pin,
- {
- crate::into_ref!(pin);
- enable_event_channel(C, pin.number());
- GpioEtmEventChannelFalling { _pin: pin }
- }
-
- /// Trigger at any edge
- pub fn any_edge<'d, PIN>(
- self,
- pin: impl Peripheral
+ 'd,
- ) -> GpioEtmEventChannelAny<'d, PIN, C>
- where
- PIN: super::Pin,
- {
- crate::into_ref!(pin);
- enable_event_channel(C, pin.number());
- GpioEtmEventChannelAny { _pin: pin }
- }
- }
-
- /// Event for rising edge
- #[non_exhaustive]
- pub struct GpioEtmEventChannelRising<'d, PIN, const C: u8>
- where
- PIN: super::Pin,
- {
- _pin: PeripheralRef<'d, PIN>,
- }
-
- impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmEventChannelRising<'d, PIN, C> where
- PIN: super::Pin
- {
- }
-
- impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelRising<'d, PIN, C>
- where
- PIN: super::Pin,
- {
- fn id(&self) -> u8 {
- 1 + C
- }
- }
-
- /// Event for falling edge
- #[non_exhaustive]
- pub struct GpioEtmEventChannelFalling<'d, PIN, const C: u8>
- where
- PIN: super::Pin,
- {
- _pin: PeripheralRef<'d, PIN>,
- }
-
- impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmEventChannelFalling<'d, PIN, C> where
- PIN: super::Pin
- {
- }
-
- impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelFalling<'d, PIN, C>
- where
- PIN: super::Pin,
- {
- fn id(&self) -> u8 {
- 9 + C
- }
- }
-
- /// Event for any edge
- #[non_exhaustive]
- pub struct GpioEtmEventChannelAny<'d, PIN, const C: u8>
- where
- PIN: super::Pin,
- {
- _pin: PeripheralRef<'d, PIN>,
- }
-
- impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmEventChannelAny<'d, PIN, C> where
- PIN: super::Pin
- {
- }
-
- impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelAny<'d, PIN, C>
- where
- PIN: super::Pin,
- {
- fn id(&self) -> u8 {
- 17 + C
- }
- }
-
- /// An ETM controlled GPIO task
- pub struct GpioEtmTaskChannel {}
-
- impl GpioEtmTaskChannel {
- // In theory we could have multiple pins assigned to the same task. Not sure how
- // useful that would be. If we want to support it, the easiest would be
- // to offer additional functions like `set2`, `set3` etc. where the
- // number is the pin-count
-
- /// Task to set a high level
- pub fn set<'d, PIN>(self, pin: impl Peripheral + 'd) -> GpioEtmTaskSet<'d, PIN, C>
- where
- PIN: super::Pin,
- {
- crate::into_ref!(pin);
- enable_task_channel(C, pin.number());
- GpioEtmTaskSet { _pin: pin }
- }
-
- /// Task to set a low level
- pub fn clear<'d, PIN>(
- self,
- pin: impl Peripheral
+ 'd,
- ) -> GpioEtmTaskClear<'d, PIN, C>
- where
- PIN: super::Pin,
- {
- crate::into_ref!(pin);
- enable_task_channel(C, pin.number());
- GpioEtmTaskClear { _pin: pin }
- }
-
- /// Task to toggle the level
- pub fn toggle<'d, PIN>(
- self,
- pin: impl Peripheral
+ 'd,
- ) -> GpioEtmTaskToggle<'d, PIN, C>
- where
- PIN: super::Pin,
- {
- crate::into_ref!(pin);
- enable_task_channel(C, pin.number());
- GpioEtmTaskToggle { _pin: pin }
- }
- }
-
- /// Task for set operation
- #[non_exhaustive]
- pub struct GpioEtmTaskSet<'d, PIN, const C: u8>
- where
- PIN: super::Pin,
- {
- _pin: PeripheralRef<'d, PIN>,
- }
-
- impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmTaskSet<'d, PIN, C> where
- PIN: super::Pin
- {
- }
-
- impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskSet<'d, PIN, C>
- where
- PIN: super::Pin,
- {
- fn id(&self) -> u8 {
- 1 + C
- }
- }
-
- /// Task for clear operation
- #[non_exhaustive]
- pub struct GpioEtmTaskClear<'d, PIN, const C: u8> {
- _pin: PeripheralRef<'d, PIN>,
- }
-
- impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmTaskClear<'d, PIN, C> where
- PIN: super::Pin
- {
- }
-
- impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskClear<'d, PIN, C>
- where
- PIN: super::Pin,
- {
- fn id(&self) -> u8 {
- 9 + C
- }
- }
-
- /// Task for toggle operation
- #[non_exhaustive]
- pub struct GpioEtmTaskToggle<'d, PIN, const C: u8> {
- _pin: PeripheralRef<'d, PIN>,
- }
-
- impl<'d, PIN, const C: u8> crate::private::Sealed for GpioEtmTaskToggle<'d, PIN, C> where
- PIN: super::Pin
- {
- }
-
- impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskToggle<'d, PIN, C>
- where
- PIN: super::Pin,
- {
- fn id(&self) -> u8 {
- 17 + C
- }
- }
-
- fn enable_task_channel(channel: u8, pin: u8) {
- let gpio_sd = unsafe { crate::peripherals::GPIO_SD::steal() };
- let ptr = unsafe { gpio_sd.etm_task_p0_cfg().as_ptr().add(pin as usize / 4) };
- let shift = 8 * (pin as usize % 4);
- // bit 0 = en, bit 1-3 = channel
- unsafe {
- ptr.write_volatile(
- ptr.read_volatile() & !(0xf << shift)
- | 1 << shift
- | (channel as u32) << (shift + 1),
- );
- }
- }
-
- fn enable_event_channel(channel: u8, pin: u8) {
- let gpio_sd = unsafe { crate::peripherals::GPIO_SD::steal() };
- gpio_sd
- .etm_event_ch_cfg(channel as usize)
- .modify(|_, w| w.etm_ch0_event_en().clear_bit());
- gpio_sd
- .etm_event_ch_cfg(channel as usize)
- .modify(|_, w| w.etm_ch0_event_sel().variant(pin));
- gpio_sd
- .etm_event_ch_cfg(channel as usize)
- .modify(|_, w| w.etm_ch0_event_en().set_bit());
- }
-}
-
-#[cfg(all(rtc_io, not(esp32)))]
-pub mod rtc_io {
- //! RTC IO
- //!
- //! # Overview
- //!
- //! The hardware provides a couple of GPIO pins with low power (LP)
- //! capabilities and analog functions. These pins can be controlled by
- //! either IO MUX or RTC IO.
- //!
- //! If controlled by RTC IO, these pins will bypass IO MUX and GPIO
- //! matrix for the use by ULP and peripherals in RTC system.
- //!
- //! When configured as RTC GPIOs, the pins can still be controlled by ULP or
- //! the peripherals in RTC system during chip Deep-sleep, and wake up the
- //! chip from Deep-sleep.
- //!
- //! # Example
- //! ```no_run
- //! let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
- //! // configure GPIO 1 as ULP output pin
- //! let lp_pin = io.pins.gpio1.into_low_power().into_push_pull_output();
- //! ```
-
- use core::marker::PhantomData;
-
- #[cfg(esp32c6)]
- use super::OpenDrain;
- use super::{Floating, Input, Output, PullDown, PullUp, PushPull, Unknown};
-
- /// A GPIO pin configured for low power operation
- pub struct LowPowerPin {
- pub(crate) private: PhantomData,
- }
-
- /// Configures a pin for use as a low power pin
- pub trait IntoLowPowerPin {
- fn into_low_power(self) -> LowPowerPin;
- }
-
- impl LowPowerPin {
- #[doc(hidden)]
- pub fn output_enable(&self, enable: bool) {
- let rtc_io = unsafe { crate::peripherals::RTC_IO::steal() };
- if enable {
- // TODO align PAC
- #[cfg(esp32s2)]
- rtc_io
- .rtc_gpio_enable_w1ts()
- .write(|w| w.reg_rtcio_reg_gpio_enable_w1ts().variant(1 << PIN));
-
- #[cfg(esp32s3)]
- rtc_io
- .rtc_gpio_enable_w1ts()
- .write(|w| w.rtc_gpio_enable_w1ts().variant(1 << PIN));
- } else {
- rtc_io
- .enable_w1tc()
- .write(|w| w.enable_w1tc().variant(1 << PIN));
- }
- }
-
- fn input_enable(&self, enable: bool) {
- get_pin_reg(PIN).modify(|_, w| w.fun_ie().bit(enable));
- }
-
- fn pullup_enable(&self, enable: bool) {
- get_pin_reg(PIN).modify(|_, w| w.rue().bit(enable));
- }
-
- fn pulldown_enable(&self, enable: bool) {
- get_pin_reg(PIN).modify(|_, w| w.rde().bit(enable));
- }
-
- #[doc(hidden)]
- pub fn set_level(&mut self, level: bool) {
- let rtc_io = unsafe { &*crate::peripherals::RTC_IO::PTR };
-
- // TODO align PACs
- #[cfg(esp32s2)]
- if level {
- rtc_io
- .rtc_gpio_out_w1ts()
- .write(|w| w.gpio_out_data_w1ts().variant(1 << PIN));
- } else {
- rtc_io
- .rtc_gpio_out_w1tc()
- .write(|w| w.gpio_out_data_w1tc().variant(1 << PIN));
- }
-
- #[cfg(esp32s3)]
- if level {
- rtc_io
- .rtc_gpio_out_w1ts()
- .write(|w| w.rtc_gpio_out_data_w1ts().variant(1 << PIN));
- } else {
- rtc_io
- .rtc_gpio_out_w1tc()
- .write(|w| w.rtc_gpio_out_data_w1tc().variant(1 << PIN));
- }
- }
-
- #[doc(hidden)]
- pub fn get_level(&self) -> bool {
- let rtc_io = unsafe { &*crate::peripherals::RTC_IO::PTR };
- (rtc_io.rtc_gpio_in().read().bits() & 1 << PIN) != 0
- }
-
- /// Configures the pin as an input with the internal pull-up resistor
- /// enabled.
- pub fn into_pull_up_input(self) -> LowPowerPin , PIN> {
- self.input_enable(true);
- self.pullup_enable(true);
- self.pulldown_enable(false);
- LowPowerPin {
- private: PhantomData,
- }
- }
-
- /// Configures the pin as an input with the internal pull-down resistor
- /// enabled.
- pub fn into_pull_down_input(self) -> LowPowerPin , PIN> {
- self.input_enable(true);
- self.pullup_enable(false);
- self.pulldown_enable(true);
- LowPowerPin {
- private: PhantomData,
- }
- }
-
- /// Configures the pin as a floating input pin.
- pub fn into_floating_input(self) -> LowPowerPin , PIN> {
- self.input_enable(true);
- self.pullup_enable(false);
- self.pulldown_enable(false);
- LowPowerPin {
- private: PhantomData,
- }
- }
-
- /// Configures the pin as an output pin.
- pub fn into_push_pull_output(self) -> LowPowerPin, PIN> {
- self.output_enable(true);
- LowPowerPin {
- private: PhantomData,
- }
- }
-
- #[cfg(esp32c6)]
- /// Configures the pin as an pullup input and a push pull output pin.
- pub fn into_open_drain_output(self) -> LowPowerPin {
- self.into_pull_up_input();
- self.into_push_pull_output();
- use crate::peripherals::GPIO;
-
- let gpio = unsafe { &*GPIO::PTR };
-
- gpio.pin(PIN).modify(|_, w| w.pad_driver().bit(true));
- self.pulldown_enable(false);
-
- LowPowerPin {
- private: PhantomData,
- }
- }
- }
-
- #[cfg(esp32s3)]
- #[inline(always)]
- fn get_pin_reg(pin: u8) -> &'static crate::peripherals::rtc_io::TOUCH_PAD0 {
- unsafe {
- let rtc_io = &*crate::peripherals::RTC_IO::PTR;
- let pin_ptr = (rtc_io.touch_pad0().as_ptr()).add(pin as usize);
-
- &*(pin_ptr
- as *const esp32s3::generic::Reg)
- }
- }
-
- #[cfg(esp32s2)]
- #[inline(always)]
- fn get_pin_reg(pin: u8) -> &'static crate::peripherals::rtc_io::TOUCH_PAD {
- unsafe {
- let rtc_io = &*crate::peripherals::RTC_IO::PTR;
- let pin_ptr = (rtc_io.touch_pad(0).as_ptr()).add(pin as usize);
-
- &*(pin_ptr as *const esp32s2::generic::Reg)
- }
- }
-}
-
-#[cfg(lp_io)]
-pub mod lp_gpio {
- //! Low Power IO (LP_IO)
- //!
- //! # Overview
- //!
- //! The hardware provides a couple of GPIO pins with low power (LP)
- //! capabilities and analog functions. These pins can be controlled by
- //! either IO MUX or LP IO MUX.
- //!
- //! If controlled by LP IO MUX, these pins will bypass IO MUX and GPIO
- //! matrix for the use by ULP and peripherals in LP system.
- //!
- //! When configured as LP GPIOs, the pins can still be controlled by ULP or
- //! the peripherals in LP system during chip Deep-sleep, and wake up the
- //! chip from Deep-sleep.
- //!
- //! # Example
- //! ```no_run
- //! let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
- //! // configure GPIO 1 as LP output pin
- //! let lp_pin = io.pins.gpio1.into_low_power().into_push_pull_output();
- //! ```
-
- use core::marker::PhantomData;
-
- #[cfg(esp32c6)]
- use super::OpenDrain;
- use super::{Floating, Input, Output, PullDown, PullUp, PushPull, Unknown};
-
- /// A GPIO pin configured for low power operation
- pub struct LowPowerPin {
- pub(crate) private: PhantomData,
- }
-
- impl LowPowerPin {
- #[doc(hidden)]
- pub fn output_enable(&self, enable: bool) {
- let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR };
- if enable {
- lp_io
- .out_enable_w1ts()
- .write(|w| w.enable_w1ts().variant(1 << PIN));
- } else {
- lp_io
- .out_enable_w1tc()
- .write(|w| w.enable_w1tc().variant(1 << PIN));
- }
- }
-
- fn input_enable(&self, enable: bool) {
- get_pin_reg(PIN).modify(|_, w| w.fun_ie().bit(enable));
- }
-
- fn pullup_enable(&self, enable: bool) {
- get_pin_reg(PIN).modify(|_, w| w.fun_wpu().bit(enable));
- }
-
- fn pulldown_enable(&self, enable: bool) {
- get_pin_reg(PIN).modify(|_, w| w.fun_wpd().bit(enable));
- }
-
- #[doc(hidden)]
- pub fn set_level(&mut self, level: bool) {
- let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR };
- if level {
- lp_io
- .out_data_w1ts()
- .write(|w| w.out_data_w1ts().variant(1 << PIN));
- } else {
- lp_io
- .out_data_w1tc()
- .write(|w| w.out_data_w1tc().variant(1 << PIN));
- }
- }
-
- #[doc(hidden)]
- pub fn get_level(&self) -> bool {
- let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR };
- (lp_io.in_().read().data_next().bits() & 1 << PIN) != 0
- }
-
- /// Configures the pin as an input with the internal pull-up resistor
- /// enabled.
- pub fn into_pull_up_input(self) -> LowPowerPin , PIN> {
- self.input_enable(true);
- self.pullup_enable(true);
- self.pulldown_enable(false);
- LowPowerPin {
- private: PhantomData,
- }
- }
-
- /// Configures the pin as an input with the internal pull-down resistor
- /// enabled.
- pub fn into_pull_down_input(self) -> LowPowerPin , PIN> {
- self.input_enable(true);
- self.pullup_enable(false);
- self.pulldown_enable(true);
- LowPowerPin {
- private: PhantomData,
- }
- }
-
- /// Configures the pin as a floating input pin.
- pub fn into_floating_input(self) -> LowPowerPin , PIN> {
- self.input_enable(true);
- self.pullup_enable(false);
- self.pulldown_enable(false);
- LowPowerPin {
- private: PhantomData,
- }
- }
-
- /// Configures the pin as an output pin.
- pub fn into_push_pull_output(self) -> LowPowerPin, PIN> {
- self.output_enable(true);
- LowPowerPin {
- private: PhantomData,
- }
- }
-
- pub fn into_open_drain_output(self) -> LowPowerPin {
- use crate::peripherals::GPIO;
-
- let gpio = unsafe { &*GPIO::PTR };
-
- gpio.pin(PIN as usize)
- .modify(|_, w| w.pad_driver().bit(true));
- self.pulldown_enable(false);
- self.into_pull_up_input().into_push_pull_output();
-
- LowPowerPin {
- private: PhantomData,
- }
- }
- }
-
- pub(crate) fn init_low_power_pin(pin: u8) {
- let lp_aon = unsafe { &*crate::peripherals::LP_AON::PTR };
-
- lp_aon
- .gpio_mux()
- .modify(|r, w| w.sel().variant(r.sel().bits() | 1 << pin));
-
- get_pin_reg(pin).modify(|_, w| w.mcu_sel().variant(0));
- }
-
- #[inline(always)]
- fn get_pin_reg(pin: u8) -> &'static crate::peripherals::lp_io::GPIO0 {
- // ideally we should change the SVD and make the GPIOx registers into an
- // array
- unsafe {
- let lp_io = &*crate::peripherals::LP_IO::PTR;
- let pin_ptr = (lp_io.gpio0().as_ptr()).add(pin as usize);
-
- &*(pin_ptr as *const esp32c6::generic::Reg)
- }
- }
-
- /// Configures a pin for use as a low power pin
- pub trait IntoLowPowerPin {
- fn into_low_power(self) -> LowPowerPin;
- }
-
- #[doc(hidden)]
- #[macro_export]
- macro_rules! lp_gpio {
- (
- $($gpionum:literal)+
- ) => {
- paste::paste!{
- $(
- impl $crate::gpio::lp_gpio::IntoLowPowerPin<$gpionum> for GpioPin {
- fn into_low_power(self) -> $crate::gpio::lp_gpio::LowPowerPin {
- $crate::gpio::lp_gpio::init_low_power_pin($gpionum);
- $crate::gpio::lp_gpio::LowPowerPin {
- private: core::marker::PhantomData,
- }
- }
- }
-
- impl $crate::gpio::RTCPin for GpioPin {
- unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8) {
- let lp_io = &*$crate::peripherals::LP_IO::ptr();
- lp_io.[< pin $gpionum >]().modify(|_, w| {
- w.wakeup_enable().bit(wakeup).int_type().bits(level)
- });
- }
-
- fn rtcio_pad_hold(&mut self, enable: bool) {
- let mask = 1 << $gpionum;
- unsafe {
- let lp_aon = &*$crate::peripherals::LP_AON::ptr();
-
- lp_aon.gpio_hold0().modify(|r, w| {
- if enable {
- w.gpio_hold0().bits(r.gpio_hold0().bits() | mask)
- } else {
- w.gpio_hold0().bits(r.gpio_hold0().bits() & !mask)
- }
- });
- }
- }
-
- /// Set the LP properties of the pin. If `mux` is true then then pin is
- /// routed to LP_IO, when false it is routed to IO_MUX.
- fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: $crate::gpio::RtcFunction) {
- let mask = 1 << $gpionum;
- unsafe {
- // Select LP_IO
- let lp_aon = &*$crate::peripherals::LP_AON::ptr();
- lp_aon
- .gpio_mux()
- .modify(|r, w| {
- if mux {
- w.sel().bits(r.sel().bits() | mask)
- } else {
- w.sel().bits(r.sel().bits() & !mask)
- }
- });
-
- // Configure input, function and select normal operation registers
- let lp_io = &*$crate::peripherals::LP_IO::ptr();
- lp_io.[< gpio $gpionum >]().modify(|_, w| {
- w
- .slp_sel().bit(false)
- .fun_ie().bit(input_enable)
- .mcu_sel().bits(func as u8)
- });
- }
- }
- }
-
- impl $crate::gpio::RTCPinWithResistors for GpioPin {
- fn rtcio_pullup(&mut self, enable: bool) {
- let lp_io = unsafe { &*$crate::peripherals::LP_IO::ptr() };
- lp_io.[< gpio $gpionum >]().modify(|_, w| w.fun_wpu().bit(enable));
- }
-
- fn rtcio_pulldown(&mut self, enable: bool) {
- let lp_io = unsafe { &*$crate::peripherals::LP_IO::ptr() };
- lp_io.[< gpio $gpionum >]().modify(|_, w| w.fun_wpd().bit(enable));
- }
- }
- )+
- }
- }
- }
-
- pub(crate) use lp_gpio;
-}
-
#[cfg(feature = "async")]
mod asynch {
use core::task::{Context, Poll};
diff --git a/esp-hal/src/gpio/rtc_io.rs b/esp-hal/src/gpio/rtc_io.rs
new file mode 100644
index 00000000000..8c1f8fa3fac
--- /dev/null
+++ b/esp-hal/src/gpio/rtc_io.rs
@@ -0,0 +1,185 @@
+//! RTC IO
+//!
+//! # Overview
+//!
+//! The hardware provides a couple of GPIO pins with low power (LP)
+//! capabilities and analog functions. These pins can be controlled by
+//! either IO MUX or RTC IO.
+//!
+//! If controlled by RTC IO, these pins will bypass IO MUX and GPIO
+//! matrix for the use by ULP and peripherals in RTC system.
+//!
+//! When configured as RTC GPIOs, the pins can still be controlled by ULP or
+//! the peripherals in RTC system during chip Deep-sleep, and wake up the
+//! chip from Deep-sleep.
+//!
+//! # Example
+//! ```no_run
+//! let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
+//! // configure GPIO 1 as ULP output pin
+//! let lp_pin = io.pins.gpio1.into_low_power().into_push_pull_output();
+//! ```
+
+use core::marker::PhantomData;
+
+#[cfg(esp32c6)]
+use super::OpenDrain;
+use super::{Floating, Input, Output, PullDown, PullUp, PushPull, Unknown};
+
+/// A GPIO pin configured for low power operation
+pub struct LowPowerPin {
+ pub(crate) private: PhantomData,
+}
+
+/// Configures a pin for use as a low power pin
+pub trait IntoLowPowerPin {
+ fn into_low_power(self) -> LowPowerPin;
+}
+
+impl LowPowerPin {
+ #[doc(hidden)]
+ pub fn output_enable(&self, enable: bool) {
+ let rtc_io = unsafe { crate::peripherals::RTC_IO::steal() };
+ if enable {
+ // TODO align PAC
+ #[cfg(esp32s2)]
+ rtc_io
+ .rtc_gpio_enable_w1ts()
+ .write(|w| w.reg_rtcio_reg_gpio_enable_w1ts().variant(1 << PIN));
+
+ #[cfg(esp32s3)]
+ rtc_io
+ .rtc_gpio_enable_w1ts()
+ .write(|w| w.rtc_gpio_enable_w1ts().variant(1 << PIN));
+ } else {
+ rtc_io
+ .enable_w1tc()
+ .write(|w| w.enable_w1tc().variant(1 << PIN));
+ }
+ }
+
+ fn input_enable(&self, enable: bool) {
+ get_pin_reg(PIN).modify(|_, w| w.fun_ie().bit(enable));
+ }
+
+ fn pullup_enable(&self, enable: bool) {
+ get_pin_reg(PIN).modify(|_, w| w.rue().bit(enable));
+ }
+
+ fn pulldown_enable(&self, enable: bool) {
+ get_pin_reg(PIN).modify(|_, w| w.rde().bit(enable));
+ }
+
+ #[doc(hidden)]
+ pub fn set_level(&mut self, level: bool) {
+ let rtc_io = unsafe { &*crate::peripherals::RTC_IO::PTR };
+
+ // TODO align PACs
+ #[cfg(esp32s2)]
+ if level {
+ rtc_io
+ .rtc_gpio_out_w1ts()
+ .write(|w| w.gpio_out_data_w1ts().variant(1 << PIN));
+ } else {
+ rtc_io
+ .rtc_gpio_out_w1tc()
+ .write(|w| w.gpio_out_data_w1tc().variant(1 << PIN));
+ }
+
+ #[cfg(esp32s3)]
+ if level {
+ rtc_io
+ .rtc_gpio_out_w1ts()
+ .write(|w| w.rtc_gpio_out_data_w1ts().variant(1 << PIN));
+ } else {
+ rtc_io
+ .rtc_gpio_out_w1tc()
+ .write(|w| w.rtc_gpio_out_data_w1tc().variant(1 << PIN));
+ }
+ }
+
+ #[doc(hidden)]
+ pub fn get_level(&self) -> bool {
+ let rtc_io = unsafe { &*crate::peripherals::RTC_IO::PTR };
+ (rtc_io.rtc_gpio_in().read().bits() & 1 << PIN) != 0
+ }
+
+ /// Configures the pin as an input with the internal pull-up resistor
+ /// enabled.
+ pub fn into_pull_up_input(self) -> LowPowerPin , PIN> {
+ self.input_enable(true);
+ self.pullup_enable(true);
+ self.pulldown_enable(false);
+ LowPowerPin {
+ private: PhantomData,
+ }
+ }
+
+ /// Configures the pin as an input with the internal pull-down resistor
+ /// enabled.
+ pub fn into_pull_down_input(self) -> LowPowerPin , PIN> {
+ self.input_enable(true);
+ self.pullup_enable(false);
+ self.pulldown_enable(true);
+ LowPowerPin {
+ private: PhantomData,
+ }
+ }
+
+ /// Configures the pin as a floating input pin.
+ pub fn into_floating_input(self) -> LowPowerPin , PIN> {
+ self.input_enable(true);
+ self.pullup_enable(false);
+ self.pulldown_enable(false);
+ LowPowerPin {
+ private: PhantomData,
+ }
+ }
+
+ /// Configures the pin as an output pin.
+ pub fn into_push_pull_output(self) -> LowPowerPin, PIN> {
+ self.output_enable(true);
+ LowPowerPin {
+ private: PhantomData,
+ }
+ }
+
+ #[cfg(esp32c6)]
+ /// Configures the pin as an pullup input and a push pull output pin.
+ pub fn into_open_drain_output(self) -> LowPowerPin {
+ self.into_pull_up_input();
+ self.into_push_pull_output();
+ use crate::peripherals::GPIO;
+
+ let gpio = unsafe { &*GPIO::PTR };
+
+ gpio.pin(PIN).modify(|_, w| w.pad_driver().bit(true));
+ self.pulldown_enable(false);
+
+ LowPowerPin {
+ private: PhantomData,
+ }
+ }
+}
+
+#[cfg(esp32s3)]
+#[inline(always)]
+fn get_pin_reg(pin: u8) -> &'static crate::peripherals::rtc_io::TOUCH_PAD0 {
+ unsafe {
+ let rtc_io = &*crate::peripherals::RTC_IO::PTR;
+ let pin_ptr = (rtc_io.touch_pad0().as_ptr()).add(pin as usize);
+
+ &*(pin_ptr as *const esp32s3::generic::Reg)
+ }
+}
+
+#[cfg(esp32s2)]
+#[inline(always)]
+fn get_pin_reg(pin: u8) -> &'static crate::peripherals::rtc_io::TOUCH_PAD {
+ unsafe {
+ let rtc_io = &*crate::peripherals::RTC_IO::PTR;
+ let pin_ptr = (rtc_io.touch_pad(0).as_ptr()).add(pin as usize);
+
+ &*(pin_ptr as *const esp32s2::generic::Reg)
+ }
+}
diff --git a/esp-hal/src/i2c.rs b/esp-hal/src/i2c.rs
index 9db81e89b03..2fefe496bfb 100644
--- a/esp-hal/src/i2c.rs
+++ b/esp-hal/src/i2c.rs
@@ -1764,7 +1764,7 @@ pub mod lp_i2c {
use fugit::HertzU32;
use crate::{
- gpio::{lp_gpio::LowPowerPin, OpenDrain},
+ gpio::{lp_io::LowPowerPin, OpenDrain},
peripherals::{LP_CLKRST, LP_I2C0},
};
diff --git a/esp-hal/src/soc/esp32c6/gpio.rs b/esp-hal/src/soc/esp32c6/gpio.rs
index 26d198ee6f8..5d67968c332 100644
--- a/esp-hal/src/soc/esp32c6/gpio.rs
+++ b/esp-hal/src/soc/esp32c6/gpio.rs
@@ -321,7 +321,7 @@ crate::gpio::analog! {
7
}
-crate::gpio::lp_gpio::lp_gpio! {
+crate::gpio::lp_io::lp_gpio! {
0
1
2
diff --git a/esp-hal/src/soc/esp32p4/gpio.rs b/esp-hal/src/soc/esp32p4/gpio.rs
index 5c854046820..a2b60c92f66 100644
--- a/esp-hal/src/soc/esp32p4/gpio.rs
+++ b/esp-hal/src/soc/esp32p4/gpio.rs
@@ -458,7 +458,7 @@ crate::gpio::gpio! {
// crate::gpio::analog! {
// }
-// crate::gpio::lp_gpio::lp_gpio! {
+// crate::gpio::lp_io::lp_gpio! {
// 0
// 1
// 2
diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs
index 5eb4033a224..e21177b07a9 100644
--- a/esp-hal/src/uart.rs
+++ b/esp-hal/src/uart.rs
@@ -2012,7 +2012,7 @@ mod asynch {
#[cfg(lp_uart)]
pub mod lp_uart {
use crate::{
- gpio::{lp_gpio::LowPowerPin, Floating, Input, Output, PushPull},
+ gpio::{lp_io::LowPowerPin, Floating, Input, Output, PushPull},
peripherals::{LP_CLKRST, LP_UART},
uart::{config, config::Config},
};
diff --git a/examples/src/bin/lp_core_basic.rs b/examples/src/bin/lp_core_basic.rs
index 9d3b839217a..53cd5bb9756 100644
--- a/examples/src/bin/lp_core_basic.rs
+++ b/examples/src/bin/lp_core_basic.rs
@@ -12,7 +12,7 @@
use esp_backtrace as _;
use esp_hal::{
- gpio::{lp_gpio::IntoLowPowerPin, IO},
+ gpio::{lp_io::IntoLowPowerPin, IO},
lp_core::{LpCore, LpCoreWakeupSource},
peripherals::Peripherals,
prelude::*,
diff --git a/examples/src/bin/lp_core_i2c.rs b/examples/src/bin/lp_core_i2c.rs
index f7e3adc0a3b..bcfa647eaaf 100644
--- a/examples/src/bin/lp_core_i2c.rs
+++ b/examples/src/bin/lp_core_i2c.rs
@@ -12,7 +12,7 @@
use esp_backtrace as _;
use esp_hal::{
- gpio::{lp_gpio::IntoLowPowerPin, IO},
+ gpio::{lp_io::IntoLowPowerPin, IO},
i2c::lp_i2c::LpI2c,
lp_core::{LpCore, LpCoreWakeupSource},
peripherals::Peripherals,
diff --git a/examples/src/bin/lp_core_uart.rs b/examples/src/bin/lp_core_uart.rs
index d12591d325d..d602c866e99 100644
--- a/examples/src/bin/lp_core_uart.rs
+++ b/examples/src/bin/lp_core_uart.rs
@@ -13,7 +13,7 @@
use esp_backtrace as _;
use esp_hal::{
clock::ClockControl,
- gpio::{lp_gpio::IntoLowPowerPin, IO},
+ gpio::{lp_io::IntoLowPowerPin, IO},
lp_core::{LpCore, LpCoreWakeupSource},
peripherals::Peripherals,
prelude::*,