From 7b3c324bebe9dd941d821ad4e8c627c1371e971d Mon Sep 17 00:00:00 2001 From: Urs Utzinger Date: Thu, 10 Aug 2023 06:23:16 -0700 Subject: [PATCH 1/5] dataready function, modify gravity, improved speed, fixed data_rate setter Providing data ready function Allow to set gravity constant Removed sleep statement when reading accelerometer, gyroscope and magnetometer Repaired accelerometer_data_rate and gyroscope_data_rate function Updated examples that use data_rate setter Created example measuring data_rate --- adafruit_icm20x.py | 40 +++++++++++++++++----- docs/examples.rst | 9 +++++ examples/icm20x_icm20649_data_rate_test.py | 33 ++++++++++++++++++ examples/icm20x_icm20649_full_test.py | 4 +-- 4 files changed, 75 insertions(+), 11 deletions(-) create mode 100644 examples/icm20x_icm20649_data_rate_test.py diff --git a/adafruit_icm20x.py b/adafruit_icm20x.py index 61bdb8f..9d58de6 100644 --- a/adafruit_icm20x.py +++ b/adafruit_icm20x.py @@ -60,6 +60,11 @@ _ICM20X_REG_INT_ENABLE_0 = 0x10 # Interrupt enable register 0 _ICM20X_REG_INT_ENABLE_1 = 0x11 # Interrupt enable register 1 +_ICM20X_REG_INT_STATUS_0 = 0x19 # Interrupt status register 0 Wake on motion, DMP int, i2c int +_ICM20X_REG_INT_STATUS_1 = 0x1A # Interrupt status register 1 data register from all sensors +_ICM20X_REG_INT_STATUS_2 = 0x1B # Interrupt status register 2 FIFO overflow +_ICM20X_REG_INT_STATUS_3 = 0x1C # Interrupt status register 3 Watermark interrupt + # Bank 2 _ICM20X_GYRO_SMPLRT_DIV = 0x00 _ICM20X_GYRO_CONFIG_1 = 0x01 @@ -159,6 +164,8 @@ class ICM20X: # pylint:disable=too-many-instance-attributes _lp_config_reg = UnaryStruct(_ICM20X_LP_CONFIG, ">B") + _data_ready = ROBit(_ICM20X_REG_INT_STATUS_1, 0) + _i2c_master_cycle_en = RWBit(_ICM20X_LP_CONFIG, 6) _accel_cycle_en = RWBit(_ICM20X_LP_CONFIG, 5) _gyro_cycle_en = RWBit(_ICM20X_LP_CONFIG, 4) @@ -236,6 +243,8 @@ def initialize(self): self.accelerometer_data_rate_divisor = 20 # ~53.57Hz self.gyro_data_rate_divisor = 10 # ~100Hz + + self._gravity = G_TO_ACCEL def reset(self): """Resets the internal registers and restores the default settings""" @@ -247,6 +256,11 @@ def reset(self): while self._reset: sleep(0.005) + def dataReady(self): + """Checks if new data is available""" + self._bank = 0 + return self._data_ready + @property def _sleep(self): self._bank = 0 @@ -266,7 +280,7 @@ def acceleration(self): """The x, y, z acceleration values returned in a 3-tuple and are in :math:`m / s ^ 2.`""" self._bank = 0 raw_accel_data = self._raw_accel_data - sleep(0.005) + # sleep(0.005) x = self._scale_xl_data(raw_accel_data[0]) y = self._scale_xl_data(raw_accel_data[1]) @@ -287,8 +301,8 @@ def gyro(self): return (x, y, z) def _scale_xl_data(self, raw_measurement): - sleep(0.005) - return raw_measurement / AccelRange.lsb[self._cached_accel_range] * G_TO_ACCEL + # sleep(0.005) + return raw_measurement / AccelRange.lsb[self._cached_accel_range] * self._gravity def _scale_gyro_data(self, raw_measurement): return ( @@ -424,7 +438,8 @@ def accelerometer_data_rate(self, value): raise AttributeError( "Accelerometer data rate must be between 0.27 and 1125.0" ) - self.accelerometer_data_rate_divisor = value + divisor = round(((1125.0 - value) / value)) + self.accelerometer_data_rate_divisor = divisor @property def gyro_data_rate(self): @@ -447,8 +462,7 @@ def gyro_data_rate(self): def gyro_data_rate(self, value): if value < self._gyro_rate_calc(4095) or value > self._gyro_rate_calc(0): raise AttributeError("Gyro data rate must be between 4.30 and 1100.0") - - divisor = round(((1125.0 - value) / value)) + divisor = round(((1100.0 - value) / value)) self.gyro_data_rate_divisor = divisor @property @@ -513,7 +527,15 @@ def _low_power(self, enabled): self._bank = 0 self._low_power_en = enabled - + @property + def gravity(self): + """The gravity magnitude in m/s^2.""" + return self._gravity + + @property.setter + def gravity(self, value): + self._gravity = value + class ICM20649(ICM20X): """Library for the ST ICM-20649 Wide-Range 6-DoF Accelerometer and Gyro. @@ -742,11 +764,11 @@ def _mag_id(self): @property def magnetic(self): - """The current magnetic field strengths onthe X, Y, and Z axes in uT (micro-teslas)""" + """The current magnetic field strengths on the X, Y, and Z axes in uT (micro-teslas)""" self._bank = 0 full_data = self._raw_mag_data - sleep(0.005) + # sleep(0.005) x = full_data[0] * _ICM20X_UT_PER_LSB y = full_data[1] * _ICM20X_UT_PER_LSB diff --git a/docs/examples.rst b/docs/examples.rst index 6bf07fe..4fc0531 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -16,6 +16,15 @@ Test using all the ICM20649 sensor capabilities :caption: examples/examples/icm20x_icm20649_full_test.py :linenos: +ICM20649 data rate test +------------------ + +Test using all the ICM20649 sensor capabilities + +.. literalinclude:: ../examples/icm20x_icm20649_data_rate_test.py + :caption: examples/examples/icm20x_icm20649_data_rate_test.py + :linenos: + ICM20948 Simple test -------------------- diff --git a/examples/icm20x_icm20649_data_rate_test.py b/examples/icm20x_icm20649_data_rate_test.py new file mode 100644 index 0000000..ac96762 --- /dev/null +++ b/examples/icm20x_icm20649_data_rate_test.py @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import time +import board +from copy import copy +from adafruit_icm20x import ICM20649, AccelRange, GyroRange + +i2c = board.I2C() # uses board.SCL and board.SDA + +ism = ICM20649(i2c, address=0x69) + +ism.accelerometer_range = AccelRange.RANGE_4G +print("Accelerometer range set to: %d g" % AccelRange.string[ism.accelerometer_range]) + +ism.gyro_range = GyroRange.RANGE_500_DPS +print("Gyro range set to: %d DPS" % GyroRange.string[ism.gyro_range]) + +ism.gyro_data_rate = 1100 # 1100 max +ism.accelerometer_data_rate = 1125 # 1125 max +print('Gyro rate: {:f}'.format(ism.gyro_data_rate)) +print('Accel rate: {:f}'.format(ism.accelerometer_data_rate)) + +previousTime = time.perf_counter() +while True: + if ism.dataReady: + currentTime = time.perf_counter() + print('\033[2J') + print( + "Accel X:%5.2f Y:%5.2f Z:%5.2f ms^2 Gyro X:%8.3f Y:%8.3f Z:%8.3f degrees/s Sample Rate: %8.1f Hz" + % (ism.acceleration + ism.gyro + (1 / (currentTime - previousTime))) + ) + previousTime = copy(currentTime) diff --git a/examples/icm20x_icm20649_full_test.py b/examples/icm20x_icm20649_full_test.py index 60f4392..f85c14f 100644 --- a/examples/icm20x_icm20649_full_test.py +++ b/examples/icm20x_icm20649_full_test.py @@ -28,8 +28,8 @@ def printNewMax(value, current_max, axis): ax_max = ay_max = az_max = 0 gx_max = gy_max = gz_max = 0 -ism.gyro_data_rate = 125 -ism.accelerometer_data_rate = 4095 +ism.gyro_data_rate = 1100 +ism.accelerometer_data_rate = 1125 st = time.monotonic() while time.monotonic() - st < 0.250: print( From c97dade29ad145036400bc6d7d873ce3c3a628f9 Mon Sep 17 00:00:00 2001 From: Urs Utzinger Date: Thu, 10 Aug 2023 07:45:30 -0700 Subject: [PATCH 2/5] Small fix Fixed gravity setter Added i2c address to 20649 examples --- adafruit_icm20x.py | 6 +++--- examples/icm20x_icm20649_data_rate_test.py | 8 +++++--- examples/icm20x_icm20649_full_test.py | 2 +- examples/icm20x_icm20649_simpletest.py | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/adafruit_icm20x.py b/adafruit_icm20x.py index 9d58de6..cb3e9c2 100644 --- a/adafruit_icm20x.py +++ b/adafruit_icm20x.py @@ -531,11 +531,11 @@ def _low_power(self, enabled): def gravity(self): """The gravity magnitude in m/s^2.""" return self._gravity - - @property.setter + + @gravity.setter def gravity(self, value): self._gravity = value - + class ICM20649(ICM20X): """Library for the ST ICM-20649 Wide-Range 6-DoF Accelerometer and Gyro. diff --git a/examples/icm20x_icm20649_data_rate_test.py b/examples/icm20x_icm20649_data_rate_test.py index ac96762..cc8c841 100644 --- a/examples/icm20x_icm20649_data_rate_test.py +++ b/examples/icm20x_icm20649_data_rate_test.py @@ -3,7 +3,6 @@ import time import board -from copy import copy from adafruit_icm20x import ICM20649, AccelRange, GyroRange i2c = board.I2C() # uses board.SCL and board.SDA @@ -18,9 +17,12 @@ ism.gyro_data_rate = 1100 # 1100 max ism.accelerometer_data_rate = 1125 # 1125 max + print('Gyro rate: {:f}'.format(ism.gyro_data_rate)) print('Accel rate: {:f}'.format(ism.accelerometer_data_rate)) +ism.gravity = 9.8 + previousTime = time.perf_counter() while True: if ism.dataReady: @@ -28,6 +30,6 @@ print('\033[2J') print( "Accel X:%5.2f Y:%5.2f Z:%5.2f ms^2 Gyro X:%8.3f Y:%8.3f Z:%8.3f degrees/s Sample Rate: %8.1f Hz" - % (ism.acceleration + ism.gyro + (1 / (currentTime - previousTime))) + % (*ism.acceleration, *ism.gyro, (1 / (currentTime - previousTime))) ) - previousTime = copy(currentTime) + previousTime = currentTime diff --git a/examples/icm20x_icm20649_full_test.py b/examples/icm20x_icm20649_full_test.py index f85c14f..3a0ef1f 100644 --- a/examples/icm20x_icm20649_full_test.py +++ b/examples/icm20x_icm20649_full_test.py @@ -17,7 +17,7 @@ def printNewMax(value, current_max, axis): i2c = board.I2C() # uses board.SCL and board.SDA # i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller -ism = ICM20649(i2c) +ism = ICM20649(i2c, address=0x69) ism.accelerometer_range = AccelRange.RANGE_30G print("Accelerometer range set to: %d g" % AccelRange.string[ism.accelerometer_range]) diff --git a/examples/icm20x_icm20649_simpletest.py b/examples/icm20x_icm20649_simpletest.py index b3582ad..f6a259a 100644 --- a/examples/icm20x_icm20649_simpletest.py +++ b/examples/icm20x_icm20649_simpletest.py @@ -7,7 +7,7 @@ i2c = board.I2C() # uses board.SCL and board.SDA # i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller -icm = adafruit_icm20x.ICM20649(i2c) +icm = adafruit_icm20x.ICM20649(i2c, address=0x69) while True: print("Acceleration: X:%.2f, Y: %.2f, Z: %.2f m/s^2" % (icm.acceleration)) From 7bfce212a76917f20885707a191d9ccdfc121ff9 Mon Sep 17 00:00:00 2001 From: Urs Utzinger Date: Sun, 17 Sep 2023 15:43:15 -0700 Subject: [PATCH 3/5] Create adafruit_icm20x.py --- build/lib/adafruit_icm20x.py | 852 +++++++++++++++++++++++++++++++++++ 1 file changed, 852 insertions(+) create mode 100644 build/lib/adafruit_icm20x.py diff --git a/build/lib/adafruit_icm20x.py b/build/lib/adafruit_icm20x.py new file mode 100644 index 0000000..cb3e9c2 --- /dev/null +++ b/build/lib/adafruit_icm20x.py @@ -0,0 +1,852 @@ +# SPDX-FileCopyrightText: 2020 Bryan Siepert for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +`adafruit_icm20x` +================================================================================ + +Library for the ST ICM20X Motion Sensor Family + +* Author(s): Bryan Siepert + +Implementation Notes +-------------------- + +**Hardware:** + +* Adafruit's ICM20649 Breakout: https://adafruit.com/product/4464 +* Adafruit's ICM20948 Breakout: https://adafruit.com/product/4554 + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://circuitpython.org/downloads + + +* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice +* Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register +""" + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ICM20X.git" +# Common imports; remove if unused or pylint will complain +from time import sleep +from adafruit_bus_device import i2c_device + +from adafruit_register.i2c_struct import UnaryStruct, ROUnaryStruct, Struct +from adafruit_register.i2c_bit import RWBit, ROBit +from adafruit_register.i2c_bits import RWBits + +_ICM20649_DEFAULT_ADDRESS = 0x68 # icm20649 default i2c address +_ICM20948_DEFAULT_ADDRESS = 0x69 # icm20649 default i2c address +_ICM20649_DEVICE_ID = 0xE1 # Correct content of WHO_AM_I register +_ICM20948_DEVICE_ID = 0xEA # Correct content of WHO_AM_I register + +# Functions using these bank-specific registers are responsible for ensuring +# that the correct bank is set +# Bank 0 +_ICM20X_WHO_AM_I = 0x00 # device_id register +_ICM20X_REG_BANK_SEL = 0x7F # register bank selection register +_ICM20X_PWR_MGMT_1 = 0x06 # primary power management register +_ICM20X_ACCEL_XOUT_H = 0x2D # first byte of accel data +_ICM20X_GYRO_XOUT_H = 0x33 # first byte of accel data +_ICM20X_I2C_MST_STATUS = 0x17 # I2C Microcontroller Status bits +_ICM20948_EXT_SLV_SENS_DATA_00 = 0x3B + +_ICM20X_USER_CTRL = 0x03 # User Control Reg. Includes I2C Microcontroller +_ICM20X_LP_CONFIG = 0x05 # Low Power config +_ICM20X_REG_INT_PIN_CFG = 0xF # Interrupt config register +_ICM20X_REG_INT_ENABLE_0 = 0x10 # Interrupt enable register 0 +_ICM20X_REG_INT_ENABLE_1 = 0x11 # Interrupt enable register 1 + +_ICM20X_REG_INT_STATUS_0 = 0x19 # Interrupt status register 0 Wake on motion, DMP int, i2c int +_ICM20X_REG_INT_STATUS_1 = 0x1A # Interrupt status register 1 data register from all sensors +_ICM20X_REG_INT_STATUS_2 = 0x1B # Interrupt status register 2 FIFO overflow +_ICM20X_REG_INT_STATUS_3 = 0x1C # Interrupt status register 3 Watermark interrupt + +# Bank 2 +_ICM20X_GYRO_SMPLRT_DIV = 0x00 +_ICM20X_GYRO_CONFIG_1 = 0x01 +_ICM20X_ACCEL_SMPLRT_DIV_1 = 0x10 +_ICM20X_ACCEL_SMPLRT_DIV_2 = 0x11 +_ICM20X_ACCEL_CONFIG_1 = 0x14 + + +# Bank 3 + +_ICM20X_I2C_MST_ODR_CONFIG = 0x0 # Sets ODR for I2C microcontroller bus +_ICM20X_I2C_MST_CTRL = 0x1 # I2C microcontroller bus config +_ICM20X_I2C_MST_DELAY_CTRL = 0x2 # I2C microcontroller bus config +_ICM20X_I2C_SLV0_ADDR = 0x3 # Sets I2C address for I2C microcontroller bus sensor 0 +_ICM20X_I2C_SLV0_REG = 0x4 # Sets register address for I2C microcontroller bus sensor 0 +_ICM20X_I2C_SLV0_CTRL = 0x5 # Controls for I2C microcontroller bus sensor 0 +_ICM20X_I2C_SLV0_DO = 0x6 # Sets I2C microcontroller bus sensor 0 data out + +_ICM20X_I2C_SLV4_ADDR = 0x13 # Sets I2C address for I2C microcontroller bus sensor 4 +_ICM20X_I2C_SLV4_REG = ( + 0x14 # Sets register address for I2C microcontroller bus sensor 4 +) +_ICM20X_I2C_SLV4_CTRL = 0x15 # Controls for I2C microcontroller bus sensor 4 +_ICM20X_I2C_SLV4_DO = 0x16 # Sets I2C microcontroller bus sensor 4 data out +_ICM20X_I2C_SLV4_DI = 0x17 # Sets I2C microcontroller bus sensor 4 data in + +_ICM20X_UT_PER_LSB = 0.15 # mag data LSB value (fixed) +_ICM20X_RAD_PER_DEG = 0.017453293 # Degrees/s to rad/s multiplier + +G_TO_ACCEL = 9.80665 + + +class CV: + """struct helper""" + + @classmethod + def add_values(cls, value_tuples): + """Add CV values to the class""" + cls.string = {} + cls.lsb = {} + + for value_tuple in value_tuples: + name, value, string, lsb = value_tuple + setattr(cls, name, value) + cls.string[value] = string + cls.lsb[value] = lsb + + @classmethod + def is_valid(cls, value): + """Validate that a given value is a member""" + return value in cls.string + + +class AccelRange(CV): + """Options for :attr:`ICM20X.accelerometer_range`""" + + pass # pylint: disable=unnecessary-pass + + +class GyroRange(CV): + """Options for :attr:`ICM20X.gyro_data_range`""" + + pass # pylint: disable=unnecessary-pass + + +class GyroDLPFFreq(CV): + """Options for :attr:`ICM20X.gyro_dlpf_cutoff`""" + + pass # pylint: disable=unnecessary-pass + + +class AccelDLPFFreq(CV): + """Options for :attr:`ICM20X.accel_dlpf_cutoff`""" + + pass # pylint: disable=unnecessary-pass + + +class ICM20X: # pylint:disable=too-many-instance-attributes + """Library for the ST ICM-20X Wide-Range 6-DoF Accelerometer and Gyro Family + + + :param ~busio.I2C i2c_bus: The I2C bus the ICM20X is connected to. + :param int address: The I2C address of the device. + + """ + + # Bank 0 + _device_id = ROUnaryStruct(_ICM20X_WHO_AM_I, ">B") + _bank_reg = UnaryStruct(_ICM20X_REG_BANK_SEL, ">B") + _reset = RWBit(_ICM20X_PWR_MGMT_1, 7) + _sleep_reg = RWBit(_ICM20X_PWR_MGMT_1, 6) + _low_power_en = RWBit(_ICM20X_PWR_MGMT_1, 5) + _clock_source = RWBits(3, _ICM20X_PWR_MGMT_1, 0) + + _raw_accel_data = Struct(_ICM20X_ACCEL_XOUT_H, ">hhh") # ds says LE :| + _raw_gyro_data = Struct(_ICM20X_GYRO_XOUT_H, ">hhh") + + _lp_config_reg = UnaryStruct(_ICM20X_LP_CONFIG, ">B") + + _data_ready = ROBit(_ICM20X_REG_INT_STATUS_1, 0) + + _i2c_master_cycle_en = RWBit(_ICM20X_LP_CONFIG, 6) + _accel_cycle_en = RWBit(_ICM20X_LP_CONFIG, 5) + _gyro_cycle_en = RWBit(_ICM20X_LP_CONFIG, 4) + + # Bank 2 + _gyro_dlpf_enable = RWBits(1, _ICM20X_GYRO_CONFIG_1, 0) + _gyro_range = RWBits(2, _ICM20X_GYRO_CONFIG_1, 1) + _gyro_dlpf_config = RWBits(3, _ICM20X_GYRO_CONFIG_1, 3) + + _accel_dlpf_enable = RWBits(1, _ICM20X_ACCEL_CONFIG_1, 0) + _accel_range = RWBits(2, _ICM20X_ACCEL_CONFIG_1, 1) + _accel_dlpf_config = RWBits(3, _ICM20X_ACCEL_CONFIG_1, 3) + + # this value is a 12-bit register spread across two bytes, big-endian first + _accel_rate_divisor = UnaryStruct(_ICM20X_ACCEL_SMPLRT_DIV_1, ">H") + _gyro_rate_divisor = UnaryStruct(_ICM20X_GYRO_SMPLRT_DIV, ">B") + AccelDLPFFreq.add_values( + ( + ( + "DISABLED", + -1, + "Disabled", + None, + ), # magical value that we will use do disable + ("FREQ_246_0HZ_3DB", 1, 246.0, None), + ("FREQ_111_4HZ_3DB", 2, 111.4, None), + ("FREQ_50_4HZ_3DB", 3, 50.4, None), + ("FREQ_23_9HZ_3DB", 4, 23.9, None), + ("FREQ_11_5HZ_3DB", 5, 11.5, None), + ("FREQ_5_7HZ_3DB", 6, 5.7, None), + ("FREQ_473HZ_3DB", 7, 473, None), + ) + ) + GyroDLPFFreq.add_values( + ( + ( + "DISABLED", + -1, + "Disabled", + None, + ), # magical value that we will use do disable + ("FREQ_196_6HZ_3DB", 0, 196.6, None), + ("FREQ_151_8HZ_3DB", 1, 151.8, None), + ("FREQ_119_5HZ_3DB", 2, 119.5, None), + ("FREQ_51_2HZ_3DB", 3, 51.2, None), + ("FREQ_23_9HZ_3DB", 4, 23.9, None), + ("FREQ_11_6HZ_3DB", 5, 11.6, None), + ("FREQ_5_7HZ_3DB", 6, 5.7, None), + ("FREQ_361_4HZ_3DB", 7, 361.4, None), + ) + ) + + @property + def _bank(self): + return self._bank_reg >> 4 + + @_bank.setter + def _bank(self, value): + self._bank_reg = value << 4 + + def __init__(self, i2c_bus, address): + self.i2c_device = i2c_device.I2CDevice(i2c_bus, address) + self._bank = 0 + if not self._device_id in [_ICM20649_DEVICE_ID, _ICM20948_DEVICE_ID]: + raise RuntimeError("Failed to find an ICM20X sensor - check your wiring!") + self.reset() + self.initialize() + + def initialize(self): + """Configure the sensors with the default settings. For use after calling :meth:`reset`""" + + self._sleep = False + self.accelerometer_range = AccelRange.RANGE_8G # pylint: disable=no-member + self.gyro_range = GyroRange.RANGE_500_DPS # pylint: disable=no-member + + self.accelerometer_data_rate_divisor = 20 # ~53.57Hz + self.gyro_data_rate_divisor = 10 # ~100Hz + + self._gravity = G_TO_ACCEL + + def reset(self): + """Resets the internal registers and restores the default settings""" + self._bank = 0 + + sleep(0.005) + self._reset = True + sleep(0.005) + while self._reset: + sleep(0.005) + + def dataReady(self): + """Checks if new data is available""" + self._bank = 0 + return self._data_ready + + @property + def _sleep(self): + self._bank = 0 + sleep(0.005) + self._sleep_reg = False + sleep(0.005) + + @_sleep.setter + def _sleep(self, sleep_enabled): + self._bank = 0 + sleep(0.005) + self._sleep_reg = sleep_enabled + sleep(0.005) + + @property + def acceleration(self): + """The x, y, z acceleration values returned in a 3-tuple and are in :math:`m / s ^ 2.`""" + self._bank = 0 + raw_accel_data = self._raw_accel_data + # sleep(0.005) + + x = self._scale_xl_data(raw_accel_data[0]) + y = self._scale_xl_data(raw_accel_data[1]) + z = self._scale_xl_data(raw_accel_data[2]) + + return (x, y, z) + + @property + def gyro(self): + """The x, y, z angular velocity values returned in a 3-tuple and + are in :math:`degrees / second`""" + self._bank = 0 + raw_gyro_data = self._raw_gyro_data + x = self._scale_gyro_data(raw_gyro_data[0]) + y = self._scale_gyro_data(raw_gyro_data[1]) + z = self._scale_gyro_data(raw_gyro_data[2]) + + return (x, y, z) + + def _scale_xl_data(self, raw_measurement): + # sleep(0.005) + return raw_measurement / AccelRange.lsb[self._cached_accel_range] * self._gravity + + def _scale_gyro_data(self, raw_measurement): + return ( + raw_measurement / GyroRange.lsb[self._cached_gyro_range] + ) * _ICM20X_RAD_PER_DEG + + @property + def accelerometer_range(self): + """Adjusts the range of values that the sensor can measure, from +/- 4G to +/-30G + Note that larger ranges will be less accurate. Must be an `AccelRange`""" + return self._cached_accel_range + + @accelerometer_range.setter + def accelerometer_range(self, value): # pylint: disable=no-member + if not AccelRange.is_valid(value): + raise AttributeError("range must be an `AccelRange`") + self._bank = 2 + sleep(0.005) + self._accel_range = value + sleep(0.005) + self._cached_accel_range = value + self._bank = 0 + + @property + def gyro_range(self): + """Adjusts the range of values that the sensor can measure, from 500 Degrees/second to 4000 + degrees/s. Note that larger ranges will be less accurate. Must be a `GyroRange` + """ + return self._cached_gyro_range + + @gyro_range.setter + def gyro_range(self, value): + if not GyroRange.is_valid(value): + raise AttributeError("range must be a `GyroRange`") + + self._bank = 2 + sleep(0.005) + self._gyro_range = value + sleep(0.005) + self._cached_gyro_range = value + self._bank = 0 + sleep(0.100) # needed to let new range settle + + @property + def accelerometer_data_rate_divisor(self): + """ + The divisor for the rate at which accelerometer measurements are taken in Hz + + .. note:: + The data rates are set indirectly by setting a rate divisor according to the + following formula: + + .. math:: + + \\text{accelerometer_data_rate} = \\frac{1125}{1 + divisor} + + This function sets the raw rate divisor. + + """ + self._bank = 2 + raw_rate_divisor = self._accel_rate_divisor + sleep(0.005) + self._bank = 0 + # rate_hz = 1125/(1+raw_rate_divisor) + return raw_rate_divisor + + @accelerometer_data_rate_divisor.setter + def accelerometer_data_rate_divisor(self, value): + # check that value <= 4095 + self._bank = 2 + sleep(0.005) + self._accel_rate_divisor = value + sleep(0.005) + + @property + def gyro_data_rate_divisor(self): + """ + The divisor for the rate at which gyro measurements are taken in Hz + + .. note:: + The data rates are set indirectly by setting a rate divisor according to the + following formula: + + .. math:: + + \\text{gyro_data_rate} = \\frac{1100}{1 + divisor} + + This function sets the raw rate divisor. + """ + + self._bank = 2 + raw_rate_divisor = self._gyro_rate_divisor + sleep(0.005) + self._bank = 0 + # rate_hz = 1100/(1+raw_rate_divisor) + return raw_rate_divisor + + @gyro_data_rate_divisor.setter + def gyro_data_rate_divisor(self, value): + # check that value <= 255 + self._bank = 2 + sleep(0.005) + self._gyro_rate_divisor = value + sleep(0.005) + + def _accel_rate_calc(self, divisor): # pylint:disable=no-self-use + return 1125 / (1 + divisor) + + def _gyro_rate_calc(self, divisor): # pylint:disable=no-self-use + return 1100 / (1 + divisor) + + @property + def accelerometer_data_rate(self): + """The rate at which accelerometer measurements are taken in Hz + + .. note:: + + The data rates are set indirectly by setting a rate divisor according to the + following formula: + + .. math:: + + \\text{accelerometer_data_rate} = \\frac{1125}{1 + divisor} + + This function does the math to find the divisor from a given rate but it will not be + exactly as specified. + """ + return self._accel_rate_calc(self.accelerometer_data_rate_divisor) + + @accelerometer_data_rate.setter + def accelerometer_data_rate(self, value): + if value < self._accel_rate_calc(4095) or value > self._accel_rate_calc(0): + raise AttributeError( + "Accelerometer data rate must be between 0.27 and 1125.0" + ) + divisor = round(((1125.0 - value) / value)) + self.accelerometer_data_rate_divisor = divisor + + @property + def gyro_data_rate(self): + """The rate at which gyro measurements are taken in Hz + + .. note:: + The data rates are set indirectly by setting a rate divisor according to the + following formula: + + .. math:: + + \\text{gyro_data_rate } = \\frac{1100}{1 + divisor} + + This function does the math to find the divisor from a given rate but it will not + be exactly as specified. + """ + return self._gyro_rate_calc(self.gyro_data_rate_divisor) + + @gyro_data_rate.setter + def gyro_data_rate(self, value): + if value < self._gyro_rate_calc(4095) or value > self._gyro_rate_calc(0): + raise AttributeError("Gyro data rate must be between 4.30 and 1100.0") + divisor = round(((1100.0 - value) / value)) + self.gyro_data_rate_divisor = divisor + + @property + def accel_dlpf_cutoff(self): + """The cutoff frequency for the accelerometer's digital low pass filter. Signals + above the given frequency will be filtered out. Must be an ``AccelDLPFCutoff``. + Use AccelDLPFCutoff.DISABLED to disable the filter + + .. note:: + Readings immediately following setting a cutoff frequency will be + inaccurate due to the filter "warming up" + + """ + self._bank = 2 + return self._accel_dlpf_config + + @accel_dlpf_cutoff.setter + def accel_dlpf_cutoff(self, cutoff_frequency): + if not AccelDLPFFreq.is_valid(cutoff_frequency): + raise AttributeError("accel_dlpf_cutoff must be an `AccelDLPFFreq`") + self._bank = 2 + # check for shutdown + if cutoff_frequency is AccelDLPFFreq.DISABLED: # pylint: disable=no-member + self._accel_dlpf_enable = False + return + self._accel_dlpf_enable = True + self._accel_dlpf_config = cutoff_frequency + + @property + def gyro_dlpf_cutoff(self): + """The cutoff frequency for the gyro's digital low pass filter. Signals above the + given frequency will be filtered out. Must be a ``GyroDLPFFreq``. Use + GyroDLPFCutoff.DISABLED to disable the filter + + .. note:: + Readings immediately following setting a cutoff frequency will be + inaccurate due to the filter "warming up" + + """ + self._bank = 2 + return self._gyro_dlpf_config + + @gyro_dlpf_cutoff.setter + def gyro_dlpf_cutoff(self, cutoff_frequency): + if not GyroDLPFFreq.is_valid(cutoff_frequency): + raise AttributeError("gyro_dlpf_cutoff must be a `GyroDLPFFreq`") + self._bank = 2 + # check for shutdown + if cutoff_frequency is GyroDLPFFreq.DISABLED: # pylint: disable=no-member + self._gyro_dlpf_enable = False + return + self._gyro_dlpf_enable = True + self._gyro_dlpf_config = cutoff_frequency + + @property + def _low_power(self): + self._bank = 0 + return self._low_power_en + + @_low_power.setter + def _low_power(self, enabled): + self._bank = 0 + self._low_power_en = enabled + + @property + def gravity(self): + """The gravity magnitude in m/s^2.""" + return self._gravity + + @gravity.setter + def gravity(self, value): + self._gravity = value + +class ICM20649(ICM20X): + """Library for the ST ICM-20649 Wide-Range 6-DoF Accelerometer and Gyro. + + :param ~busio.I2C i2c_bus: The I2C bus the ICM20649 is connected to. + :param int address: The I2C address of the device. Defaults to :const:`0x68` + + **Quickstart: Importing and using the ICM20649 temperature sensor** + + Here is an example of using the :class:`ICM2020649` class. + First you will need to import the libraries to use the sensor + + .. code-block:: python + + import board + import adafruit_icm20x + + Once this is done you can define your `board.I2C` object and define your sensor object + + .. code-block:: python + + i2c = board.I2C() # uses board.SCL and board.SDA + icm = adafruit_icm20x.ICM20649(i2c) + + Now you have access to the acceleration using :attr:`acceleration` attribute and + the gyro information using the :attr:`gyro` attribute. + + .. code-block:: python + + acceleration = icm.acceleration + gyro = icm.gyro + + """ + + def __init__(self, i2c_bus, address=_ICM20649_DEFAULT_ADDRESS): + AccelRange.add_values( + ( + ("RANGE_4G", 0, 4, 8192), + ("RANGE_8G", 1, 8, 4096.0), + ("RANGE_16G", 2, 16, 2048), + ("RANGE_30G", 3, 30, 1024), + ) + ) + + GyroRange.add_values( + ( + ("RANGE_500_DPS", 0, 500, 65.5), + ("RANGE_1000_DPS", 1, 1000, 32.8), + ("RANGE_2000_DPS", 2, 2000, 16.4), + ("RANGE_4000_DPS", 3, 4000, 8.2), + ) + ) + super().__init__(i2c_bus, address) + + +# https://www.y-ic.es/datasheet/78/SMDSW.020-2OZ.pdf page 19 +_AK09916_WIA1 = 0x00 +_AK09916_WIA2 = 0x01 +_AK09916_ST1 = 0x10 +_AK09916_HXL = 0x11 +_AK09916_HXH = 0x12 +_AK09916_HYL = 0x13 +_AK09916_HYH = 0x14 +_AK09916_HZL = 0x15 +_AK09916_HZH = 0x16 +_AK09916_ST2 = 0x18 +_AK09916_CNTL2 = 0x31 +_AK09916_CNTL3 = 0x32 + + +class MagDataRate(CV): + """Options for :attr:`ICM20948.magnetometer_data_rate`""" + + pass # pylint: disable=unnecessary-pass + + +class ICM20948(ICM20X): # pylint:disable=too-many-instance-attributes + """Library for the ST ICM-20948 Wide-Range 6-DoF Accelerometer and Gyro. + + :param ~busio.I2C i2c_bus: The I2C bus the ICM20948 is connected to. + :param int address: The I2C address of the device. Defaults to :const:`0x69` + + **Quickstart: Importing and using the ICM20948 temperature sensor** + + Here is an example of using the :class:`ICM20948` class. + First you will need to import the libraries to use the sensor + + .. code-block:: python + + import board + import adafruit_icm20x + + Once this is done you can define your `board.I2C` object and define your sensor object + + .. code-block:: python + + i2c = board.I2C() # uses board.SCL and board.SDA + icm = adafruit_icm20x.ICM20948(i2c) + + Now you have access to the acceleration using :attr:`acceleration` attribute, + the gyro information using the :attr:`gyro` attribute and the magnetic information + using the :attr:`magnetic` attribute + + .. code-block:: python + + acceleration = icm.acceleration + gyro = icm.gyro + magnetic = icm.magnetic + + + """ + + _slave_finished = ROBit(_ICM20X_I2C_MST_STATUS, 6) + + # mag data is LE + _raw_mag_data = Struct(_ICM20948_EXT_SLV_SENS_DATA_00, "B") + _i2c_master_enable = RWBit(_ICM20X_USER_CTRL, 5) # TODO: use this in sw reset + _i2c_master_reset = RWBit(_ICM20X_USER_CTRL, 1) + + _slave0_addr = UnaryStruct(_ICM20X_I2C_SLV0_ADDR, ">B") + _slave0_reg = UnaryStruct(_ICM20X_I2C_SLV0_REG, ">B") + _slave0_ctrl = UnaryStruct(_ICM20X_I2C_SLV0_CTRL, ">B") + _slave0_do = UnaryStruct(_ICM20X_I2C_SLV0_DO, ">B") + + _slave4_addr = UnaryStruct(_ICM20X_I2C_SLV4_ADDR, ">B") + _slave4_reg = UnaryStruct(_ICM20X_I2C_SLV4_REG, ">B") + _slave4_ctrl = UnaryStruct(_ICM20X_I2C_SLV4_CTRL, ">B") + _slave4_do = UnaryStruct(_ICM20X_I2C_SLV4_DO, ">B") + _slave4_di = UnaryStruct(_ICM20X_I2C_SLV4_DI, ">B") + + def __init__(self, i2c_bus, address=_ICM20948_DEFAULT_ADDRESS): + AccelRange.add_values( + ( + ("RANGE_2G", 0, 2, 16384), + ("RANGE_4G", 1, 4, 8192), + ("RANGE_8G", 2, 8, 4096.0), + ("RANGE_16G", 3, 16, 2048), + ) + ) + GyroRange.add_values( + ( + ("RANGE_250_DPS", 0, 250, 131.0), + ("RANGE_500_DPS", 1, 500, 65.5), + ("RANGE_1000_DPS", 2, 1000, 32.8), + ("RANGE_2000_DPS", 3, 2000, 16.4), + ) + ) + + # https://www.y-ic.es/datasheet/78/SMDSW.020-2OZ.pdf page 9 + MagDataRate.add_values( + ( + ("SHUTDOWN", 0x0, "Shutdown", None), + ("SINGLE", 0x1, "Single", None), + ("RATE_10HZ", 0x2, 10, None), + ("RATE_20HZ", 0x4, 20, None), + ("RATE_50HZ", 0x6, 50, None), + ("RATE_100HZ", 0x8, 100, None), + ) + ) + super().__init__(i2c_bus, address) + self._magnetometer_init() + + # A million thanks to the SparkFun folks for their library that I pillaged to write this method! + # See their Python library here: + # https://github.com/sparkfun/Qwiic_9DoF_IMU_ICM20948_Py + @property + def _mag_configured(self): + success = False + for _i in range(5): + success = self._mag_id() is not None + + if success: + return True + self._reset_i2c_master() + # i2c microcontroller stuck, try resetting + return False + + def _reset_i2c_master(self): + self._bank = 0 + self._i2c_master_reset = True + + def _magnetometer_enable(self): + self._bank = 0 + sleep(0.100) + self._bypass_i2c_master = False + sleep(0.005) + + # no repeated start, i2c microcontroller clock = 345.60kHz + self._bank = 3 + sleep(0.100) + self._i2c_master_control = 0x17 + sleep(0.100) + + self._bank = 0 + sleep(0.100) + self._i2c_master_enable = True + sleep(0.020) + + def _magnetometer_init(self): + self._magnetometer_enable() + self.magnetometer_data_rate = ( + MagDataRate.RATE_100HZ # pylint: disable=no-member + ) + + if not self._mag_configured: + return False + + self._setup_mag_readout() + + return True + + # set up slave0 for reading into the bank 0 data registers + def _setup_mag_readout(self): + self._bank = 3 + self._slave0_addr = 0x8C + sleep(0.005) + self._slave0_reg = 0x11 + sleep(0.005) + self._slave0_ctrl = 0x89 # enable + sleep(0.005) + + def _mag_id(self): + return self._read_mag_register(0x01) + + @property + def magnetic(self): + """The current magnetic field strengths on the X, Y, and Z axes in uT (micro-teslas)""" + + self._bank = 0 + full_data = self._raw_mag_data + # sleep(0.005) + + x = full_data[0] * _ICM20X_UT_PER_LSB + y = full_data[1] * _ICM20X_UT_PER_LSB + z = full_data[2] * _ICM20X_UT_PER_LSB + + return (x, y, z) + + @property + def magnetometer_data_rate(self): + """The rate at which the magnetometer takes measurements to update its output registers""" + # read mag DR register + self._read_mag_register(_AK09916_CNTL2) + + @magnetometer_data_rate.setter + def magnetometer_data_rate(self, mag_rate): + # From https://www.y-ic.es/datasheet/78/SMDSW.020-2OZ.pdf page 9 + + # "When user wants to change operation mode, transit to Power-down mode first and then + # transit to other modes. After Power-down mode is set, at least 100 microsectons (Twait) + # is needed before setting another mode" + if not MagDataRate.is_valid(mag_rate): + raise AttributeError("range must be an `MagDataRate`") + self._write_mag_register( + _AK09916_CNTL2, MagDataRate.SHUTDOWN # pylint: disable=no-member + ) + sleep(0.001) + self._write_mag_register(_AK09916_CNTL2, mag_rate) + + def _read_mag_register(self, register_addr, slave_addr=0x0C): + self._bank = 3 + + slave_addr |= 0x80 # set top bit for read + + self._slave4_addr = slave_addr + sleep(0.005) + self._slave4_reg = register_addr + sleep(0.005) + self._slave4_ctrl = ( + 0x80 # enable, don't raise interrupt, write register value, no delay + ) + sleep(0.005) + self._bank = 0 + + finished = False + for _i in range(100): + finished = self._slave_finished + if finished: # bueno! :) + break + sleep(0.010) + + if not finished: + return None + + self._bank = 3 + mag_register_data = self._slave4_di + sleep(0.005) + return mag_register_data + + def _write_mag_register(self, register_addr, value, slave_addr=0x0C): + self._bank = 3 + + self._slave4_addr = slave_addr + sleep(0.005) + self._slave4_reg = register_addr + sleep(0.005) + self._slave4_do = value + sleep(0.005) + self._slave4_ctrl = ( + 0x80 # enable, don't raise interrupt, write register value, no delay + ) + sleep(0.005) + self._bank = 0 + + finished = False + for _i in range(100): + finished = self._slave_finished + if finished: # bueno! :) + break + sleep(0.010) + + return finished From 9e7c5dd99c468116214f57d16d7afae9548fdf0f Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 26 Aug 2024 10:12:57 -0500 Subject: [PATCH 4/5] merge main. rename to snake_case --- adafruit_icm20x.py | 15 +- build/lib/adafruit_icm20x.py | 852 --------------------- examples/icm20x_icm20649_data_rate_test.py | 15 +- 3 files changed, 19 insertions(+), 863 deletions(-) delete mode 100644 build/lib/adafruit_icm20x.py diff --git a/adafruit_icm20x.py b/adafruit_icm20x.py index cfc860a..32f5bec 100644 --- a/adafruit_icm20x.py +++ b/adafruit_icm20x.py @@ -60,8 +60,12 @@ _ICM20X_REG_INT_ENABLE_0 = 0x10 # Interrupt enable register 0 _ICM20X_REG_INT_ENABLE_1 = 0x11 # Interrupt enable register 1 -_ICM20X_REG_INT_STATUS_0 = 0x19 # Interrupt status register 0 Wake on motion, DMP int, i2c int -_ICM20X_REG_INT_STATUS_1 = 0x1A # Interrupt status register 1 data register from all sensors +_ICM20X_REG_INT_STATUS_0 = ( + 0x19 # Interrupt status register 0 Wake on motion, DMP int, i2c int +) +_ICM20X_REG_INT_STATUS_1 = ( + 0x1A # Interrupt status register 1 data register from all sensors +) _ICM20X_REG_INT_STATUS_2 = 0x1B # Interrupt status register 2 FIFO overflow _ICM20X_REG_INT_STATUS_3 = 0x1C # Interrupt status register 3 Watermark interrupt @@ -256,7 +260,7 @@ def reset(self): while self._reset: sleep(0.005) - def dataReady(self): + def data_ready(self): """Checks if new data is available""" self._bank = 0 return self._data_ready @@ -300,7 +304,9 @@ def gyro(self): return (x, y, z) def _scale_xl_data(self, raw_measurement): - return raw_measurement / AccelRange.lsb[self._cached_accel_range] * self._gravity + return ( + raw_measurement / AccelRange.lsb[self._cached_accel_range] * self._gravity + ) def _scale_gyro_data(self, raw_measurement): return ( @@ -534,6 +540,7 @@ def gravity(self): def gravity(self, value): self._gravity = value + class ICM20649(ICM20X): """Library for the ST ICM-20649 Wide-Range 6-DoF Accelerometer and Gyro. diff --git a/build/lib/adafruit_icm20x.py b/build/lib/adafruit_icm20x.py deleted file mode 100644 index cb3e9c2..0000000 --- a/build/lib/adafruit_icm20x.py +++ /dev/null @@ -1,852 +0,0 @@ -# SPDX-FileCopyrightText: 2020 Bryan Siepert for Adafruit Industries -# -# SPDX-License-Identifier: MIT - -""" -`adafruit_icm20x` -================================================================================ - -Library for the ST ICM20X Motion Sensor Family - -* Author(s): Bryan Siepert - -Implementation Notes --------------------- - -**Hardware:** - -* Adafruit's ICM20649 Breakout: https://adafruit.com/product/4464 -* Adafruit's ICM20948 Breakout: https://adafruit.com/product/4554 - -**Software and Dependencies:** - -* Adafruit CircuitPython firmware for the supported boards: - https://circuitpython.org/downloads - - -* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice -* Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register -""" - -__version__ = "0.0.0+auto.0" -__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ICM20X.git" -# Common imports; remove if unused or pylint will complain -from time import sleep -from adafruit_bus_device import i2c_device - -from adafruit_register.i2c_struct import UnaryStruct, ROUnaryStruct, Struct -from adafruit_register.i2c_bit import RWBit, ROBit -from adafruit_register.i2c_bits import RWBits - -_ICM20649_DEFAULT_ADDRESS = 0x68 # icm20649 default i2c address -_ICM20948_DEFAULT_ADDRESS = 0x69 # icm20649 default i2c address -_ICM20649_DEVICE_ID = 0xE1 # Correct content of WHO_AM_I register -_ICM20948_DEVICE_ID = 0xEA # Correct content of WHO_AM_I register - -# Functions using these bank-specific registers are responsible for ensuring -# that the correct bank is set -# Bank 0 -_ICM20X_WHO_AM_I = 0x00 # device_id register -_ICM20X_REG_BANK_SEL = 0x7F # register bank selection register -_ICM20X_PWR_MGMT_1 = 0x06 # primary power management register -_ICM20X_ACCEL_XOUT_H = 0x2D # first byte of accel data -_ICM20X_GYRO_XOUT_H = 0x33 # first byte of accel data -_ICM20X_I2C_MST_STATUS = 0x17 # I2C Microcontroller Status bits -_ICM20948_EXT_SLV_SENS_DATA_00 = 0x3B - -_ICM20X_USER_CTRL = 0x03 # User Control Reg. Includes I2C Microcontroller -_ICM20X_LP_CONFIG = 0x05 # Low Power config -_ICM20X_REG_INT_PIN_CFG = 0xF # Interrupt config register -_ICM20X_REG_INT_ENABLE_0 = 0x10 # Interrupt enable register 0 -_ICM20X_REG_INT_ENABLE_1 = 0x11 # Interrupt enable register 1 - -_ICM20X_REG_INT_STATUS_0 = 0x19 # Interrupt status register 0 Wake on motion, DMP int, i2c int -_ICM20X_REG_INT_STATUS_1 = 0x1A # Interrupt status register 1 data register from all sensors -_ICM20X_REG_INT_STATUS_2 = 0x1B # Interrupt status register 2 FIFO overflow -_ICM20X_REG_INT_STATUS_3 = 0x1C # Interrupt status register 3 Watermark interrupt - -# Bank 2 -_ICM20X_GYRO_SMPLRT_DIV = 0x00 -_ICM20X_GYRO_CONFIG_1 = 0x01 -_ICM20X_ACCEL_SMPLRT_DIV_1 = 0x10 -_ICM20X_ACCEL_SMPLRT_DIV_2 = 0x11 -_ICM20X_ACCEL_CONFIG_1 = 0x14 - - -# Bank 3 - -_ICM20X_I2C_MST_ODR_CONFIG = 0x0 # Sets ODR for I2C microcontroller bus -_ICM20X_I2C_MST_CTRL = 0x1 # I2C microcontroller bus config -_ICM20X_I2C_MST_DELAY_CTRL = 0x2 # I2C microcontroller bus config -_ICM20X_I2C_SLV0_ADDR = 0x3 # Sets I2C address for I2C microcontroller bus sensor 0 -_ICM20X_I2C_SLV0_REG = 0x4 # Sets register address for I2C microcontroller bus sensor 0 -_ICM20X_I2C_SLV0_CTRL = 0x5 # Controls for I2C microcontroller bus sensor 0 -_ICM20X_I2C_SLV0_DO = 0x6 # Sets I2C microcontroller bus sensor 0 data out - -_ICM20X_I2C_SLV4_ADDR = 0x13 # Sets I2C address for I2C microcontroller bus sensor 4 -_ICM20X_I2C_SLV4_REG = ( - 0x14 # Sets register address for I2C microcontroller bus sensor 4 -) -_ICM20X_I2C_SLV4_CTRL = 0x15 # Controls for I2C microcontroller bus sensor 4 -_ICM20X_I2C_SLV4_DO = 0x16 # Sets I2C microcontroller bus sensor 4 data out -_ICM20X_I2C_SLV4_DI = 0x17 # Sets I2C microcontroller bus sensor 4 data in - -_ICM20X_UT_PER_LSB = 0.15 # mag data LSB value (fixed) -_ICM20X_RAD_PER_DEG = 0.017453293 # Degrees/s to rad/s multiplier - -G_TO_ACCEL = 9.80665 - - -class CV: - """struct helper""" - - @classmethod - def add_values(cls, value_tuples): - """Add CV values to the class""" - cls.string = {} - cls.lsb = {} - - for value_tuple in value_tuples: - name, value, string, lsb = value_tuple - setattr(cls, name, value) - cls.string[value] = string - cls.lsb[value] = lsb - - @classmethod - def is_valid(cls, value): - """Validate that a given value is a member""" - return value in cls.string - - -class AccelRange(CV): - """Options for :attr:`ICM20X.accelerometer_range`""" - - pass # pylint: disable=unnecessary-pass - - -class GyroRange(CV): - """Options for :attr:`ICM20X.gyro_data_range`""" - - pass # pylint: disable=unnecessary-pass - - -class GyroDLPFFreq(CV): - """Options for :attr:`ICM20X.gyro_dlpf_cutoff`""" - - pass # pylint: disable=unnecessary-pass - - -class AccelDLPFFreq(CV): - """Options for :attr:`ICM20X.accel_dlpf_cutoff`""" - - pass # pylint: disable=unnecessary-pass - - -class ICM20X: # pylint:disable=too-many-instance-attributes - """Library for the ST ICM-20X Wide-Range 6-DoF Accelerometer and Gyro Family - - - :param ~busio.I2C i2c_bus: The I2C bus the ICM20X is connected to. - :param int address: The I2C address of the device. - - """ - - # Bank 0 - _device_id = ROUnaryStruct(_ICM20X_WHO_AM_I, ">B") - _bank_reg = UnaryStruct(_ICM20X_REG_BANK_SEL, ">B") - _reset = RWBit(_ICM20X_PWR_MGMT_1, 7) - _sleep_reg = RWBit(_ICM20X_PWR_MGMT_1, 6) - _low_power_en = RWBit(_ICM20X_PWR_MGMT_1, 5) - _clock_source = RWBits(3, _ICM20X_PWR_MGMT_1, 0) - - _raw_accel_data = Struct(_ICM20X_ACCEL_XOUT_H, ">hhh") # ds says LE :| - _raw_gyro_data = Struct(_ICM20X_GYRO_XOUT_H, ">hhh") - - _lp_config_reg = UnaryStruct(_ICM20X_LP_CONFIG, ">B") - - _data_ready = ROBit(_ICM20X_REG_INT_STATUS_1, 0) - - _i2c_master_cycle_en = RWBit(_ICM20X_LP_CONFIG, 6) - _accel_cycle_en = RWBit(_ICM20X_LP_CONFIG, 5) - _gyro_cycle_en = RWBit(_ICM20X_LP_CONFIG, 4) - - # Bank 2 - _gyro_dlpf_enable = RWBits(1, _ICM20X_GYRO_CONFIG_1, 0) - _gyro_range = RWBits(2, _ICM20X_GYRO_CONFIG_1, 1) - _gyro_dlpf_config = RWBits(3, _ICM20X_GYRO_CONFIG_1, 3) - - _accel_dlpf_enable = RWBits(1, _ICM20X_ACCEL_CONFIG_1, 0) - _accel_range = RWBits(2, _ICM20X_ACCEL_CONFIG_1, 1) - _accel_dlpf_config = RWBits(3, _ICM20X_ACCEL_CONFIG_1, 3) - - # this value is a 12-bit register spread across two bytes, big-endian first - _accel_rate_divisor = UnaryStruct(_ICM20X_ACCEL_SMPLRT_DIV_1, ">H") - _gyro_rate_divisor = UnaryStruct(_ICM20X_GYRO_SMPLRT_DIV, ">B") - AccelDLPFFreq.add_values( - ( - ( - "DISABLED", - -1, - "Disabled", - None, - ), # magical value that we will use do disable - ("FREQ_246_0HZ_3DB", 1, 246.0, None), - ("FREQ_111_4HZ_3DB", 2, 111.4, None), - ("FREQ_50_4HZ_3DB", 3, 50.4, None), - ("FREQ_23_9HZ_3DB", 4, 23.9, None), - ("FREQ_11_5HZ_3DB", 5, 11.5, None), - ("FREQ_5_7HZ_3DB", 6, 5.7, None), - ("FREQ_473HZ_3DB", 7, 473, None), - ) - ) - GyroDLPFFreq.add_values( - ( - ( - "DISABLED", - -1, - "Disabled", - None, - ), # magical value that we will use do disable - ("FREQ_196_6HZ_3DB", 0, 196.6, None), - ("FREQ_151_8HZ_3DB", 1, 151.8, None), - ("FREQ_119_5HZ_3DB", 2, 119.5, None), - ("FREQ_51_2HZ_3DB", 3, 51.2, None), - ("FREQ_23_9HZ_3DB", 4, 23.9, None), - ("FREQ_11_6HZ_3DB", 5, 11.6, None), - ("FREQ_5_7HZ_3DB", 6, 5.7, None), - ("FREQ_361_4HZ_3DB", 7, 361.4, None), - ) - ) - - @property - def _bank(self): - return self._bank_reg >> 4 - - @_bank.setter - def _bank(self, value): - self._bank_reg = value << 4 - - def __init__(self, i2c_bus, address): - self.i2c_device = i2c_device.I2CDevice(i2c_bus, address) - self._bank = 0 - if not self._device_id in [_ICM20649_DEVICE_ID, _ICM20948_DEVICE_ID]: - raise RuntimeError("Failed to find an ICM20X sensor - check your wiring!") - self.reset() - self.initialize() - - def initialize(self): - """Configure the sensors with the default settings. For use after calling :meth:`reset`""" - - self._sleep = False - self.accelerometer_range = AccelRange.RANGE_8G # pylint: disable=no-member - self.gyro_range = GyroRange.RANGE_500_DPS # pylint: disable=no-member - - self.accelerometer_data_rate_divisor = 20 # ~53.57Hz - self.gyro_data_rate_divisor = 10 # ~100Hz - - self._gravity = G_TO_ACCEL - - def reset(self): - """Resets the internal registers and restores the default settings""" - self._bank = 0 - - sleep(0.005) - self._reset = True - sleep(0.005) - while self._reset: - sleep(0.005) - - def dataReady(self): - """Checks if new data is available""" - self._bank = 0 - return self._data_ready - - @property - def _sleep(self): - self._bank = 0 - sleep(0.005) - self._sleep_reg = False - sleep(0.005) - - @_sleep.setter - def _sleep(self, sleep_enabled): - self._bank = 0 - sleep(0.005) - self._sleep_reg = sleep_enabled - sleep(0.005) - - @property - def acceleration(self): - """The x, y, z acceleration values returned in a 3-tuple and are in :math:`m / s ^ 2.`""" - self._bank = 0 - raw_accel_data = self._raw_accel_data - # sleep(0.005) - - x = self._scale_xl_data(raw_accel_data[0]) - y = self._scale_xl_data(raw_accel_data[1]) - z = self._scale_xl_data(raw_accel_data[2]) - - return (x, y, z) - - @property - def gyro(self): - """The x, y, z angular velocity values returned in a 3-tuple and - are in :math:`degrees / second`""" - self._bank = 0 - raw_gyro_data = self._raw_gyro_data - x = self._scale_gyro_data(raw_gyro_data[0]) - y = self._scale_gyro_data(raw_gyro_data[1]) - z = self._scale_gyro_data(raw_gyro_data[2]) - - return (x, y, z) - - def _scale_xl_data(self, raw_measurement): - # sleep(0.005) - return raw_measurement / AccelRange.lsb[self._cached_accel_range] * self._gravity - - def _scale_gyro_data(self, raw_measurement): - return ( - raw_measurement / GyroRange.lsb[self._cached_gyro_range] - ) * _ICM20X_RAD_PER_DEG - - @property - def accelerometer_range(self): - """Adjusts the range of values that the sensor can measure, from +/- 4G to +/-30G - Note that larger ranges will be less accurate. Must be an `AccelRange`""" - return self._cached_accel_range - - @accelerometer_range.setter - def accelerometer_range(self, value): # pylint: disable=no-member - if not AccelRange.is_valid(value): - raise AttributeError("range must be an `AccelRange`") - self._bank = 2 - sleep(0.005) - self._accel_range = value - sleep(0.005) - self._cached_accel_range = value - self._bank = 0 - - @property - def gyro_range(self): - """Adjusts the range of values that the sensor can measure, from 500 Degrees/second to 4000 - degrees/s. Note that larger ranges will be less accurate. Must be a `GyroRange` - """ - return self._cached_gyro_range - - @gyro_range.setter - def gyro_range(self, value): - if not GyroRange.is_valid(value): - raise AttributeError("range must be a `GyroRange`") - - self._bank = 2 - sleep(0.005) - self._gyro_range = value - sleep(0.005) - self._cached_gyro_range = value - self._bank = 0 - sleep(0.100) # needed to let new range settle - - @property - def accelerometer_data_rate_divisor(self): - """ - The divisor for the rate at which accelerometer measurements are taken in Hz - - .. note:: - The data rates are set indirectly by setting a rate divisor according to the - following formula: - - .. math:: - - \\text{accelerometer_data_rate} = \\frac{1125}{1 + divisor} - - This function sets the raw rate divisor. - - """ - self._bank = 2 - raw_rate_divisor = self._accel_rate_divisor - sleep(0.005) - self._bank = 0 - # rate_hz = 1125/(1+raw_rate_divisor) - return raw_rate_divisor - - @accelerometer_data_rate_divisor.setter - def accelerometer_data_rate_divisor(self, value): - # check that value <= 4095 - self._bank = 2 - sleep(0.005) - self._accel_rate_divisor = value - sleep(0.005) - - @property - def gyro_data_rate_divisor(self): - """ - The divisor for the rate at which gyro measurements are taken in Hz - - .. note:: - The data rates are set indirectly by setting a rate divisor according to the - following formula: - - .. math:: - - \\text{gyro_data_rate} = \\frac{1100}{1 + divisor} - - This function sets the raw rate divisor. - """ - - self._bank = 2 - raw_rate_divisor = self._gyro_rate_divisor - sleep(0.005) - self._bank = 0 - # rate_hz = 1100/(1+raw_rate_divisor) - return raw_rate_divisor - - @gyro_data_rate_divisor.setter - def gyro_data_rate_divisor(self, value): - # check that value <= 255 - self._bank = 2 - sleep(0.005) - self._gyro_rate_divisor = value - sleep(0.005) - - def _accel_rate_calc(self, divisor): # pylint:disable=no-self-use - return 1125 / (1 + divisor) - - def _gyro_rate_calc(self, divisor): # pylint:disable=no-self-use - return 1100 / (1 + divisor) - - @property - def accelerometer_data_rate(self): - """The rate at which accelerometer measurements are taken in Hz - - .. note:: - - The data rates are set indirectly by setting a rate divisor according to the - following formula: - - .. math:: - - \\text{accelerometer_data_rate} = \\frac{1125}{1 + divisor} - - This function does the math to find the divisor from a given rate but it will not be - exactly as specified. - """ - return self._accel_rate_calc(self.accelerometer_data_rate_divisor) - - @accelerometer_data_rate.setter - def accelerometer_data_rate(self, value): - if value < self._accel_rate_calc(4095) or value > self._accel_rate_calc(0): - raise AttributeError( - "Accelerometer data rate must be between 0.27 and 1125.0" - ) - divisor = round(((1125.0 - value) / value)) - self.accelerometer_data_rate_divisor = divisor - - @property - def gyro_data_rate(self): - """The rate at which gyro measurements are taken in Hz - - .. note:: - The data rates are set indirectly by setting a rate divisor according to the - following formula: - - .. math:: - - \\text{gyro_data_rate } = \\frac{1100}{1 + divisor} - - This function does the math to find the divisor from a given rate but it will not - be exactly as specified. - """ - return self._gyro_rate_calc(self.gyro_data_rate_divisor) - - @gyro_data_rate.setter - def gyro_data_rate(self, value): - if value < self._gyro_rate_calc(4095) or value > self._gyro_rate_calc(0): - raise AttributeError("Gyro data rate must be between 4.30 and 1100.0") - divisor = round(((1100.0 - value) / value)) - self.gyro_data_rate_divisor = divisor - - @property - def accel_dlpf_cutoff(self): - """The cutoff frequency for the accelerometer's digital low pass filter. Signals - above the given frequency will be filtered out. Must be an ``AccelDLPFCutoff``. - Use AccelDLPFCutoff.DISABLED to disable the filter - - .. note:: - Readings immediately following setting a cutoff frequency will be - inaccurate due to the filter "warming up" - - """ - self._bank = 2 - return self._accel_dlpf_config - - @accel_dlpf_cutoff.setter - def accel_dlpf_cutoff(self, cutoff_frequency): - if not AccelDLPFFreq.is_valid(cutoff_frequency): - raise AttributeError("accel_dlpf_cutoff must be an `AccelDLPFFreq`") - self._bank = 2 - # check for shutdown - if cutoff_frequency is AccelDLPFFreq.DISABLED: # pylint: disable=no-member - self._accel_dlpf_enable = False - return - self._accel_dlpf_enable = True - self._accel_dlpf_config = cutoff_frequency - - @property - def gyro_dlpf_cutoff(self): - """The cutoff frequency for the gyro's digital low pass filter. Signals above the - given frequency will be filtered out. Must be a ``GyroDLPFFreq``. Use - GyroDLPFCutoff.DISABLED to disable the filter - - .. note:: - Readings immediately following setting a cutoff frequency will be - inaccurate due to the filter "warming up" - - """ - self._bank = 2 - return self._gyro_dlpf_config - - @gyro_dlpf_cutoff.setter - def gyro_dlpf_cutoff(self, cutoff_frequency): - if not GyroDLPFFreq.is_valid(cutoff_frequency): - raise AttributeError("gyro_dlpf_cutoff must be a `GyroDLPFFreq`") - self._bank = 2 - # check for shutdown - if cutoff_frequency is GyroDLPFFreq.DISABLED: # pylint: disable=no-member - self._gyro_dlpf_enable = False - return - self._gyro_dlpf_enable = True - self._gyro_dlpf_config = cutoff_frequency - - @property - def _low_power(self): - self._bank = 0 - return self._low_power_en - - @_low_power.setter - def _low_power(self, enabled): - self._bank = 0 - self._low_power_en = enabled - - @property - def gravity(self): - """The gravity magnitude in m/s^2.""" - return self._gravity - - @gravity.setter - def gravity(self, value): - self._gravity = value - -class ICM20649(ICM20X): - """Library for the ST ICM-20649 Wide-Range 6-DoF Accelerometer and Gyro. - - :param ~busio.I2C i2c_bus: The I2C bus the ICM20649 is connected to. - :param int address: The I2C address of the device. Defaults to :const:`0x68` - - **Quickstart: Importing and using the ICM20649 temperature sensor** - - Here is an example of using the :class:`ICM2020649` class. - First you will need to import the libraries to use the sensor - - .. code-block:: python - - import board - import adafruit_icm20x - - Once this is done you can define your `board.I2C` object and define your sensor object - - .. code-block:: python - - i2c = board.I2C() # uses board.SCL and board.SDA - icm = adafruit_icm20x.ICM20649(i2c) - - Now you have access to the acceleration using :attr:`acceleration` attribute and - the gyro information using the :attr:`gyro` attribute. - - .. code-block:: python - - acceleration = icm.acceleration - gyro = icm.gyro - - """ - - def __init__(self, i2c_bus, address=_ICM20649_DEFAULT_ADDRESS): - AccelRange.add_values( - ( - ("RANGE_4G", 0, 4, 8192), - ("RANGE_8G", 1, 8, 4096.0), - ("RANGE_16G", 2, 16, 2048), - ("RANGE_30G", 3, 30, 1024), - ) - ) - - GyroRange.add_values( - ( - ("RANGE_500_DPS", 0, 500, 65.5), - ("RANGE_1000_DPS", 1, 1000, 32.8), - ("RANGE_2000_DPS", 2, 2000, 16.4), - ("RANGE_4000_DPS", 3, 4000, 8.2), - ) - ) - super().__init__(i2c_bus, address) - - -# https://www.y-ic.es/datasheet/78/SMDSW.020-2OZ.pdf page 19 -_AK09916_WIA1 = 0x00 -_AK09916_WIA2 = 0x01 -_AK09916_ST1 = 0x10 -_AK09916_HXL = 0x11 -_AK09916_HXH = 0x12 -_AK09916_HYL = 0x13 -_AK09916_HYH = 0x14 -_AK09916_HZL = 0x15 -_AK09916_HZH = 0x16 -_AK09916_ST2 = 0x18 -_AK09916_CNTL2 = 0x31 -_AK09916_CNTL3 = 0x32 - - -class MagDataRate(CV): - """Options for :attr:`ICM20948.magnetometer_data_rate`""" - - pass # pylint: disable=unnecessary-pass - - -class ICM20948(ICM20X): # pylint:disable=too-many-instance-attributes - """Library for the ST ICM-20948 Wide-Range 6-DoF Accelerometer and Gyro. - - :param ~busio.I2C i2c_bus: The I2C bus the ICM20948 is connected to. - :param int address: The I2C address of the device. Defaults to :const:`0x69` - - **Quickstart: Importing and using the ICM20948 temperature sensor** - - Here is an example of using the :class:`ICM20948` class. - First you will need to import the libraries to use the sensor - - .. code-block:: python - - import board - import adafruit_icm20x - - Once this is done you can define your `board.I2C` object and define your sensor object - - .. code-block:: python - - i2c = board.I2C() # uses board.SCL and board.SDA - icm = adafruit_icm20x.ICM20948(i2c) - - Now you have access to the acceleration using :attr:`acceleration` attribute, - the gyro information using the :attr:`gyro` attribute and the magnetic information - using the :attr:`magnetic` attribute - - .. code-block:: python - - acceleration = icm.acceleration - gyro = icm.gyro - magnetic = icm.magnetic - - - """ - - _slave_finished = ROBit(_ICM20X_I2C_MST_STATUS, 6) - - # mag data is LE - _raw_mag_data = Struct(_ICM20948_EXT_SLV_SENS_DATA_00, "B") - _i2c_master_enable = RWBit(_ICM20X_USER_CTRL, 5) # TODO: use this in sw reset - _i2c_master_reset = RWBit(_ICM20X_USER_CTRL, 1) - - _slave0_addr = UnaryStruct(_ICM20X_I2C_SLV0_ADDR, ">B") - _slave0_reg = UnaryStruct(_ICM20X_I2C_SLV0_REG, ">B") - _slave0_ctrl = UnaryStruct(_ICM20X_I2C_SLV0_CTRL, ">B") - _slave0_do = UnaryStruct(_ICM20X_I2C_SLV0_DO, ">B") - - _slave4_addr = UnaryStruct(_ICM20X_I2C_SLV4_ADDR, ">B") - _slave4_reg = UnaryStruct(_ICM20X_I2C_SLV4_REG, ">B") - _slave4_ctrl = UnaryStruct(_ICM20X_I2C_SLV4_CTRL, ">B") - _slave4_do = UnaryStruct(_ICM20X_I2C_SLV4_DO, ">B") - _slave4_di = UnaryStruct(_ICM20X_I2C_SLV4_DI, ">B") - - def __init__(self, i2c_bus, address=_ICM20948_DEFAULT_ADDRESS): - AccelRange.add_values( - ( - ("RANGE_2G", 0, 2, 16384), - ("RANGE_4G", 1, 4, 8192), - ("RANGE_8G", 2, 8, 4096.0), - ("RANGE_16G", 3, 16, 2048), - ) - ) - GyroRange.add_values( - ( - ("RANGE_250_DPS", 0, 250, 131.0), - ("RANGE_500_DPS", 1, 500, 65.5), - ("RANGE_1000_DPS", 2, 1000, 32.8), - ("RANGE_2000_DPS", 3, 2000, 16.4), - ) - ) - - # https://www.y-ic.es/datasheet/78/SMDSW.020-2OZ.pdf page 9 - MagDataRate.add_values( - ( - ("SHUTDOWN", 0x0, "Shutdown", None), - ("SINGLE", 0x1, "Single", None), - ("RATE_10HZ", 0x2, 10, None), - ("RATE_20HZ", 0x4, 20, None), - ("RATE_50HZ", 0x6, 50, None), - ("RATE_100HZ", 0x8, 100, None), - ) - ) - super().__init__(i2c_bus, address) - self._magnetometer_init() - - # A million thanks to the SparkFun folks for their library that I pillaged to write this method! - # See their Python library here: - # https://github.com/sparkfun/Qwiic_9DoF_IMU_ICM20948_Py - @property - def _mag_configured(self): - success = False - for _i in range(5): - success = self._mag_id() is not None - - if success: - return True - self._reset_i2c_master() - # i2c microcontroller stuck, try resetting - return False - - def _reset_i2c_master(self): - self._bank = 0 - self._i2c_master_reset = True - - def _magnetometer_enable(self): - self._bank = 0 - sleep(0.100) - self._bypass_i2c_master = False - sleep(0.005) - - # no repeated start, i2c microcontroller clock = 345.60kHz - self._bank = 3 - sleep(0.100) - self._i2c_master_control = 0x17 - sleep(0.100) - - self._bank = 0 - sleep(0.100) - self._i2c_master_enable = True - sleep(0.020) - - def _magnetometer_init(self): - self._magnetometer_enable() - self.magnetometer_data_rate = ( - MagDataRate.RATE_100HZ # pylint: disable=no-member - ) - - if not self._mag_configured: - return False - - self._setup_mag_readout() - - return True - - # set up slave0 for reading into the bank 0 data registers - def _setup_mag_readout(self): - self._bank = 3 - self._slave0_addr = 0x8C - sleep(0.005) - self._slave0_reg = 0x11 - sleep(0.005) - self._slave0_ctrl = 0x89 # enable - sleep(0.005) - - def _mag_id(self): - return self._read_mag_register(0x01) - - @property - def magnetic(self): - """The current magnetic field strengths on the X, Y, and Z axes in uT (micro-teslas)""" - - self._bank = 0 - full_data = self._raw_mag_data - # sleep(0.005) - - x = full_data[0] * _ICM20X_UT_PER_LSB - y = full_data[1] * _ICM20X_UT_PER_LSB - z = full_data[2] * _ICM20X_UT_PER_LSB - - return (x, y, z) - - @property - def magnetometer_data_rate(self): - """The rate at which the magnetometer takes measurements to update its output registers""" - # read mag DR register - self._read_mag_register(_AK09916_CNTL2) - - @magnetometer_data_rate.setter - def magnetometer_data_rate(self, mag_rate): - # From https://www.y-ic.es/datasheet/78/SMDSW.020-2OZ.pdf page 9 - - # "When user wants to change operation mode, transit to Power-down mode first and then - # transit to other modes. After Power-down mode is set, at least 100 microsectons (Twait) - # is needed before setting another mode" - if not MagDataRate.is_valid(mag_rate): - raise AttributeError("range must be an `MagDataRate`") - self._write_mag_register( - _AK09916_CNTL2, MagDataRate.SHUTDOWN # pylint: disable=no-member - ) - sleep(0.001) - self._write_mag_register(_AK09916_CNTL2, mag_rate) - - def _read_mag_register(self, register_addr, slave_addr=0x0C): - self._bank = 3 - - slave_addr |= 0x80 # set top bit for read - - self._slave4_addr = slave_addr - sleep(0.005) - self._slave4_reg = register_addr - sleep(0.005) - self._slave4_ctrl = ( - 0x80 # enable, don't raise interrupt, write register value, no delay - ) - sleep(0.005) - self._bank = 0 - - finished = False - for _i in range(100): - finished = self._slave_finished - if finished: # bueno! :) - break - sleep(0.010) - - if not finished: - return None - - self._bank = 3 - mag_register_data = self._slave4_di - sleep(0.005) - return mag_register_data - - def _write_mag_register(self, register_addr, value, slave_addr=0x0C): - self._bank = 3 - - self._slave4_addr = slave_addr - sleep(0.005) - self._slave4_reg = register_addr - sleep(0.005) - self._slave4_do = value - sleep(0.005) - self._slave4_ctrl = ( - 0x80 # enable, don't raise interrupt, write register value, no delay - ) - sleep(0.005) - self._bank = 0 - - finished = False - for _i in range(100): - finished = self._slave_finished - if finished: # bueno! :) - break - sleep(0.010) - - return finished diff --git a/examples/icm20x_icm20649_data_rate_test.py b/examples/icm20x_icm20649_data_rate_test.py index cc8c841..3102732 100644 --- a/examples/icm20x_icm20649_data_rate_test.py +++ b/examples/icm20x_icm20649_data_rate_test.py @@ -15,21 +15,22 @@ ism.gyro_range = GyroRange.RANGE_500_DPS print("Gyro range set to: %d DPS" % GyroRange.string[ism.gyro_range]) -ism.gyro_data_rate = 1100 # 1100 max -ism.accelerometer_data_rate = 1125 # 1125 max +ism.gyro_data_rate = 1100 # 1100 max +ism.accelerometer_data_rate = 1125 # 1125 max -print('Gyro rate: {:f}'.format(ism.gyro_data_rate)) -print('Accel rate: {:f}'.format(ism.accelerometer_data_rate)) +print("Gyro rate: {:f}".format(ism.gyro_data_rate)) +print("Accel rate: {:f}".format(ism.accelerometer_data_rate)) ism.gravity = 9.8 previousTime = time.perf_counter() while True: - if ism.dataReady: + if ism.data_ready: currentTime = time.perf_counter() - print('\033[2J') + print("\033[2J") print( - "Accel X:%5.2f Y:%5.2f Z:%5.2f ms^2 Gyro X:%8.3f Y:%8.3f Z:%8.3f degrees/s Sample Rate: %8.1f Hz" + "Accel X:%5.2f Y:%5.2f Z:%5.2f ms^2 " + "Gyro X:%8.3f Y:%8.3f Z:%8.3f degrees/s Sample Rate: %8.1f Hz" % (*ism.acceleration, *ism.gyro, (1 / (currentTime - previousTime))) ) previousTime = currentTime From d95ef12752402f3e2821d27c10a9c5ce95b4db4a Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 26 Aug 2024 10:19:02 -0500 Subject: [PATCH 5/5] fix docs build --- docs/examples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples.rst b/docs/examples.rst index 4fc0531..8d2ed7c 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -17,7 +17,7 @@ Test using all the ICM20649 sensor capabilities :linenos: ICM20649 data rate test ------------------- +----------------------- Test using all the ICM20649 sensor capabilities