From e05c52cc9ce7f11edf43ae6cabbb1d830a34ae41 Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Wed, 5 Jun 2024 14:52:48 +0200 Subject: [PATCH 1/2] Add Flex/AnyFlex pin drivers --- esp-hal/CHANGELOG.md | 2 + esp-hal/src/gpio/mod.rs | 401 +++++++++++++++++++++++++++++++++++++++- hil-test/tests/gpio.rs | 42 ++++- 3 files changed, 443 insertions(+), 2 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index c887360cf15..15d7098e7b3 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Add Flex / AnyFlex GPIO pin driver + ### Fixed ### Changed diff --git a/esp-hal/src/gpio/mod.rs b/esp-hal/src/gpio/mod.rs index 517edc9721e..01b8dbffb5b 100644 --- a/esp-hal/src/gpio/mod.rs +++ b/esp-hal/src/gpio/mod.rs @@ -1774,6 +1774,122 @@ where } } +/// GPIO flexible pin driver. +pub struct Flex<'d, P> { + pin: PeripheralRef<'d, P>, +} + +impl<'d, P> Flex<'d, P> +where + P: InputPin + OutputPin, +{ + /// Create GPIO flexible pin driver for a [Pin]. + /// No mode change happens. + #[inline] + pub fn new(pin: impl crate::peripheral::Peripheral

+ 'd) -> Self { + crate::into_ref!(pin); + Self { pin } + } + + /// Set the GPIO to open-drain mode. + pub fn set_as_open_drain(&mut self, pull: Pull) { + self.pin.set_to_open_drain_output(private::Internal); + self.pin + .internal_pull_down(pull == Pull::Down, private::Internal); + self.pin + .internal_pull_up(pull == Pull::Up, private::Internal); + } + + /// Set the GPIO to input mode. + pub fn set_as_input(&mut self, pull: Pull) { + self.pin + .init_input(pull == Pull::Down, pull == Pull::Up, private::Internal); + } + + /// Set the GPIO to output mode. + pub fn set_as_output(&mut self) { + self.pin.set_to_push_pull_output(private::Internal); + } + + /// Get whether the pin input level is high. + #[inline] + pub fn is_high(&self) -> bool { + self.pin.is_input_high(private::Internal) + } + + /// Get whether the pin input level is low. + #[inline] + pub fn is_low(&self) -> bool { + !self.is_high() + } + + /// Get the current pin input level. + #[inline] + pub fn get_level(&self) -> Level { + self.is_high().into() + } + + /// Listen for interrupts + #[inline] + pub fn listen(&mut self, event: Event) { + self.pin.listen(event, private::Internal); + } + + /// Clear the interrupt status bit for this Pin + #[inline] + pub fn clear_interrupt(&mut self) { + self.pin.clear_interrupt(private::Internal); + } + + /// Set the output as high. + #[inline] + pub fn set_high(&mut self) { + self.pin.set_output_high(true, private::Internal); + } + + /// Set the output as low. + #[inline] + pub fn set_low(&mut self) { + self.pin.set_output_high(false, private::Internal); + } + + /// Set the output level. + #[inline] + pub fn set_level(&mut self, level: Level) { + self.pin.set_output_high(level.into(), private::Internal); + } + + /// Is the output pin set as high? + #[inline] + pub fn is_set_high(&self) -> bool { + self.pin.is_set_high(private::Internal) + } + + /// Is the output pin set as low? + #[inline] + pub fn is_set_low(&self) -> bool { + !self.pin.is_set_high(private::Internal) + } + + /// What level output is set to + #[inline] + pub fn get_output_level(&self) -> Level { + self.pin.is_set_high(private::Internal).into() + } + + /// Toggle pin output + #[inline] + pub fn toggle(&mut self) { + let level = !self.pin.is_set_high(private::Internal); + self.pin.set_output_high(level, private::Internal); + } + + /// Configure the [DriveStrength] of the pin + pub fn set_drive_strength(&mut self, strength: DriveStrength) { + self.pin.set_drive_strength(strength, private::Internal); + } +} + /// Generic GPIO output driver. pub struct AnyOutput<'d> { pin: ErasedPin, @@ -2003,6 +2119,121 @@ impl<'d> AnyOutputOpenDrain<'d> { } } +/// Generic GPIO flexible pin driver. +pub struct AnyFlex<'d> { + pin: ErasedPin, + _phantom: PhantomData<&'d ()>, +} + +impl<'d> AnyFlex<'d> { + /// Create GPIO flexible pin driver for a [Pin]. + /// No mode change happens. + #[inline] + pub fn new( + pin: impl crate::peripheral::Peripheral

