diff --git a/adafruit_lsm6ds/__init__.py b/adafruit_lsm6ds/__init__.py index 1ea20cc..9bd7159 100644 --- a/adafruit_lsm6ds/__init__.py +++ b/adafruit_lsm6ds/__init__.py @@ -55,6 +55,7 @@ __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LSM6DS.git" +import struct from time import sleep from math import radians from micropython import const @@ -62,7 +63,7 @@ from adafruit_register.i2c_struct import ROUnaryStruct, Struct from adafruit_register.i2c_bits import RWBits -from adafruit_register.i2c_bit import RWBit +from adafruit_register.i2c_bit import RWBit, ROBit try: from typing import Tuple, Optional @@ -139,6 +140,7 @@ class AccelHPF(CV): LSM6DS_CHIP_ID = const(0x6C) +_LSM6DS_MLC_INT1 = const(0x0D) _LSM6DS_WHOAMI = const(0xF) _LSM6DS_CTRL1_XL = const(0x10) _LSM6DS_CTRL2_G = const(0x11) @@ -146,15 +148,26 @@ class AccelHPF(CV): _LSM6DS_CTRL8_XL = const(0x17) _LSM6DS_CTRL9_XL = const(0x18) _LSM6DS_CTRL10_C = const(0x19) +_LSM6DS_ALL_INT_SRC = const(0x1A) _LSM6DS_OUT_TEMP_L = const(0x20) _LSM6DS_OUTX_L_G = const(0x22) _LSM6DS_OUTX_L_A = const(0x28) +_LSM6DS_MLC_STATUS = const(0x38) _LSM6DS_STEP_COUNTER = const(0x4B) +_LSM6DS_TAP_CFG0 = const(0x56) _LSM6DS_TAP_CFG = const(0x58) - +_LSM6DS_MLC0_SRC = const(0x70) _MILLI_G_TO_ACCEL = 0.00980665 +_LSM6DS_EMB_FUNC_EN_A = const(0x04) +_LSM6DS_EMB_FUNC_EN_B = const(0x05) +_LSM6DS_FUNC_CFG_ACCESS = const(0x01) +_LSM6DS_FUNC_CFG_BANK_USER = const(0) +_LSM6DS_FUNC_CFG_BANK_HUB = const(1) +_LSM6DS_FUNC_CFG_BANK_EMBED = const(2) + + class LSM6DS: # pylint: disable=too-many-instance-attributes """Driver for the LSM6DSOX 6-axis accelerometer and gyroscope. @@ -171,7 +184,9 @@ class LSM6DS: # pylint: disable=too-many-instance-attributes _raw_accel_data = Struct(_LSM6DS_OUTX_L_A, " None: + def __init__( + self, i2c_bus: I2C, address: int = LSM6DS_DEFAULT_ADDRESS, ucf: str = None + ) -> None: self._cached_accel_range = None self._cached_gyro_range = None @@ -215,6 +239,9 @@ def __init__(self, i2c_bus: I2C, address: int = LSM6DS_DEFAULT_ADDRESS) -> None: self.accelerometer_range = AccelRange.RANGE_4G # pylint: disable=no-member self.gyro_range = GyroRange.RANGE_250_DPS # pylint: disable=no-member + # Load and configure MLC if UCF file is provided + if ucf is not None: + self.load_mlc(ucf) def reset(self) -> None: "Resets the sensor's configuration into an initial state" @@ -376,3 +403,62 @@ def temperature(self) -> float: return (temp - 2 ** 13) * 0.0625 return temp * 0.0625 + + def _set_embedded_functions(self, enable, emb_ab=None): + """Enable/disable embedded functions - returns prior settings when disabled""" + self._mem_bank = 1 + if enable: + self._emb_func_en_a = emb_ab[0] + self._emb_func_en_b = emb_ab[1] + else: + emb_a = self._emb_func_en_a + emb_b = self._emb_func_en_b + self._emb_func_en_a = (emb_a[0] & 0xC7,) + self._emb_func_en_b = (emb_b[0] & 0xE6,) + emb_ab = (emb_a, emb_b) + + self._mem_bank = 0 + return emb_ab + + def load_mlc(self, ucf): + """Load MLC configuration file into sensor""" + buf = bytearray(2) + with self.i2c_device as i2c: + # Load MLC config from file + with open(ucf, "r") as ucf_file: + for line in ucf_file: + if line.startswith("Ac"): + command = [int(v, 16) for v in line.strip().split(" ")[1:3]] + buf[0] = command[0] + buf[1] = command[1] + i2c.write(buf) + + # Disable embudded function -- save current settings + emb_ab = self._set_embedded_functions(False) + + # Disable I3C interface + self._i3c_disable = 1 + + # Enable Block Data Update + self._block_data_enable = 1 + + # Route signals on interrupt pin 1 + self._mem_bank = 1 + self._route_int1 &= 1 + self._mem_bank = 0 + + # Configure interrupt pin mode + self._tap_latch = 1 + self._tap_clear = 1 + + # Enabble Embedded Functions using previously stored settings + self._set_embedded_functions(True, emb_ab) + + def read_mlc_output(self): + """Read MLC results""" + buf = None + if self._mlc_status: + self._mem_bank = 1 + buf = self._mlc0_src + self._mem_bank = 0 + return buf diff --git a/adafruit_lsm6ds/lsm6dsox.py b/adafruit_lsm6ds/lsm6dsox.py index 3f5baa5..215b5e6 100644 --- a/adafruit_lsm6ds/lsm6dsox.py +++ b/adafruit_lsm6ds/lsm6dsox.py @@ -50,6 +50,8 @@ class LSM6DSOX(LSM6DS): # pylint: disable=too-many-instance-attributes CHIP_ID = LSM6DS_CHIP_ID - def __init__(self, i2c_bus: I2C, address: int = LSM6DS_DEFAULT_ADDRESS) -> None: - super().__init__(i2c_bus, address) + def __init__( + self, i2c_bus: I2C, address: int = LSM6DS_DEFAULT_ADDRESS, ucf: str = None + ) -> None: + super().__init__(i2c_bus, address, ucf) self._i3c_disable = True diff --git a/examples/lsm6ds_lsm6dsox_mlc_test.py b/examples/lsm6ds_lsm6dsox_mlc_test.py new file mode 100644 index 0000000..16c8d30 --- /dev/null +++ b/examples/lsm6ds_lsm6dsox_mlc_test.py @@ -0,0 +1,59 @@ +# SPDX-FileCopyrightText: Copyright (c) 2020 Bryan Siepert for Adafruit Industries +# +# SPDX-License-Identifier: MIT +# LSM6DSOX IMU MLC (Machine Learning Core) Example. +# Download the raw UCF file, copy to storage and reset. + +# NOTE: The pre-trained models (UCF files) for the examples can be found here: +# https://github.com/STMicroelectronics/STMems_Machine_Learning_Core/tree/master/application_examples/lsm6dsox + +import time +import board +from adafruit_lsm6ds.lsm6dsox import LSM6DSOX +from adafruit_lsm6ds import Rate, AccelRange, GyroRange + +i2c = board.STEMMA_I2C() # uses board.SCL and board.SDA + + +# Vibration detection example +UCF_FILE = "lsm6dsox_vibration_monitoring.ucf" +UCF_LABELS = {0: "no vibration", 1: "low vibration", 2: "high vibration"} +# NOTE: Selected data rate and scale must match the MLC data rate and scale. +lsm = LSM6DSOX(i2c, ucf=UCF_FILE) +lsm.gyro_range = GyroRange.RANGE_2000_DPS +lsm.accelerometer_range = AccelRange.RANGE_4G +lsm.accelerometer_data_rate = Rate.RATE_26_HZ +lsm.gyro_data_rate = Rate.RATE_26_HZ + + +# Head gestures example +# UCF_FILE = "lsm6dsox_head_gestures.ucf" +# UCF_LABELS = {0:"Nod", 1:"Shake", 2:"Stationary", 3:"Swing", 4:"Walk"} +# NOTE: Selected data rate and scale must match the MLC data rate and scale. +# lsm = LSM6DSOX(i2c, ucf=UCF_FILE) +# lsm.gyro_range = GyroRange.RANGE_250_DPS +# lsm.accelerometer_range = AccelRange.RANGE_2G +# lsm.accelerometer_data_rate = Rate.RATE_26_HZ +# lsm.gyro_data_rate = Rate.RATE_26_HZ + +# 6 DOF Position example +# UCF_FILE = "lsm6dsox_six_d_position.ucf" +# UCF_LABELS = {0:"None", 1:"X-UP", 2:"X-DOWN", 3:"Y-UP", 4:"Y-DOWN", 5:"Z-UP", 6:"Z-DOWN"} +# NOTE: Selected data rate and scale must match the MLC data rate and scale. +# lsm = LSM6DSOX(i2c, ucf=UCF_FILE) +# lsm.gyro_range = GyroRange.RANGE_250_DPS +# lsm.accelerometer_range = AccelRange.RANGE_2G +# lsm.accelerometer_data_rate = Rate.RATE_26_HZ +# lsm.gyro_data_rate = Rate.RATE_26_HZ + + +print("MLC configured...") + +while True: + buf = lsm.read_mlc_output() + if buf is not None: + print(UCF_LABELS[buf[0]]) + # delay to allow interrupt flag to clear + # interrupt stays high for one sample interval + # (38.4 ms @ 26 Hz ot 19.2 ms @ 52Hz) + time.sleep(0.05)