Skip to content

Commit

Permalink
Merge pull request #52 from jerryneedell/jerryn_mlc
Browse files Browse the repository at this point in the history
Add access to MLC for LSM6DSOX
  • Loading branch information
ladyada authored Mar 12, 2022
2 parents 7261e46 + 039c91a commit b1c28e8
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 6 deletions.
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)

0 comments on commit b1c28e8

Please sign in to comment.