From 30a47b991131d015e47cc38da39fcfd14fa22aa9 Mon Sep 17 00:00:00 2001 From: Robert Budai Date: Fri, 1 Nov 2024 15:22:44 +0200 Subject: [PATCH] Add support for adxl380 and adxl382 Signed-off-by: Robert Budai --- adi/__init__.py | 1 + adi/adxl380.py | 187 +++++++++++++++++++++++++++++ doc/source/devices/adi.adxl380.rst | 7 ++ doc/source/devices/index.rst | 1 + examples/adxl380_example.py | 114 ++++++++++++++++++ supported_parts.md | 2 + test/emu/devices/adxl382.xml | 74 ++++++++++++ test/emu/hardware_map.yml | 8 ++ test/test_adxl380.py | 45 +++++++ 9 files changed, 439 insertions(+) create mode 100644 adi/adxl380.py create mode 100644 doc/source/devices/adi.adxl380.rst create mode 100644 examples/adxl380_example.py create mode 100644 test/emu/devices/adxl382.xml create mode 100644 test/test_adxl380.py diff --git a/adi/__init__.py b/adi/__init__.py index 57dfd9ce8..2db3ec9ee 100644 --- a/adi/__init__.py +++ b/adi/__init__.py @@ -90,6 +90,7 @@ from adi.adxl313 import adxl313 from adi.adxl345 import adxl345 from adi.adxl355 import adxl355 +from adi.adxl380 import adxl380 from adi.adxrs290 import adxrs290 from adi.cn0511 import cn0511 from adi.cn0532 import cn0532 diff --git a/adi/adxl380.py b/adi/adxl380.py new file mode 100644 index 000000000..ab36fe7af --- /dev/null +++ b/adi/adxl380.py @@ -0,0 +1,187 @@ +# Copyright (C) 2021-2024 Analog Devices, Inc. +# +# SPDX short identifier: ADIBSD + +from decimal import Decimal + +from adi.attribute import attribute +from adi.context_manager import context_manager +from adi.rx_tx import rx + + +class adxl380(rx, context_manager, attribute): + """ adxl380 3-axis accelerometer """ + + _device_name = "adxl380" + _rx_unbuffered_data = True + _rx_data_si_type = float + + def __init__(self, uri="", device_name=None): + context_manager.__init__(self, uri, self._device_name) + + compatible_parts = ["adxl380", "adxl382"] + + if not device_name: + device_name = compatible_parts[0] + + if device_name not in compatible_parts: + raise Exception( + "Not a compatible device:" + + str(device_name) + + ".Please select from:" + + str(compatible_parts) + ) + else: + print("Device found: ", device_name) + self._ctrl = self._ctx.find_device(device_name) + self._rxadc = self._ctx.find_device(device_name) + + if self._ctrl is None: + print( + "No device found with device_name = " + + device_name + + ". Searching for a device found in the compatible list." + ) + for i in compatible_parts: + self._ctrl = self._ctx.find_device(i) + self._rxadc = self._ctx.find_device(i) + if self._ctrl is not None: + print("Found device = " + i + ". Will use this device instead.") + break + if self._ctrl is None: + raise Exception("No compatible device found") + + self.accel_x = self._channel(self._ctrl, "accel_x") + self.accel_y = self._channel(self._ctrl, "accel_y") + self.accel_z = self._channel(self._ctrl, "accel_z") + self.temp = self._tempchannel(self._ctrl, "temp") + self._rxadc = self._ctx.find_device("adxl380") + self._rx_channel_names = ["accel_x", "accel_y", "accel_z"] + rx.__init__(self) + self.rx_buffer_size = 16 # Make default buffer smaller + + @property + def sampling_frequency(self): + """Device sampling frequency""" + return self._get_iio_dev_attr_str("sampling_frequency") + + @sampling_frequency.setter + def sampling_frequency(self, value): + self._set_iio_dev_attr_str("sampling_frequency", str(Decimal(value).real)) + + @property + def sampling_frequency_available(self): + """Device available sampling frequency""" + return self._get_iio_dev_attr_str("sampling_frequency_available") + + @property + def waiting_for_supplier(self): + """Device waiting for supplier""" + return self._get_iio_dev_attr_str("waiting_for_supplier") + + def to_degrees(self, raw): + """Convert raw to degrees Celsius""" + return (raw + self.temp.offset) * self.temp.scale / 1000.0 + + class _tempchannel(attribute): + """adxl380 temperature channel""" + + def __init__(self, ctrl, channel_name): + self.name = channel_name + self._ctrl = ctrl + + @property + def offset(self): + """adxl380 temperature offset value""" + return self._get_iio_attr(self.name, "offset", False) + + @property + def raw(self): + """adxl380 temperature raw value""" + return self._get_iio_attr(self.name, "raw", False) + + @property + def scale(self): + """adxl380 channel scale value""" + return self._get_iio_attr(self.name, "scale", False) + + class _channel(attribute): + """adxl380 acceleration channel""" + + def __init__(self, ctrl, channel_name): + self.name = channel_name + self._ctrl = ctrl + + @property + def calibbias(self): + """adxl380 channel offset""" + return self._get_iio_attr(self.name, "calibbias", False) + + @calibbias.setter + def calibbias(self, value): + self._set_iio_attr(self.name, "calibbias", False, value) + + @property + def filter_high_pass_3db_frequency(self): + """adxl380 highpass filter cutoff frequency""" + return self._get_iio_attr( + self.name, "filter_high_pass_3db_frequency", False + ) + + @filter_high_pass_3db_frequency.setter + def filter_high_pass_3db_frequency(self, value): + self._set_iio_attr( + self.name, + "filter_high_pass_3db_frequency", + False, + str(Decimal(value).real), + ) + + @property + def filter_high_pass_3db_frequency_available(self): + """Provides all available highpass filter cutoff frequency settings for the adxl380 channels""" + return self._get_iio_attr( + self.name, "filter_high_pass_3db_frequency_available", False + ) + + @property + def filter_low_pass_3db_frequency(self): + """adxl380 lowpass filter cutoff frequency""" + return self._get_iio_attr(self.name, "filter_low_pass_3db_frequency", False) + + @filter_low_pass_3db_frequency.setter + def filter_low_pass_3db_frequency(self, value): + self._set_iio_attr( + self.name, + "filter_low_pass_3db_frequency", + False, + str(Decimal(value).real), + ) + + @property + def filter_low_pass_3db_frequency_available(self): + """Provides all available lowpass filter cutoff frequency settings for the adxl380 channels""" + return self._get_iio_attr( + self.name, "filter_low_pass_3db_frequency_available", False + ) + + @property + def raw(self): + """adxl380 channel raw value""" + return self._get_iio_attr(self.name, "raw", False) + + @property + def scale(self): + """adxl380 channel scale(gain)""" + return float(self._get_iio_attr_str(self.name, "scale", False)) + + @scale.setter + def scale(self, value): + self._set_iio_attr( + self.name, "scale", False, str(Decimal(value).real), + ) + + @property + def scale_available(self): + """Provides all available scale settings for the adxl380 channels""" + return self._get_iio_attr(self.name, "scale_available", False) diff --git a/doc/source/devices/adi.adxl380.rst b/doc/source/devices/adi.adxl380.rst new file mode 100644 index 000000000..d79a5a199 --- /dev/null +++ b/doc/source/devices/adi.adxl380.rst @@ -0,0 +1,7 @@ +adxl380 +================== + +.. automodule:: adi.adxl380 + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/devices/index.rst b/doc/source/devices/index.rst index b22265878..1856b5aba 100644 --- a/doc/source/devices/index.rst +++ b/doc/source/devices/index.rst @@ -96,6 +96,7 @@ Supported Devices adi.adxl313 adi.adxl345 adi.adxl355 + adi.adxl380 adi.adxrs290 adi.cn0511 adi.cn0532 diff --git a/examples/adxl380_example.py b/examples/adxl380_example.py new file mode 100644 index 000000000..37cede9df --- /dev/null +++ b/examples/adxl380_example.py @@ -0,0 +1,114 @@ +# Copyright (C) 2021 Analog Devices, Inc. +# +# SPDX short identifier: ADIBSD + +import sys +import time + +import adi + +my_dev_name = sys.argv[1] +my_uri = sys.argv[2] + +print("uri: " + str(my_uri)) + +# Set up adxl380 +my_acc = adi.adxl380(uri=my_uri, device_name=my_dev_name) +# my_acc.rx_buffer_size = 16 +my_acc.rx_enabled_channels = [0, 1, 2] + +print("\nChecking temperature channel...") +print("Temperature raw: " + str(my_acc.temp.raw)) +print("Calculated Temperature: " + str(my_acc.to_degrees(my_acc.temp.raw))) + + +print("\nInitial sample frequency:") +print("Sample frequency: " + str(my_acc.sampling_frequency)) + +print("Single calculated acceleration values:") +print("\nX acceleration: " + str(my_acc.accel_x.raw * my_acc.accel_x.scale) + " m/s^2") +print("Y acceleration: " + str(my_acc.accel_y.raw * my_acc.accel_y.scale) + " m/s^2") +print("Z acceleration: " + str(my_acc.accel_z.raw * my_acc.accel_z.scale) + " m/s^2") + +print("\nSetting sample frequencies to 1000 sps...") +my_acc.sampling_frequency = "{:.6f}".format(16000) +time.sleep(0.25) + +print("Verifying new sample rate: " + str(my_acc.sampling_frequency)) +print("Setting back to 4000 sps...") +my_acc.sampling_frequency = "{:.6f}".format(4000.0) +time.sleep(0.25) + +my_acc.rx_output_type = "raw" +print("\nData using buffered rx(), raw:") +print(my_acc.rx()) + +cutoffs = my_acc.accel_x.filter_high_pass_3db_frequency_available +print("\nAvailable highpass cutoff frequencies: " + str(cutoffs)) + +print( + "\nSetting highpass cutoff frequency to " + + str(cutoffs[1]) + + " then taking a 2 second nap to settle..." +) +my_acc.accel_x.filter_high_pass_3db_frequency = "{:.6f}".format(cutoffs[1]) +my_acc.accel_y.filter_high_pass_3db_frequency = "{:.6f}".format(cutoffs[1]) +my_acc.accel_z.filter_high_pass_3db_frequency = "{:.6f}".format(cutoffs[1]) + +print( + "\nX highpass cutoff frequency: " + + str(my_acc.accel_x.filter_high_pass_3db_frequency) +) +print( + "Y highpass cutoff frequency: " + str(my_acc.accel_y.filter_high_pass_3db_frequency) +) +print( + "Z highpass cutoff frequency: " + str(my_acc.accel_z.filter_high_pass_3db_frequency) +) + +time.sleep(2.0) + +print( + "\nAccelerations after highpass, should be close to zero if the adxl380 is sitting still...\n" +) +print("X acceleration: " + str(my_acc.accel_x.raw * my_acc.accel_x.scale) + " m/s^2") +print("Y acceleration: " + str(my_acc.accel_y.raw * my_acc.accel_y.scale) + " m/s^2") +print("Z acceleration: " + str(my_acc.accel_z.raw * my_acc.accel_z.scale) + " m/s^2") + +print( + "\nSetting highpass cutoff frequency back to zero, then taking a 4 second nap to settle..." +) +my_acc.accel_x.filter_high_pass_3db_frequency = "{:.6f}".format(0.0) +my_acc.accel_y.filter_high_pass_3db_frequency = "{:.6f}".format(0.0) +my_acc.accel_z.filter_high_pass_3db_frequency = "{:.6f}".format(0.0) + +time.sleep(4.0) + +print("\nAccelerations after highpass settling...") + +print("X acceleration: " + str(my_acc.accel_x.raw * my_acc.accel_x.scale) + " m/s^2") +print("Y acceleration: " + str(my_acc.accel_y.raw * my_acc.accel_y.scale) + " m/s^2") +print("Z acceleration: " + str(my_acc.accel_z.raw * my_acc.accel_z.scale) + " m/s^2") + +print("\nSetting offset for each axis...\n") +print("X offset set to: " + str(my_acc.accel_x.raw >> 4)) +print("Y offset set to: " + str(my_acc.accel_y.raw >> 4)) +print("Z offset set to: " + str(my_acc.accel_z.raw >> 4)) + +my_acc.accel_x.calibbias = "{:.6f}".format(my_acc.accel_x.raw >> 4) +my_acc.accel_y.calibbias = "{:.6f}".format(my_acc.accel_y.raw >> 4) +my_acc.accel_z.calibbias = "{:.6f}".format(my_acc.accel_z.raw >> 4) + +print( + "\nAccelerations after setting offset, should be close to zero if the adxl380 is sitting still...\n" +) +print("X acceleration: " + str(my_acc.accel_x.raw * my_acc.accel_x.scale) + " m/s^2") +print("Y acceleration: " + str(my_acc.accel_y.raw * my_acc.accel_y.scale) + " m/s^2") +print("Z acceleration: " + str(my_acc.accel_z.raw * my_acc.accel_z.scale) + " m/s^2") + +print("\nSetting offset for each axis back to 0...") +my_acc.accel_x.calibbias = "{:.6f}".format(0.0) +my_acc.accel_y.calibbias = "{:.6f}".format(0.0) +my_acc.accel_z.calibbias = "{:.6f}".format(0.0) + +del my_acc diff --git a/supported_parts.md b/supported_parts.md index 035de9857..b59977b20 100644 --- a/supported_parts.md +++ b/supported_parts.md @@ -160,6 +160,8 @@ - ADXL314 - ADXL345 - ADXL355 +- ADXL380 +- ADXL382 - ADXL1002 - ADXRS290 - CN0511 diff --git a/test/emu/devices/adxl382.xml b/test/emu/devices/adxl382.xml new file mode 100644 index 000000000..57c503f13 --- /dev/null +++ b/test/emu/devices/adxl382.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/emu/hardware_map.yml b/test/emu/hardware_map.yml index 48b1938f9..d89acb582 100644 --- a/test/emu/hardware_map.yml +++ b/test/emu/hardware_map.yml @@ -327,6 +327,14 @@ adxl345: - filename: adxl345.xml - data_devices: - iio:device0 +adxl380: + - adxl380 + - pyadi_iio_class_support: + - adxl380 + - emulate: + - filename: adxl382.xml + - data_devices: + - iio:device0 ad7746: - ad7746 - pyadi_iio_class_support: diff --git a/test/test_adxl380.py b/test/test_adxl380.py new file mode 100644 index 000000000..c00e6372c --- /dev/null +++ b/test/test_adxl380.py @@ -0,0 +1,45 @@ +import pytest + +hardware = "adxl380" +classname = "adi.adxl380" + +######################################### +@pytest.mark.iio_hardware(hardware) +@pytest.mark.parametrize("classname", [(classname)]) +@pytest.mark.parametrize("channel", [0, 1, 2]) +def test_adxl380_rx_data(test_dma_rx, iio_uri, classname, channel): + test_dma_rx(iio_uri, classname, channel, buffer_size=2 ** 5) + + +######################################### +@pytest.mark.iio_hardware(hardware) +@pytest.mark.parametrize("classname", [(classname)]) +@pytest.mark.parametrize( + "attr, start, stop, step, tol, repeats, sub_channel", + [ + ("calibbias", 0, 30000, 10000, 1, 3, "accel_x"), + ("calibbias", 0, 30000, 10000, 1, 3, "accel_y"), + ("calibbias", 0, 30000, 10000, 1, 3, "accel_z"), + ("filter_high_pass_3db_frequency", 39.520000, 39.520000, 1, 1, 3, "accel_x"), + ("filter_high_pass_3db_frequency", 39.520000, 39.520000, 1, 1, 3, "accel_y"), + ("filter_high_pass_3db_frequency", 39.520000, 39.520000, 1, 1, 3, "accel_z"), + ("filter_low_pass_3db_frequency", 4000, 4000, 1, 1, 3, "accel_x"), + ("filter_low_pass_3db_frequency", 4000, 4000, 1, 1, 3, "accel_y"), + ("filter_low_pass_3db_frequency", 4000, 4000, 1, 1, 3, "accel_z"), + ], +) +def test_adxl380_attr( + test_attribute_single_value, + iio_uri, + classname, + attr, + start, + stop, + step, + tol, + repeats, + sub_channel, +): + test_attribute_single_value( + iio_uri, classname, attr, start, stop, step, tol, repeats, sub_channel + )