Source code for micropython_icg20660.icg20660

# SPDX-FileCopyrightText: Copyright (c) 2023 Jose D. Montoya
#
# SPDX-License-Identifier: MIT
"""
`icg20660`
================================================================================

MicroPython Driver for the TDK ICG20660 Accelerometer/Gyro sensor


* Author(s): Jose D. Montoya


"""

import time
from micropython import const
from micropython_icg20660.i2c_helpers import CBits, RegisterStruct

try:
    from typing import Tuple
except ImportError:
    pass


__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/jposada202020/MicroPython_ICG20660.git"

_DEVICE_ID = const(0x75)
_GYRO_CONFIG = const(0x1B)
_CONFIG = const(0x1A)
_SMPLRT_DIV = const(0x19)
_ACCEL_CONFIG = const(0x1C)
_ACCEL_XOUT_H = const(0x3B)  # first byte of accel data
_GYRO_XOUT_H = const(0x43)  # first byte of accel data
_PWR_MGMT_1 = const(0x6B)

GYRO_DLPF_DISABLED = const(0b10)
GYRO_DLPF_ENABLED = const(0b00)
gyro_dlpf_mode_values = (GYRO_DLPF_DISABLED, GYRO_DLPF_ENABLED)

DLPF_CFG_0 = const(0b000)
DLPF_CFG_1 = const(0b001)
DLPF_CFG_2 = const(0b010)
DLPF_CFG_7 = const(0b111)
gyro_dlpf_configuration_values = (DLPF_CFG_0, DLPF_CFG_1, DLPF_CFG_2, DLPF_CFG_7)

FS_125_DPS = const(0b00)
FS_250_DPS = const(0b01)
FS_500_DPS = const(0b10)
gyro_full_scale_values = (FS_125_DPS, FS_250_DPS, FS_500_DPS)
gyro_full_scale_sensitivity = (262, 131, 65.5)

rate_values = {
    500.0: 1,
    250.0: 3,
    200.0: 4,
    125.0: 7,
    100.0: 9,
    62.5: 15,
    50.0: 19,
    31.3: 31,
    15.6: 63,
    10.0: 99,
    7.8: 127,
    3.9: 255,
}
data_rate_values = (
    500.0,
    250.0,
    200.0,
    125.0,
    100.0,
    62.5,
    50.0,
    31.3,
    15.6,
    10.0,
    7.8,
    3.9,
)
rate_divisor_values = (1, 3, 4, 7, 9, 15, 19, 31, 63, 99, 127, 255)

RANGE_2G = const(0b00)
RANGE_4G = const(0b01)
RANGE_8G = const(0b10)
RANGE_16G = const(0b11)
acceleration_range_values = (RANGE_2G, RANGE_4G, RANGE_8G, RANGE_16G)
acc_range_sensitivity = (16384, 8192, 4096, 2048)


