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);