Skip to content

Commit b1c28e8

Browse files
authored
Merge pull request #52 from jerryneedell/jerryn_mlc
Add access to MLC for LSM6DSOX
2 parents 7261e46 + 039c91a commit b1c28e8

File tree

3 files changed

+153
-6
lines changed

3 files changed

+153
-6
lines changed

adafruit_lsm6ds/__init__.py

+90-4
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,15 @@
5555
__version__ = "0.0.0-auto.0"
5656
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LSM6DS.git"
5757

58+
import struct
5859
from time import sleep
5960
from math import radians
6061
from micropython import const
6162
from adafruit_bus_device import i2c_device
6263

6364
from adafruit_register.i2c_struct import ROUnaryStruct, Struct
6465
from adafruit_register.i2c_bits import RWBits
65-
from adafruit_register.i2c_bit import RWBit
66+
from adafruit_register.i2c_bit import RWBit, ROBit
6667

6768
try:
6869
from typing import Tuple, Optional
@@ -139,22 +140,34 @@ class AccelHPF(CV):
139140

140141
LSM6DS_CHIP_ID = const(0x6C)
141142

143+
_LSM6DS_MLC_INT1 = const(0x0D)
142144
_LSM6DS_WHOAMI = const(0xF)
143145
_LSM6DS_CTRL1_XL = const(0x10)
144146
_LSM6DS_CTRL2_G = const(0x11)
145147
_LSM6DS_CTRL3_C = const(0x12)
146148
_LSM6DS_CTRL8_XL = const(0x17)
147149
_LSM6DS_CTRL9_XL = const(0x18)
148150
_LSM6DS_CTRL10_C = const(0x19)
151+
_LSM6DS_ALL_INT_SRC = const(0x1A)
149152
_LSM6DS_OUT_TEMP_L = const(0x20)
150153
_LSM6DS_OUTX_L_G = const(0x22)
151154
_LSM6DS_OUTX_L_A = const(0x28)
155+
_LSM6DS_MLC_STATUS = const(0x38)
152156
_LSM6DS_STEP_COUNTER = const(0x4B)
157+
_LSM6DS_TAP_CFG0 = const(0x56)
153158
_LSM6DS_TAP_CFG = const(0x58)
154-
159+
_LSM6DS_MLC0_SRC = const(0x70)
155160
_MILLI_G_TO_ACCEL = 0.00980665
156161

157162

163+
_LSM6DS_EMB_FUNC_EN_A = const(0x04)
164+
_LSM6DS_EMB_FUNC_EN_B = const(0x05)
165+
_LSM6DS_FUNC_CFG_ACCESS = const(0x01)
166+
_LSM6DS_FUNC_CFG_BANK_USER = const(0)
167+
_LSM6DS_FUNC_CFG_BANK_HUB = const(1)
168+
_LSM6DS_FUNC_CFG_BANK_EMBED = const(2)
169+
170+
158171
class LSM6DS: # pylint: disable=too-many-instance-attributes
159172

