Skip to content

Commit

Permalink
Simplify traits and erratum 36 workaround, add Pins::steal (esp-rs#…
Browse files Browse the repository at this point in the history
…2335)

* Simplify erratum 36

* Add Pins::steal

* Fix typo

* Move pin operations into Flex

* Cleanup braces

* Avoid panicking when handling erratum

* Add disable_usb_pad to other devices

* Touch up changelog

* Fix changelog
  • Loading branch information
bugadani authored Oct 18, 2024
1 parent 326e006 commit d32a733
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 295 deletions.
3 changes: 2 additions & 1 deletion esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- A new config option `PLACE_SWITCH_TABLES_IN_RAM` to improve performance (especially for interrupts) at the cost of slightly more RAM usage (#2331)
- A new config option `PLACE_ANON_IN_RAM` to improve performance (especially for interrupts) at the cost of RAM usage (#2331)
- Add burst transfer support to DMA buffers (#2236)
- Add burst transfer support to DMA buffers (#2336)
- `AnyPin` now implements `From<GpioPin<N>>`. (#2326)
- `Pins::steal()` to unsafely obtain GPIO. (#2335)

### Changed

Expand Down
216 changes: 79 additions & 137 deletions esp-hal/src/gpio/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,75 +448,7 @@ pub trait PeripheralOutput: PeripheralSignal {
}

/// Trait implemented by pins which can be used as inputs.
pub trait InputPin: Pin + PeripheralInput + Into<AnyPin> + 'static {
/// Listen for interrupts
#[doc(hidden)]
fn listen(&mut self, event: Event, _: private::Internal) {
self.listen_with_options(event, true, false, false, private::Internal)
}

/// Checks if listening for interrupts is enabled for this Pin
#[doc(hidden)]
fn is_listening(&self, _: private::Internal) -> bool {
is_listening(self.number())
}

/// Listen for interrupts
#[doc(hidden)]
fn listen_with_options(
&mut self,
event: Event,
int_enable: bool,
nmi_enable: bool,
wake_up_from_light_sleep: bool,
_: private::Internal,
) {
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.number(),
gpio_intr_enable(int_enable, nmi_enable),
event as u8,
wake_up_from_light_sleep,
)
}

/// Stop listening for interrupts
#[doc(hidden)]
fn unlisten(&mut self, _: private::Internal) {
unsafe {
(*GPIO::PTR)
.pin(self.number() as usize)
.modify(|_, w| w.int_ena().bits(0).int_type().bits(0).int_ena().bits(0));
}
}

/// Checks if the interrupt status bit for this Pin is set
#[doc(hidden)]
fn is_interrupt_set(&self, _: private::Internal) -> bool {
self.gpio_bank(private::Internal).read_interrupt_status() & 1 << (self.number() % 32) != 0
}

/// Clear the interrupt status bit for this Pin
#[doc(hidden)]
fn clear_interrupt(&mut self, _: private::Internal) {
self.gpio_bank(private::Internal)
.write_interrupt_status_clear(1 << (self.number() % 32));
}

/// Enable this pin as a wake up source
#[doc(hidden)]
fn wakeup_enable(&mut self, enable: bool, event: WakeEvent, _: private::Internal) {
self.listen_with_options(event.into(), false, false, enable, private::Internal);
}
}
pub trait InputPin: Pin + PeripheralInput + Into<AnyPin> + 'static {}

/// Trait implemented by pins which can be used as outputs.
pub trait OutputPin: Pin + PeripheralOutput + Into<AnyPin> + 'static {}
Expand Down Expand Up @@ -722,17 +654,13 @@ impl<const GPIONUM: u8> GpioPin<GPIONUM>
where
Self: Pin,
{
pub(crate) fn new() -> Self {
Self
}

/// 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::new()
Self
}

/// Returns a peripheral [input][interconnect::InputSignal] connected to
Expand All @@ -747,13 +675,19 @@ where

/// 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(any(esp32c3, esp32s3))]
#[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");
}
}

Expand Down Expand Up @@ -792,7 +726,7 @@ where
let pull_down = pull == Pull::Down;

#[cfg(esp32)]
crate::soc::gpio::errata36(GPIONUM, Some(pull_up), Some(pull_down));
crate::soc::gpio::errata36(self.degrade_pin(private::Internal), pull_up, pull_down);

get_io_mux_reg(GPIONUM).modify(|_, w| {
w.fun_wpd().bit(pull_down);
Expand All @@ -808,7 +742,7 @@ where
fn init_input(&self, pull: Pull, _: private::Internal) {
self.pull_direction(pull, private::Internal);

#[cfg(any(esp32c3, esp32s3))]
#[cfg(usb_device)]
disable_usb_pads(GPIONUM);

get_io_mux_reg(GPIONUM).modify(|_, w| unsafe {
Expand Down Expand Up @@ -982,10 +916,10 @@ impl Io {
/// *Note:* You probably don't want to use this, it is intended to be used
/// in very specific use cases. Async GPIO functionality will not work
/// when instantiating `Io` using this constructor.
pub fn new_no_bind_interrupt(gpio: GPIO, _io_mux: IO_MUX) -> Self {
pub fn new_no_bind_interrupt(_gpio: GPIO, _io_mux: IO_MUX) -> Self {
Io {
_io_mux,
pins: gpio.pins(),
pins: unsafe { Pins::steal() },
}
}
}
Expand Down Expand Up @@ -1080,11 +1014,16 @@ macro_rules! gpio {
)+
}

impl GPIO {
pub(crate) fn pins(self) -> Pins {
Pins {
impl Pins {
/// Unsafely create GPIO pins.
///
/// # Safety
///
/// The caller must ensure that only one instance of a pin is in use at one time.
pub unsafe fn steal() -> Self {
Self {
$(
[< gpio $gpionum >]: GpioPin::new(),
[< gpio $gpionum >]: GpioPin::steal(),
)+
}
}
Expand Down Expand Up @@ -1255,15 +1194,15 @@ macro_rules! rtc_pins {
let rtcio = unsafe { &*$crate::peripherals::RTC_IO::PTR };

paste::paste! {
rtcio.$pin_reg.modify(|_, w| w.[< $prefix rue >]().bit([< enable >]));
rtcio.$pin_reg.modify(|_, w| w.[< $prefix rue >]().bit(enable));
}
}

fn rtcio_pulldown(&mut self, enable: bool) {
let rtcio = unsafe { &*$crate::peripherals::RTC_IO::PTR };

paste::paste! {
rtcio.$pin_reg.modify(|_, w| w.[< $prefix rde >]().bit([< enable >]));
rtcio.$pin_reg.modify(|_, w| w.[< $prefix rde >]().bit(enable));
}
}
}
Expand All @@ -1277,6 +1216,23 @@ macro_rules! rtc_pins {
$crate::gpio::rtc_pins!($pin_num, $rtc_pin, $pin_reg, $prefix, $hold $(, $rue )?);
)+

#[cfg(esp32)]
pub(crate) fn errata36(mut pin: AnyPin, pull_up: bool, pull_down: bool) {
use $crate::gpio::{Pin, RtcPinWithResistors};

let has_pullups = match pin.number() {
$(
$( $pin_num => $rue, )?
)+
_ => false,
};

if has_pullups {
pin.rtcio_pullup(pull_up);
pin.rtcio_pulldown(pull_down);
}
}

#[doc(hidden)]
#[macro_export]
macro_rules! handle_rtcio {
Expand Down Expand Up @@ -1474,7 +1430,7 @@ macro_rules! analog {
impl $crate::gpio::AnalogPin for GpioPin<$pin_num> {
/// Configures the pin for analog mode.
fn set_analog(&self, _: $crate::private::Internal) {
use $crate::peripherals::{GPIO};
use $crate::peripherals::GPIO;

get_io_mux_reg($pin_num).modify(|_,w| unsafe {
w.mcu_sel().bits(1);
Expand Down Expand Up @@ -1986,35 +1942,65 @@ where
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.pin.listen(event, private::Internal);
self.listen_with_options(event, true, false, false)
}

/// Stop listening for interrupts
pub fn unlisten(&mut self) {
self.pin.unlisten(private::Internal);
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.clear_interrupt(private::Internal);
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.is_interrupt_set(private::Internal)
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.pin.wakeup_enable(enable, event, private::Internal);
self.listen_with_options(event.into(), false, false, enable);
}

/// Returns a peripheral [input][interconnect::InputSignal] connected to
Expand Down Expand Up @@ -2230,51 +2216,7 @@ pub(crate) mod internal {
}
}

impl InputPin for AnyPin {
fn listen_with_options(
&mut self,
event: Event,
int_enable: bool,
nmi_enable: bool,
wake_up_from_light_sleep: bool,
_: private::Internal,
) {
handle_gpio_input!(&mut self.0, target, {
InputPin::listen_with_options(
target,
event,
int_enable,
nmi_enable,
wake_up_from_light_sleep,
private::Internal,
)
})
}

fn unlisten(&mut self, _: private::Internal) {
handle_gpio_input!(&mut self.0, target, {
InputPin::unlisten(target, private::Internal)
})
}

fn is_interrupt_set(&self, _: private::Internal) -> bool {
handle_gpio_input!(&self.0, target, {
InputPin::is_interrupt_set(target, private::Internal)
})
}

fn clear_interrupt(&mut self, _: private::Internal) {
handle_gpio_input!(&mut self.0, target, {
InputPin::clear_interrupt(target, private::Internal)
})
}

fn listen(&mut self, event: Event, _: private::Internal) {
handle_gpio_input!(&mut self.0, target, {
InputPin::listen(target, event, private::Internal)
})
}
}
impl InputPin for AnyPin {}

impl PeripheralOutput for AnyPin {
fn set_to_open_drain_output(&mut self, _: private::Internal) {
Expand Down Expand Up @@ -2415,7 +2357,7 @@ fn is_listening(pin_num: u8) -> bool {
}

fn set_int_enable(gpio_num: u8, int_ena: u8, int_type: u8, wake_up_from_light_sleep: bool) {
let gpio = unsafe { &*crate::peripherals::GPIO::PTR };
let gpio = unsafe { &*GPIO::PTR };
gpio.pin(gpio_num as usize).modify(|_, w| unsafe {
w.int_ena().bits(int_ena);
w.int_type().bits(int_type);
Expand Down
8 changes: 3 additions & 5 deletions esp-hal/src/rtc_cntl/sleep/esp32c6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use crate::{
clock::Clock,
efuse::Efuse,
gpio::{Pins, RtcFunction},
peripherals::Peripherals,
rtc_cntl::{
rtc::{
rtc_clk_cpu_freq_set_xtal,
Expand Down Expand Up @@ -900,13 +899,12 @@ impl RtcSleepConfig {
fn wake_io_reset() {
// loosely based on esp_deep_sleep_wakeup_io_reset

let peripherals = unsafe {
// We're stealing peripherals to do some uninitialization after waking up from
let mut pins = unsafe {
// We're stealing pins to do some uninitialization after waking up from
// deep sleep. We have to be careful to only touch settings that were enabled
// by deep sleep setup.
Peripherals::steal()
Pins::steal()
};
let mut pins = peripherals.GPIO.pins();

Ext1WakeupSource::wake_io_reset(&mut pins);
}
Expand Down
Loading

0 comments on commit d32a733

Please sign in to comment.