Skip to content

Add access to MLC for LSM6DSOX #52

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 90 additions & 4 deletions adafruit_lsm6ds/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,15 @@
__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
from adafruit_bus_device import i2c_device

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
Expand Down Expand Up @@ -139,22 +140,34 @@ 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)
_LSM6DS_CTRL3_C = const(0x12)
_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.
Expand All @@ -171,7 +184,9 @@ class LSM6DS: # pylint: disable=too-many-instance-attributes
_raw_accel_data = Struct(_LSM6DS_OUTX_L_A, "<hhh")
_raw_gyro_data = Struct(_LSM6DS_OUTX_L_G, "<hhh")
_raw_temp_data = Struct(_LSM6DS_OUT_TEMP_L, "<h")

_emb_func_en_a = Struct(_LSM6DS_EMB_FUNC_EN_A, "<b")
_emb_func_en_b = Struct(_LSM6DS_EMB_FUNC_EN_B, "<b")
_mlc0_src = Struct(_LSM6DS_MLC0_SRC, "<bbbbbbbb")
# RWBits:
_accel_range = RWBits(2, _LSM6DS_CTRL1_XL, 2)
_accel_data_rate = RWBits(4, _LSM6DS_CTRL1_XL, 4)
Expand All @@ -187,13 +202,22 @@ class LSM6DS: # pylint: disable=too-many-instance-attributes
_i3c_disable = RWBit(_LSM6DS_CTRL9_XL, 1)
_pedometer_reset = RWBit(_LSM6DS_CTRL10_C, 1)
_func_enable = RWBit(_LSM6DS_CTRL10_C, 2)
_mem_bank = RWBit(_LSM6DS_FUNC_CFG_ACCESS, 7)
_mlc_status = ROBit(_LSM6DS_MLC_STATUS, 0)
_i3c_disable = RWBit(_LSM6DS_CTRL9_XL, 0)
_block_data_enable = RWBit(_LSM6DS_CTRL3_C, 4)
_route_int1 = RWBit(_LSM6DS_MLC_INT1, 0)
_tap_latch = RWBit(_LSM6DS_TAP_CFG0, 0)
_tap_clear = RWBit(_LSM6DS_TAP_CFG0, 6)
_ped_enable = RWBit(_LSM6DS_TAP_CFG, 6)
pedometer_steps = ROUnaryStruct(_LSM6DS_STEP_COUNTER, "<h")
"""The number of steps detected by the pedometer. You must enable with `pedometer_enable`
before calling. Use ``pedometer_reset`` to reset the number of steps"""
CHIP_ID = None

def __init__(self, i2c_bus: I2C, address: int = LSM6DS_DEFAULT_ADDRESS) -> 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

Expand All @@ -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"
Expand Down Expand Up @@ -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
6 changes: 4 additions & 2 deletions adafruit_lsm6ds/lsm6dsox.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
59 changes: 59 additions & 0 deletions examples/lsm6ds_lsm6dsox_mlc_test.py
Original file line number Diff line number Diff line change
@@ -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)