Skip to content

Commit

Permalink
Merge pull request #565 from ribdp/add-ad405x-support
Browse files Browse the repository at this point in the history
adi:ad405x: Add support for AD4052
  • Loading branch information
tfcollins authored Jun 13, 2024
2 parents 165ff17 + 86872f9 commit 6450ff9
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 1 deletion.
1 change: 1 addition & 0 deletions adi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# SPDX short identifier: ADIBSD

from adi.ad2s1210 import ad2s1210
from adi.ad405x import ad405x
from adi.ad469x import ad469x
from adi.ad579x import ad579x
from adi.ad717x import ad717x
Expand Down
178 changes: 178 additions & 0 deletions adi/ad405x.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# Copyright (C) 2024 Analog Devices, Inc.
#
# SPDX short identifier: ADIBSD

from decimal import Decimal

import numpy as np
from adi.attribute import attribute
from adi.context_manager import context_manager
from adi.rx_tx import rx


class ad405x(rx, context_manager):
""" AD405x ADC """

_complex_data = False
channels = [] # type: ignore
_device_name = ""

def __init__(self, uri="", device_name=""):

context_manager.__init__(self, uri, self._device_name)

compatible_parts = ["ad4052", "ad4050"]

self._ctrl = None

if not device_name:
device_name = compatible_parts[0]
else:
if device_name not in compatible_parts:
raise Exception("Not a compatible device: " + device_name)

# Select the device matching device_name as working device
for device in self._ctx.devices:
if device.name == device_name:
self._ctrl = device
self._rxadc = device
break

self.channels = []
for ch in self._ctrl.channels:
name = ch._id
self._rx_channel_names.append(name)
self.channels.append(self._channel(self._ctrl, name))

rx.__init__(self)

@property
def operating_mode_avail(self):
"""Get available operating modes."""
return self._get_iio_dev_attr_str("operating_mode_available")

@property
def operating_mode(self):
"""Get operating mode."""
return self._get_iio_dev_attr_str("operating_mode")

@operating_mode.setter
def operating_mode(self, value):
"""Set operating mode."""
if value in self.operating_mode_avail:
self._set_iio_dev_attr_str("operating_mode", value)
else:
raise ValueError(
"Error: Operating mode not supported \nUse one of: "
+ str(self.operating_mode_avail)
)

@property
def burst_sample_rate(self):
"""Get burst sample rate. Only available in Burst Averaging Mode."""
if "burst_sample_rate" in self._ctrl._attrs.keys():
return self._get_iio_dev_attr("burst_sample_rate")
else:
raise ValueError(
"Error: Burst sample rate not supported in " + self.operating_mode
)

@burst_sample_rate.setter
def burst_sample_rate(self, value):
"""Set burst sample rate."""
if "burst_sample_rate" in self._ctrl._attrs.keys():
self._set_iio_dev_attr("burst_sample_rate", value)
else:
raise Exception(
"Error: Burst sample rate not supported in " + self.operating_mode
)

@property
def avg_filter_length_avail(self):
"""Get available average filter length. Only available in Burst Averaging Mode."""
if "avg_filter_length_available" in self._ctrl._attrs.keys():
return self._get_iio_dev_attr("avg_filter_length_available")
else:
raise Exception(
"Error: Average filter length not supported in " + self.operating_mode
)

@property
def avg_filter_length(self):
"""Get average filter length. Only available in Burst Averaging Mode."""
if "avg_filter_length" in self._ctrl._attrs.keys():
return self._get_iio_dev_attr("avg_filter_length")
else:
raise Exception(
"Error: Average filter length not supported in " + self.operating_mode
)

@avg_filter_length.setter
def avg_filter_length(self, value):
"""Set average filter length."""
if "avg_filter_length_available" in self._ctrl._attrs.keys():
if value in self.avg_filter_length_avail:
self._set_iio_dev_attr("avg_filter_length", value)
else:
raise ValueError(
"Error: Average filter length not supported \nUse one of: "
+ str(self.avg_filter_length_avail)
)
else:
raise Exception(
"Error: Average filter length not supported in " + self.operating_mode
)

@property
def sampling_frequency(self):
"""Get sampling frequency."""
return self._get_iio_dev_attr("sampling_frequency")

@sampling_frequency.setter
def sampling_frequency(self, value):
"""Set sampling_frequency."""
self._set_iio_dev_attr("sampling_frequency", value)

class _channel(attribute):
"""AD405x channel"""

def __init__(self, ctrl, channel_name):
self.name = channel_name
self._ctrl = ctrl

@property
def raw(self):
"""AD405x channel raw value"""
return self._get_iio_attr(self.name, "raw", False)

@property
def offset(self):
"""AD405x channel system calibration"""
return self._get_iio_attr_str(self.name, "offset", False)

@offset.setter
def offset(self, value):
self._set_iio_attr(self.name, "offset", False, str(Decimal(value).real))

@property
def scale(self):
"""AD405x channel scale"""
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))

def to_volts(self, index, val):
"""Converts raw value to SI"""
_scale = self.channel[index].scale

ret = None

if isinstance(val, np.uint16):
ret = val * _scale

if isinstance(val, np.ndarray):
ret = [x * _scale for x in val]

return ret
7 changes: 7 additions & 0 deletions doc/source/devices/adi.ad405x.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ad405x
================

