Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for ADF4382 #626

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions adi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
224 changes: 224 additions & 0 deletions adi/adf4382.py
Original file line number Diff line number Diff line change
@@ -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)
7 changes: 7 additions & 0 deletions doc/source/devices/adi.adf4382.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
adf4382
==================

.. automodule:: adi.adf4382
:members:
:undoc-members:
:show-inheritance:
1 change: 1 addition & 0 deletions doc/source/devices/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Supported Devices
adi.adf4159
adi.adf4355
adi.adf4371
adi.adf4382
adi.adf5610
adi.adg2128
adi.adis16375
Expand Down
28 changes: 28 additions & 0 deletions examples/adf4382_example.py
Original file line number Diff line number Diff line change
@@ -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)))
1 change: 1 addition & 0 deletions supported_parts.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
- ADF4159
- ADF4355
- ADF4371
- ADF4382
- ADF5610
- ADG2128
- ADIS16375
Expand Down
1 change: 1 addition & 0 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ def checkemulation(c):
"adar1000",
"adf4159",
"adf4355",
"adf4382",
"adf4371",
"adf5610",
"adg2128",
Expand Down
3 changes: 3 additions & 0 deletions test/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
)
Expand Down
87 changes: 87 additions & 0 deletions test/test_adf4382.py
Original file line number Diff line number Diff line change
@@ -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)
Loading