Adafruit_CircuitPython_Debo.../adafruit_debouncer.py
2020-03-09 20:07:51 -04:00

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