[docs] class ICG20660: """Driver for the ICG20660 Sensor connected over I2C. :param ~machine.I2C i2c: The I2C bus the ICG20660 is connected to. :param int address: The I2C device address. Defaults to :const:`0x69` :raises RuntimeError: if the sensor is not found **Quickstart: Importing and using the device** Here is an example of using the :class:`ICG20660` class. First you will need to import the libraries to use the sensor .. code-block:: python from machine import Pin, I2C from micropython_icg20660 import icg20660 Once this is done you can define your `machine.I2C` object and define your sensor object .. code-block:: python i2c = I2C(1, sda=Pin(2), scl=Pin(3)) icg20660 = icg20660.ICG20660(i2c) Now you have access to the attributes .. code-block:: python accx, accy, accz = icg20660.acceleration """ _device_id = RegisterStruct(_DEVICE_ID, "B") _rate_divisor = RegisterStruct(_SMPLRT_DIV, "B") _sleep = CBits(1, _PWR_MGMT_1, 6) _gyro_full_scale = CBits(2, _CONFIG, 3) _gyro_dlpf_configuration = CBits(3, _CONFIG, 0) _gyro_dlpf_mode = CBits(2, _GYRO_CONFIG, 0) _acceleration_range = CBits(2, _ACCEL_CONFIG, 3) _raw_accel_data = RegisterStruct(_ACCEL_XOUT_H, ">hhh") _raw_gyro_data = RegisterStruct(_GYRO_XOUT_H, ">hhh") def __init__(self, i2c, address: int = 0x69) -> None: self._i2c = i2c self._address = address if self._device_id != 0x91: raise RuntimeError("Failed to find ICG20660") self._sleep = 0 self.gyro_full_scale = FS_125_DPS self.acceleration_range = RANGE_2G @property def gyro_dlpf_mode(self) -> str: """ Sensor gyro_dlpf_mode. Enables gyro DLPF +-----------------------------------------+------------------+ | Mode | Value | +=========================================+==================+ | :py:const:`icg20660.GYRO_DLPF_DISABLED` | :py:const:`0b10` | +-----------------------------------------+------------------+ | :py:const:`icg20660.GYRO_DLPF_ENABLED` | :py:const:`0b00` | +-----------------------------------------+------------------+ """ values = ("GYRO_DLPF_DISABLED", "N/A", "GYRO_DLPF_ENABLED") return values[self._gyro_dlpf_mode] @gyro_dlpf_mode.setter def gyro_dlpf_mode(self, value: int) -> None: if value not in gyro_dlpf_mode_values: raise ValueError("Value must be a valid gyro_dlpf_mode setting") self._gyro_dlpf_mode = value @property def gyro_dlpf_configuration(self) -> str: """ Sensor gyro_dlpf_configuration. For this to have an effect, :attr:`gyro_dlpf_mode` must be enabled. The gyroscope and temperature sensor will be filtered. For more details please refer to the datasheet. +---------------------------------+-------------------+ | Mode | Value | +=================================+===================+ | :py:const:`icg20660.DLPF_CFG_0` | :py:const:`0b000` | +---------------------------------+-------------------+ | :py:const:`icg20660.DLPF_CFG_1` | :py:const:`0b001` | +---------------------------------+-------------------+ | :py:const:`icg20660.DLPF_CFG_2` | :py:const:`0b010` | +---------------------------------+-------------------+ | :py:const:`icg20660.DLPF_CFG_7` | :py:const:`0b111` | +---------------------------------+-------------------+ """ values = ("DLPF_CFG_0", "DLPF_CFG_1", "DLPF_CFG_2", "DLPF_CFG_7") return values[self._gyro_dlpf_configuration] @gyro_dlpf_configuration.setter def gyro_dlpf_configuration(self, value: int) -> None: if value not in gyro_dlpf_configuration_values: raise ValueError("Value must be a valid dlpf_configuration setting") self._gyro_dlpf_configuration = value @property def gyro_full_scale(self) -> str: """ Sensor gyro_full_scale +---------------------------------+------------------+ | Mode | Value | +=================================+==================+ | :py:const:`icg20660.FS_125_DPS` | :py:const:`0b00` | +---------------------------------+------------------+ | :py:const:`icg20660.FS_250_DPS` | :py:const:`0b01` | +---------------------------------+------------------+ | :py:const:`icg20660.FS_500_DPS` | :py:const:`0b10` | +---------------------------------+------------------+ """ values = ("FS_125_DPS", "FS_250_DPS", "FS_500_DPS") return values[self._gyro_full_scale] @gyro_full_scale.setter def gyro_full_scale(self, value: int) -> None: if value not in gyro_full_scale_values: raise ValueError("Value must be a valid gyro_full_scale setting") self._gyro_full_scale = value self._memory_gyro_fs = value @property def data_rate(self): """The rate at which sensor measurements are taken in Hz""" return list(rate_values.keys())[ list(rate_values.values()).index(self.data_rate_divisor) ] @data_rate.setter def data_rate(self, value): """ .. note:: The data rates are set indirectly by setting a rate divisor according to the following formula: .. math:: \\text{data_rate } = \\frac{1000}{1 + divisor} Accepted values are: | * 500.0 | * 250.0 | * 200.0 | * 125.0 | * 100.0 | * 62.5 | * 50.0 | * 31.3 | * 15.6 | * 10.0 | * 7.8 | * 3.9 """ if value not in data_rate_values: raise ValueError("Data rate must be a valid setting") self.data_rate_divisor = rate_values[value] @property def data_rate_divisor(self): """ Accepted values are: | * 1 | * 3 | * 4 | * 7 | * 9 | * 15 | * 19 | * 31 | * 63 | * 99 | * 127 | * 255 """ raw_rate_divisor = self._rate_divisor return raw_rate_divisor @data_rate_divisor.setter def data_rate_divisor(self, value): if value not in rate_divisor_values: raise ValueError("Value must be a valid data rate divisor setting") self._rate_divisor = value @property def acceleration_range(self) -> str: """ Sensor acceleration_range +--------------------------------+------------------+ | Mode | Value | +================================+==================+ | :py:const:`icg20660.RANGE_2G` | :py:const:`0b00` | +--------------------------------+------------------+ | :py:const:`icg20660.RANGE_4G` | :py:const:`0b01` | +--------------------------------+------------------+ | :py:const:`icg20660.RANGE_8G` | :py:const:`0b10` | +--------------------------------+------------------+ | :py:const:`icg20660.RANGE_16G` | :py:const:`0b11` | +--------------------------------+------------------+ """ values = ("RANGE_2G", "RANGE_4G", "RANGE_8G", "RANGE_16G") return values[self._acceleration_range] @acceleration_range.setter def acceleration_range(self, value: int) -> None: if value not in acceleration_range_values: raise ValueError("Value must be a valid acceleration_range setting") self._acceleration_range = value self._memory_accel_range = value @property def acceleration(self) -> Tuple[float, float, float]: """ Acceleration Property. The x, y, z acceleration values returned in a 3-tuple and are in :math:`m / s ^ 2.` :return: Acceleration Values """ raw_measurement = self._raw_accel_data time.sleep(0.005) x = ( raw_measurement[0] / acc_range_sensitivity[self._memory_accel_range] * 9.80665 ) y = ( raw_measurement[1] / acc_range_sensitivity[self._memory_accel_range] * 9.80665 ) z = ( raw_measurement[2] / acc_range_sensitivity[self._memory_accel_range] * 9.80665 ) return x, y, z @property def gyro(self): """ Gyro Property. The x, y, z angular velocity values returned in a 3-tuple and are in :math:`degrees / second` :return: Angular velocity Values """ raw_measurement = self._raw_gyro_data time.sleep(0.005) x = ( raw_measurement[0] / gyro_full_scale_sensitivity[self._memory_gyro_fs] * 0.017453293 ) y = ( raw_measurement[1] / gyro_full_scale_sensitivity[self._memory_gyro_fs] * 0.017453293 ) z = ( raw_measurement[2] / gyro_full_scale_sensitivity[self._memory_gyro_fs] * 0.017453293 ) return x, y, z