use std::os::unix::io::AsRawFd;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;
use super::soft_pwm::SoftPwm;
use crate::gpio::{interrupt::AsyncInterrupt, GpioState, Level, Mode, PullUpDown, Result, Trigger};
const NANOS_PER_SEC: f64 = 1_000_000_000.0;
macro_rules! impl_pin {
() => {
#[inline]
pub fn pin(&self) -> u8 {
self.pin.pin
}
};
}
macro_rules! impl_input {
() => {
#[inline]
pub fn read(&self) -> Level {
self.pin.read()
}
#[inline]
pub fn is_low(&self) -> bool {
self.pin.read() == Level::Low
}
#[inline]
pub fn is_high(&self) -> bool {
self.pin.read() == Level::High
}
};
}
macro_rules! impl_output {
() => {
#[inline]
pub fn write(&mut self, level: Level) {
self.pin.write(level)
}
#[inline]
pub fn set_low(&mut self) {
self.pin.set_low()
}
#[inline]
pub fn set_high(&mut self) {
self.pin.set_high()
}
#[inline]
pub fn toggle(&mut self) {
if self.pin.read() == Level::Low {
self.set_high();
} else {
self.set_low();
}
}
pub fn set_pwm(&mut self, period: Duration, pulse_width: Duration) -> Result<()> {
if let Some(ref mut soft_pwm) = self.soft_pwm {
soft_pwm.reconfigure(period, pulse_width);
} else {
self.soft_pwm = Some(SoftPwm::new(
self.pin.pin,
self.pin.gpio_state.clone(),
period,
pulse_width,
));
}
#[cfg(feature = "hal")]
{
let period_s =
period.as_secs() as f64 + (f64::from(period.subsec_nanos()) / NANOS_PER_SEC);
let pulse_width_s = pulse_width.as_secs() as f64
+ (f64::from(pulse_width.subsec_nanos()) / NANOS_PER_SEC);
if period_s > 0.0 {
self.frequency = 1.0 / period_s;
self.duty_cycle = (pulse_width_s / period_s).min(1.0);
} else {
self.frequency = 0.0;
self.duty_cycle = 0.0;
}
}
Ok(())
}
pub fn set_pwm_frequency(&mut self, frequency: f64, duty_cycle: f64) -> Result<()> {
let period = if frequency <= 0.0 {
0.0
} else {
(1.0 / frequency) * NANOS_PER_SEC
};
let pulse_width = period * duty_cycle.max(0.0).min(1.0);
self.set_pwm(
Duration::from_nanos(period as u64),
Duration::from_nanos(pulse_width as u64),
)
}
pub fn clear_pwm(&mut self) -> Result<()> {
if let Some(mut soft_pwm) = self.soft_pwm.take() {
soft_pwm.stop()?;
}
Ok(())
}
};
}
macro_rules! impl_reset_on_drop {
() => {
pub fn reset_on_drop(&self) -> bool {
self.reset_on_drop
}
pub fn set_reset_on_drop(&mut self, reset_on_drop: bool) {
self.reset_on_drop = reset_on_drop;
}
};
}
macro_rules! impl_drop {
($struct:ident) => {
impl Drop for $struct {
fn drop(&mut self) {
if !self.reset_on_drop {
return;
}
if let Some(prev_mode) = self.prev_mode {
self.pin.set_mode(prev_mode);
}
if self.pud_mode != PullUpDown::Off {
self.pin.set_pullupdown(PullUpDown::Off);
}
}
}
};
}
macro_rules! impl_eq {
($struct:ident) => {
impl PartialEq for $struct {
fn eq(&self, other: &$struct) -> bool {
self.pin == other.pin
}
}
impl<'a> PartialEq<&'a $struct> for $struct {
fn eq(&self, other: &&'a $struct) -> bool {
self.pin == other.pin
}
}
impl<'a> PartialEq<$struct> for &'a $struct {
fn eq(&self, other: &$struct) -> bool {
self.pin == other.pin
}
}
impl Eq for $struct {}
};
}
#[derive(Debug)]
pub struct Pin {
pub(crate) pin: u8,
gpio_state: Arc<GpioState>,
}
impl Pin {
#[inline]
pub(crate) fn new(pin: u8, gpio_state: Arc<GpioState>) -> Pin {
Pin { pin, gpio_state }
}
#[inline]
pub fn pin(&self) -> u8 {
self.pin
}
#[inline]
pub fn mode(&self) -> Mode {
self.gpio_state.gpio_mem.mode(self.pin)
}
#[inline]
pub fn read(&self) -> Level {
self.gpio_state.gpio_mem.level(self.pin)
}
#[inline]
pub fn into_input(self) -> InputPin {
InputPin::new(self, PullUpDown::Off)
}
#[inline]
pub fn into_input_pulldown(self) -> InputPin {
InputPin::new(self, PullUpDown::PullDown)
}
#[inline]
pub fn into_input_pullup(self) -> InputPin {
InputPin::new(self, PullUpDown::PullUp)
}
#[inline]
pub fn into_output(self) -> OutputPin {
OutputPin::new(self)
}
#[inline]
pub fn into_output_low(mut self) -> OutputPin {
self.set_low();
OutputPin::new(self)
}
#[inline]
pub fn into_output_high(mut self) -> OutputPin {
self.set_high();
OutputPin::new(self)
}
#[inline]
pub fn into_io(self, mode: Mode) -> IoPin {
IoPin::new(self, mode)
}
#[inline]
pub(crate) fn set_mode(&mut self, mode: Mode) {
self.gpio_state.gpio_mem.set_mode(self.pin, mode);
}
#[inline]
pub(crate) fn set_pullupdown(&mut self, pud: PullUpDown) {
self.gpio_state.gpio_mem.set_pullupdown(self.pin, pud);
}
#[inline]
pub(crate) fn set_low(&mut self) {
self.gpio_state.gpio_mem.set_low(self.pin);
}
#[inline]
pub(crate) fn set_high(&mut self) {
self.gpio_state.gpio_mem.set_high(self.pin);
}
#[inline]
pub(crate) fn write(&mut self, level: Level) {
match level {
Level::Low => self.set_low(),
Level::High => self.set_high(),
};
}
}
impl Drop for Pin {
fn drop(&mut self) {
self.gpio_state.pins_taken[self.pin as usize].store(false, Ordering::SeqCst);
}
}
impl_eq!(Pin);
#[derive(Debug)]
pub struct InputPin {
pub(crate) pin: Pin,
prev_mode: Option<Mode>,
async_interrupt: Option<AsyncInterrupt>,
reset_on_drop: bool,
pud_mode: PullUpDown,
}
impl InputPin {
pub(crate) fn new(mut pin: Pin, pud_mode: PullUpDown) -> InputPin {
let prev_mode = pin.mode();
let prev_mode = if prev_mode == Mode::Input {
None
} else {
pin.set_mode(Mode::Input);
Some(prev_mode)
};
pin.set_pullupdown(pud_mode);
InputPin {
pin,
prev_mode,
async_interrupt: None,
reset_on_drop: true,
pud_mode,
}
}
impl_pin!();
impl_input!();
pub fn set_interrupt(&mut self, trigger: Trigger) -> Result<()> {
self.clear_async_interrupt()?;
(*self.pin.gpio_state.sync_interrupts.lock().unwrap()).set_interrupt(self.pin(), trigger)
}
pub fn clear_interrupt(&mut self) -> Result<()> {
(*self.pin.gpio_state.sync_interrupts.lock().unwrap()).clear_interrupt(self.pin())
}
pub fn poll_interrupt(
&mut self,
reset: bool,
timeout: Option<Duration>,
) -> Result<Option<Level>> {
let opt =
(*self.pin.gpio_state.sync_interrupts.lock().unwrap()).poll(&[self], reset, timeout)?;
if let Some(trigger) = opt {
Ok(Some(trigger.1))
} else {
Ok(None)
}
}
pub fn set_async_interrupt<C>(&mut self, trigger: Trigger, callback: C) -> Result<()>
where
C: FnMut(Level) + Send + 'static,
{
self.clear_interrupt()?;
self.clear_async_interrupt()?;
self.async_interrupt = Some(AsyncInterrupt::new(
self.pin.gpio_state.cdev.as_raw_fd(),
self.pin(),
trigger,
callback,
)?);
Ok(())
}
pub fn clear_async_interrupt(&mut self) -> Result<()> {
if let Some(mut interrupt) = self.async_interrupt.take() {
interrupt.stop()?;
}
Ok(())
}
impl_reset_on_drop!();
}
impl_drop!(InputPin);
impl_eq!(InputPin);
#[derive(Debug)]
pub struct OutputPin {
pin: Pin,
prev_mode: Option<Mode>,
reset_on_drop: bool,
pud_mode: PullUpDown,
pub(crate) soft_pwm: Option<SoftPwm>,
#[cfg(feature = "hal")]
pub(crate) frequency: f64,
#[cfg(feature = "hal")]
pub(crate) duty_cycle: f64,
}
impl OutputPin {
pub(crate) fn new(mut pin: Pin) -> OutputPin {
let prev_mode = pin.mode();
let prev_mode = if prev_mode == Mode::Output {
None
} else {
pin.set_mode(Mode::Output);
Some(prev_mode)
};
OutputPin {
pin,
prev_mode,
reset_on_drop: true,
pud_mode: PullUpDown::Off,
soft_pwm: None,
#[cfg(feature = "hal")]
frequency: 0.0,
#[cfg(feature = "hal")]
duty_cycle: 0.0,
}
}
impl_pin!();
#[inline]
pub fn is_set_low(&self) -> bool {
self.pin.read() == Level::Low
}
#[inline]
pub fn is_set_high(&self) -> bool {
self.pin.read() == Level::High
}
impl_output!();
impl_reset_on_drop!();
}
impl_drop!(OutputPin);
impl_eq!(OutputPin);
#[derive(Debug)]
pub struct IoPin {
pin: Pin,
mode: Mode,
prev_mode: Option<Mode>,
reset_on_drop: bool,
pud_mode: PullUpDown,
pub(crate) soft_pwm: Option<SoftPwm>,
#[cfg(feature = "hal")]
pub(crate) frequency: f64,
#[cfg(feature = "hal")]
pub(crate) duty_cycle: f64,
}
impl IoPin {
pub(crate) fn new(mut pin: Pin, mode: Mode) -> IoPin {
let prev_mode = pin.mode();
let prev_mode = if prev_mode == mode {
None
} else {
pin.set_mode(mode);
Some(prev_mode)
};
IoPin {
pin,
mode,
prev_mode,
reset_on_drop: true,
pud_mode: PullUpDown::Off,
soft_pwm: None,
#[cfg(feature = "hal")]
frequency: 0.0,
#[cfg(feature = "hal")]
duty_cycle: 0.0,
}
}
impl_pin!();
#[inline]
pub fn mode(&self) -> Mode {
self.pin.mode()
}
#[inline]
pub fn set_mode(&mut self, mode: Mode) {
if self.prev_mode.is_none() && mode != self.mode {
self.prev_mode = Some(self.mode);
}
self.pin.set_mode(mode);
}
#[inline]
pub fn set_pullupdown(&mut self, pud: PullUpDown) {
self.pin.set_pullupdown(pud);
self.pud_mode = pud;
}
impl_input!();
impl_output!();
impl_reset_on_drop!();
}
impl_drop!(IoPin);
impl_eq!(IoPin);