1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/* debouncer.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! A wrapper around a standard Pin which inverts the sense of the pin.
//! Useful where you have, for example, active-low inputs.
//!
//! # Usage
//! Anywhere you would use a Pin, you can use a Inverter instead.  Create
//! the debouncer using one of the methods provided by the [`avr_oxide::devices::UsesPin`] trait,
//! passing the pin you wish to wrap to the constructor.
//!
//! ```rust,no_run
//! # #![no_std]
//! # #![no_main]
//! #
//! # use avr_oxide::alloc::boxed::Box;
//! # use avr_oxide::devices::UsesPin;
//! # use avr_oxide::devices::debouncer::Debouncer;
//! # use avr_oxide::devices::inverter::Inverter;
//! # use avr_oxide::devices::{ OxideButton, button::ButtonState };
//! # use avr_oxide::boards::board;
//! # use avr_oxide::StaticWrap;
//! #
//! # #[avr_oxide::main(chip="atmega4809")]
//! # pub fn main() {
//!   let supervisor = avr_oxide::oxide::instance();
//!
//!   let mut green_button = StaticWrap::new(OxideButton::using(Inverter::using(Debouncer::with_pin(board::pin_a(2)))));
//! #
//! #   // Now enter the event loop
//! #   supervisor.run();
//! # }
//! ```


// Imports ===================================================================
use core::any::Any;
use core::cell::Cell;
use avr_oxide::alloc::boxed::Box;
use avr_oxide::hal::generic::callback::IsrCallback;
use avr_oxide::hal::generic::port::{InterruptMode, Pin, PinIsrCallback, PinMode};
use avr_oxide::{isr_cb_invoke, panic_if_none};
use avr_oxide::devices::UsesPin;
use avr_oxide::util::OwnOrBorrow;

// Declarations ==============================================================
pub struct Inverter {
  pin: OwnOrBorrow<'static,dyn Pin>,
  handler: Cell<PinIsrCallback>
}

// Code ======================================================================
impl Into<OwnOrBorrow<'static, dyn Pin + 'static>> for Inverter {
  fn into(self) -> OwnOrBorrow<'static, dyn Pin> {
    OwnOrBorrow::Own(Box::new(self))
  }
}

impl UsesPin for Inverter {
  /**
   * Create a Debouncer which will wrap the given underlying Pin instance to
   * provide a debounced version of it.
   */
  fn using<OP: Into<OwnOrBorrow<'static, dyn Pin>>>(pin: OP) -> Self {
    let pin : OwnOrBorrow<dyn Pin> = pin.into();

    Inverter {
      pin,
      handler: Cell::new(IsrCallback::Nop(()))
    }
  }
}

impl Pin for Inverter {
  fn set_mode(&self, mode: PinMode) {
    self.pin.set_mode(mode)
  }

  fn toggle(&self) {
    self.pin.toggle()
  }

  fn set_high(&self) {
    self.pin.set_low()
  }

  fn set_low(&self) {
    self.pin.set_high()
  }

  fn set(&self, high: bool) {
    self.pin.set(!high)
  }

  /**
   * Gets the pin state.  Reads multiple samples and only returns once the
   * pin has reached a steady state.
   */
  fn get(&self) -> bool {
    !self.pin.get()
  }

  fn set_interrupt_mode(&self, mode: InterruptMode) {
    self.pin.set_interrupt_mode(mode)
  }

  /**
   * Listen for interrupts, calling the given callback once we receive one.
   */
  fn listen(&'static self, handler: PinIsrCallback) {
    self.handler.replace(handler);

    self.pin.listen(IsrCallback::WithData(|isotoken,_source,id,state,udata|{
      let myself = unsafe { &*(panic_if_none!(udata, avr_oxide::oserror::OsError::InternalError) as *const Self) };

      isr_cb_invoke!(isotoken, myself.handler.get(), myself, id, !state);
    }, self as &dyn Any));
  }
}


// Tests =====================================================================