diff --git a/adi/__init__.py b/adi/__init__.py index 8b36a295a..567db267a 100644 --- a/adi/__init__.py +++ b/adi/__init__.py @@ -57,6 +57,7 @@ from adi.adf4159 import adf4159 from adi.adf4355 import adf4355 from adi.adf4371 import adf4371 +from adi.adf4382 import adf4382 from adi.adf5610 import adf5610 from adi.adg2128 import adg2128 from adi.adis16460 import adis16460 diff --git a/adi/adf4382.py b/adi/adf4382.py new file mode 100644 index 000000000..e1f976d15 --- /dev/null +++ b/adi/adf4382.py @@ -0,0 +1,224 @@ +# Copyright (C) 2023-2025 Analog Devices, Inc. +# +# SPDX short identifier: ADIBSD + +from adi.attribute import attribute +from adi.context_manager import context_manager + + +class adf4382(attribute, context_manager): + """ADF4382 Microwave Wideband Synthesizer with Integrated VCO + + parameters: + uri: type=string + URI of IIO context with ADF4382 + """ + + _device_name = "adf4382" + _charge_pump_options = ( + "0.700000", + "0.900000", + "1.100000", + "1.300000", + "1.500000", + "1.900000", + "2.300000", + "2.700000", + "3.100000", + "3.900000", + "4.700000", + "5.500000", + "6.300000", + "7.900000", + "9.500000", + "11.100000", + ) + + def __init__(self, uri=""): + context_manager.__init__(self, uri, self._device_name) + + # Find the device + self._ctrl = self._ctx.find_device(self._device_name) + + # Raise an exception if the device isn't found + if not self._ctrl: + raise Exception("ADF4355 device not found") + + @property + def altvolt0_en(self): + return bool(self._get_iio_attr("altvoltage0", "en", True, self._ctrl)) + + @altvolt0_en.setter + def altvolt0_en(self, value): + self._set_iio_attr("altvoltage0", "en", True, int(value), self._ctrl) + + @property + def altvolt0_output_power(self): + return self._get_iio_attr("altvoltage0", "output_power", True, self._ctrl) + + @altvolt0_output_power.setter + def altvolt0_output_power(self, value): + self._set_iio_attr_int( + "altvoltage0", "output_power", True, int(value), self._ctrl + ) + + @property + def altvolt0_frequency(self): + return self._get_iio_attr("altvoltage0", "frequency", True, self._ctrl) + + @altvolt0_frequency.setter + def altvolt0_frequency(self, value): + self._set_iio_attr_int("altvoltage0", "frequency", True, value, self._ctrl) + + @property + def altvolt0_phase(self): + return self._get_iio_attr("altvoltage0", "phase", True, self._ctrl) + + @altvolt0_phase.setter + def altvolt0_phase(self, value): + self._set_iio_attr_int("altvoltage0", "phase", True, value, self._ctrl) + + @property + def altvolt1_en(self): + return bool(self._get_iio_attr("altvoltage1", "en", True, self._ctrl)) + + @altvolt1_en.setter + def altvolt1_en(self, value): + self._set_iio_attr("altvoltage1", "en", True, int(value), self._ctrl) + + @property + def altvolt1_output_power(self): + return self._get_iio_attr("altvoltage1", "output_power", True, self._ctrl) + + @altvolt1_output_power.setter + def altvolt1_output_power(self, value): + self._set_iio_attr_int( + "altvoltage1", "output_power", True, int(value), self._ctrl + ) + + @property + def altvolt1_frequency(self): + return self._get_iio_attr("altvoltage1", "frequency", True, self._ctrl) + + @altvolt1_frequency.setter + def altvolt1_frequency(self, value): + self._set_iio_attr_int("altvoltage1", "frequency", True, value, self._ctrl) + + @property + def altvolt1_phase(self): + return self._get_iio_attr("altvoltage1", "phase", True, self._ctrl) + + @altvolt1_phase.setter + def altvolt1_phase(self, value): + self._set_iio_attr_int("altvoltage1", "phase", True, value, self._ctrl) + + @property + def bleed_current(self): + return self._get_iio_dev_attr("bleed_current", self._ctrl) + + @bleed_current.setter + def bleed_current(self, value): + self._set_iio_dev_attr("bleed_current", value, self._ctrl) + + @property + def charge_pump_current(self): + return self._get_iio_dev_attr("charge_pump_current", self._ctrl) + + @charge_pump_current.setter + def charge_pump_current(self, value): + # Check that the value is valid + if value.lower().strip() not in self._charge_pump_options: + raise ValueError( + f"charge_pump_current of \"{value}\" is invalid. Valid options: {', '.join(self._charge_pump_options)}" + ) + + self._set_iio_dev_attr("charge_pump_current", value, self._ctrl) + + @property + def reference_divider(self): + return self._get_iio_dev_attr("reference_divider", self._ctrl) + + @reference_divider.setter + def reference_divider(self, value): + self._set_iio_dev_attr("reference_divider", value, self._ctrl) + + @property + def reference_doubler_en(self): + return self._get_iio_dev_attr("reference_doubler_en", self._ctrl) + + @reference_doubler_en.setter + def reference_doubler_en(self, value): + self._set_iio_dev_attr("reference_doubler_en", value, self._ctrl) + + @property + def reference_frequency(self): + return self._get_iio_dev_attr("reference_frequency", self._ctrl) + + @reference_frequency.setter + def reference_frequency(self, value): + self._set_iio_dev_attr("reference_frequency", value, self._ctrl) + + @property + def sw_sync_en(self): + return self._get_iio_dev_attr("sw_sync_en", self._ctrl) + + @sw_sync_en.setter + def sw_sync_en(self, value): + self._set_iio_dev_attr("sw_sync_en", value, self._ctrl) + + @property + def ezsync_setup(self): + return self._get_iio_dev_attr("ezsync_setup", self._ctrl) + + @ezsync_setup.setter + def ezsync_setup(self, value): + self._set_iio_dev_attr("ezsync_setup", value, self._ctrl) + + @property + def timed_sync_setup(self): + return self._get_iio_dev_attr("timed_sync_setup", self._ctrl) + + @timed_sync_setup.setter + def timed_sync_setup(self, value): + self._set_iio_dev_attr("timed_sync_setup", value, self._ctrl) + + @property + def fastcal_en(self): + return self._get_iio_dev_attr("fastcal_en", self._ctrl) + + @fastcal_en.setter + def fastcal_en(self, value): + self._set_iio_dev_attr("fastcal_en", value, self._ctrl) + + @property + def fastcal_lut_en(self): + return self._get_iio_dev_attr("fastcal_lut_en", self._ctrl) + + @fastcal_lut_en.setter + def fastcal_lut_en(self, value): + self._set_iio_dev_attr("fastcal_lut_en", value, self._ctrl) + + @property + def change_frequency(self): + return self._get_iio_dev_attr("change_frequency", self._ctrl) + + @change_frequency.setter + def change_frequency(self, value): + self._set_iio_dev_attr("change_frequency", value, self._ctrl) + + @property + def start_calibration(self): + return self._get_iio_dev_attr("start_calibration", self._ctrl) + + @start_calibration.setter + def start_calibration(self, value): + self._set_iio_dev_attr("start_calibration", value, self._ctrl) + + def reg_read(self, reg): + """Direct Register Access via debugfs""" + self._set_iio_debug_attr_str("direct_reg_access", reg, self._ctrl) + return self._get_iio_debug_attr_str("direct_reg_access", self._ctrl) + + def reg_write(self, reg, value): + """Direct Register Access via debugfs""" + self._set_iio_debug_attr_str("direct_reg_access", f"{reg} {value}", self._ctrl) diff --git a/doc/source/devices/adi.adf4382.rst b/doc/source/devices/adi.adf4382.rst new file mode 100644 index 000000000..02c4f7e9c --- /dev/null +++ b/doc/source/devices/adi.adf4382.rst @@ -0,0 +1,7 @@ +adf4382 +================== + +.. automodule:: adi.adf4382 + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/devices/index.rst b/doc/source/devices/index.rst index 1856b5aba..dff533fb1 100644 --- a/doc/source/devices/index.rst +++ b/doc/source/devices/index.rst @@ -65,6 +65,7 @@ Supported Devices adi.adf4159 adi.adf4355 adi.adf4371 + adi.adf4382 adi.adf5610 adi.adg2128 adi.adis16375 diff --git a/examples/adf4382_example.py b/examples/adf4382_example.py new file mode 100644 index 000000000..05e1c81c1 --- /dev/null +++ b/examples/adf4382_example.py @@ -0,0 +1,28 @@ +# Copyright (C) 2023-2024 Analog Devices, Inc. +# +# SPDX short identifier: ADIBSD + +import adi + +# Create pll instance connected to serial port +pll = adi.adf4382(uri="serial:/dev/ttyACM0,115200,8n1n") + +# Configure pll attributes +pll.sw_sync_en = 0 # Disable sync +pll.reference_frequency = 125000000 # Input reference clock +pll.reference_doubler_en = 1 # Enable reference doubler +pll.reference_divider = 1 # Set reference divider +pll.charge_pump_current = "11.100000" # Set charge pump current in mA + +pll.altvolt0_frequency = 20000000000 # Output reference clock +pll.altvolt0_en = 1 # Enable output channel 0 +pll.altvolt0_output_power = 9 # Set output amplitude of ch. 0 +pll.altvolt1_en = 0 # Disable output channel 0 + +# Configure fast calibration +# pll.fastcal_en = 1 # Enable fast calibration +# pll.fastcal_lut_en = 1 # Enable use of fast calibration Look-Up Table + +pll.reg_write("0x0A", "0x55") # Use debug attribute to write to schratchpad +val = pll.reg_read(0x0A) # Use debug attribute to read the schratchpad +print(hex(int(val))) diff --git a/supported_parts.md b/supported_parts.md index 14a8abce8..fb1565cb6 100644 --- a/supported_parts.md +++ b/supported_parts.md @@ -125,6 +125,7 @@ - ADF4159 - ADF4355 - ADF4371 +- ADF4382 - ADF5610 - ADG2128 - ADIS16375 diff --git a/tasks.py b/tasks.py index d4291aa00..e139cd908 100644 --- a/tasks.py +++ b/tasks.py @@ -259,6 +259,7 @@ def checkemulation(c): "adar1000", "adf4159", "adf4355", + "adf4382", "adf4371", "adf5610", "adg2128", diff --git a/test/common.py b/test/common.py index 545033a21..0e05d63eb 100644 --- a/test/common.py +++ b/test/common.py @@ -54,6 +54,9 @@ def pytest_addoption(parser): action="store_true", help="Run tests that use observation data paths", ) + parser.addoption( + "--no-os", action="store_true", help="Run tests for No-OS", + ) parser.addoption( "--lvds", action="store_true", help="Run tests for LVDS", ) diff --git a/test/test_adf4382.py b/test/test_adf4382.py new file mode 100644 index 000000000..62e1f6a27 --- /dev/null +++ b/test/test_adf4382.py @@ -0,0 +1,87 @@ +import pytest + +# ADF4382 isn't in the adi_hardware_map.py in pylibiio +# This value will be changed to change to FMCOMMS11 +hardware = ["adf4382"] +classname = "adi.adf4382" + +######################################### +@pytest.mark.iio_hardware(hardware) +@pytest.mark.parametrize("classname", [(classname)]) +@pytest.mark.parametrize( + "attr, start, stop, step, tol, repeats", + [ + ("altvolt0_output_power", 1, 11, 1, 0, 5), + ("altvolt1_output_power", 1, 11, 1, 0, 5), + ], +) +def test_adf4382_attrs( + test_attribute_single_value, + iio_uri, + classname, + attr, + start, + stop, + step, + tol, + repeats, +): + test_attribute_single_value( + iio_uri, classname, attr, start, stop, step, tol, repeats + ) + + +######################################### +@pytest.mark.iio_hardware(hardware) +@pytest.mark.parametrize("classname", [(classname)]) +@pytest.mark.parametrize( + "attr, value", + [("altvolt0_en", 0), ("altvolt0_en", 1), ("altvolt1_en", 0), ("altvolt1_en", 1),], +) +def test_adf4382_attrs_bool( + test_attribute_single_value_boolean, iio_uri, classname, attr, value, +): + test_attribute_single_value_boolean(iio_uri, classname, attr, value) + + +######################################### +@pytest.mark.no_os_test +@pytest.mark.iio_hardware(hardware) +@pytest.mark.parametrize("classname", [(classname)]) +@pytest.mark.parametrize( + "attr, start, stop, step, tol, repeats", + [("reference_divider", 1, 63, 1, 0, 1), ("bleed_current", 0, 8191, 100, 0, 1),], +) +def test_adf4382_attrs_no_os( + test_attribute_single_value, + iio_uri, + classname, + attr, + start, + stop, + step, + tol, + repeats, +): + test_attribute_single_value( + iio_uri, classname, attr, start, stop, step, tol, repeats + ) + + +######################################### +@pytest.mark.no_os_test +@pytest.mark.iio_hardware(hardware) +@pytest.mark.parametrize("classname", [(classname)]) +@pytest.mark.parametrize( + "attr, value", + [ + ("reference_doubler_en", 0), + ("reference_doubler_en", 1), + ("sw_sync_en", 0), + ("sw_sync_en", 1), + ], +) +def test_adf4382_attrs_bool_no_os( + test_attribute_single_value_boolean, iio_uri, classname, attr, value, +): + test_attribute_single_value_boolean(iio_uri, classname, attr, value)