Simplify Flex (#3387)

* Simplify Flex

* Rename functions
This commit is contained in:
Dániel Buga 2025-04-22 14:30:34 +02:00 committed by GitHub
parent 4bc9ef2f5b
commit ffbcae162a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 157 additions and 122 deletions

View File

@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add DMA memcpy support to the S2 (#3352)
- Some config options can now only be set when the `unstable` feature in enabled (#3365)
- Bump Rust edition to 2024, bump MSRV to 1.85. (#3391)
- Added `Flex::enable_output` (#3387)
- Added `Flex::set_output_enable` (#3387)
### Changed
@ -44,6 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- All `Camera` pins are now configured using `with_*()` methods (#3237)
- The `ESP_HAL_CONFIG_PLACE_SPI_DRIVER_IN_RAM` configuration option has been renamed to `ESP_HAL_CONFIG_PLACE_SPI_MASTER_DRIVER_IN_RAM`. (#3402)
- Made the `ParlIo` traits for `TxPins`, `RxPins`, `ConfigurePins` public (#3398)
- Renamed `Flex::enable_input` to `set_input_enable` (#3387)
### Fixed
@ -59,7 +62,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- ESP32/ESP32-S2: Avoid running into timeouts with reads/writes larger than the FIFO (#3199)
- ESP32: Enforce required pointer alignments in DMA buffers (#3296)
- ESP32-C6: Keep ADC enabled to improve radio signal strength (#3249)
- Flex: Revert removal of `Flex::set_as_input` (#3250)
- Fix off-by-one in the allowed range of the spi clock calculations (#3266)
- Fix PCNT counter not keeping the peripheral enabled (#3334)
- Fixed an issue where inverting a pin via the interconnect matrix was ineffective (#3312)
@ -73,6 +75,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The `Peripheral` trait and `PeripheralRef` struct have been removed (#3302, #3305)
- Removed the inherent `degrade` method from peripheral singletons. (#3305)
- Removed the `FullDuplex` trait from the PARL_IO driver. (#3339)
- Removed `Flex::{set_as_input, set_as_output, set_drive_strength, set_as_open_drain, pull_direction}` functions (#3387)
## v1.0.0-beta.0

View File

@ -102,6 +102,38 @@ The affected types in the `gpio::interconnect` module are:
- `InputConnection`
- `OutputConnection`
### Flex API surface has been simplified
The `enable_input` method has been renamed to `set_input_enable`.
The `Flex` driver no longer provides the following functions:
- set_as_input
- set_as_output
- set_drive_strength
- set_as_open_drain
- pull_direction
The individual configurations can be set via `apply_input_config` and `apply_output_config`.
The input buffer and output driver can be separately enabled via `set_input_enable` and
`set_output_enable`.
Normally you only need to configure your pin once, after which changing modes can be done by calling
`set_input_enable` and/or `set_output_enable`.
```diff
- flex.set_as_input(pull_direction);
+ flex.apply_input_config(&InputConfig::default().with_pull(pull_direction)); // only if needed
+ flex.set_output_enable(false);
+ flex.set_input_enable(true);
- flex.set_as_output(); // or set_as_open_drain(pull_direction)
+ flex.apply_output_config(&OutputConfig::default().with_drive_mode(open_drain_or_push_pull)); // only if needed
+ flex.set_input_enable(false); // optional
+ flex.set_level(initial_level); // optional
+ flex.set_output_enable(true);
```
## I2S driver now takes `DmaDescriptor`s later in construction
```diff

View File

@ -462,7 +462,7 @@ impl<'d> InputSignal<'d> {
pub fn input_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::InputSignal)];
pub fn init_input(&self, pull: Pull);
pub fn is_input_high(&self) -> bool;
pub fn enable_input(&self, on: bool);
pub fn set_input_enable(&self, on: bool);
}
}
}
@ -500,7 +500,7 @@ impl<'d> DirectInputSignal<'d> {
fn input_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::InputSignal)];
fn init_input(&self, pull: Pull);
fn is_input_high(&self) -> bool;
fn enable_input(&self, on: bool);
fn set_input_enable(&self, on: bool);
}
}
}
@ -574,12 +574,12 @@ impl<'d> OutputSignal<'d> {
pub fn input_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::InputSignal)];
pub fn init_input(&self, pull: Pull);
pub fn is_input_high(&self) -> bool;
pub fn enable_input(&self, on: bool);
pub fn set_input_enable(&self, on: bool);
pub fn output_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::OutputSignal)];
pub fn set_to_open_drain_output(&self);
pub fn set_to_push_pull_output(&self);
pub fn enable_output(&self, on: bool);
pub fn set_output_enable(&self, on: bool);
pub fn set_output_high(&self, on: bool);
pub fn set_drive_strength(&self, strength: gpio::DriveStrength);
pub fn enable_open_drain(&self, on: bool);
@ -625,12 +625,12 @@ impl<'d> DirectOutputSignal<'d> {
fn input_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::InputSignal)];
fn init_input(&self, pull: Pull);
fn is_input_high(&self) -> bool;
fn enable_input(&self, on: bool);
fn set_input_enable(&self, on: bool);
fn output_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::OutputSignal)];
fn set_to_open_drain_output(&self);
fn set_to_push_pull_output(&self);
fn enable_output(&self, on: bool);
fn set_output_enable(&self, on: bool);
fn set_output_high(&self, on: bool);
fn set_drive_strength(&self, strength: gpio::DriveStrength);
fn enable_open_drain(&self, on: bool);
@ -745,7 +745,7 @@ impl InputConnection<'_> {
pub fn init_input(&self, pull: Pull);
pub fn is_input_high(&self) -> bool;
pub fn input_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::InputSignal)];
pub fn enable_input(&self, on: bool);
pub fn set_input_enable(&self, on: bool);
// This doesn't need to be public, the intended way is `connect_to` and `disconnect_from`
fn connect_input_to_peripheral(&self, signal: gpio::InputSignal);
@ -830,11 +830,11 @@ impl OutputConnection<'_> {
pub fn output_signals(&self, _internal: private::Internal) -> &'static [(AlternateFunction, gpio::OutputSignal)];
pub fn pull_direction(&self, pull: Pull);
pub fn init_input(&self, pull: Pull);
pub fn enable_input(&self, on: bool);
pub fn set_input_enable(&self, on: bool);
pub fn set_to_open_drain_output(&self);
pub fn set_to_push_pull_output(&self);
pub fn enable_output(&self, on: bool);
pub fn set_output_enable(&self, on: bool);
pub fn set_output_high(&self, on: bool);
pub fn set_drive_strength(&self, strength: gpio::DriveStrength);
pub fn enable_open_drain(&self, on: bool);

View File

@ -1072,7 +1072,7 @@ pub enum DriveMode {
/// resistors.
#[cfg_attr(
feature = "unstable",
doc = "\n\nEnable the input related functionality by using [Output::into_flex] and enabling input via [Flex::enable_input]"
doc = "\n\nEnable the input related functionality by using [Output::into_flex] and enabling input via [Flex::set_input_enable]"
)]
OpenDrain,
}
@ -1160,7 +1160,7 @@ impl<'d> Output<'d> {
};
this.set_level(initial_level);
this.apply_config(&config);
this.pin.pin.enable_output(true);
this.pin.pin.set_output_enable(true);
this
}
@ -1366,8 +1366,8 @@ impl<'d> Input<'d> {
pub fn new(pin: impl InputPin + 'd, config: InputConfig) -> Self {
let mut pin = Flex::new(pin);
pin.pin.enable_output(false);
pin.enable_input(true);
pin.pin.set_output_enable(false);
pin.set_input_enable(true);
pin.apply_input_config(&config);
Self { pin }
@ -1582,7 +1582,14 @@ impl<'d> Input<'d> {
/// Flexible pin driver.
///
/// This driver allows changing the pin mode between input and output.
/// This pin driver can act as either input, or output, or both at the same
/// time. The input and output are (not counting the shared pull direction)
/// separately configurable, and they have independent enable states.
///
/// Enabling the input stage does not change the output stage, and vice versa.
/// Disabling the input or output stages don't forget their configuration.
/// Disabling the output stage will not change the output level, but it will
/// disable the driver.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Flex<'d> {
@ -1602,6 +1609,8 @@ impl<'d> Flex<'d> {
#[cfg(usb_device)]
disable_usb_pads(pin.number());
pin.set_output_enable(false);
GPIO::regs()
.func_out_sel_cfg(pin.number() as usize)
.modify(|_, w| unsafe { w.out_sel().bits(OutputSignal::GPIO as OutputSignalType) });
@ -1620,23 +1629,24 @@ impl<'d> Flex<'d> {
self.pin.number()
}
/// Returns a peripheral [input][interconnect::InputSignal] connected to
/// this pin.
// Input functions
/// Applies the given input configuration to the pin.
///
/// The input signal can be passed to peripherals in place of an input pin.
/// ```rust, no_run
#[doc = crate::before_snippet!()]
/// # use esp_hal::gpio::Flex;
/// let pin1_gpio = Flex::new(peripherals.GPIO1);
/// // Can be passed as an input.
/// let pin1 = pin1_gpio.peripheral_input();
/// # Ok(())
/// # }
/// ```
/// This function does not set the pin as input (i.e. it does not enable the
/// input buffer). Note that the pull direction is common between the
/// input and output configuration.
#[inline]
#[instability::unstable]
pub fn peripheral_input(&self) -> interconnect::InputSignal<'d> {
unsafe { AnyPin::steal(self.number()) }.split().0
pub fn apply_input_config(&mut self, config: &InputConfig) {
self.pin.pull_direction(config.pull);
}
/// Enable or disable the GPIO pin input buffer.
#[inline]
#[instability::unstable]
pub fn set_input_enable(&mut self, enable_input: bool) {
self.pin.set_input_enable(enable_input);
}
/// Get whether the pin input level is high.
@ -1741,19 +1751,46 @@ impl<'d> Flex<'d> {
self.listen_with_options(event.into(), false, false, enable)
}
/// Set the GPIO to output mode.
// Output functions
/// Applies the given output configuration to the pin.
///
/// This function does not set the pin to output (i.e. it does not enable
/// the output driver). Note that the pull direction is common between
/// the input and output configuration.
#[inline]
#[instability::unstable]
pub fn set_as_output(&mut self) {
self.pin.set_to_push_pull_output();
pub fn apply_output_config(&mut 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.pin.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
});
GPIO::regs().pin(self.number() as usize).modify(|_, w| {
w.pad_driver()
.bit(config.drive_mode == DriveMode::OpenDrain)
});
}
/// Set the GPIO to input mode.
/// Enable or disable the GPIO pin output driver.
///
/// The output level will be set to the last value. Use [`Self::set_high`],
/// [`Self::set_low`] or [`Self::set_level`] to set the output level before
/// enabling the output.
///
/// This function does not disable the input buffer.
#[inline]
#[instability::unstable]
pub fn set_as_input(&mut self, pull: Pull) {
self.pin.init_input(pull);
self.pin.enable_output(false);
pub fn set_output_enable(&mut self, enable_output: bool) {
self.pin.set_output_enable(enable_output);
}
/// Set the output as high.
@ -1806,63 +1843,25 @@ impl<'d> Flex<'d> {
self.set_level(!level);
}
/// Configure the [DriveStrength] of the pin
// Other/common functions
/// Returns a peripheral [input][interconnect::InputSignal] connected to
/// this pin.
///
/// The input signal can be passed to peripherals in place of an input pin.
/// ```rust, no_run
#[doc = crate::before_snippet!()]
/// # use esp_hal::gpio::Flex;
/// let pin1_gpio = Flex::new(peripherals.GPIO1);
/// // Can be passed as an input.
/// let pin1 = pin1_gpio.peripheral_input();
/// # Ok(())
/// # }
/// ```
#[inline]
#[instability::unstable]
pub fn set_drive_strength(&mut self, strength: DriveStrength) {
self.pin.set_drive_strength(strength);
}
/// Set the GPIO to open-drain mode.
#[inline]
#[instability::unstable]
pub fn set_as_open_drain(&mut self, pull: Pull) {
self.pin.set_to_open_drain_output();
self.pin.pull_direction(pull);
}
/// Configure pullup/pulldown resistors.
#[inline]
#[instability::unstable]
pub fn pull_direction(&mut self, pull: Pull) {
self.pin.pull_direction(pull);
}
/// Enable or disable the GPIO pin input buffer.
#[inline]
#[instability::unstable]
pub fn enable_input(&mut self, enable_input: bool) {
self.pin.enable_input(enable_input);
}
/// Applies the given output configuration to the pin.
#[inline]
#[instability::unstable]
pub fn apply_output_config(&mut 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.pin.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
});
GPIO::regs().pin(self.number() as usize).modify(|_, w| {
w.pad_driver()
.bit(config.drive_mode == DriveMode::OpenDrain)
});
}
/// Applies the given input configuration to the pin.
#[inline]
#[instability::unstable]
pub fn apply_input_config(&mut self, config: &InputConfig) {
self.pin.pull_direction(config.pull);
pub fn peripheral_input(&self) -> interconnect::InputSignal<'d> {
unsafe { AnyPin::steal(self.number()) }.split().0
}
/// Split the pin into an input and output signal.
@ -1983,14 +1982,14 @@ impl<'lt> AnyPin<'lt> {
/// Enable or disable the GPIO pin output buffer.
#[inline]
pub(crate) fn enable_output(&self, enable: bool) {
pub(crate) fn set_output_enable(&self, enable: bool) {
assert!(self.is_output() || !enable);
self.bank().write_out_en(self.mask(), enable);
}
/// Enable input for the pin
#[inline]
pub(crate) fn enable_input(&self, on: bool) {
pub(crate) fn set_input_enable(&self, on: bool) {
io_mux_reg(self.number()).modify(|_, w| w.fun_ie().bit(on));
}
@ -2027,7 +2026,7 @@ impl<'lt> AnyPin<'lt> {
open_drain: bool,
input_enable: Option<bool>,
) {
self.enable_output(true);
self.set_output_enable(true);
let gpio = GPIO::regs();

View File

@ -19,7 +19,7 @@ impl Level {
pub(crate) fn init_input(&self, _pull: Pull) {}
pub(crate) fn enable_input(&self, _on: bool) {}
pub(crate) fn set_input_enable(&self, _on: bool) {}
pub(crate) fn is_input_high(&self) -> bool {
*self == Level::High
@ -27,7 +27,7 @@ impl Level {
pub(crate) fn set_to_open_drain_output(&self) {}
pub(crate) fn set_to_push_pull_output(&self) {}
pub(crate) fn enable_output(&self, _on: bool) {}
pub(crate) fn set_output_enable(&self, _on: bool) {}
pub(crate) fn set_output_high(&self, _on: bool) {}
pub(crate) fn set_drive_strength(&self, _strength: DriveStrength) {}
pub(crate) fn enable_open_drain(&self, _on: bool) {}

View File

@ -1050,7 +1050,7 @@ where
pin.set_output_high(true);
pin.set_to_open_drain_output();
pin.enable_input(true);
pin.set_input_enable(true);
pin.pull_direction(Pull::Up);
input.connect_to(&pin);

View File

@ -306,7 +306,7 @@ impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> PwmPin<'d, PWM, OP,
pin.set_update_method(config.update_method);
PWM::output_signal::<OP, IS_A>().connect_to(&pin.pin);
pin.pin.enable_output(true);
pin.pin.set_output_enable(true);
pin
}

View File

@ -122,7 +122,7 @@ impl<const UNIT: usize, const NUM: usize> Channel<'_, UNIT, NUM> {
if (signal as usize) <= crate::gpio::INPUT_SIGNAL_MAX as usize {
let source = source.into();
source.enable_input(true);
source.set_input_enable(true);
signal.connect_to(&source);
}
self
@ -180,7 +180,7 @@ impl<const UNIT: usize, const NUM: usize> Channel<'_, UNIT, NUM> {
if (signal as usize) <= crate::gpio::INPUT_SIGNAL_MAX as usize {
let source = source.into();
source.enable_input(true);
source.set_input_enable(true);
signal.connect_to(&source);
}
self

View File

@ -884,7 +884,7 @@ where
/// `with_sio0`.
pub fn with_mosi(mut self, mosi: impl PeripheralOutput<'d>) -> Self {
let mosi = mosi.into();
mosi.enable_output(false);
mosi.set_output_enable(false);
self.pins.mosi_pin = mosi.connect_with_guard(self.driver().info.mosi);
@ -900,7 +900,7 @@ where
/// [DataMode::SingleTwoDataLines]
pub fn with_miso(self, miso: impl PeripheralInput<'d>) -> Self {
let miso = miso.into();
miso.enable_input(true);
miso.set_input_enable(true);
self.driver().info.miso.connect_to(&miso);
@ -923,8 +923,8 @@ where
#[instability::unstable]
pub fn with_sio0(mut self, mosi: impl PeripheralOutput<'d>) -> Self {
let mosi = mosi.into();
mosi.enable_output(true);
mosi.enable_input(true);
mosi.set_output_enable(true);
mosi.set_input_enable(true);
self.driver().info.sio0_input.connect_to(&mosi);
self.pins.mosi_pin = mosi.connect_with_guard(self.driver().info.mosi);
@ -947,8 +947,8 @@ where
#[instability::unstable]
pub fn with_sio1(mut self, sio1: impl PeripheralOutput<'d>) -> Self {
let sio1 = sio1.into();
sio1.enable_input(true);
sio1.enable_output(true);
sio1.set_input_enable(true);
sio1.set_output_enable(true);
self.driver().info.miso.connect_to(&sio1);
self.pins.sio1_pin = sio1.connect_with_guard(self.driver().info.sio1_output);
@ -968,8 +968,8 @@ where
pub fn with_sio2(mut self, sio2: impl PeripheralOutput<'d>) -> Self {
// TODO: panic if not QSPI?
let sio2 = sio2.into();
sio2.enable_input(true);
sio2.enable_output(true);
sio2.set_input_enable(true);
sio2.set_output_enable(true);
unwrap!(self.driver().info.sio2_input).connect_to(&sio2);
self.pins.sio2_pin = self
@ -993,8 +993,8 @@ where
pub fn with_sio3(mut self, sio3: impl PeripheralOutput<'d>) -> Self {
// TODO: panic if not QSPI?
let sio3 = sio3.into();
sio3.enable_input(true);
sio3.enable_output(true);
sio3.set_input_enable(true);
sio3.set_output_enable(true);
unwrap!(self.driver().info.sio3_input).connect_to(&sio3);
self.pins.sio3_pin = self

View File

@ -122,7 +122,7 @@ impl<'d> Spi<'d, Blocking> {
#[instability::unstable]
pub fn with_sck(self, sclk: impl PeripheralInput<'d>) -> Self {
let sclk = sclk.into();
sclk.enable_input(true);
sclk.set_input_enable(true);
self.spi.info().sclk.connect_to(&sclk);
self
}
@ -131,7 +131,7 @@ impl<'d> Spi<'d, Blocking> {
#[instability::unstable]
pub fn with_mosi(self, mosi: impl PeripheralInput<'d>) -> Self {
let mosi = mosi.into();
mosi.enable_input(true);
mosi.set_input_enable(true);
self.spi.info().mosi.connect_to(&mosi);
self
}
@ -149,7 +149,7 @@ impl<'d> Spi<'d, Blocking> {
#[instability::unstable]
pub fn with_cs(self, cs: impl PeripheralInput<'d>) -> Self {
let cs = cs.into();
cs.enable_input(true);
cs.set_input_enable(true);
self.spi.info().cs.connect_to(&cs);
self
}

View File

@ -338,8 +338,8 @@ mod tests {
let mut test_gpio2 = Flex::new(ctx.test_gpio2);
test_gpio1.set_high();
test_gpio1.set_as_output();
test_gpio2.enable_input(true);
test_gpio1.set_output_enable(true);
test_gpio2.set_input_enable(true);
ctx.delay.delay_millis(1);
@ -352,8 +352,8 @@ mod tests {
assert_eq!(test_gpio1.is_set_high(), false);
assert_eq!(test_gpio2.is_high(), false);
test_gpio1.enable_input(true);
test_gpio2.set_as_output();
test_gpio1.set_input_enable(true);
test_gpio2.set_output_enable(true);
ctx.delay.delay_millis(1);
assert_eq!(test_gpio1.is_high(), false);

View File

@ -10,7 +10,8 @@
//! does not directly equal to the time it takes to process the interrupt.
//!
//! Outputs used: GPIO2, GPIO4. These are toggled one after the other, so the
//! generated square waves are 90 degrees out of phase.
//! generated square waves are 90 degrees out of phase. DO NOT CONNECT GPIO2 and
//! GPIO4 - they read back themselves.
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 esp32h2
@ -35,15 +36,15 @@ async fn main(spawner: Spawner) {
let mut enc_a = Flex::new(unsafe { p.GPIO2.clone_unchecked() });
let enc_a_clone = Flex::new(p.GPIO2);
enc_a.set_as_output();
enc_a.apply_output_config(&OutputConfig::default());
enc_a.enable_input(true);
enc_a.set_output_enable(true);
enc_a.set_input_enable(true);
let mut enc_b = Flex::new(unsafe { p.GPIO4.clone_unchecked() });
let enc_b_clone = Flex::new(p.GPIO4);
enc_b.set_as_output();
enc_b.apply_output_config(&OutputConfig::default());
enc_b.enable_input(true);
enc_b.set_output_enable(true);
enc_b.set_input_enable(true);
let timg0 = TimerGroup::new(p.TIMG0);
esp_hal_embassy::init(timg0.timer0);