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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/* debouncer.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! A wrapper around a standard Pin which adds simple software debouncing.
//!
//! # Usage
//! Anywhere you would use a Pin, you can use a Debouncer 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::{ 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(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 Debouncer {
  pin: OwnOrBorrow<'static,dyn Pin>,
  last_event_state: Cell<Option<bool>>,
  handler: Cell<PinIsrCallback>
}

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

impl UsesPin for Debouncer {
  /**
   * 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();

    Debouncer {
      pin,
      last_event_state: Cell::new(Option::None),
      handler: Cell::new(IsrCallback::Nop(()))
    }
  }
}

impl Pin for Debouncer {
  fn set_mode(&self, mode: PinMode) {
    self.pin.set_mode(mode)
  }
  fn toggle(&self) {
    self.pin.toggle()
  }
  fn set_high(&self) {
    self.pin.set_high()
  }
  fn set_low(&self) {
    self.pin.set_low()
  }
  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 {
    let mut state : u8 = 0b10101010;

    while (state != 0x00) && (state != 0xff) {
      state <<= 1;

      state |= match self.pin.get() { false => 0, true => 1};
    }

    match state {
      0xff => true,
      0x00 => false,
      _ => avr_oxide::oserror::halt(avr_oxide::oserror::OsError::InternalError)
    }
  }

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

  /**
   * Listen for interrupts, calling the given callback once we receive one.
   * We will filter out unwanted ('bounce') interrupts by waiting for the
   * underlying pin to become stable and then determining whether or not
   * the callback should be called.
   */
  fn listen(&'static self, handler: PinIsrCallback) {
    self.handler.replace(handler);

    // If we've not been called before, there is no prior state to compare to,
    // so we initialise.
    if self.last_event_state.get().is_none() {
      self.last_event_state.replace(Some(self.get()));
    }
    // OK, set up the interrupt handler.  Our callback will be called when
    // the underlying pin generates an event.  We will then wait for the
    // line to settle down, and then decide to call OUR listener only if
    // it's actually changed state stably.
    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) };

      let old_state = panic_if_none!(myself.last_event_state.get(), avr_oxide::oserror::OsError::InternalError);
      let new_state = myself.get();

      if old_state != new_state {

        myself.last_event_state.replace(Some(new_state));

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


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