130 lines
4.6 KiB
Python
130 lines
4.6 KiB
Python
# The MIT License (MIT)
|
|
#
|
|
# Copyright (c) 2019 Dave Astels for Adafruit Industries
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
# THE SOFTWARE.
|
|
"""
|
|
`adafruit_debouncer`
|
|
====================================================
|
|
|
|
Debounces an arbitrary predicate function (typically created as a lambda) of 0 arguments.
|
|
Since a very common use is debouncing a digital input pin, the initializer accepts a pin number
|
|
instead of a lambda.
|
|
|
|
* Author(s): Dave Astels
|
|
|
|
Implementation Notes
|
|
--------------------
|
|
|
|
**Hardware:**
|
|
|
|
**Software and Dependencies:**
|
|
|
|
* Adafruit CircuitPython firmware for the supported boards:
|
|
https://github.com/adafruit/circuitpython/releases
|
|
"""
|
|
|
|
# imports
|
|
|
|
__version__ = "0.0.0-auto.0"
|
|
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Debouncer.git"
|
|
|
|
import time
|
|
from micropython import const
|
|
|
|
_DEBOUNCED_STATE = const(0x01)
|
|
_UNSTABLE_STATE = const(0x02)
|
|
_CHANGED_STATE = const(0x04)
|
|
|
|
|
|
class Debouncer():
|
|
"""Debounce an input pin or an arbitrary predicate"""
|
|
|
|
def __init__(self, io_or_predicate, interval=0.010):
|
|
"""Make am instance.
|
|
:param DigitalInOut/function io_or_predicate: the DigitalIO or function to debounce
|
|
:param int interval: bounce threshold in seconds (default is 0.010, i.e. 10 milliseconds)
|
|
"""
|
|
self.state = 0x00
|
|
if hasattr(io_or_predicate, "value"):
|
|
self.function = lambda: io_or_predicate.value
|
|
else:
|
|
self.function = io_or_predicate
|
|
if self.function():
|
|
self._set_state(_DEBOUNCED_STATE | _UNSTABLE_STATE)
|
|
self.previous_time = 0
|
|
self.interval = interval
|
|
self._previous_state_duration = 0
|
|
self._state_changed_time = 0
|
|
|
|
def _set_state(self, bits):
|
|
self.state |= bits
|
|
|
|
def _unset_state(self, bits):
|
|
self.state &= ~bits
|
|
|
|
def _toggle_state(self, bits):
|
|
self.state ^= bits
|
|
|
|
def _get_state(self, bits):
|
|
return (self.state & bits) != 0
|
|
|
|
def update(self):
|
|
"""Update the debouncer state. MUST be called frequently"""
|
|
now = time.monotonic()
|
|
self._unset_state(_CHANGED_STATE)
|
|
current_state = self.function()
|
|
if current_state != self._get_state(_UNSTABLE_STATE):
|
|
self.previous_time = now
|
|
self._toggle_state(_UNSTABLE_STATE)
|
|
else:
|
|
if now - self.previous_time >= self.interval:
|
|
if current_state != self._get_state(_DEBOUNCED_STATE):
|
|
self.previous_time = now
|
|
self._toggle_state(_DEBOUNCED_STATE)
|
|
self._set_state(_CHANGED_STATE)
|
|
self._previous_state_duration = now - self._state_changed_time
|
|
self._state_changed_time = now
|
|
|
|
@property
|
|
def value(self):
|
|
"""Return the current debounced value."""
|
|
return self._get_state(_DEBOUNCED_STATE)
|
|
|
|
@property
|
|
def rose(self):
|
|
"""Return whether the debounced value went from low to high at the most recent update."""
|
|
return self._get_state(_DEBOUNCED_STATE) and self._get_state(_CHANGED_STATE)
|
|
|
|
@property
|
|
def fell(self):
|
|
"""Return whether the debounced value went from high to low at the most recent update."""
|
|
return (not self._get_state(_DEBOUNCED_STATE)) and self._get_state(
|
|
_CHANGED_STATE
|
|
)
|
|
|
|
@property
|
|
def last_duration(self):
|
|
"""Return the amount of time the state was stable prior to the most recent transition."""
|
|
return self._previous_state_duration
|
|
|
|
@property
|
|
def current_duration(self):
|
|
"""Return the time since the most recent transition."""
|
|
return time.monotonic() - self._state_changed_time
|