+ 'd, + ) -> Self { + crate::into_ref!(pin); + let pin = pin.erased_pin(private::Internal); + Self { + pin, + _phantom: PhantomData, + } + } + + /// Set the GPIO to open-drain mode. + pub fn set_as_open_drain(&mut self, pull: Pull) { + self.pin.set_to_open_drain_output(private::Internal); + self.pin + .internal_pull_down(pull == Pull::Down, private::Internal); + self.pin + .internal_pull_up(pull == Pull::Up, private::Internal); + } + + /// Set the GPIO to input mode. + pub fn set_as_input(&mut self, pull: Pull) { + self.pin + .init_input(pull == Pull::Down, pull == Pull::Up, private::Internal); + } + + /// Set the GPIO to output mode. + pub fn set_as_output(&mut self) { + self.pin.set_to_push_pull_output(private::Internal); + } + + /// Get whether the pin input level is high. + #[inline] + pub fn is_high(&self) -> bool { + self.pin.is_input_high(private::Internal) + } + + /// Get whether the pin input level is low. + #[inline] + pub fn is_low(&self) -> bool { + !self.is_high() + } + + /// Get the current pin input level. + #[inline] + pub fn get_level(&self) -> Level { + self.is_high().into() + } + + /// Listen for interrupts + #[inline] + pub fn listen(&mut self, event: Event) { + self.pin.listen(event, private::Internal); + } + + /// Clear the interrupt status bit for this Pin + #[inline] + pub fn clear_interrupt(&mut self) { + self.pin.clear_interrupt(private::Internal); + } + + /// Set the output as high. + #[inline] + pub fn set_high(&mut self) { + self.pin.set_output_high(true, private::Internal); + } + + /// Set the output as low. + #[inline] + pub fn set_low(&mut self) { + self.pin.set_output_high(false, private::Internal); + } + + /// Set the output level. + #[inline] + pub fn set_level(&mut self, level: Level) { + self.pin.set_output_high(level.into(), private::Internal); + } + + /// Is the output pin set as high? + #[inline] + pub fn is_set_high(&self) -> bool { + self.pin.is_set_high(private::Internal) + } + + /// Is the output pin set as low? + #[inline] + pub fn is_set_low(&self) -> bool { + !self.pin.is_set_high(private::Internal) + } + + /// What level output is set to + #[inline] + pub fn get_output_level(&self) -> Level { + self.pin.is_set_high(private::Internal).into() + } + + /// Toggle pin output + #[inline] + pub fn toggle(&mut self) { + let pin = &mut self.pin; + pin.set_output_high(!pin.is_set_high(private::Internal), private::Internal); + } +} + pub(crate) mod internal { use super::*; @@ -2519,7 +2750,59 @@ mod embedded_hal_02_impls { } } - // TODO AnyInput, AnyOutput, AnyOpenDrainOutput + impl<'d, P> digital::InputPin for Flex<'d, P> + where + P: InputPin + OutputPin, + { + type Error = core::convert::Infallible; + + fn is_high(&self) -> Result { + Ok(self.pin.is_input_high(private::Internal)) + } + fn is_low(&self) -> Result { + Ok(!self.pin.is_input_high(private::Internal)) + } + } + + impl<'d, P> digital::OutputPin for Flex<'d, P> + where + P: InputPin + OutputPin, + { + type Error = core::convert::Infallible; + + fn set_high(&mut self) -> Result<(), Self::Error> { + self.pin.set_output_high(true, private::Internal); + Ok(()) + } + fn set_low(&mut self) -> Result<(), Self::Error> { + self.pin.set_output_high(false, private::Internal); + Ok(()) + } + } + + impl<'d, P> digital::StatefulOutputPin for Flex<'d, P> + where + P: InputPin + OutputPin, + { + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } + fn is_set_low(&self) -> Result { + Ok(self.is_set_low()) + } + } + + impl<'d, P> digital::ToggleableOutputPin for Flex<'d, P> + where + P: InputPin + OutputPin, + { + type Error = core::convert::Infallible; + + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle(); + Ok(()) + } + } impl<'d> digital::InputPin for AnyInput<'d> { type Error = core::convert::Infallible; @@ -2604,6 +2887,48 @@ mod embedded_hal_02_impls { Ok(()) } } + + impl<'d> digital::InputPin for AnyFlex<'d> { + type Error = core::convert::Infallible; + + fn is_high(&self) -> Result { + Ok(self.pin.is_input_high(private::Internal)) + } + fn is_low(&self) -> Result { + Ok(!self.pin.is_input_high(private::Internal)) + } + } + + impl<'d> digital::OutputPin for AnyFlex<'d> { + type Error = core::convert::Infallible; + + fn set_high(&mut self) -> Result<(), Self::Error> { + self.pin.set_output_high(true, private::Internal); + Ok(()) + } + fn set_low(&mut self) -> Result<(), Self::Error> { + self.pin.set_output_high(false, private::Internal); + Ok(()) + } + } + + impl<'d> digital::StatefulOutputPin for AnyFlex<'d> { + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } + fn is_set_low(&self) -> Result { + Ok(self.is_set_low()) + } + } + + impl<'d> digital::ToggleableOutputPin for AnyFlex<'d> { + type Error = core::convert::Infallible; + + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle(); + Ok(()) + } + } } #[cfg(feature = "embedded-hal")] @@ -2715,6 +3040,54 @@ mod embedded_hal_impls { } } + impl<'d, P> digital::InputPin for Flex<'d, P> + where + P: InputPin + OutputPin, + { + fn is_high(&mut self) -> Result { + Ok(Flex::is_high(self)) + } + + fn is_low(&mut self) -> Result { + Ok(Flex::is_low(self)) + } + } + + impl<'d, P> digital::ErrorType for Flex<'d, P> + where + P: InputPin + OutputPin, + { + type Error = core::convert::Infallible; + } + + impl<'d, P> digital::OutputPin for Flex<'d, P> + where + P: InputPin + OutputPin, + { + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + } + + impl<'d, P> digital::StatefulOutputPin for Flex<'d, P> + where + P: InputPin + OutputPin, + { + fn is_set_high(&mut self) -> Result { + Ok(Self::is_set_high(self)) + } + + fn is_set_low(&mut self) -> Result { + Ok(Self::is_set_low(self)) + } + } + impl<'d> digital::ErrorType for AnyInput<'d> { type Error = core::convert::Infallible; } @@ -2780,6 +3153,32 @@ mod embedded_hal_impls { Ok(Self::is_set_low(self)) } } + + impl<'d> digital::ErrorType for AnyFlex<'d> { + type Error = core::convert::Infallible; + } + + impl<'d> digital::OutputPin for AnyFlex<'d> { + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + } + + impl<'d> digital::StatefulOutputPin for AnyFlex<'d> { + fn is_set_high(&mut self) -> Result { + Ok(Self::is_set_high(self)) + } + + fn is_set_low(&mut self) -> Result { + Ok(Self::is_set_low(self)) + } + } } #[cfg(feature = "embedded-hal")] diff --git a/hil-test/tests/gpio.rs b/hil-test/tests/gpio.rs index 7b71515af90..88fe797f372 100644 --- a/hil-test/tests/gpio.rs +++ b/hil-test/tests/gpio.rs @@ -71,7 +71,7 @@ pub fn interrupt_handler() { mod tests { use defmt::assert_eq; use embassy_time::{Duration, Timer}; - use esp_hal::gpio::{Event, OutputOpenDrain}; + use esp_hal::gpio::{Event, Flex, OutputOpenDrain}; use portable_atomic::{AtomicUsize, Ordering}; use super::*; @@ -226,4 +226,44 @@ mod tests { assert_eq!(io2.is_low(), true); assert_eq!(io4.is_low(), true); } + + #[test] + fn test_gpio_flex(ctx: Context<'static>) { + let mut io2 = Flex::new(unsafe { GpioPin::<2>::steal() }); + let mut io4 = Flex::new(unsafe { GpioPin::<4>::steal() }); + + io2.set_high(); + io2.set_as_output(); + io4.set_as_input(Pull::None); + + ctx.delay.delay_millis(1); + + assert_eq!(io2.is_set_high(), true); + assert_eq!(io4.is_high(), true); + + io2.set_low(); + ctx.delay.delay_millis(1); + + assert_eq!(io2.is_set_high(), false); + assert_eq!(io4.is_high(), false); + + io2.set_as_input(Pull::None); + io4.set_as_output(); + ctx.delay.delay_millis(1); + + assert_eq!(io2.is_high(), false); + assert_eq!(io4.is_set_high(), false); + + io4.set_high(); + ctx.delay.delay_millis(1); + + assert_eq!(io2.is_high(), true); + assert_eq!(io4.is_set_high(), true); + + io4.set_low(); + ctx.delay.delay_millis(1); + + assert_eq!(io2.is_low(), true); + assert_eq!(io4.is_set_low(), true); + } } From 1c183100c2cdffbfadd130d835786b99d6e6082a Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Wed, 5 Jun 2024 14:55:26 +0200 Subject: [PATCH 2/2] CHANGELOG.md --- esp-hal/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 15d7098e7b3..6d77ed40695 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add Flex / AnyFlex GPIO pin driver +- Add Flex / AnyFlex GPIO pin driver (#1659) ### Fixed