.. automodule:: adi.ad405x
: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 @@ -10,6 +10,7 @@ Supported Devices
adi.QuadMxFE_multi
adi.ad2s1210
adi.ad4020
adi.ad405x
adi.ad4110
adi.ad4130
adi.ad4630
Expand Down
61 changes: 61 additions & 0 deletions examples/ad4052_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Copyright (C) 2023 Analog Devices, Inc.
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
# - Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# - Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# - Neither the name of Analog Devices, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
# - The use of this software may or may not infringe the patent rights
# of one or more patent holders. This license does not release you
# from the requirement that you obtain separate licenses from these
# patent holders to use this software.
# - Use of the software either in source or binary form, must be run
# on or directly connected to an Analog Devices Inc. component.
#
# THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED.
#
# IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, INTELLECTUAL PROPERTY
# RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import adi
import matplotlib.pyplot as plt
import numpy as np

device = adi.ad405x("serial:COM13,230400", "ad4052")

######## User configuration ##########
device.rx_enabled_channels = ["voltage0"]

device.rx_buffer_size = (
400 # Size of the IIO buffer (buffer is submitted during call to rx() method)
)
device._rx_data_type = np.uint16

data = device.rx()

plt.plot(range(0, len(data)), data, label="channel0")
plt.xlabel("Data Point")
plt.ylabel("ADC counts")
plt.legend(
bbox_to_anchor=(0.0, 1.02, 1.0, 0.102),
loc="lower left",
ncol=4,
mode="expand",
borderaxespad=0.0,
)

plt.show()
1 change: 1 addition & 0 deletions supported_parts.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- AD4002 (AD4006, AD4010)
- AD4003 (AD4007, AD4011)
- AD4020
- AD405x
- AD4130
- AD4110
- AD4111
Expand Down
1 change: 1 addition & 0 deletions test/emu/devices/ad405x.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE context [<!ELEMENT context (device | context-attribute)*><!ELEMENT context-attribute EMPTY><!ELEMENT device (channel | attribute | debug-attribute | buffer-attribute)*><!ELEMENT channel (scan-element?, attribute*)><!ELEMENT attribute EMPTY><!ELEMENT scan-element EMPTY><!ELEMENT debug-attribute EMPTY><!ELEMENT buffer-attribute EMPTY><!ATTLIST context name CDATA #REQUIRED description CDATA #IMPLIED><!ATTLIST context-attribute name CDATA #REQUIRED value CDATA #REQUIRED><!ATTLIST device id CDATA #REQUIRED name CDATA #IMPLIED><!ATTLIST channel id CDATA #REQUIRED type (input|output) #REQUIRED name CDATA #IMPLIED><!ATTLIST scan-element index CDATA #REQUIRED format CDATA #REQUIRED scale CDATA #IMPLIED><!ATTLIST attribute name CDATA #REQUIRED filename CDATA #IMPLIED value CDATA #IMPLIED><!ATTLIST debug-attribute name CDATA #REQUIRED value CDATA #IMPLIED><!ATTLIST buffer-attribute name CDATA #REQUIRED value CDATA #IMPLIED>]><context name="serial" description="no-OS/projects/NO_OS_PROJECT 0.1" ><context-attribute name="uri" value="serial:/dev/ttyS0,230400,8n1n" /><context-attribute name="serial,port" value="/dev/ttyS0" /><context-attribute name="serial,description" value="ttyS0" /><device id="iio:device0" name="ad4052" ><channel id="voltage0" name="Chn0" type="input" ><attribute name="raw" filename="in_voltage0_raw" value="0" /><attribute name="scale" filename="in_voltage0_scale" value="0.0762939" /><attribute name="offset" filename="in_voltage0_offset" value="0" /><attribute name="operating_mode" value="config_mode" /><attribute name="operating_mode_available" value="config_mode adc_mode" /><attribute name="burst_sample_rate" value="2msps" /><attribute name="avg_filter_length" value="2" /><attribute name="avg_filter_length_available" value="2 4 8 16 32 64 128 256 512 1024 2048 4096" /><attribute name="sampling_frequency" value="62500" /><debug-attribute name="direct_reg_access" value="0" /></device></context>
11 changes: 10 additions & 1 deletion test/emu/hardware_map.yml
Original file line number Diff line number Diff line change
Expand Up @@ -582,4 +582,13 @@ lm75:
- emulate:
- filename: lm75.xml
- data_devices:
- hwmon0
- hwmon0

ad405x:
- ad4052
- pyadi_iio_class_support:
- ad405x
- emulate:
- filename: ad405x.xml
- data_devices:
- iio:device0
25 changes: 25 additions & 0 deletions test/test_ad405x.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import pytest

hardware = ["ad4052"]
classname = "adi.ad405x"

#########################################
@pytest.mark.iio_hardware(hardware)
@pytest.mark.parametrize("classname", [(classname)])
@pytest.mark.parametrize(
"attr, val",
[
("sampling_frequency", [10000, 50000, 100000, 200000, 500000, 1000000],),
("operating_mode", ["config_mode", "adc_mode"],),
],
)
def test_ad405x_attr(test_attribute_multiple_values, iio_uri, classname, attr, val):
test_attribute_multiple_values(iio_uri, classname, attr, val, 1)


#########################################
@pytest.mark.iio_hardware(hardware, True)
@pytest.mark.parametrize("classname", [(classname)])
@pytest.mark.parametrize("channel", [0])
def test_ad405x_rx_data(test_dma_rx, iio_uri, classname, channel):
test_dma_rx(iio_uri, classname, channel, buffer_size=2 ** 11)

0 comments on commit 6450ff9

Please sign in to comment.