diff --git a/CHANGELOG.md b/CHANGELOG.md index 547c2b9..4ab9990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +- Implement `embedded_hal::digital::blocking::IoPin` for `CdevPin` and `SysfsPin` + ## [v0.4.0-alpha.1] - 2021-09-27 ### Changed diff --git a/src/cdev_pin.rs b/src/cdev_pin.rs index 5a11fe1..9d91a95 100644 --- a/src/cdev_pin.rs +++ b/src/cdev_pin.rs @@ -5,7 +5,7 @@ /// Newtype around [`gpio_cdev::LineHandle`] that implements the `embedded-hal` traits /// /// [`gpio_cdev::LineHandle`]: https://docs.rs/gpio-cdev/0.5.0/gpio_cdev/struct.LineHandle.html -pub struct CdevPin(pub gpio_cdev::LineHandle, bool); +pub struct CdevPin(pub gpio_cdev::LineHandle, gpio_cdev::LineInfo); impl CdevPin { /// See [`gpio_cdev::Line::request`][0] for details. @@ -13,7 +13,43 @@ impl CdevPin { /// [0]: https://docs.rs/gpio-cdev/0.5.0/gpio_cdev/struct.Line.html#method.request pub fn new(handle: gpio_cdev::LineHandle) -> Result { let info = handle.line().info()?; - Ok(CdevPin(handle, info.is_active_low())) + Ok(CdevPin(handle, info)) + } + + fn get_input_flags(&self) -> gpio_cdev::LineRequestFlags { + if self.1.is_active_low() { + return gpio_cdev::LineRequestFlags::INPUT | gpio_cdev::LineRequestFlags::ACTIVE_LOW; + } + gpio_cdev::LineRequestFlags::INPUT + } + + fn get_output_flags(&self) -> gpio_cdev::LineRequestFlags { + let mut flags = gpio_cdev::LineRequestFlags::OUTPUT; + if self.1.is_active_low() { + flags.insert(gpio_cdev::LineRequestFlags::ACTIVE_LOW); + } + if self.1.is_open_drain() { + flags.insert(gpio_cdev::LineRequestFlags::OPEN_DRAIN); + } else if self.1.is_open_source() { + flags.insert(gpio_cdev::LineRequestFlags::OPEN_SOURCE); + } + return flags; + } +} + +/// Converts a pin state to the gpio_cdev compatible numeric value, accounting +/// for the active_low condition. +fn state_to_value(state: embedded_hal::digital::PinState, is_active_low: bool) -> u8 { + if is_active_low { + match state { + embedded_hal::digital::PinState::High => 0, + embedded_hal::digital::PinState::Low => 1, + } + } else { + match state { + embedded_hal::digital::PinState::High => 1, + embedded_hal::digital::PinState::Low => 0, + } } } @@ -21,19 +57,17 @@ impl embedded_hal::digital::blocking::OutputPin for CdevPin { type Error = gpio_cdev::errors::Error; fn set_low(&mut self) -> Result<(), Self::Error> { - if self.1 { - self.0.set_value(1) - } else { - self.0.set_value(0) - } + self.0.set_value(state_to_value( + embedded_hal::digital::PinState::Low, + self.1.is_active_low(), + )) } fn set_high(&mut self) -> Result<(), Self::Error> { - if self.1 { - self.0.set_value(0) - } else { - self.0.set_value(1) - } + self.0.set_value(state_to_value( + embedded_hal::digital::PinState::High, + self.1.is_active_low(), + )) } } @@ -41,11 +75,12 @@ impl embedded_hal::digital::blocking::InputPin for CdevPin { type Error = gpio_cdev::errors::Error; fn is_high(&self) -> Result { - if !self.1 { - self.0.get_value().map(|val| val != 0) - } else { - self.0.get_value().map(|val| val == 0) - } + self.0.get_value().map(|val| { + val == state_to_value( + embedded_hal::digital::PinState::High, + self.1.is_active_low(), + ) + }) } fn is_low(&self) -> Result { @@ -53,6 +88,49 @@ impl embedded_hal::digital::blocking::InputPin for CdevPin { } } +impl embedded_hal::digital::blocking::IoPin for CdevPin { + type Error = gpio_cdev::errors::Error; + + fn into_input_pin(self) -> Result { + if self.1.direction() == gpio_cdev::LineDirection::In { + return Ok(self); + } + let line = self.0.line().clone(); + let input_flags = self.get_input_flags(); + let consumer = self.1.consumer().unwrap_or("").to_owned(); + + // Drop self to free the line before re-requesting it in a new mode. + std::mem::drop(self); + + CdevPin::new(line.request(input_flags, 0, &consumer)?) + } + + fn into_output_pin( + self, + state: embedded_hal::digital::PinState, + ) -> Result { + if self.1.direction() == gpio_cdev::LineDirection::Out { + return Ok(self); + } + + let line = self.0.line().clone(); + let output_flags = self.get_output_flags(); + let consumer = self.1.consumer().unwrap_or("").to_owned(); + + // Drop self to free the line before re-requesting it in a new mode. + std::mem::drop(self); + + CdevPin::new(line.request( + output_flags, + state_to_value( + state, + output_flags.intersects(gpio_cdev::LineRequestFlags::ACTIVE_LOW), + ), + &consumer, + )?) + } +} + impl core::ops::Deref for CdevPin { type Target = gpio_cdev::LineHandle; diff --git a/src/sysfs_pin.rs b/src/sysfs_pin.rs index 3d606ee..f569125 100644 --- a/src/sysfs_pin.rs +++ b/src/sysfs_pin.rs @@ -64,6 +64,26 @@ impl embedded_hal::digital::blocking::InputPin for SysfsPin { } } +impl embedded_hal::digital::blocking::IoPin for SysfsPin { + type Error = sysfs_gpio::Error; + + fn into_input_pin(self) -> Result { + self.set_direction(sysfs_gpio::Direction::In)?; + Ok(self) + } + + fn into_output_pin( + self, + state: embedded_hal::digital::PinState, + ) -> Result { + self.set_direction(match state { + embedded_hal::digital::PinState::High => sysfs_gpio::Direction::High, + embedded_hal::digital::PinState::Low => sysfs_gpio::Direction::Low, + })?; + Ok(self) + } +} + impl core::ops::Deref for SysfsPin { type Target = sysfs_gpio::Pin;