"""LIS2HH Driver - i2c Accelerometer
"""
import time
# Device registers
LIS_TEMP_L = 0x0c # Temperature sensor
LIS_TEMP_H = 0x0d
LIS_DEV_ID = 0x0f # hardcoded WHO_AM_I ID
LIS_CTRL_REG0 = 0x1e
LIS_TMP_CFG = 0x1f
LIS_CTRL_REG1 = 0x20
LIS_CTRL_REG2 = 0x21
LIS_CTRL_REG3 = 0x22
LIS_CTRL_REG4 = 0x23
LIS_CTRL_REG5 = 0x24
LIS_CTRL_REG6 = 0x25
LIS_REFERENCE = 0x26
LIS_STATUS_REG = 0x27
LIS_OUT_REG_MULTI = 0x28 # 3 axes XYZ, each 16bit 2's complement
LIS_FIFO_CTRL = 0x2e
LIS_FIFO_SRC = 0x2f
LIS_IG1_CFG = 0x30 # Interrupt Generator 1
LIS_IG1_SRC = 0x31
LIS_IG1_THRESH = 0x32
LIS_IG1_DURATION = 0x33
LIS_IG2_CFG = 0x34 # Interrupt Generator 2
LIS_IG2_SRC = 0x35
LIS_IG2_THRESH = 0x36
LIS_IG2_DURATION = 0x37
LIS_VFY_DEV_ID = 0x33
# Default values
LIS_DEF_CTRL0 = 0b10010000 # Disable SA0 pullup
LIS_DEF_CTRL1 = 0b0111_0_111 # ODR=400Hz, High-Rez mode, XYZ axes enabled
LIS_DEF_CTRL2 = 0b10000111 # High pass filter bypassed to FIFO, configured 8Hz cutoff for IRQ
LIS_DEF_CTRL3 = 0b00000000 # No IRQ pin
LIS_DEF_CTRL4 = 0b1_0_00_1_00_0 # Block data update, Little-endian, 2g full-scale, 12-bit
LIS_DEF_CTRL5 = 0b00000000 # FIFO disabled, non-latching IRQ
LIS_DEF_CTRL6 = 0b00000010 # INT2 disabled, Interrupt polarity active low
LIS_DEF_FIFO_CTRL = 0b10000000 # Stream mode, Watermark = 0
[docs]class LIS2DH:
"""Device driver for LIS2DH accelerometer, controlled via i2c bus."""
# Note: default addr assumes SA0 tied high or floating
def __init__(self, i2c, addr=0x19):
self.i2c = i2c
self.i2c.unlock()
self.addr = addr
self.orientation = (0, 0, 0)
self.buf1 = bytearray(1) # Pre-allocate command/resp buffer usable from IRQ
self.buf2 = bytearray(2)
self.reset()
[docs] def reset(self):
"""Init registers to default config"""
self.set_byte(LIS_CTRL_REG1, LIS_DEF_CTRL1)
self.set_byte(LIS_CTRL_REG2, LIS_DEF_CTRL2)
self.set_byte(LIS_CTRL_REG3, LIS_DEF_CTRL3)
self.set_byte(LIS_CTRL_REG4, LIS_DEF_CTRL4)
self.set_byte(LIS_CTRL_REG5, LIS_DEF_CTRL5)
self.set_byte(LIS_CTRL_REG6, LIS_DEF_CTRL6)
[docs] def set_byte(self, reg, val):
"""Set byte register"""
self.buf2[0] = reg
self.buf2[1] = val
self.i2c.try_lock()
self.i2c.writeto(self.addr, self.buf2)
self.i2c.unlock()
def get_byte(self, reg):
self.buf1[0] = reg
self.i2c.try_lock()
self.i2c.writeto(self.addr, self.buf1)
self.i2c.readfrom_into(self.addr, self.buf1)
self.i2c.unlock()
return self.buf1[0]
[docs] def verify_id(self):
"""Verify device ID - return True if correct"""
return self.get_byte(LIS_DEV_ID) == LIS_VFY_DEV_ID
def poll_x(self):
stat = 0
count = 10000
while (stat & 0b00001000) == 0 and count:
stat = self.get_byte(LIS_STATUS_REG)
count -= 1
if count:
xl = self.get_byte(0x28)
xh = self.get_byte(0x29)
print(hex(xl), hex(xh), 'x=', self.signed12((xl, xh)), 'count=', count)
else:
print("timeout")
[docs] def wait_xyz_ready(self):
"""Poll status register and return True when READY, False if timeout"""
stat = 0
count = 1000
while (stat & 0b00001000) == 0 and count:
stat = self.get_byte(LIS_STATUS_REG)
count -= 1
return bool(count)
[docs] @staticmethod
def test_exceeds(a, b, delta):
"""Test each iterable value: a[0..n] - b[0..n] > delta"""
for i, j in zip(a, b):
if abs(i - j) < delta:
return False
return True
[docs] def self_test(self):
"""Run self-test and return device to default state. Return True if passed"""
result = False
normal = self.read()
# print("normal=", normal)
self.set_byte(LIS_CTRL_REG4, 0b1_0_00_1_10_0) # Induce positive force
time.sleep(0.200)
pos_test = self.read()
# print("pos=", pos_test)
if self.test_exceeds(normal, pos_test, 1000):
self.set_byte(LIS_CTRL_REG4, 0b1_0_00_1_01_0) # Induce negative force
time.sleep(0.200)
neg_test = self.read()
# print("neg=", neg_test)
if self.test_exceeds(neg_test, normal, 1000):
result = True
self.set_byte(LIS_CTRL_REG4, LIS_DEF_CTRL4) # Back to normal
return result
@staticmethod
def signed12(buf):
val = ((buf[1] << 4) | (buf[0] >> 4)) & 0x0fff # Little endian 12-bit left-justified
return (val & 0xf800) - (val & 0x07ff) # Convert to 2's complement signed integer
@staticmethod
def signed16(buf):
val = (buf[1] << 8) | buf[0] # Little endian 16-bit 2's complement value
return (val & 0x8000) - (val & 0x7fff) # Convert to signed integer
[docs] def read(self):
"""Read current XYZ axis values and update orientation"""
# Auto address increment isn't working on lis2dh12 as it did on lis2hh12. For now, do byte-at-a-time.
if self.wait_xyz_ready():
xl = self.get_byte(0x28)
xh = self.get_byte(0x29)
x = self.signed16((xl, xh))
yl = self.get_byte(0x2a)
yh = self.get_byte(0x2b)
y = self.signed16((yl, yh))
zl = self.get_byte(0x2c)
zh = self.get_byte(0x2d)
z = self.signed16((zl, zh))
# Change accelerometer coords to match (x,y,z) on PCB silkscreen
self.orientation = (y, x, z)
else:
print("timeout")
return self.orientation
[docs] def dump_axes(self):
"""Debug - send axis values to stdout"""
print("X={}, Y={}, Z={}".format(*self.read()))