Skip to main content

rpi_pal/gpio/
pin.rs

1use std::os::unix::io::AsRawFd;
2use std::sync::atomic::Ordering;
3use std::sync::Arc;
4use std::time::Duration;
5
6use super::soft_pwm::SoftPwm;
7use crate::gpio::{
8    interrupt::AsyncInterrupt, Bias, Event, GpioState, Level, Mode, Result, Trigger,
9};
10
11const NANOS_PER_SEC: f64 = 1_000_000_000.0;
12
13macro_rules! impl_pin {
14    () => {
15        /// Returns the GPIO pin number.
16        ///
17        /// Pins are addressed by their BCM numbers, rather than their physical location.
18        #[inline]
19        pub fn pin(&self) -> u8 {
20            self.pin.pin
21        }
22    };
23}
24
25macro_rules! impl_input {
26    () => {
27        /// Reads the pin's logic level.
28        #[inline]
29        pub fn read(&self) -> Level {
30            self.pin.read()
31        }
32
33        /// Reads the pin's logic level, and returns `true` if it's set to [`Low`].
34        ///
35        /// [`Low`]: enum.Level.html#variant.Low
36        #[inline]
37        pub fn is_low(&self) -> bool {
38            self.pin.read() == Level::Low
39        }
40
41        /// Reads the pin's logic level, and returns `true` if it's set to [`High`].
42        ///
43        /// [`High`]: enum.Level.html#variant.High
44        #[inline]
45        pub fn is_high(&self) -> bool {
46            self.pin.read() == Level::High
47        }
48
49        /// Configures the built-in pull-up/pull-down resistors.
50        #[inline]
51        pub fn set_bias(&mut self, bias: Bias) {
52            self.pin.set_bias(bias);
53            self.bias = bias;
54        }
55    };
56}
57
58macro_rules! impl_output {
59    () => {
60        /// Sets the pin's output state.
61        #[inline]
62        pub fn write(&mut self, level: Level) {
63            self.pin.write(level)
64        }
65
66        /// Sets the pin's output state to [`Low`].
67        ///
68        /// [`Low`]: enum.Level.html#variant.Low
69        #[inline]
70        pub fn set_low(&mut self) {
71            self.pin.set_low()
72        }
73
74        /// Sets the pin's output state to [`High`].
75        ///
76        /// [`High`]: enum.Level.html#variant.High
77        #[inline]
78        pub fn set_high(&mut self) {
79            self.pin.set_high()
80        }
81
82        /// Toggles the pin's output state between [`Low`] and [`High`].
83        ///
84        /// [`Low`]: enum.Level.html#variant.Low
85        /// [`High`]: enum.Level.html#variant.High
86        #[inline]
87        pub fn toggle(&mut self) {
88            if self.pin.read() == Level::Low {
89                self.set_high();
90            } else {
91                self.set_low();
92            }
93        }
94
95        /// Configures a software-based PWM signal.
96        ///
97        /// `period` indicates the time it takes to complete one cycle.
98        ///
99        /// `pulse_width` indicates the amount of time the PWM signal is active during a
100        /// single period.
101        ///
102        /// Software-based PWM is inherently inaccurate on a multi-threaded OS due to
103        /// scheduling/preemption. If an accurate or faster PWM signal is required, use the
104        /// hardware [`Pwm`] peripheral instead. More information can be found [here].
105        ///
106        /// If `set_pwm` is called when a PWM thread is already active, the existing thread
107        /// will be reconfigured at the end of the current cycle.
108        ///
109        /// [`Pwm`]: ../pwm/struct.Pwm.html
110        /// [here]: index.html#software-based-pwm
111        pub fn set_pwm(&mut self, period: Duration, pulse_width: Duration) -> Result<()> {
112            if let Some(ref mut soft_pwm) = self.soft_pwm {
113                soft_pwm.reconfigure(period, pulse_width);
114            } else {
115                self.soft_pwm = Some(SoftPwm::new(
116                    self.pin.pin,
117                    self.pin.gpio_state.clone(),
118                    period,
119                    pulse_width,
120                ));
121            }
122
123            // Store frequency/duty cycle for the embedded-hal PwmPin implementation.
124            #[cfg(any(
125                feature = "embedded-hal-0",
126                feature = "embedded-hal",
127                feature = "embedded-hal-nb"
128            ))]
129            {
130                let period_s =
131                    period.as_secs() as f64 + (f64::from(period.subsec_nanos()) / NANOS_PER_SEC);
132                let pulse_width_s = pulse_width.as_secs() as f64
133                    + (f64::from(pulse_width.subsec_nanos()) / NANOS_PER_SEC);
134
135                if period_s > 0.0 {
136                    self.frequency = 1.0 / period_s;
137                    self.duty_cycle = (pulse_width_s / period_s).min(1.0);
138                } else {
139                    self.frequency = 0.0;
140                    self.duty_cycle = 0.0;
141                }
142            }
143
144            Ok(())
145        }
146
147        /// Configures a software-based PWM signal.
148        ///
149        /// `set_pwm_frequency` is a convenience method that converts `frequency` to a period and
150        /// `duty_cycle` to a pulse width, and then calls [`set_pwm`].
151        ///
152        /// `frequency` is specified in hertz (Hz).
153        ///
154        /// `duty_cycle` is specified as a floating point value between `0.0` (0%) and `1.0` (100%).
155        ///
156        /// [`set_pwm`]: #method.set_pwm
157        pub fn set_pwm_frequency(&mut self, frequency: f64, duty_cycle: f64) -> Result<()> {
158            let period = if frequency <= 0.0 {
159                0.0
160            } else {
161                (1.0 / frequency) * NANOS_PER_SEC
162            };
163            let pulse_width = period * duty_cycle.max(0.0).min(1.0);
164
165            self.set_pwm(
166                Duration::from_nanos(period as u64),
167                Duration::from_nanos(pulse_width as u64),
168            )
169        }
170
171        /// Stops a previously configured software-based PWM signal.
172        ///
173        /// The thread responsible for emulating the PWM signal is stopped at the end
174        /// of the current cycle.
175        pub fn clear_pwm(&mut self) -> Result<()> {
176            if let Some(mut soft_pwm) = self.soft_pwm.take() {
177                soft_pwm.stop()?;
178            }
179
180            Ok(())
181        }
182    };
183}
184
185macro_rules! impl_reset_on_drop {
186    () => {
187        /// Returns the value of `reset_on_drop`.
188        pub fn reset_on_drop(&self) -> bool {
189            self.reset_on_drop
190        }
191
192        /// When enabled, resets the pin's mode to its original state and disables the
193        /// built-in pull-up/pull-down resistors when the pin goes out of scope.
194        /// By default, this is set to `true`.
195        ///
196        /// ## Note
197        ///
198        /// Drop methods aren't called when a process is abnormally terminated, for
199        /// instance when a user presses <kbd>Ctrl</kbd> + <kbd>C</kbd>, and the `SIGINT` signal
200        /// isn't caught. You can catch those using crates such as [`simple_signal`].
201        ///
202        /// [`simple_signal`]: https://crates.io/crates/simple-signal
203        pub fn set_reset_on_drop(&mut self, reset_on_drop: bool) {
204            self.reset_on_drop = reset_on_drop;
205        }
206    };
207}
208
209macro_rules! impl_drop {
210    ($struct:ident) => {
211        impl Drop for $struct {
212            /// Resets the pin's mode and disables the built-in pull-up/pull-down
213            /// resistors if `reset_on_drop` is set to `true` (default).
214            fn drop(&mut self) {
215                if !self.reset_on_drop {
216                    return;
217                }
218
219                if let Some(prev_mode) = self.prev_mode {
220                    self.pin.set_mode(prev_mode);
221                }
222
223                if self.bias != Bias::Off {
224                    self.pin.set_bias(Bias::Off);
225                }
226            }
227        }
228    };
229}
230
231macro_rules! impl_eq {
232    ($struct:ident) => {
233        impl PartialEq for $struct {
234            fn eq(&self, other: &$struct) -> bool {
235                self.pin == other.pin
236            }
237        }
238
239        impl<'a> PartialEq<&'a $struct> for $struct {
240            fn eq(&self, other: &&'a $struct) -> bool {
241                self.pin == other.pin
242            }
243        }
244
245        impl<'a> PartialEq<$struct> for &'a $struct {
246            fn eq(&self, other: &$struct) -> bool {
247                self.pin == other.pin
248            }
249        }
250
251        impl Eq for $struct {}
252    };
253}
254
255/// Unconfigured GPIO pin.
256///
257/// `Pin`s are constructed by retrieving them using [`Gpio::get`].
258///
259/// An unconfigured `Pin` can be used to read the pin's mode and logic level.
260/// Converting the `Pin` to an [`InputPin`], [`OutputPin`] or [`IoPin`] through the
261/// various `into_` methods available on `Pin` configures the appropriate mode, and
262/// provides access to additional methods relevant to the selected pin mode.
263///
264/// The `embedded-hal` trait implementations for `Pin` can be enabled by specifying
265/// the optional `hal` feature in the dependency declaration for the `rpi_pal` crate.
266///
267/// [`Gpio::get`]: struct.Gpio.html#method.get
268/// [`InputPin`]: struct.InputPin.html
269/// [`OutputPin`]: struct.OutputPin.html
270/// [`IoPin`]: struct.IoPin.html
271#[derive(Debug)]
272pub struct Pin {
273    pub(crate) pin: u8,
274    gpio_state: Arc<GpioState>,
275}
276
277impl Pin {
278    #[inline]
279    pub(crate) fn new(pin: u8, gpio_state: Arc<GpioState>) -> Pin {
280        Pin { pin, gpio_state }
281    }
282
283    /// Returns the GPIO pin number.
284    ///
285    /// Pins are addressed by their BCM GPIO numbers, rather than their physical location.
286    #[inline]
287    pub fn pin(&self) -> u8 {
288        self.pin
289    }
290
291    /// Returns the pin's mode.
292    #[inline]
293    pub fn mode(&self) -> Mode {
294        self.gpio_state.gpio_mem.mode(self.pin)
295    }
296
297    /// Reads the pin's logic level.
298    #[inline]
299    pub fn read(&self) -> Level {
300        self.gpio_state.gpio_mem.level(self.pin)
301    }
302
303    /// Consumes the `Pin` and returns an [`InputPin`]. Sets the mode to [`Input`]
304    /// and disables the pin's built-in pull-up/pull-down resistors.
305    ///
306    /// [`InputPin`]: struct.InputPin.html
307    /// [`Input`]: enum.Mode.html#variant.Input
308    #[inline]
309    pub fn into_input(self) -> InputPin {
310        InputPin::new(self, Bias::Off)
311    }
312
313    /// Consumes the `Pin` and returns an [`InputPin`]. Sets the mode to [`Input`]
314    /// and enables the pin's built-in pull-down resistor.
315    ///
316    /// The pull-down resistor is disabled when `InputPin` goes out of scope if [`reset_on_drop`]
317    /// is set to `true` (default).
318    ///
319    /// [`InputPin`]: struct.InputPin.html
320    /// [`Input`]: enum.Mode.html#variant.Input
321    /// [`reset_on_drop`]: struct.InputPin.html#method.set_reset_on_drop
322    #[inline]
323    pub fn into_input_pulldown(self) -> InputPin {
324        InputPin::new(self, Bias::PullDown)
325    }
326
327    /// Consumes the `Pin` and returns an [`InputPin`]. Sets the mode to [`Input`]
328    /// and enables the pin's built-in pull-up resistor.
329    ///
330    /// The pull-up resistor is disabled when `InputPin` goes out of scope if [`reset_on_drop`]
331    /// is set to `true` (default).
332    ///
333    /// [`InputPin`]: struct.InputPin.html
334    /// [`Input`]: enum.Mode.html#variant.Input
335    /// [`reset_on_drop`]: struct.InputPin.html#method.set_reset_on_drop
336    #[inline]
337    pub fn into_input_pullup(self) -> InputPin {
338        InputPin::new(self, Bias::PullUp)
339    }
340
341    /// Consumes the `Pin` and returns an [`OutputPin`]. Sets the mode to [`Mode::Output`]
342    /// and leaves the logic level unchanged.
343    #[inline]
344    pub fn into_output(self) -> OutputPin {
345        OutputPin::new(self)
346    }
347
348    /// Consumes the `Pin` and returns an [`OutputPin`]. Changes the logic level to
349    /// [`Level::Low`] and then sets the mode to [`Mode::Output`].
350    #[inline]
351    pub fn into_output_low(mut self) -> OutputPin {
352        self.set_low();
353
354        OutputPin::new(self)
355    }
356
357    /// Consumes the `Pin` and returns an [`OutputPin`]. Changes the logic level to
358    /// [`Level::High`] and then sets the mode to [`Mode::Output`].
359    #[inline]
360    pub fn into_output_high(mut self) -> OutputPin {
361        self.set_high();
362
363        OutputPin::new(self)
364    }
365
366    /// Consumes the `Pin` and returns an [`IoPin`]. Sets the mode to the specified mode.
367    ///
368    /// [`IoPin`]: struct.IoPin.html
369    /// [`Mode`]: enum.Mode.html
370    #[inline]
371    pub fn into_io(self, mode: Mode) -> IoPin {
372        IoPin::new(self, mode)
373    }
374
375    #[inline]
376    pub(crate) fn set_mode(&mut self, mode: Mode) {
377        self.gpio_state.gpio_mem.set_mode(self.pin, mode);
378    }
379
380    #[inline]
381    pub(crate) fn set_bias(&mut self, bias: Bias) {
382        self.gpio_state.gpio_mem.set_bias(self.pin, bias);
383    }
384
385    #[inline]
386    pub(crate) fn set_low(&mut self) {
387        self.gpio_state.gpio_mem.set_low(self.pin);
388    }
389
390    #[inline]
391    pub(crate) fn set_high(&mut self) {
392        self.gpio_state.gpio_mem.set_high(self.pin);
393    }
394
395    #[inline]
396    pub(crate) fn write(&mut self, level: Level) {
397        match level {
398            Level::Low => self.set_low(),
399            Level::High => self.set_high(),
400        };
401    }
402}
403
404impl Drop for Pin {
405    fn drop(&mut self) {
406        // Release taken pin
407        self.gpio_state.pins_taken[self.pin as usize].store(false, Ordering::SeqCst);
408    }
409}
410
411impl_eq!(Pin);
412
413/// GPIO pin configured as input.
414///
415/// `InputPin`s are constructed by converting a [`Pin`] using [`Pin::into_input`],
416/// [`Pin::into_input_pullup`] or [`Pin::into_input_pulldown`]. The pin's mode is
417/// automatically set to [`Mode::Input`].
418///
419/// An `InputPin` can be used to read a pin's logic level, or (a)synchronously poll for
420/// interrupt trigger events.
421///
422/// The `embedded-hal` trait implementations for `InputPin` can be enabled by specifying
423/// the optional `hal` feature in the dependency declaration for the `rpi_pal` crate.
424///
425/// [`Pin`]: struct.Pin.html
426/// [`Mode::Input`]: enum.Mode.html#variant.Input
427/// [`Pin::into_input`]: struct.Pin.html#method.into_input
428/// [`Pin::into_input_pullup`]: struct.Pin.html#method.into_input_pullup
429/// [`Pin::into_input_pulldown`]: struct.Pin.html#method.into_input_pulldown
430#[derive(Debug)]
431pub struct InputPin {
432    pub(crate) pin: Pin,
433    prev_mode: Option<Mode>,
434    async_interrupt: Option<AsyncInterrupt>,
435    reset_on_drop: bool,
436    bias: Bias,
437}
438
439impl InputPin {
440    pub(crate) fn new(mut pin: Pin, bias: Bias) -> InputPin {
441        let prev_mode = pin.mode();
442
443        let prev_mode = if prev_mode == Mode::Input {
444            None
445        } else {
446            pin.set_mode(Mode::Input);
447            Some(prev_mode)
448        };
449
450        pin.set_bias(bias);
451
452        InputPin {
453            pin,
454            prev_mode,
455            async_interrupt: None,
456            reset_on_drop: true,
457            bias,
458        }
459    }
460
461    impl_pin!();
462    impl_input!();
463
464    /// Configures a synchronous interrupt trigger.
465    ///
466    /// An optional debounce duration can be specified to filter unwanted input noise.
467    ////
468    /// After configuring a synchronous interrupt trigger, call [`poll_interrupt`] or
469    /// [`Gpio::poll_interrupts`] to block while waiting for a trigger event.
470    ///
471    /// Any previously configured (a)synchronous interrupt triggers will be cleared.
472    ///
473    /// [`poll_interrupt`]: #method.poll_interrupt
474    /// [`Gpio::poll_interrupts`]: struct.Gpio.html#method.poll_interrupts
475    pub fn set_interrupt(&mut self, trigger: Trigger, debounce: Option<Duration>) -> Result<()> {
476        self.clear_async_interrupt()?;
477
478        // Each pin can only be configured for a single trigger type
479        (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).set_interrupt(
480            self.pin(),
481            trigger,
482            debounce,
483        )
484    }
485
486    /// Removes a previously configured synchronous interrupt trigger.
487    pub fn clear_interrupt(&mut self) -> Result<()> {
488        (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).clear_interrupt(self.pin())
489    }
490
491    /// Blocks until an interrupt is triggered on the pin, or a timeout occurs.
492    ///
493    /// This only works after the pin has been configured for synchronous interrupts using
494    /// [`set_interrupt`]. Asynchronous interrupt triggers are automatically polled on a separate thread.
495    ///
496    /// Calling `poll_interrupt` blocks any other calls to `poll_interrupt` (including on other `InputPin`s) or
497    /// [`Gpio::poll_interrupts`] until it returns. If you need to poll multiple pins simultaneously, use
498    /// [`Gpio::poll_interrupts`] to block while waiting for any of the interrupts to trigger, or switch to
499    /// using asynchronous interrupts with [`set_async_interrupt`].
500    ///
501    /// Setting `reset` to `false` returns any cached interrupt trigger events if available. Setting `reset` to `true`
502    /// clears all cached events before polling for new events.
503    ///
504    /// The `timeout` duration indicates how long the call will block while waiting
505    /// for interrupt trigger events, after which an `Ok(None))` is returned.
506    /// `timeout` can be set to `None` to wait indefinitely.
507    ///
508    /// [`set_interrupt`]: #method.set_interrupt
509    /// [`Gpio::poll_interrupts`]: struct.Gpio.html#method.poll_interrupts
510    /// [`set_async_interrupt`]: #method.set_async_interrupt
511    pub fn poll_interrupt(
512        &mut self,
513        reset: bool,
514        timeout: Option<Duration>,
515    ) -> Result<Option<Event>> {
516        let opt =
517            (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).poll(&[self], reset, timeout)?;
518
519        if let Some(trigger) = opt {
520            Ok(Some(trigger.1))
521        } else {
522            Ok(None)
523        }
524    }
525
526    /// Configures an asynchronous interrupt trigger, which executes the callback on a
527    /// separate thread when the interrupt is triggered.
528    ///
529    /// An optional debounce duration can be specified to filter unwanted input noise.
530    ///
531    /// The callback closure or function pointer is called with a single [`Event`] argument.
532    ///
533    /// Any previously configured (a)synchronous interrupt triggers for this pin are cleared
534    /// when `set_async_interrupt` is called, or when `InputPin` goes out of scope.
535    ///
536    /// [`clear_async_interrupt`]: #method.clear_async_interrupt
537    /// [`Event`]: struct.Event.html
538    pub fn set_async_interrupt<C>(
539        &mut self,
540        trigger: Trigger,
541        debounce: Option<Duration>,
542        callback: C,
543    ) -> Result<()>
544    where
545        C: FnMut(Event) + Send + 'static,
546    {
547        self.clear_interrupt()?;
548        self.clear_async_interrupt()?;
549
550        self.async_interrupt = Some(AsyncInterrupt::new(
551            self.pin.gpio_state.cdev.as_raw_fd(),
552            self.pin(),
553            trigger,
554            debounce,
555            callback,
556        )?);
557
558        Ok(())
559    }
560
561    /// Removes a previously configured asynchronous interrupt trigger.
562    pub fn clear_async_interrupt(&mut self) -> Result<()> {
563        if let Some(mut interrupt) = self.async_interrupt.take() {
564            interrupt.stop()?;
565        }
566
567        Ok(())
568    }
569
570    impl_reset_on_drop!();
571}
572
573impl_drop!(InputPin);
574impl_eq!(InputPin);
575
576/// GPIO pin configured as output.
577///
578/// `OutputPin`s are constructed by converting a [`Pin`] using [`Pin::into_output`],
579/// [`Pin::into_output_low`] or [`Pin::into_output_high`]. The pin's mode is automatically set to
580/// [`Mode::Output`].
581///
582/// An `OutputPin` can be used to change a pin's output state.
583///
584/// The `embedded-hal` trait implementations for `OutputPin` can be enabled by specifying
585/// the optional `hal` feature in the dependency declaration for the `rpi_pal` crate.
586///
587/// [`Pin`]: struct.Pin.html
588/// [`Mode::Output`]: enum.Mode.html#variant.Output
589/// [`Pin::into_output_low`]: struct.Pin.html#method.into_output_low
590/// [`Pin::into_output_high`]: struct.Pin.html#method.into_output_high
591#[derive(Debug)]
592pub struct OutputPin {
593    pin: Pin,
594    prev_mode: Option<Mode>,
595    reset_on_drop: bool,
596    bias: Bias,
597    pub(crate) soft_pwm: Option<SoftPwm>,
598    // Stores the softpwm frequency. Used for embedded_hal::PwmPin.
599    #[cfg(any(
600        feature = "embedded-hal-0",
601        feature = "embedded-hal",
602        feature = "embedded-hal-nb"
603    ))]
604    pub(crate) frequency: f64,
605    // Stores the softpwm duty cycle. Used for embedded_hal::PwmPin.
606    #[cfg(any(
607        feature = "embedded-hal-0",
608        feature = "embedded-hal",
609        feature = "embedded-hal-nb"
610    ))]
611    pub(crate) duty_cycle: f64,
612}
613
614impl OutputPin {
615    pub(crate) fn new(mut pin: Pin) -> OutputPin {
616        let prev_mode = pin.mode();
617
618        let prev_mode = if prev_mode == Mode::Output {
619            None
620        } else {
621            pin.set_mode(Mode::Output);
622            Some(prev_mode)
623        };
624
625        OutputPin {
626            pin,
627            prev_mode,
628            reset_on_drop: true,
629            bias: Bias::Off,
630            soft_pwm: None,
631            #[cfg(any(
632                feature = "embedded-hal-0",
633                feature = "embedded-hal",
634                feature = "embedded-hal-nb"
635            ))]
636            frequency: 0.0,
637            #[cfg(any(
638                feature = "embedded-hal-0",
639                feature = "embedded-hal",
640                feature = "embedded-hal-nb"
641            ))]
642            duty_cycle: 0.0,
643        }
644    }
645
646    impl_pin!();
647
648    /// Returns `true` if the pin's output state is set to [`Low`].
649    ///
650    /// [`Low`]: enum.Level.html#variant.Low
651    #[inline]
652    pub fn is_set_low(&self) -> bool {
653        self.pin.read() == Level::Low
654    }
655
656    /// Returns `true` if the pin's output state is set to [`High`].
657    ///
658    /// [`High`]: enum.Level.html#variant.High
659    #[inline]
660    pub fn is_set_high(&self) -> bool {
661        self.pin.read() == Level::High
662    }
663
664    impl_output!();
665    impl_reset_on_drop!();
666}
667
668impl_drop!(OutputPin);
669impl_eq!(OutputPin);
670
671/// GPIO pin that can be (re)configured for any mode or alternate function.
672///
673/// `IoPin`s are constructed by converting a [`Pin`] using [`Pin::into_io`].
674/// The pin's mode is automatically set to the specified mode.
675///
676/// An `IoPin` can be reconfigured for any available mode. Depending on the
677/// mode, some methods may not have any effect. For instance, calling a method that
678/// alters the pin's output state won't cause any changes when the pin's mode is set
679/// to [`Mode::Input`].
680///
681/// The `embedded-hal` trait implementations for `IoPin` can be enabled by specifying
682/// the optional `hal` feature in the dependency declaration for the `rpi_pal` crate.
683///
684/// [`Pin`]: struct.Pin.html
685/// [`Mode::Input`]: enum.Mode.html#variant.Input
686/// [`Pin::into_io`]: struct.Pin.html#method.into_io
687#[derive(Debug)]
688pub struct IoPin {
689    pin: Pin,
690    mode: Mode,
691    prev_mode: Option<Mode>,
692    reset_on_drop: bool,
693    bias: Bias,
694    pub(crate) soft_pwm: Option<SoftPwm>,
695    // Stores the softpwm frequency. Used for embedded_hal::PwmPin.
696    #[cfg(any(
697        feature = "embedded-hal-0",
698        feature = "embedded-hal",
699        feature = "embedded-hal-nb"
700    ))]
701    pub(crate) frequency: f64,
702    // Stores the softpwm duty cycle. Used for embedded_hal::PwmPin.
703    #[cfg(any(
704        feature = "embedded-hal-0",
705        feature = "embedded-hal",
706        feature = "embedded-hal-nb"
707    ))]
708    pub(crate) duty_cycle: f64,
709}
710
711impl IoPin {
712    pub(crate) fn new(mut pin: Pin, mode: Mode) -> IoPin {
713        let prev_mode = pin.mode();
714
715        let prev_mode = if prev_mode == mode {
716            None
717        } else {
718            pin.set_mode(mode);
719            Some(prev_mode)
720        };
721
722        IoPin {
723            pin,
724            mode,
725            prev_mode,
726            reset_on_drop: true,
727            bias: Bias::Off,
728            soft_pwm: None,
729            #[cfg(any(
730                feature = "embedded-hal-0",
731                feature = "embedded-hal",
732                feature = "embedded-hal-nb"
733            ))]
734            frequency: 0.0,
735            #[cfg(any(
736                feature = "embedded-hal-0",
737                feature = "embedded-hal",
738                feature = "embedded-hal-nb"
739            ))]
740            duty_cycle: 0.0,
741        }
742    }
743
744    impl_pin!();
745
746    /// Returns the pin's mode.
747    #[inline]
748    pub fn mode(&self) -> Mode {
749        self.pin.mode()
750    }
751
752    /// Sets the pin's mode.
753    #[inline]
754    pub fn set_mode(&mut self, mode: Mode) {
755        // If self.prev_mode is set to None, that means the
756        // requested mode during construction was the same as
757        // the current mode. Save that mode if we're changing
758        // it to something else now, so we can reset it on drop.
759        if self.prev_mode.is_none() && mode != self.mode {
760            self.prev_mode = Some(self.mode);
761        }
762
763        self.pin.set_mode(mode);
764    }
765
766    impl_input!();
767    impl_output!();
768    impl_reset_on_drop!();
769}
770
771impl_drop!(IoPin);
772impl_eq!(IoPin);