Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions esp-hal/src/gpio/low_level/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,25 @@ pub(crate) fn set_interrupt_priority(priority: Priority) {
}

fn errata36(pin: &AnyPin<'_>, pull_up: bool, pull_down: bool) {
use crate::gpio::{LpPinWithResistors, Pin};
use crate::gpio::{Pin, lp_io::low_level};

for_each_lp_function! {
(LP_GPIOn $( (($_sig:ident, LP_GPIOn, $_n:literal), $gpio:ident, $_af:ident, $_lp_in:tt $_lp_out:tt) ),* ) => {
const LP_IO_PINS: &[u8] = &[ $( $crate::peripherals::$gpio::NUMBER ),* ];
(LP_GPIOn $( (($_sig:ident, LP_GPIOn, $n:literal), $gpio:ident, $_af:ident, $_lp_in:tt $_lp_out:tt) ),* ) => {
// The digital pin number, and the number the low-power registers index the pad by.
const LP_IO_PINS: &[(u8, u8)] = &[ $( ($crate::peripherals::$gpio::NUMBER, $n) ),* ];
};
};

if LP_IO_PINS.contains(&pin.number()) && pin.is_output() {
pin.lp_pullup(pull_up);
pin.lp_pulldown(pull_down);
let lp = LP_IO_PINS
.iter()
.find(|(gpio, _)| *gpio == pin.number())
.map(|(_, lp)| *lp);

// The low-power registers of the pads that cannot pull anything warn instead.
if let Some(lp) = lp
&& pin.is_output()
{
low_level::pullup_enable(lp, pull_up);
low_level::pulldown_enable(lp, pull_down);
}
}
117 changes: 55 additions & 62 deletions esp-hal/src/gpio/lp_io/low_level/esp32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,61 +11,15 @@ macro_rules! lp_io_analog {
fn lp_number(&self) -> u8 {
$lp_pin
}

fn lp_set_config(
&self,
input_enable: bool,
mux: bool,
func: LpFunction,
) {
RTC_IO::regs()
.$pin_reg
.modify(|_, w| unsafe {
w.[<$prefix fun_ie>]().bit(input_enable);
w.[<$prefix mux_sel>]().bit(mux);
w.[<$prefix fun_sel>]().bits(func as u8)
});
}

fn apply_wakeup(&self, wakeup: bool, level: crate::gpio::WakeEvent) {
RTC_IO::regs().pin($lp_pin).modify(|_, w| unsafe {
w.wakeup_enable().bit(wakeup);
w.int_type().bits(level as u8)
});
}

fn lp_pad_hold(&self, enable: bool) {
LPWR::regs()
.hold_force()
.modify(|_, w| w.$hold().bit(enable));
}
}

// Only output pins have PU/PD resistors.
for_each_gpio! {
($n:tt, $pin_peri $in_afs:tt $out_afs:tt ($input:tt [Output])) => {
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
impl crate::gpio::LpPinWithResistors
for crate::peripherals::$pin_peri<'_>
{
fn lp_pullup(&self, enable: bool) {
pullup_enable($lp_pin, enable)
}

fn lp_pulldown(&self, enable: bool) {
pulldown_enable($lp_pin, enable)
}
}
};
}

impl crate::peripherals::$pin_peri<'_> {
#[cfg(feature = "unstable")]
pub(crate) fn set_analog_impl(&self) {
use crate::gpio::LpPin;
use crate::gpio::{LpPin, Pin};

output_enable(self.lp_number(), false);
set_open_drain_output(self.lp_number(), false);
set_open_drain_output(self.number(), false);

RTC_IO::regs().$pin_reg.modify(|_, w| {
w.[<$prefix fun_ie>]().clear_bit();
Expand Down Expand Up @@ -94,6 +48,34 @@ macro_rules! lp_io_analog {
lp_io_analog!($pin_peri, $lp_pin, $pin_reg, $prefix, $hold);
)+

// Every pad has a register of its own, so the pad-specific work dispatches on the
// low-power number.
pub(crate) fn set_config(lp: u8, input_enable: bool, mux: bool, func: LpFunction) {
paste::paste! {
match lp {
$(
$lp_pin => {
RTC_IO::regs().$pin_reg.modify(|_, w| unsafe {
w.[<$prefix fun_ie>]().bit(input_enable);
w.[<$prefix mux_sel>]().bit(mux);
w.[<$prefix fun_sel>]().bits(func as u8)
});
}
)+
_ => unreachable!(),
}
}
}

pub(crate) fn pad_hold(lp: u8, enable: bool) {
LPWR::regs().hold_force().modify(|_, w| {
match lp {
$( $lp_pin => w.$hold().bit(enable), )+
_ => unreachable!(),
}
});
}

macro_rules! set_one_pad_field {
$(
($lp_pin, $field:ident, $enable:ident) => {{
Expand Down Expand Up @@ -176,39 +158,50 @@ macro_rules! set_pull_field {
}};
}

// esp32 arms its pads through ext0 and ext1, not one by one.
#[expect(dead_code)]
pub(crate) fn apply_wakeup(lp: u8, wakeup: bool, event: crate::gpio::WakeEvent) {
RTC_IO::regs().pin(lp as usize).modify(|_, w| unsafe {
w.wakeup_enable().bit(wakeup);
w.int_type().bits(event as u8)
});
}

#[expect(dead_code)]
pub(super) fn init_pin(pin: &impl crate::gpio::LpPin, input_enable: bool) -> u8 {
pin.lp_set_config(input_enable, true, LpFunction::LP_GPIO);
pin.lp_number()
pub(crate) fn init_pin(lp: u8, input_enable: bool) -> u8 {
set_config(lp, input_enable, true, LpFunction::LP_GPIO);
lp
}

pub(super) fn output_enable(pin: u8, enable: bool) {
pub(crate) fn output_enable(lp: u8, enable: bool) {
if enable {
RTC_IO::regs()
.enable_w1ts()
.write(|w| unsafe { w.enable_w1ts().bits(1 << pin) });
.write(|w| unsafe { w.enable_w1ts().bits(1 << lp) });
} else {
RTC_IO::regs()
.enable_w1tc()
.write(|w| unsafe { w.enable_w1tc().bits(1 << pin) });
.write(|w| unsafe { w.enable_w1tc().bits(1 << lp) });
}
}

#[expect(dead_code)]
pub(super) fn input_enable(pin: u8, enable: bool) {
set_pad_field!(pin, fun_ie, enable);
pub(crate) fn input_enable(lp: u8, enable: bool) {
set_pad_field!(lp, fun_ie, enable);
}

pub(super) fn pullup_enable(pin: u8, enable: bool) {
set_pull_field!(pin, rue, enable);
pub(crate) fn pullup_enable(lp: u8, enable: bool) {
set_pull_field!(lp, rue, enable);
}

pub(super) fn pulldown_enable(pin: u8, enable: bool) {
set_pull_field!(pin, rde, enable);
pub(crate) fn pulldown_enable(lp: u8, enable: bool) {
set_pull_field!(lp, rde, enable);
}

pub(super) fn set_open_drain_output(pin: u8, enable: bool) {
// The pad driver bit lives in the digital GPIO peripheral, and this chip numbers its low-power
// pads differently, so this one takes the digital number.
pub(crate) fn set_open_drain_output(gpio: u8, enable: bool) {
GPIO::regs()
.pin(pin as usize)
.pin(gpio as usize)
.modify(|_, w| w.pad_driver().bit(enable));
}
114 changes: 42 additions & 72 deletions esp-hal/src/gpio/lp_io/low_level/esp32h2.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
gpio::{AlternateFunction, LpPin, LpPinWithResistors, WakeEvent, lp_io::LpFunction},
gpio::{AlternateFunction, LpPin, lp_io::LpFunction},
peripherals::{GPIO, IO_MUX, LP_AON},
};

Expand All @@ -10,80 +10,46 @@ for_each_lp_function! {
fn lp_number(&self) -> u8 {
$pin
}

fn apply_wakeup(&self, wakeup: bool, level: WakeEvent) {
let mask = 1 << $pin;
LP_AON::regs().ext_wakeup_cntl().modify(|r, w| unsafe {
let select = r.ext_wakeup_sel().bits();
let levels = r.ext_wakeup_lv().bits();

w.ext_wakeup_filter().set_bit();
w.ext_wakeup_sel().bits(if wakeup {
select | mask
} else {
select & !mask
});
w.ext_wakeup_lv().bits(if level == WakeEvent::HighLevel {
levels | mask
} else {
levels & !mask
})
});
}

fn lp_pad_hold(&self, enable: bool) {
let mask = 1 << lp_pin_to_gpio($pin);
LP_AON::regs()
.gpio_hold0()
.modify(|r, w| unsafe {
let bits = r.gpio_hold0().bits();
w.gpio_hold0().bits(if enable {
bits | mask
} else {
bits & !mask
})
});
}

// The LP core reaches the pad through the digital IO MUX, so there is no low-power
// function to select.
fn lp_set_config(&self, input_enable: bool, _mux: bool, _func: LpFunction) {
IO_MUX::regs().gpio(lp_pin_to_gpio($pin) as usize)
.modify(|_, w| unsafe {
w.slp_sel().bit(false);
w.mcu_sel().bits(AlternateFunction::GPIO as u8);
w.fun_ie().bit(input_enable)
});
}
}
};
}

#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
impl LpPinWithResistors for crate::peripherals::$gpio<'_> {
fn lp_pullup(&self, enable: bool) {
pullup_enable($pin, enable);
}
pub(crate) fn pad_hold(lp: u8, enable: bool) {
let mask = 1 << lp_pin_to_gpio(lp);
LP_AON::regs().gpio_hold0().modify(|r, w| unsafe {
let bits = r.gpio_hold0().bits();
w.gpio_hold0()
.bits(if enable { bits | mask } else { bits & !mask })
});
}

fn lp_pulldown(&self, enable: bool) {
pulldown_enable($pin, enable);
}
}
};
/// Configures the pad.
///
/// The low-power domain reaches the pad through the digital IO MUX, so there is no low-power
/// function to select.
pub(crate) fn set_config(lp: u8, input_enable: bool, _mux: bool, _func: LpFunction) {
IO_MUX::regs()
.gpio(lp_pin_to_gpio(lp) as usize)
.modify(|_, w| unsafe {
w.slp_sel().bit(false);
w.mcu_sel().bits(AlternateFunction::GPIO as u8);
w.fun_ie().bit(input_enable)
});
}

#[expect(dead_code)]
pub(super) fn init_pin(pin: &impl LpPin, enable_input: bool) -> u8 {
let lp_pin = pin.lp_number();
input_enable(lp_pin, enable_input);
lp_pin
pub(crate) fn init_pin(lp: u8, enable_input: bool) -> u8 {
input_enable(lp, enable_input);
lp
}

fn lp_pin_to_gpio(pin: u8) -> u8 {
pin + 7
fn lp_pin_to_gpio(lp: u8) -> u8 {
lp + 7
}

#[expect(dead_code)]
pub(super) fn output_enable(pin: u8, enable: bool) {
let gpio = lp_pin_to_gpio(pin);
pub(crate) fn output_enable(lp: u8, enable: bool) {
let gpio = lp_pin_to_gpio(lp);
if enable {
GPIO::regs()
.enable_w1ts()
Expand All @@ -95,27 +61,31 @@ pub(super) fn output_enable(pin: u8, enable: bool) {
}
}

pub(super) fn input_enable(pin: u8, enable: bool) {
pub(crate) fn input_enable(lp: u8, enable: bool) {
IO_MUX::regs()
.gpio(lp_pin_to_gpio(pin) as usize)
.gpio(lp_pin_to_gpio(lp) as usize)
.modify(|_, w| w.fun_ie().bit(enable));
}

pub(super) fn pullup_enable(pin: u8, enable: bool) {
#[expect(dead_code)]
pub(crate) fn pullup_enable(lp: u8, enable: bool) {
IO_MUX::regs()
.gpio(lp_pin_to_gpio(pin) as usize)
.gpio(lp_pin_to_gpio(lp) as usize)
.modify(|_, w| w.fun_wpu().bit(enable));
}

pub(super) fn pulldown_enable(pin: u8, enable: bool) {
#[expect(dead_code)]
pub(crate) fn pulldown_enable(lp: u8, enable: bool) {
IO_MUX::regs()
.gpio(lp_pin_to_gpio(pin) as usize)
.gpio(lp_pin_to_gpio(lp) as usize)
.modify(|_, w| w.fun_wpd().bit(enable));
}

// The pad driver bit lives in the digital GPIO peripheral, and this chip numbers its low-power
// pads differently, so this one takes the digital number.
#[expect(dead_code)]
pub(super) fn set_open_drain_output(pin: u8, enable: bool) {
pub(crate) fn set_open_drain_output(gpio: u8, enable: bool) {
GPIO::regs()
.pin(pin as usize)
.pin(gpio as usize)
.modify(|_, w| w.pad_driver().bit(enable));
}
Loading
Loading