|
# The MIT License (MIT)
|
|
#
|
|
# Copyright (c) 2017 Dave Astels for Adafruit Industries
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
# THE SOFTWARE.
|
|
"""
|
|
`adafruit_LSM303`
|
|
====================================================
|
|
|
|
|
|
CircuitPython driver for the LSM303 accelerometer + magnetometer.
|
|
|
|
* Author(s): Dave Astels
|
|
"""
|
|
|
|
try:
|
|
import struct
|
|
except ImportError:
|
|
import ustruct as struct
|
|
from micropython import const
|
|
from adafruit_bus_device.i2c_device import I2CDevice
|
|
|
|
__version__ = "1.1.0"
|
|
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LSM303.git"
|
|
|
|
# pylint: disable=bad-whitespace
|
|
_ADDRESS_ACCEL = const(0x19) # (0x32 >> 1) // 0011001x
|
|
_ADDRESS_MAG = const(0x1E) # (0x3C >> 1) // 0011110x
|
|
_ID = const(0xD4) # (0b11010100)
|
|
|
|
# Accelerometer registers
|
|
_REG_ACCEL_CTRL_REG1_A = const(0x20)
|
|
_REG_ACCEL_CTRL_REG2_A = const(0x21)
|
|
_REG_ACCEL_CTRL_REG3_A = const(0x22)
|
|
_REG_ACCEL_CTRL_REG4_A = const(0x23)
|
|
_REG_ACCEL_CTRL_REG5_A = const(0x24)
|
|
_REG_ACCEL_CTRL_REG6_A = const(0x25)
|
|
_REG_ACCEL_REFERENCE_A = const(0x26)
|
|
_REG_ACCEL_STATUS_REG_A = const(0x27)
|
|
_REG_ACCEL_OUT_X_L_A = const(0x28)
|
|
_REG_ACCEL_OUT_X_H_A = const(0x29)
|
|
_REG_ACCEL_OUT_Y_L_A = const(0x2A)
|
|
_REG_ACCEL_OUT_Y_H_A = const(0x2B)
|
|
_REG_ACCEL_OUT_Z_L_A = const(0x2C)
|
|
_REG_ACCEL_OUT_Z_H_A = const(0x2D)
|
|
_REG_ACCEL_FIFO_CTRL_REG_A = const(0x2E)
|
|
_REG_ACCEL_FIFO_SRC_REG_A = const(0x2F)
|
|
_REG_ACCEL_INT1_CFG_A = const(0x30)
|
|
_REG_ACCEL_INT1_SOURCE_A = const(0x31)
|
|
_REG_ACCEL_INT1_THS_A = const(0x32)
|
|
_REG_ACCEL_INT1_DURATION_A = const(0x33)
|
|
_REG_ACCEL_INT2_CFG_A = const(0x34)
|
|
_REG_ACCEL_INT2_SOURCE_A = const(0x35)
|
|
_REG_ACCEL_INT2_THS_A = const(0x36)
|
|
_REG_ACCEL_INT2_DURATION_A = const(0x37)
|
|
_REG_ACCEL_CLICK_CFG_A = const(0x38)
|
|
_REG_ACCEL_CLICK_SRC_A = const(0x39)
|
|
_REG_ACCEL_CLICK_THS_A = const(0x3A)
|
|
_REG_ACCEL_TIME_LIMIT_A = const(0x3B)
|
|
_REG_ACCEL_TIME_LATENCY_A = const(0x3C)
|
|
_REG_ACCEL_TIME_WINDOW_A = const(0x3D)
|
|
|
|
# Magnetometer registers
|
|
_REG_MAG_CRA_REG_M = const(0x00)
|
|
_REG_MAG_CRB_REG_M = const(0x01)
|
|
_REG_MAG_MR_REG_M = const(0x02)
|
|
_REG_MAG_OUT_X_H_M = const(0x03)
|
|
_REG_MAG_OUT_X_L_M = const(0x04)
|
|
_REG_MAG_OUT_Z_H_M = const(0x05)
|
|
_REG_MAG_OUT_Z_L_M = const(0x06)
|
|
_REG_MAG_OUT_Y_H_M = const(0x07)
|
|
_REG_MAG_OUT_Y_L_M = const(0x08)
|
|
_REG_MAG_SR_REG_M = const(0x09)
|
|
_REG_MAG_IRA_REG_M = const(0x0A)
|
|
_REG_MAG_IRB_REG_M = const(0x0B)
|
|
_REG_MAG_IRC_REG_M = const(0x0C)
|
|
_REG_MAG_TEMP_OUT_H_M = const(0x31)
|
|
_REG_MAG_TEMP_OUT_L_M = const(0x32)
|
|
|
|
# Magnetometer gains
|
|
MAGGAIN_1_3 = const(0x20) # +/- 1.3
|
|
MAGGAIN_1_9 = const(0x40) # +/- 1.9
|
|
MAGGAIN_2_5 = const(0x60) # +/- 2.5
|
|
MAGGAIN_4_0 = const(0x80) # +/- 4.0
|
|
MAGGAIN_4_7 = const(0xA0) # +/- 4.7
|
|
MAGGAIN_5_6 = const(0xC0) # +/- 5.6
|
|
MAGGAIN_8_1 = const(0xE0) # +/- 8.1
|
|
|
|
# Magentometer rates
|
|
MAGRATE_0_7 = const(0x00) # 0.75 Hz
|
|
MAGRATE_1_5 = const(0x01) # 1.5 Hz
|
|
MAGRATE_3_0 = const(0x62) # 3.0 Hz
|
|
MAGRATE_7_5 = const(0x03) # 7.5 Hz
|
|
MAGRATE_15 = const(0x04) # 15 Hz
|
|
MAGRATE_30 = const(0x05) # 30 Hz
|
|
MAGRATE_75 = const(0x06) # 75 Hz
|
|
MAGRATE_220 = const(0x07) # 200 Hz
|
|
|
|
# Conversion constants
|
|
_LSM303ACCEL_MG_LSB = 16704.0
|
|
_GRAVITY_STANDARD = 9.80665 # Earth's gravity in m/s^2
|
|
_GAUSS_TO_MICROTESLA = 100.0 # Gauss to micro-Tesla multiplier
|
|
# pylint: enable=bad-whitespace
|
|
|
|
|
|
class LSM303(object):
|
|
"""Driver for the LSM303 accelerometer/magnetometer."""
|
|
|
|
# Class-level buffer for reading and writing data with the sensor.
|
|
# This reduces memory allocations but means the code is not re-entrant or
|
|
# thread safe!
|
|
_BUFFER = bytearray(6)
|
|
|
|
def __init__(self, i2c):
|
|
self._accel_device = I2CDevice(i2c, _ADDRESS_ACCEL)
|
|
self._mag_device = I2CDevice(i2c, _ADDRESS_MAG)
|
|
self._write_u8(self._accel_device, _REG_ACCEL_CTRL_REG1_A, 0x27) # Enable the accelerometer
|
|
self._write_u8(self._mag_device, _REG_MAG_MR_REG_M, 0x00) # Enable the magnetometer
|
|
self._lsm303mag_gauss_lsb_xy = 1100.0
|
|
self._lsm303mag_gauss_lsb_z = 980.0
|
|
self._mag_gain = MAGGAIN_1_3
|
|
self._mag_rate = MAGRATE_0_7
|
|
|
|
@property
|
|
def raw_acceleration(self):
|
|
"""The raw accelerometer sensor values.
|
|
A 3-tuple of X, Y, Z axis values that are 16-bit signed integers.
|
|
"""
|
|
self._read_bytes(self._accel_device, _REG_ACCEL_OUT_X_L_A | 0x80, 6, self._BUFFER)
|
|
return struct.unpack_from('<hhh', self._BUFFER[0:6])
|
|
|
|
@property
|
|
def acceleration(self):
|
|
"""The processed accelerometer sensor values.
|
|
A 3-tuple of X, Y, Z axis values in meters per second squared that are signed floats.
|
|
"""
|
|
raw_accel_data = self.raw_acceleration
|
|
return tuple([n / _LSM303ACCEL_MG_LSB * _GRAVITY_STANDARD for n in raw_accel_data])
|
|
|
|
@property
|
|
def raw_magnetic(self):
|
|
"""The raw magnetometer sensor values.
|
|
A 3-tuple of X, Y, Z axis values that are 16-bit signed integers.
|
|
"""
|
|
self._read_bytes(self._mag_device, _REG_MAG_OUT_X_H_M, 6, self._BUFFER)
|
|
raw_values = struct.unpack_from('>hhh', self._BUFFER[0:6])
|
|
return tuple([n >> 4 for n in raw_values])
|
|
|
|
@property
|
|
def magnetic(self):
|
|
"""The processed magnetometer sensor values.
|
|
A 3-tuple of X, Y, Z axis values in microteslas that are signed floats.
|
|
"""
|
|
mag_x, mag_y, mag_z = self.raw_magnetic
|
|
return (mag_x / self._lsm303mag_gauss_lsb_xy * _GAUSS_TO_MICROTESLA,
|
|
mag_y / self._lsm303mag_gauss_lsb_xy * _GAUSS_TO_MICROTESLA,
|
|
mag_z / self._lsm303mag_gauss_lsb_z * _GAUSS_TO_MICROTESLA)
|
|
|
|
@property
|
|
def mag_gain(self):
|
|
"""The magnetometer's gain."""
|
|
return self._mag_gain
|
|
|
|
@mag_gain.setter
|
|
def mag_gain(self, value):
|
|
assert value in (MAGGAIN_1_3, MAGGAIN_1_9, MAGGAIN_2_5, MAGGAIN_4_0, MAGGAIN_4_7,
|
|
MAGGAIN_5_6, MAGGAIN_8_1)
|
|
|
|
self._mag_gain = value
|
|
self._write_u8(self._mag_device, _REG_MAG_CRB_REG_M, self._mag_gain)
|
|
if self._mag_gain == MAGGAIN_1_3:
|
|
self._lsm303mag_gauss_lsb_xy = 1100.0
|
|
self._lsm303mag_gauss_lsb_z = 980.0
|
|
elif self._mag_gain == MAGGAIN_1_9:
|
|
self._lsm303mag_gauss_lsb_xy = 855.0
|
|
self._lsm303mag_gauss_lsb_z = 760.0
|
|
elif self._mag_gain == MAGGAIN_2_5:
|
|
self._lsm303mag_gauss_lsb_xy = 670.0
|
|
self._lsm303mag_gauss_lsb_z = 600.0
|
|
elif self._mag_gain == MAGGAIN_4_0:
|
|
self._lsm303mag_gauss_lsb_xy = 450.0
|
|
self._lsm303mag_gauss_lsb_z = 400.0
|
|
elif self._mag_gain == MAGGAIN_4_7:
|
|
self._lsm303mag_gauss_lsb_xy = 400.0
|
|
self._lsm303mag_gauss_lsb_z = 355.0
|
|
elif self._mag_gain == MAGGAIN_5_6:
|
|
self._lsm303mag_gauss_lsb_xy = 330.0
|
|
self._lsm303mag_gauss_lsb_z = 295.0
|
|
elif self._mag_gain == MAGGAIN_8_1:
|
|
self._lsm303mag_gauss_lsb_xy = 230.0
|
|
self._lsm303mag_gauss_lsb_z = 205.0
|
|
|
|
@property
|
|
def mag_rate(self):
|
|
"""The magnetometer update rate."""
|
|
return self._mag_rate
|
|
|
|
@mag_rate.setter
|
|
def mag_rate(self, value):
|
|
assert value in (MAGRATE_0_7, MAGRATE_1_5, MAGRATE_3_0, MAGRATE_7_5, MAGRATE_15, MAGRATE_30,
|
|
MAGRATE_75, MAGRATE_220)
|
|
|
|
self._mag_rate = value
|
|
reg_m = ((value & 0x07) << 2) & 0xFF
|
|
self._write_u8(self._mag_device, _REG_MAG_CRA_REG_M, reg_m)
|
|
|
|
def _read_u8(self, device, address):
|
|
with device as i2c:
|
|
self._BUFFER[0] = address & 0xFF
|
|
i2c.write(self._BUFFER, end=1, stop=False)
|
|
i2c.readinto(self._BUFFER, end=1)
|
|
return self._BUFFER[0]
|
|
|
|
def _write_u8(self, device, address, val):
|
|
with device as i2c:
|
|
self._BUFFER[0] = address & 0xFF
|
|
self._BUFFER[1] = val & 0xFF
|
|
i2c.write(self._BUFFER, end=2)
|
|
|
|
@staticmethod
|
|
def _read_bytes(device, address, count, buf):
|
|
with device as i2c:
|
|
buf[0] = address & 0xFF
|
|
i2c.write(buf, end=1, stop=False)
|
|
i2c.readinto(buf, end=count)
|