From 728fa9443cba90d37ce61d8250c03e7f0c0498c5 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 14 Dec 2017 17:47:04 -0800 Subject: [PATCH] Stop mixing single and double tap detection. This changes the API to have single boolean `tapped` property (formerly read_click). This change aligns the behavior with the expectation that a tap thats part of a double tap does not register as a single tap. It will now only detect single or double taps as set by `set_tap`. Names with click were renamed to tap so they align with what is actually happening rather than the data sheet names. A comment in the code points this out. The tap example now also deduplicates taps because tap detection holds the status for a while. --- adafruit_lis3dh.py | 76 +++++++++++++++++------------------ examples/spinner.py | 6 +-- examples/spinner_advanced.py | 6 +-- examples/{click.py => tap.py} | 39 +++++++++--------- 4 files changed, 63 insertions(+), 64 deletions(-) rename examples/{click.py => tap.py} (54%) diff --git a/adafruit_lis3dh.py b/adafruit_lis3dh.py index d59d32e..ba523ed 100755 --- a/adafruit_lis3dh.py +++ b/adafruit_lis3dh.py @@ -81,11 +81,10 @@ def __init__(self): @property def data_rate(self): - """Get/set the data rate of the accelerometer. Can be DATA_RATE_400_HZ, - DATA_RATE_200_HZ, DATA_RATE_100_HZ, DATA_RATE_50_HZ, DATA_RATE_25_HZ, - DATA_RATE_10_HZ, DATA_RATE_1_HZ, DATA_RATE_POWERDOWN, DATA_RATE_LOWPOWER_1K6HZ, - or DATA_RATE_LOWPOWER_5KHZ. - """ + """The data rate of the accelerometer. Can be DATA_RATE_400_HZ, DATA_RATE_200_HZ, + DATA_RATE_100_HZ, DATA_RATE_50_HZ, DATA_RATE_25_HZ, DATA_RATE_10_HZ, + DATA_RATE_1_HZ, DATA_RATE_POWERDOWN, DATA_RATE_LOWPOWER_1K6HZ, or + DATA_RATE_LOWPOWER_5KHZ.""" ctl1 = self._read_register_byte(REG_CTRL1) return (ctl1 >> 4) & 0x0F @@ -98,9 +97,8 @@ def data_rate(self, rate): @property def range(self): - """Get/set the range of the accelerometer. Can be RANGE_2_G, RANGE_4_G, - RANGE_8_G, or RANGE_16_G. - """ + """The range of the accelerometer. Can be RANGE_2_G, RANGE_4_G, RANGE_8_G, or + RANGE_16_G.""" ctl4 = self._read_register_byte(REG_CTRL4) return (ctl4 >> 4) & 0x03 @@ -183,35 +181,35 @@ def read_adc_mV(self, adc): # pylint: disable=invalid-name # y1 = 900 return 1800+(raw+32512)*(-900/65024) - def read_click_raw(self): - """Read the raw click register byte value.""" - return self._read_register_byte(REG_CLICKSRC) - - def read_click(self): - """Read a 2-tuple of bools where the first value is True if a single - click was detected and the second value is True if a double click was - detected. - """ - raw = self.read_click_raw() - return (raw & 0x10 > 0, raw & 0x20 > 0) - - def set_click(self, click, threshold, *, - time_limit=10, time_latency=20, time_window=255, click_cfg=None): - """Set the click detection parameters. Must specify at least: - click - Set to 0 to disable click detection, 1 to detect only single - clicks, and 2 to detect single & double clicks. - threshold - A threshold for the click detection. The higher the value - the less sensitive the detection. This changes based on - the accelerometer range. Good values are 5-10 for 16G, - 10-20 for 8G, 20-40 for 4G, and 40-80 for 2G. - Optionally specify (see datasheet for meaning of these): - time_limit - Time limit register value (default 10). - time_latency - Time latency register value (default 20). - time_window - Time window register value (default 255). - """ - if (click < 0 or click > 2) and click_cfg is None: - raise ValueError('Click must be 0 (disabled), 1 (single click), or 2 (double click)!') - if click == 0 and click_cfg is None: + @property + def tapped(self): + """True if a tap was detected recently. Whether its a single tap or double tap is + determined by the tap param on `set_tap`. This may be True over multiple reads + even if only a single tap or single double tap occurred.""" + raw = self._read_register_byte(REG_CLICKSRC) + return raw & 0x40 > 0 + + def set_tap(self, tap, threshold, *, + time_limit=10, time_latency=20, time_window=255, click_cfg=None): + """Set the tap detection parameters. + + .. note:: Tap related registers are called CLICK_ in the datasheet. + + :param int tap: 0 to disable tap detection, 1 to detect only single + taps, and 2 to detect only double taps. + :param int threshold: A threshold for the tap detection. The higher the value + the less sensitive the detection. This changes based on the accelerometer + range. Good values are 5-10 for 16G, 10-20 for 8G, 20-40 for 4G, and 40-80 for + 2G. + :param int time_limit: TIME_LIMIT register value (default 10). + :param int time_latency: TIME_LATENCY register value (default 20). + :param int time_window: TIME_WINDOW register value (default 255). + :param int click_cfg: CLICK_CFG register value.""" + if (tap < 0 or tap > 2) and click_cfg is None: + raise ValueError('Tap must be 0 (disabled), 1 (single tap), or 2 (double tap)!') + if threshold > 127 or threshold < 0: + raise ValueError('Threshold out of range (0-127)') + if tap == 0 and click_cfg is None: # Disable click interrupt. r = self._read_register_byte(REG_CTRL3) r &= ~(0x80) # Turn off I1_CLICK. @@ -224,9 +222,9 @@ def set_click(self, click, threshold, *, if click_cfg is not None: # Custom click configuration register value specified, use it. self._write_register_byte(REG_CLICKCFG, click_cfg) - elif click == 1: + elif tap == 1: self._write_register_byte(REG_CLICKCFG, 0x15) # Turn on all axes & singletap. - elif click == 2: + elif tap == 2: self._write_register_byte(REG_CLICKCFG, 0x2A) # Turn on all axes & doubletap. self._write_register_byte(REG_CLICKTHS, threshold) self._write_register_byte(REG_TIMELIMIT, time_limit) diff --git a/examples/spinner.py b/examples/spinner.py index 2b2023e..33a244c 100644 --- a/examples/spinner.py +++ b/examples/spinner.py @@ -65,10 +65,10 @@ def get_position(self, delta): lis3dh.range = ACCEL_RANGE # Enable single click detection, but use a custom CLICK_CFG register value # to only detect clicks on the X axis (instead of all 3 X, Y, Z axes). -lis3dh.set_click(1, TAP_THRESHOLD, click_cfg=0x01) +lis3dh.set_tap(1, TAP_THRESHOLD, click_cfg=0x01) # Enable LIS3DH FIFO in stream mode. This reaches in to the LIS3DH library to # call internal methods that change a few register values. This must be done -# AFTER calling set_click above because the set_click function also changes +# AFTER calling set_tap above because the set_tap function also changes # REG_CTRL5. # pylint: disable=protected-access lis3dh._write_register_byte(adafruit_lis3dh.REG_CTRL5, 0b01001000) @@ -85,7 +85,7 @@ def get_position(self, delta): while True: # Read the raw click detection register value and check if there was # a click detected. - clicksrc = lis3dh.read_click_raw() + clicksrc = lis3dh._read_register_byte(adafruit_lis3dh.REG_CLICKSRC) # pylint: disable=protected-access if clicksrc & 0b01000000 > 0: # Click was detected! Quickly read 32 values from the accelerometer # FIFO and look for the maximum magnitude values. diff --git a/examples/spinner_advanced.py b/examples/spinner_advanced.py index 0fe00b3..73521ab 100644 --- a/examples/spinner_advanced.py +++ b/examples/spinner_advanced.py @@ -183,10 +183,10 @@ def update(self, position, primary, secondary): lis3dh.range = ACCEL_RANGE # Enable single click detection, but use a custom CLICK_CFG register value # to only detect clicks on the X axis (instead of all 3 X, Y, Z axes). -lis3dh.set_click(1, TAP_THRESHOLD, click_cfg=0x01) +lis3dh.set_tap(1, TAP_THRESHOLD, click_cfg=0x01) # Enable LIS3DH FIFO in stream mode. This reaches in to the LIS3DH library to # call internal methods that change a few register values. This must be done -# AFTER calling set_click above because the set_click function also changes +# AFTER calling set_tap above because the set_tap function also changes # REG_CTRL5. The FIFO stream mode will keep track of the 32 last X,Y,Z accel # readings in a FIFO buffer so they can be read later to see a history of # recent acceleration. This is handy to look for the maximum/minimum impulse @@ -219,7 +219,7 @@ def update(self, position, primary, secondary): # Read the raw click detection register value and check if there was # a click detected. Remember only the X axis causes clicks because of # the register configuration set previously. - clicksrc = lis3dh.read_click_raw() + clicksrc = lis3dh._read_register_byte(adafruit_lis3dh.REG_CLICKSRC) # pylint: disable=protected-access if clicksrc & 0b01000000 > 0: # Click was detected! Quickly read 32 values from the accelerometer # and look for the maximum magnitude values. Because the diff --git a/examples/click.py b/examples/tap.py similarity index 54% rename from examples/click.py rename to examples/tap.py index 12c5220..7796a1f 100644 --- a/examples/click.py +++ b/examples/tap.py @@ -1,8 +1,7 @@ -# Click detection example. +# Tap detection example. # Will loop forever printing when a single or double click is detected. # Open the serial port after running to see the output printed. # Author: Tony DiCola -import time import board import adafruit_lis3dh @@ -12,7 +11,12 @@ # Hardware I2C setup: import busio i2c = busio.I2C(board.SCL, board.SDA) -lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c) +lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c, address=0x19) + +# Hardware I2C setup on CircuitPlayground Express: +# import busio +# i2c = busio.I2C(board.ACCELEROMETER_SCL, board.ACCELEROMETER_SDA) +# lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c, address=0x19) # Software I2C setup: #import bitbangio @@ -29,26 +33,23 @@ # Set range of accelerometer (can be RANGE_2_G, RANGE_4_G, RANGE_8_G or RANGE_16_G). lis3dh.range = adafruit_lis3dh.RANGE_2_G -# Set click detection to double and single clicks. The first parameter is a value: -# - 0 = Disable click detection. -# - 1 = Detect single clicks. -# - 2 = Detect single and double clicks. +# Set tap detection to double taps. The first parameter is a value: +# - 0 = Disable tap detection. +# - 1 = Detect single taps. +# - 2 = Detect double taps. # The second parameter is the threshold and a higher value means less sensitive -# click detection. Note the threshold should be set based on the range above: +# tap detection. Note the threshold should be set based on the range above: # - 2G = 40-80 threshold # - 4G = 20-40 threshold # - 8G = 10-20 threshold # - 16G = 5-10 threshold -lis3dh.set_click(2, 80) +lis3dh.set_tap(2, 80) -# Loop forever printing if a single or double click is detected. +# Loop forever printing if a double tap is detected. A single double tap may cause multiple +# `tapped` reads to return true so we make sure the last once was False. +last_tap = False while True: - # Read the click detection. Two booleans are returned, single click and - # double click detected. Each can be independently true/false. - single, double = lis3dh.read_click() - if single: - print('Single click!') - if double: - print('Double click!') - # Small delay to keep things responsive but give time for interrupt processing. - time.sleep(0.05) + tap = lis3dh.tapped + if tap and not last_tap: + print('Double tap!') + last_tap = tap