# SPDX-FileCopyrightText: Copyright (c) 2023 Jose D. Montoya
#
# SPDX-License-Identifier: MIT
"""
`ICM20948`
================================================================================
MicroPython Driver for the ICM20948 Accelerometer/Gyro Sensor
* Author(s): Jose D. Montoya
Implementation Notes
--------------------
**Software and Dependencies:**
This library depends on Micropython
"""
# pylint: disable=line-too-long
from time import sleep
from micropython import const
from micropython_icm20948.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_ICM20948.git"
_REG_WHOAMI = const(0x69)
_DEVICE_ID = const(0x00)
_PWR_MGMT_1 = const(0x06)
_PWR_MGMT_2 = const(0x07)
_REG_BANK_SEL = const(0x7F)
_ACCEL_CONFIG = const(0x14)
_GYRO_CONFIG_1 = const(0x01)
_GYRO_SMPLRT_DIV = const(0x00)
_ACCEL_SMPLRT_DIV_1 = const(0x10)
_ACCEL_XOUT_H = const(0x2D) # first byte of accel data
_GYRO_XOUT_H = const(0x33) # first byte of accel data
_TEMP_OUT = const(0x3A)
# ICM20948
CLK_SELECT_INTERNAL = const(0b000)
CLK_SELECT_BEST = const(0b001)
CLK_SELECT_STOP = const(0b111)
clk_values = (CLK_SELECT_INTERNAL, CLK_SELECT_BEST, CLK_SELECT_STOP)
# ICM20948
ACC_DISABLED = const(0b111)
GYRO_DISABLED = const(0b111)
ACC_ENABLED = const(0b000)
GYRO_ENABLED = const(0b000)
TEMP_ENABLED = const(0b0)
TEMP_DISABLED = const(0b1)
gyro_en_values = (GYRO_DISABLED, GYRO_ENABLED)
acc_en_values = (ACC_DISABLED, ACC_ENABLED)
temperature_en_values = (TEMP_DISABLED, TEMP_ENABLED)
# ICM20948 USer Bank
USER_BANK_0 = const(0)
USER_BANK_1 = const(1)
USER_BANK_2 = const(2)
USER_BANK_3 = const(3)
user_bank_values = (USER_BANK_0, USER_BANK_1, USER_BANK_2, USER_BANK_3)
# ACC Range ICM20948
RANGE_2G = const(0b00)
RANGE_4G = const(0b01)
RANGE_8G = const(0b10)
RANGE_16G = const(0b11)
acc_range_values = (RANGE_2G, RANGE_4G, RANGE_8G, RANGE_16G)
acc_range_sensitivity = (16384, 8192, 4096, 2048)
# Acceleration Rate Divisor Values
acc_rate_values = {
140.6: 7,
102.3: 10,
70.3: 15,
48.9: 22,
35.2: 31,
17.6: 63,
8.8: 127,
4.4: 255,
2.2: 513,
1.1: 1022,
0.55: 2044,
0.27: 4095,
}
acc_data_rate_values = (
140.6,
102.3,
70.3,
48.9,
35.2,
17.6,
8.8,
4.4,
2.2,
1.1,
0.55,
0.27,
)
acc_rate_divisor_values = (7, 10, 15, 22, 31, 63, 127, 255, 513, 1022, 2044, 4095)
FREQ_246_0 = const(0b001)
FREQ_111_4 = const(0b010)
FREQ_50_4 = const(0b011)
FREQ_23_9 = const(0b100)
FREQ_11_5 = const(0b101)
FREQ_5_7 = const(0b110)
FREQ_473 = const(0b111)
acc_filter_values = (
FREQ_246_0,
FREQ_111_4,
FREQ_50_4,
FREQ_23_9,
FREQ_11_5,
FREQ_5_7,
FREQ_473,
)
# Gyro Full Scale
FS_250_DPS = const(0b00)
FS_500_DPS = const(0b01)
FS_1000_DPS = const(0b10)
FS_2000_DPS = const(0b11)
gyro_full_scale_values = (FS_250_DPS, FS_500_DPS, FS_1000_DPS, FS_2000_DPS)
gyro_full_scale_sensitivity = (131, 65.5, 32.8, 16.4)
# Gyro Rate Divisor Values
gyro_rate_values = {
562.5: 1,
375.0: 2,
281.3: 3,
225.0: 4,
187.5: 5,
140.6: 7,
125.0: 8,
102.3: 10,
70.3: 15,
66.2: 16,
48.9: 22,
35.2: 31,
34.1: 32,
17.6: 63,
17.3: 64,
4.4: 255,
}
gyro_data_rate_values = (
562.5,
375.0,
281.3,
225.0,
187.5,
140.6,
125.0,
102.3,
70.3,
66.2,
48.9,
35.2,
34.1,
17.6,
17.3,
4.4,
)
gyro_rate_divisor_values = (1, 2, 3, 4, 5, 7, 8, 10, 15, 16, 22, 31, 32, 63, 64, 255)
G_FREQ_196_6 = const(0b000)
G_FREQ_151_8 = const(0b001)
G_FREQ_119_5 = const(0b010)
G_FREQ_51_2 = const(0b011)
G_FREQ_23_9 = const(0b100)
G_FREQ_11_6 = const(0b101)
G_FREQ_5_7 = const(0b110)
G_FREQ_361_4 = const(0b111)
gyro_filter_values = (
G_FREQ_196_6,
G_FREQ_151_8,
G_FREQ_119_5,
G_FREQ_51_2,
G_FREQ_23_9,
G_FREQ_11_6,
G_FREQ_5_7,
G_FREQ_361_4,
)
[docs]class ICM20948:
"""Main class for the Sensor
:param ~machine.I2C i2c: The I2C bus the ICM20948 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:`micropython_icm20948.ICM20948` class.
First you will need to import the libraries to use the sensor
.. code-block:: python
from machine import Pin, I2C
import micropython_icm20948.icm20948 as icm20948
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))
icm = icm20948.ICM20948(i2c)
Now you have access to the :attr:`acceleration` attribute and :attr:`gyro` attribute
.. code-block:: python
accx, accy, accz = icm.accelerometer
gyro = icm.gyro
"""
# Register definitions
_device_id = RegisterStruct(_DEVICE_ID, "B")
_pwr_mgt_1 = RegisterStruct(_PWR_MGMT_1, "B")
_pwr_mgt_2 = RegisterStruct(_PWR_MGMT_2, "B")
# Register PWR_MGMT_1 (0x06)
# | DEVICE RESET | SLEEP | LP_EN | ---- | TEMP_DIS | CLKSEL(2) | CLKSEL(1) | CLKSEL(0) |
_clock_select = CBits(3, _PWR_MGMT_1, 0)
_temp_enabled = CBits(1, _PWR_MGMT_1, 3)
_sleep = CBits(1, _PWR_MGMT_1, 6)
_reset = CBits(1, _PWR_MGMT_1, 7)
# Register PWR_MGMT_2 (0x07)
# |----|----|DISABLE_ACCEL(2)|DISABLE_ACCEL(1)|DISABLE_ACCEL(0)|DISABLE_GYRO(2)|DISABLE_GYRO(1)|DISABLE_GYRO(0)|
_gyro_enable = CBits(3, _PWR_MGMT_2, 0)
_acc_enable = CBits(3, _PWR_MGMT_2, 3)
_raw_accel_data = RegisterStruct(_ACCEL_XOUT_H, ">hhh")
_raw_gyro_data = RegisterStruct(_GYRO_XOUT_H, ">hhh")
_raw_temp_data = RegisterStruct(_GYRO_XOUT_H, ">hhhh")
# Register REG_BANK_SEL (0x7F)
# | ---- | ---- | USER_BANK(1) | USER_BANK(0) | ---- | ---- | ---- | ---- |
_user_bank = CBits(2, _REG_BANK_SEL, 4)
# BANK 2
_gyro_rate_divisor = RegisterStruct(_GYRO_SMPLRT_DIV, ">B")
_acc_rate_divisor = RegisterStruct(_ACCEL_SMPLRT_DIV_1, ">H")
# ACCEL_CONFIG (0x14)
# |----|----|ACCEL_DLPFCFG(2)|ACCEL_DLPFCFG(1)|ACCEL_DLPFCFG(0)|ACCEL_FS_SEL[(1)|ACCEL_FS_SEL[(0)|ACCEL_FCHOICE|
_acc_choice = CBits(1, _ACCEL_CONFIG, 0)
_acc_data_range = CBits(2, _ACCEL_CONFIG, 1)
_acc_dplcfg = CBits(3, _ACCEL_CONFIG, 3)
# _GYRO_CONFIG_1 (0x01)
# |----|----|GYRO_DLPFCFG(2)|GYRO_DLPFCFG(1)|GYRO_DLPFCFG(0)|GYRO_FS_SEL[(1)|GYRO_FS_SEL[(0)|GYRO_FCHOICE|
_gyro_choice = CBits(0, _GYRO_CONFIG_1, 0)
_gyro_full_scale = CBits(2, _GYRO_CONFIG_1, 1)
_gyro_dplcfg = CBits(3, _GYRO_CONFIG_1, 3)
def __init__(self, i2c, address=0x69):
self._i2c = i2c
self._address = address
if self._device_id != 0xEA:
raise RuntimeError("Failed to find the ICM20948 sensor!")
self.reset = True
self._sleep = 0
self._bank = 0
self.accelerometer_range = RANGE_2G
self.gyro_full_scale = FS_500_DPS
self.acc_data_rate_divisor = 22
self.gyro_data_rate_divisor = 10
@property
def clock_select(self):
"""
CLK_SELECT_INTERNAL: Internal 20 MHz oscillator
CLK_SELECT_BEST: Auto selects the best available clock source - PLL if
ready, else use the Internal oscillator
CLK_SELECT_STOP: Stops the clock and keeps timing generator in reset
NOTE: CLKSEL should be set to ``CLK_SELECT_BEST`` to achieve full
gyroscope performance.
+------------------------------------------+-------------------+
| Mode | Value |
+==========================================+===================+
| :py:const:`icm20948.CLK_SELECT_INTERNAL` | :py:const:`0b000` |
+------------------------------------------+-------------------+
| :py:const:`icm20948.CLK_SELECT_BEST` | :py:const:`0b001` |
+------------------------------------------+-------------------+
| :py:const:`icm20948.CLK_SELECT_STOP` | :py:const:`0b111` |
+------------------------------------------+-------------------+
"""
values = {0: "CLK_SELECT_INTERNAL", 1: "CLK_SELECT_BEST", 7: "CLK_SELECT_STOP"}
return values[self._clock_select]
@clock_select.setter
def clock_select(self, value):
if value not in clk_values:
raise ValueError("Select a valid Clock Select setting")
self._clock_select = value
@property
def reset(self) -> int:
"""
Reset the internal registers and restores the default settings. Write a 1 to set the
reset, the bit will auto clear
"""
return self._reset
@reset.setter
def reset(self, value: int = 1) -> None:
self._reset = value
sleep(1)
@property
def gyro_enabled(self) -> str:
"""
Gyro Enabled
+------------------------------------+------------------------------------------------------+
| Mode | Value |
+====================================+======================================================+
| :py:const:`icm20948.GYRO_ENABLED` | :py:const:`0b000` Gyroscope (all axes) on |
+------------------------------------+------------------------------------------------------+
| :py:const:`icm20948.GYRO_DISABLED` | :py:const:`0b111` Gyroscope (all axes) disabled |
+------------------------------------+------------------------------------------------------+
"""
values = {0: "GYRO_DISABLED", 7: "GYRO_ENABLED"}
return values[self._gyro_enable]
@gyro_enabled.setter
def gyro_enabled(self, value: int) -> None:
if value not in gyro_en_values:
raise ValueError("Value must be a valid Gyro Enabled setting")
self._gyro_enable = value
@property
def acc_enabled(self) -> str:
"""
Accelerometer enabled
+------------------------------------+------------------------------------------------------+
| Mode | Value |
+====================================+======================================================+
| :py:const:`icm20948.ACC_ENABLED` | :py:const:`0b000` Accelerometer (all axes) on |
+------------------------------------+------------------------------------------------------+
| :py:const:`icm20948.ACC_DISABLED` | :py:const:`0b111` Accelerometer (all axes) disabled |
+------------------------------------+------------------------------------------------------+
"""
values = {0: "ACC_DISABLED", 7: "ACC_ENABLED"}
return values[self._acc_enable]
@acc_enabled.setter
def acc_enabled(self, value: int) -> None:
if value not in acc_en_values:
raise ValueError("Value must be a valid Accelerometer Enabled setting")
self._acc_enable = value
@property
def temperature_enabled(self) -> str:
"""
Temperature Enabled. When set to 1, this bit disables the temperature sensor.
+------------------------------------+----------------------------------------+
| Mode | Value |
+====================================+========================================+
| :py:const:`icm20948.TEMP_ENABLED` | :py:const:`0b0` Temperature on |
+------------------------------------+----------------------------------------+
| :py:const:`icm20948.TEMP_DISABLED` | :py:const:`0b1` Temperature disabled |
+------------------------------------+----------------------------------------+
"""
values = ("TEMP_DISABLED", "TEMP_ENABLED")
return values[self._temp_enabled]
@temperature_enabled.setter
def temperature_enabled(self, value: int) -> None:
if value not in temperature_en_values:
raise ValueError("Value must be a valid Temperature Enabled setting")
self._temp_enabled = 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
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) -> Tuple[float, float, float]:
"""
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
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
@property
def power_bank(self) -> int:
"""
Power bank selected
"""
return self._user_bank
@power_bank.setter
def power_bank(self, value: int) -> None:
self._user_bank = value
sleep(0.005)
@property
def accelerometer_range(self) -> str:
"""
Sensor acceleration_range
+--------------------------------+------------------+
| Mode | Value |
+================================+==================+
| :py:const:`icm20948.RANGE_2G` | :py:const:`0b00` |
+--------------------------------+------------------+
| :py:const:`icm20948.RANGE_4G` | :py:const:`0b01` |
+--------------------------------+------------------+
| :py:const:`icm20948.RANGE_8G` | :py:const:`0b10` |
+--------------------------------+------------------+
| :py:const:`icm20948.RANGE_16G` | :py:const:`0b11` |
+--------------------------------+------------------+
"""
values = ("RANGE_2G", "RANGE_4G", "RANGE_8G", "RANGE_16G")
return values[self._memory_accel_range]
@accelerometer_range.setter
def accelerometer_range(self, value: int) -> None:
if value not in acc_range_values:
raise ValueError("Value must be a valid Accelerometer Range Setting")
self._user_bank = 2
self._acc_data_range = value
self._memory_accel_range = value
self._user_bank = 0
@property
def gyro_full_scale(self) -> str:
"""
Sensor gyro_full_scale
+----------------------------------+------------------+
| Mode | Value |
+==================================+==================+
| :py:const:`icm20948.FS_250_DPS` | :py:const:`0b00` |
+----------------------------------+------------------+
| :py:const:`icm20948.FS_500_DPS` | :py:const:`0b01` |
+----------------------------------+------------------+
| :py:const:`icm20948.FS_1000_DPS` | :py:const:`0b10` |
+----------------------------------+------------------+
| :py:const:`icm20948.FS_2000_DPS` | :py:const:`0b11` |
+----------------------------------+------------------+
"""
values = ("FS_250_DPS", "FS_500_DPS", "FS_1000_DPS", "FS_2000_DPS")
return values[self._memory_gyro_fs]
@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._user_bank = 2
self._gyro_full_scale = value
self._memory_gyro_fs = value
self._user_bank = 0
@property
def temperature(self) -> float:
"""
Temperature Value. In the setup tested, there is the need to read either the values
from acceleration, gyro and temperature or gyro and temperature at the same time
in order to have a logic temperature value.
"""
return (self._raw_temp_data[3] / 333.87) + 21
@property
def gyro_data_rate(self):
"""The rate at which gyro measurements are taken in Hz"""
return list(gyro_rate_values.keys())[
list(gyro_rate_values.values()).index(self.gyro_data_rate_divisor)
]
@gyro_data_rate.setter
def gyro_data_rate(self, value: int) -> None:
"""
.. note::
The data rates are set indirectly by setting a rate divisor according to the
following formula:
.. math::
\\text{gyro_data_rate } = \\frac{1125}{1 + divisor}
However, this library will accept only data rates specified in the following list to match the
right divisor.
Accepted values are:
| * 562.5
| * 375.0
| * 281.3
| * 225.0
| * 187.5
| * 140.6
| * 125.0
| * 102.3
| * 70.3
| * 66.2
| * 48.9
| * 35.2
| * 34.1
| * 17.6
| * 17.3
| * 4.4
"""
if value not in gyro_data_rate_values:
raise ValueError("Gyro data rate must be a valid setting")
self.gyro_data_rate_divisor = gyro_rate_values[value]
@property
def gyro_data_rate_divisor(self) -> int:
"""
Accepted values are:
| * 1
| * 2
| * 3
| * 4
| * 5
| * 7
| * 8
| * 10
| * 15
| * 16
| * 22
| * 31
| * 32
| * 63
| * 64
| * 255
"""
self._user_bank = 2
raw_rate_divisor = self._gyro_rate_divisor
self._user_bank = 0
return raw_rate_divisor
@gyro_data_rate_divisor.setter
def gyro_data_rate_divisor(self, value: int) -> None:
if value not in gyro_rate_divisor_values:
raise ValueError("Value must be a valid gyro data rate divisor setting")
self._user_bank = 2
self._gyro_rate_divisor = value
self._user_bank = 0
@property
def acc_data_rate(self):
"""The rate at which accelerometer measurements are taken in Hz"""
return list(acc_rate_values.keys())[
list(acc_rate_values.values()).index(self.acc_data_rate_divisor)
]
@acc_data_rate.setter
def acc_data_rate(self, value: int) -> None:
"""
.. note::
The data rates are set indirectly by setting a rate divisor according to the
following formula:
.. math::
\\text{acc_data_rate } = \\frac{1125}{1 + divisor}
However, this library will accept only data rates specified in the following list to match the
right divisor.
Accepted values are:
| * 140.6
| * 102.3
| * 70.3
| * 48.9
| * 35.2
| * 17.6
| * 8.8
| * 4.4
| * 2.2
| * 1.1
| * 0.55
| * 0.27
"""
if value not in acc_data_rate_values:
raise ValueError("Accelerometer data rate must be a valid setting")
self.acc_data_rate_divisor = acc_rate_values[value]
@property
def acc_data_rate_divisor(self) -> int:
"""
Accepted values are:
| * 7
| * 10
| * 15
| * 22
| * 31
| * 63
| * 127
| * 255
| * 513
| * 1022
| * 2044
| * 4095
"""
self._user_bank = 2
raw_rate_divisor = self._acc_rate_divisor
self._user_bank = 0
return raw_rate_divisor
@acc_data_rate_divisor.setter
def acc_data_rate_divisor(self, value: int) -> None:
if value not in acc_rate_divisor_values:
raise ValueError(
"Value must be a valid acceleration data rate divisor setting"
)
self._user_bank = 2
self._acc_rate_divisor = value
self._user_bank = 0
@property
def acc_dlpf_cutoff(self) -> int:
"""The cutoff frequency for the accelerometer's digital low pass filter. Signals
above the given frequency will be filtered out.
.. note::
Readings immediately following setting a cutoff frequency will be
inaccurate due to the filter "warming up"
+---------------------------------+-------------------+
| Mode | Value |
+=================================+===================+
| :py:const:`icm20948.FREQ_246_0` | :py:const:`0b001` |
+---------------------------------+-------------------+
| :py:const:`icm20948.FREQ_111_4` | :py:const:`0b010` |
+---------------------------------+-------------------+
| :py:const:`icm20948.FREQ_50_4` | :py:const:`0b011` |
+---------------------------------+-------------------+
| :py:const:`icm20948.FREQ_23_9` | :py:const:`0b100` |
+---------------------------------+-------------------+
| :py:const:`icm20948.FREQ_11_5` | :py:const:`0b101` |
+---------------------------------+-------------------+
| :py:const:`icm20948.FREQ_5_7` | :py:const:`0b110` |
+---------------------------------+-------------------+
| :py:const:`icm20948.FREQ_473` | :py:const:`0b111` |
+---------------------------------+-------------------+
"""
values = (
"FREQ_246_0",
"FREQ_111_4",
"FREQ_50_4",
"FREQ_23_9",
"FREQ_11_5",
"FREQ_5_7",
"FREQ_473",
)
self._user_bank = 2
raw_value = self._acc_dplcfg
self._user_bank = 0
return values[raw_value - 1]
@acc_dlpf_cutoff.setter
def acc_dlpf_cutoff(self, value: int) -> None:
if value not in acc_filter_values:
raise ValueError("Value must be a valid dlpf setting")
self._user_bank = 2
self._acc_dplcfg = value
self._user_bank = 0
@property
def acc_filter_choice(self) -> int:
"""Enables accelerometer DLPF"""
self._user_bank = 2
raw_value = self._acc_choice
self._user_bank = 0
return raw_value
@acc_filter_choice.setter
def acc_filter_choice(self, value: int) -> None:
self._user_bank = 2
self._acc_choice = value
self._user_bank = 0
@property
def gyro_dlpf_cutoff(self) -> int:
"""The cutoff frequency for the gyro's digital low pass filter. Signals
above the given frequency will be filtered out.
.. note::
Readings immediately following setting a cutoff frequency will be
inaccurate due to the filter "warming up"
+-----------------------------------+-------------------+
| Mode | Value |
+===================================+===================+
| :py:const:`icm20948.G_FREQ_196_6` | :py:const:`0b000` |
+-----------------------------------+-------------------+
| :py:const:`icm20948.G_FREQ_151_8` | :py:const:`0b001` |
+-----------------------------------+-------------------+
| :py:const:`icm20948.G_FREQ_119_5` | :py:const:`0b010` |
+-----------------------------------+-------------------+
| :py:const:`icm20948.G_FREQ_51_2` | :py:const:`0b011` |
+-----------------------------------+-------------------+
| :py:const:`icm20948.G_FREQ_23_9` | :py:const:`0b100` |
+-----------------------------------+-------------------+
| :py:const:`icm20948.G_FREQ_11_6` | :py:const:`0b101` |
+-----------------------------------+-------------------+
| :py:const:`icm20948.G_FREQ_5_7` | :py:const:`0b110` |
+-----------------------------------+-------------------+
| :py:const:`icm20948.G_FREQ_361_4` | :py:const:`0b111` |
+-----------------------------------+-------------------+
"""
values = (
"G_FREQ_196_6",
"G_FREQ_151_8",
"G_FREQ_119_5",
"G_FREQ_51_2",
"G_FREQ_23_9",
"G_FREQ_11_6",
"G_FREQ_5_7",
"G_FREQ_361_4",
)
self._user_bank = 2
raw_value = self._gyro_dplcfg
self._user_bank = 0
return values[raw_value]
@gyro_dlpf_cutoff.setter
def gyro_dlpf_cutoff(self, value: int) -> None:
if value not in gyro_filter_values:
raise ValueError("Value must be a valid dlpf setting")
self._user_bank = 2
self._gyro_dplcfg = value
self._user_bank = 0
@property
def gyro_filter_choice(self) -> int:
"""Enables gyro DLPF"""
self._user_bank = 2
raw_value = self._gyro_choice
self._user_bank = 0
return raw_value
@gyro_filter_choice.setter
def gyro_filter_choice(self, value: int) -> None:
self._user_bank = 2
self._gyro_choice = value
self._user_bank = 0