# Adafruit LIS3DH Accelerometer Micropython Driver # Based on the Arduino LIS3DH driver from: # https://github.com/adafruit/Adafruit_LIS3DH/ # and the Circuitpython Port by Tony DiCola #Port by Julian Finn # License: MIT License (https://en.wikipedia.org/wiki/MIT_License) """ `adafruit_lis3dh` ==================================================== Micropython driver for the LIS3DH accelerometer. Based on the CircuitPython version by Tony DiCola Port by Julian Finn Implementation Notes -------------------- **Hardware:** * `Adafruit LIS3DH Triple-Axis Accelerometer Breakout `_ * `Circuit Playground Express `_ **Software and Dependencies:** * Adafruit CircuitPython firmware for the ESP8622 and M0-based boards: https://github.com/adafruit/circuitpython/releases * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice """ import time import math try: from collections import namedtuple except ImportError: from ucollections import namedtuple try: import struct except ImportError: import ustruct as struct from micropython import const __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LIS3DH.git" # Register addresses: # pylint: disable=bad-whitespace _REG_OUTADC1_L = const(0x08) _REG_WHOAMI = const(0x0F) _REG_TEMPCFG = const(0x1F) _REG_CTRL1 = const(0x20) _REG_CTRL2 = const(0x21) #High pass filter selection _REG_CTRL3 = const(0x22) _REG_CTRL4 = const(0x23) _REG_CTRL5 = const(0x24) _REG_OUT_X_L = const(0x28) _REG_INT1SRC = const(0x31) _REG_CLICKCFG = const(0x38) _REG_CLICKSRC = const(0x39) _REG_CLICKTHS = const(0x3A) _REG_TIMELIMIT = const(0x3B) _REG_TIMELATENCY = const(0x3C) _REG_TIMEWINDOW = const(0x3D) HP_FILTER_RESET = const(0x26) INT1_CFG = const(0x30) INT1_SRC = const(0x31) INT1_THS = const(0x32) INT1_DURATION = const(0x33) # Register value constants: RANGE_16_G = const(0b11) # +/- 16g RANGE_8_G = const(0b10) # +/- 8g RANGE_4_G = const(0b01) # +/- 4g RANGE_2_G = const(0b00) # +/- 2g (default value) DATARATE_1344_HZ = const(0b1001) # 1.344 KHz DATARATE_400_HZ = const(0b0111) # 400Hz DATARATE_200_HZ = const(0b0110) # 200Hz DATARATE_100_HZ = const(0b0101) # 100Hz DATARATE_50_HZ = const(0b0100) # 50Hz DATARATE_25_HZ = const(0b0011) # 25Hz DATARATE_10_HZ = const(0b0010) # 10 Hz DATARATE_1_HZ = const(0b0001) # 1 Hz DATARATE_POWERDOWN = const(0) DATARATE_LOWPOWER_1K6HZ = const(0b1000) DATARATE_LOWPOWER_5KHZ = const(0b1001) # Other constants STANDARD_GRAVITY = 9.806 # pylint: enable=bad-whitespace # the named tuple returned by the class AccelerationTuple = namedtuple("acceleration", ("x", "y", "z")) class LIS3DH: """Driver base for the LIS3DH accelerometer.""" def __init__(self, int1=None, int2=None): # Check device ID. device_id = self._read_register_byte(_REG_WHOAMI) print("device id") print (device_id) #if device_id != 0x33: # raise RuntimeError('Failed to find LIS3DH!') # Reboot self._write_register_byte(_REG_CTRL5, 0x80) time.sleep(0.01) # takes 5ms # Enable all axes, normal mode. self._write_register_byte(_REG_CTRL1, 0x07) # Set 400Hz data rate. self.data_rate = DATARATE_400_HZ # High res & BDU enabled. self._write_register_byte(_REG_CTRL4, 0x88) # High-pass filter enabled on data and interrupt1 self._write_register_byte(_REG_CTRL2, 0x09) # Interrupt driven to INT1 pad self._write_register_byte(_REG_CTRL3, 0x40) # Enable ADCs. self._write_register_byte(_REG_TEMPCFG, 0x80) # Latch interrupt for INT1 self._write_register_byte(_REG_CTRL5, 0x08) # Threshold as a multiple of 16mg. 4 * 16 = 64mg self._write_register_byte(INT1_THS, 0x04) # self._write_register_byte(INT1_DURATION, 0x00) # self._read_register_byte(HP_FILTER_RESET) # Configure interrupt when any of the X, Y or Z axes exceeds (rather than stay below) the threshold self._write_register_byte(INT1_CFG, 0x2a) # Clears/Resets the interupt self._read_register_byte(INT1_SRC) # Initialise interrupt pins self._int1 = int1 self._int2 = int2 if self._int1: self._int1.direction = machine.Pin.IN self._int1.pull = machine.Pin.PULL_UP @property def data_rate(self): """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 @data_rate.setter def data_rate(self, rate): ctl1 = self._read_register_byte(_REG_CTRL1) ctl1 &= ~(0xF0) ctl1 |= rate << 4 self._write_register_byte(_REG_CTRL1, ctl1) @property def range(self): """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 @range.setter def range(self, range_value): ctl4 = self._read_register_byte(_REG_CTRL4) ctl4 &= ~0x30 ctl4 |= range_value << 4 self._write_register_byte(_REG_CTRL4, ctl4) @property def acceleration(self): """The x, y, z acceleration values returned in a 3-tuple and are in m / s ^ 2.""" divider = 1 accel_range = self.range if accel_range == RANGE_16_G: divider = 1365 elif accel_range == RANGE_8_G: divider = 4096 elif accel_range == RANGE_4_G: divider = 8190 elif accel_range == RANGE_2_G: divider = 16380 x, y, z = struct.unpack(' shake_threshold def read_adc_raw(self, adc): """Retrieve the raw analog to digital converter value. ADC must be a value 1, 2, or 3. """ if adc < 1 or adc > 3: raise ValueError('ADC must be a value 1 to 3!') return struct.unpack(' 0 def set_tap(self, tap, threshold, *, time_limit=10, time_latency=20, time_window=255, click_cfg=None): """ 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)') ctrl3 = self._read_register_byte(_REG_CTRL3) if tap == 0 and click_cfg is None: # Disable click interrupt. self._write_register_byte(_REG_CTRL3, ctrl3 & ~(0x80)) # Turn off I1_CLICK. self._write_register_byte(_REG_CLICKCFG, 0) return else: self._write_register_byte(_REG_CTRL3, ctrl3 | 0x80) # Turn on int1 click output if click_cfg is None: if tap == 1: click_cfg = 0x15 # Turn on all axes & singletap. if tap == 2: click_cfg = 0x2A # Turn on all axes & doubletap. # Or, if a custom click configuration register value specified, use it. self._write_register_byte(_REG_CLICKCFG, click_cfg) self._write_register_byte(_REG_CLICKTHS, 0x80 | threshold) self._write_register_byte(_REG_TIMELIMIT, time_limit) self._write_register_byte(_REG_TIMELATENCY, time_latency) self._write_register_byte(_REG_TIMEWINDOW, time_window) def _read_register_byte(self, register): # Read a byte register value and return it. return self._read_register(register, 1)[0] def _read_register(self, register, length): # Read an arbitrarily long register (specified by length number of # bytes) and return a bytearray of the retrieved data. # Subclasses MUST implement this! raise NotImplementedError def _write_register_byte(self, register, value): # Write a single byte register at the specified register address. # Subclasses MUST implement this! raise NotImplementedError """ class LIS3DH_I2C(LIS3DH): def __init__(self, i2c, *, address=0x18, int1=None, int2=None): from machine import I2C self.address = address self._i2c = i2c self._buffer = bytearray(6) super().__init__(int1=int1, int2=int2) def _read_register(self, register, length): self._buffer[0] = register & 0xFF self._i2c.writeto(self.address, self._buffer[0:1]) self._i2c.readfrom_into(self.address, self._buffer[0:length]) return self._buffer def _write_register_byte(self, register, value): self._buffer[0] = register & 0xFF self._buffer[1] = value & 0xFF self._i2c.writeto(self.address, self._buffer[0:2]) """ class LIS3DH_I2C(LIS3DH): """Driver for the LIS3DH accelerometer connected over I2C.""" def __init__(self, i2c, *, address=0x18, int1=None, int2=None): from machine import I2C self.address = address self._i2c = i2c self._buffer = bytearray(6) super().__init__(int1=int1, int2=int2) def _read_register(self, register, length): return self._i2c.readfrom_mem(0x18,register,length) def _write_register_byte(self, register, value): self._buffer[0] = register & 0xFF self._buffer[1] = value & 0xFF self._i2c.writeto(self.address, self._buffer[0:2]) class LIS3DH_SPI(LIS3DH): """Driver for the LIS3DH accelerometer connected over SPI.""" def __init__(self, spi, cs, *, baudrate=100000, int1=None, int2=None): import adafruit_bus_device.spi_device as spi_device self._spi = spi_device.SPIDevice(spi, cs, baudrate=baudrate) self._buffer = bytearray(6) super().__init__(int1=int1, int2=int2) def _read_register(self, register, length): if length == 1: self._buffer[0] = (register | 0x80) & 0xFF # Read single, bit 7 high. else: self._buffer[0] = (register | 0xC0) & 0xFF # Read multiple, bit 6&7 high. with self._spi as spi: spi.write(self._buffer, start=0, end=1) # pylint: disable=no-member spi.readinto(self._buffer, start=0, end=length) # pylint: disable=no-member return self._buffer def _write_register_byte(self, register, value): self._buffer[0] = register & 0x7F # Write, bit 7 low. self._buffer[1] = value & 0xFF with self._spi as spi: spi.write(self._buffer, start=0, end=2) # pylint: disable=no-member