160173
"""Driver for the LSM6DSOX 6-axis accelerometer and gyroscope.
@@ -171,7 +184,9 @@ class LSM6DS: # pylint: disable=too-many-instance-attributes
171184
_raw_accel_data = Struct(_LSM6DS_OUTX_L_A, "<hhh")
172185
_raw_gyro_data = Struct(_LSM6DS_OUTX_L_G, "<hhh")
173186
_raw_temp_data = Struct(_LSM6DS_OUT_TEMP_L, "<h")
174-
187+
_emb_func_en_a = Struct(_LSM6DS_EMB_FUNC_EN_A, "<b")
188+
_emb_func_en_b = Struct(_LSM6DS_EMB_FUNC_EN_B, "<b")
189+
_mlc0_src = Struct(_LSM6DS_MLC0_SRC, "<bbbbbbbb")
175190
# RWBits:
176191
_accel_range = RWBits(2, _LSM6DS_CTRL1_XL, 2)
177192
_accel_data_rate = RWBits(4, _LSM6DS_CTRL1_XL, 4)
@@ -187,13 +202,22 @@ class LSM6DS: # pylint: disable=too-many-instance-attributes
187202
_i3c_disable = RWBit(_LSM6DS_CTRL9_XL, 1)
188203
_pedometer_reset = RWBit(_LSM6DS_CTRL10_C, 1)
189204
_func_enable = RWBit(_LSM6DS_CTRL10_C, 2)
205+
_mem_bank = RWBit(_LSM6DS_FUNC_CFG_ACCESS, 7)
206+
_mlc_status = ROBit(_LSM6DS_MLC_STATUS, 0)
207+
_i3c_disable = RWBit(_LSM6DS_CTRL9_XL, 0)
208+
_block_data_enable = RWBit(_LSM6DS_CTRL3_C, 4)
209+
_route_int1 = RWBit(_LSM6DS_MLC_INT1, 0)
210+
_tap_latch = RWBit(_LSM6DS_TAP_CFG0, 0)
211+
_tap_clear = RWBit(_LSM6DS_TAP_CFG0, 6)
190212
_ped_enable = RWBit(_LSM6DS_TAP_CFG, 6)
191213
pedometer_steps = ROUnaryStruct(_LSM6DS_STEP_COUNTER, "<h")
192214
"""The number of steps detected by the pedometer. You must enable with `pedometer_enable`
193215
before calling. Use ``pedometer_reset`` to reset the number of steps"""
194216
CHIP_ID = None
195217

196-
def __init__(self, i2c_bus: I2C, address: int = LSM6DS_DEFAULT_ADDRESS) -> None:
218+
def __init__(
219+
self, i2c_bus: I2C, address: int = LSM6DS_DEFAULT_ADDRESS, ucf: str = None
220+
) -> None:
197221
self._cached_accel_range = None
198222
self._cached_gyro_range = None
199223

@@ -215,6 +239,9 @@ def __init__(self, i2c_bus: I2C, address: int = LSM6DS_DEFAULT_ADDRESS) -> None:
215239

216240
self.accelerometer_range = AccelRange.RANGE_4G # pylint: disable=no-member
217241
self.gyro_range = GyroRange.RANGE_250_DPS # pylint: disable=no-member
242+
# Load and configure MLC if UCF file is provided
243+
if ucf is not None:
244+
self.load_mlc(ucf)
218245

219246
def reset(self) -> None:
220247
"Resets the sensor's configuration into an initial state"
@@ -376,3 +403,62 @@ def temperature(self) -> float:
376403
return (temp - 2 ** 13) * 0.0625
377404

378405
return temp * 0.0625
406+
407+
def _set_embedded_functions(self, enable, emb_ab=None):
408+
"""Enable/disable embedded functions - returns prior settings when disabled"""
409+
self._mem_bank = 1
410+
if enable:
411+
self._emb_func_en_a = emb_ab[0]
412+
self._emb_func_en_b = emb_ab[1]
413+
else:
414+
emb_a = self._emb_func_en_a
415+
emb_b = self._emb_func_en_b
416+
self._emb_func_en_a = (emb_a[0] & 0xC7,)
417+
self._emb_func_en_b = (emb_b[0] & 0xE6,)
418+
emb_ab = (emb_a, emb_b)
419+
420+
self._mem_bank = 0
421+
return emb_ab
422+
423+
def load_mlc(self, ucf):
424+
"""Load MLC configuration file into sensor"""
425+
buf = bytearray(2)
426+
with self.i2c_device as i2c:
427+
# Load MLC config from file
428+
with open(ucf, "r") as ucf_file:
429+
for line in ucf_file:
430+
if line.startswith("Ac"):
431+
command = [int(v, 16) for v in line.strip().split(" ")[1:3]]
432+
buf[0] = command[0]
433+
buf[1] = command[1]
434+
i2c.write(buf)
435+
436+
# Disable embudded function -- save current settings
437+
emb_ab = self._set_embedded_functions(False)
438+
439+
# Disable I3C interface
440+
self._i3c_disable = 1
441+
442+
# Enable Block Data Update
443+
self._block_data_enable = 1
444+
445+
# Route signals on interrupt pin 1
446+
self._mem_bank = 1
447+
self._route_int1 &= 1
448+
self._mem_bank = 0
449+
450+
# Configure interrupt pin mode
451+
self._tap_latch = 1
452+
self._tap_clear = 1
453+
454+
# Enabble Embedded Functions using previously stored settings
455+
self._set_embedded_functions(True, emb_ab)
456+
457+
def read_mlc_output(self):
458+
"""Read MLC results"""
459+
buf = None
460+
if self._mlc_status:
461+
self._mem_bank = 1
462+
buf = self._mlc0_src
463+
self._mem_bank = 0
464+
return buf

adafruit_lsm6ds/lsm6dsox.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class LSM6DSOX(LSM6DS): # pylint: disable=too-many-instance-attributes
5050

5151
CHIP_ID = LSM6DS_CHIP_ID
5252

53-
def __init__(self, i2c_bus: I2C, address: int = LSM6DS_DEFAULT_ADDRESS) -> None:
54-
super().__init__(i2c_bus, address)
53+
def __init__(
54+
self, i2c_bus: I2C, address: int = LSM6DS_DEFAULT_ADDRESS, ucf: str = None
55+
) -> None:
56+
super().__init__(i2c_bus, address, ucf)
5557
self._i3c_disable = True

examples/lsm6ds_lsm6dsox_mlc_test.py

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2020 Bryan Siepert for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
# LSM6DSOX IMU MLC (Machine Learning Core) Example.
5+
# Download the raw UCF file, copy to storage and reset.
6+
7+
# NOTE: The pre-trained models (UCF files) for the examples can be found here:
8+
# https://github.com/STMicroelectronics/STMems_Machine_Learning_Core/tree/master/application_examples/lsm6dsox
9+
10+
import time
11+
import board
12+
from adafruit_lsm6ds.lsm6dsox import LSM6DSOX
13+
from adafruit_lsm6ds import Rate, AccelRange, GyroRange
14+
15+
i2c = board.STEMMA_I2C() # uses board.SCL and board.SDA
16+
17+
18+
# Vibration detection example
19+
UCF_FILE = "lsm6dsox_vibration_monitoring.ucf"
20+
UCF_LABELS = {0: "no vibration", 1: "low vibration", 2: "high vibration"}
21+
# NOTE: Selected data rate and scale must match the MLC data rate and scale.
22+
lsm = LSM6DSOX(i2c, ucf=UCF_FILE)
23+
lsm.gyro_range = GyroRange.RANGE_2000_DPS
24+
lsm.accelerometer_range = AccelRange.RANGE_4G
25+
lsm.accelerometer_data_rate = Rate.RATE_26_HZ
26+
lsm.gyro_data_rate = Rate.RATE_26_HZ
27+
28+
29+
# Head gestures example
30+
# UCF_FILE = "lsm6dsox_head_gestures.ucf"
31+
# UCF_LABELS = {0:"Nod", 1:"Shake", 2:"Stationary", 3:"Swing", 4:"Walk"}
32+
# NOTE: Selected data rate and scale must match the MLC data rate and scale.
33+
# lsm = LSM6DSOX(i2c, ucf=UCF_FILE)
34+
# lsm.gyro_range = GyroRange.RANGE_250_DPS
35+
# lsm.accelerometer_range = AccelRange.RANGE_2G
36+
# lsm.accelerometer_data_rate = Rate.RATE_26_HZ
37+
# lsm.gyro_data_rate = Rate.RATE_26_HZ
38+
39+
# 6 DOF Position example
40+
# UCF_FILE = "lsm6dsox_six_d_position.ucf"
41+
# UCF_LABELS = {0:"None", 1:"X-UP", 2:"X-DOWN", 3:"Y-UP", 4:"Y-DOWN", 5:"Z-UP", 6:"Z-DOWN"}
42+
# NOTE: Selected data rate and scale must match the MLC data rate and scale.
43+
# lsm = LSM6DSOX(i2c, ucf=UCF_FILE)
44+
# lsm.gyro_range = GyroRange.RANGE_250_DPS
45+
# lsm.accelerometer_range = AccelRange.RANGE_2G
46+
# lsm.accelerometer_data_rate = Rate.RATE_26_HZ
47+
# lsm.gyro_data_rate = Rate.RATE_26_HZ
48+
49+
50+
print("MLC configured...")
51+
52+
while True:
53+
buf = lsm.read_mlc_output()
54+
if buf is not None:
55+
print(UCF_LABELS[buf[0]])
56+
# delay to allow interrupt flag to clear
57+
# interrupt stays high for one sample interval
58+
# (38.4 ms @ 26 Hz ot 19.2 ms @ 52Hz)
59+
time.sleep(0.05)

0 commit comments

Comments
 (0)