diff --git a/adi/__init__.py b/adi/__init__.py index 7549e1615..1579bd790 100644 --- a/adi/__init__.py +++ b/adi/__init__.py @@ -96,6 +96,7 @@ from adi.ltc2387 import ltc2387 from adi.ltc2499 import ltc2499 from adi.ltc2664 import ltc2664 +from adi.ltc2672 import ltc2672 from adi.ltc2688 import ltc2688 from adi.ltc2983 import ltc2983 from adi.max9611 import max9611 diff --git a/adi/ltc2672.py b/adi/ltc2672.py new file mode 100644 index 000000000..7bf97c69d --- /dev/null +++ b/adi/ltc2672.py @@ -0,0 +1,219 @@ +# Copyright (C) 2024 Analog Devices, Inc. +# +# SPDX short identifier: ADIBSD + +from decimal import Decimal + +from adi.attribute import attribute +from adi.context_manager import context_manager + + +class ltc2672(context_manager, attribute): + """LTC2672 DAC""" + + _complex_data = False + _device_name = "" + + def __init__(self, uri="", device_name=""): + """Constructor for LTC2672 class""" + context_manager.__init__(self, uri, self._device_name) + + compatible_parts = ["ltc2672-16", "ltc2672-12"] + + self._ctrl = None + self.channel = [] + + if not device_name: + device_name = compatible_parts[0] + else: + if device_name not in compatible_parts: + raise Exception( + f"Not a compatible device: {device_name}. Supported device names" + f"are: {','.join(compatible_parts)}" + ) + + # Select the device matching device_name as working device + for device in self._ctx.devices: + if device.name == device_name: + self._ctrl = device + break + + if not self._ctrl: + raise Exception("Error in selecting matching device") + + self.output_bits = [] + for ch in self._ctrl.channels: + name = ch.id + self.output_bits.append(ch.data_format.bits) + self.channel.append(self._channel(self._ctrl, name)) + + @property + def all_chns_span(self): + """Get all channels span in mA""" + return self._get_iio_dev_attr_str("all_chns_span") + + @property + def all_chns_span_avail(self): + """Get list of span options in mA""" + return self._get_iio_dev_attr_str("all_chns_span_available") + + @all_chns_span.setter + def all_chns_span(self, value): + """Set all channels span""" + if value in self.all_chns_span_avail: + self._set_iio_dev_attr_str("all_chns_span", value) + else: + raise ValueError( + "Error: span setting not supported \nUse one of: " + "str(self.all_chns_span_avail)" + ) + + @property + def all_chns_raw(self): + """Get raw value""" + return self._get_iio_dev_attr_str("all_chns_raw") + + @all_chns_raw.setter + def all_chns_raw(self, value): + """Set raw value""" + self._set_iio_dev_attr_str("all_chns_raw", value) + + @property + def all_chns_current(self): + """Get current value in mA""" + return self._get_iio_dev_attr_str("all_chns_current") + + @all_chns_current.setter + def all_chns_current(self, value): + """Set current value in mA""" + self._set_iio_dev_attr_str("all_chns_current", value) + + @property + def all_chns_powerdown(self): + """Get powerdown value""" + return self._get_iio_dev_attr_str("all_chns_powerdown") + + @property + def all_chns_powerdown_avail(self): + """Get powerdown options""" + return self._get_iio_dev_attr_str("all_chns_powerdown_available") + + @all_chns_powerdown.setter + def all_chns_powerdown(self, value): + """Set all channels powerdown""" + if value in self.all_chns_powerdown_avail: + self._set_iio_dev_attr_str("all_chns_powerdown", value) + else: + raise ValueError( + "Error: powerdown option not supported \nUse one of: " + "str(self.all_chns_powerdown_avail)" + ) + + @property + def mux(self): + """Get mux setting value""" + return self._get_iio_dev_attr_str("mux") + + @property + def mux_avail(self): + """Get mux setting options""" + return self._get_iio_dev_attr_str("mux_available") + + @mux.setter + def mux(self, value): + """Set mux option""" + if value in self.mux_avail: + self._set_iio_dev_attr_str("mux", value) + else: + raise ValueError( + "Error: mux setting not supported \nUse one of: " "str(self.mux_avail)" + ) + + @property + def fault_detect(self): + """Get fault condition if any""" + return self._get_iio_dev_attr_str("fault_detect") + + @property + def fault_detect_avail(self): + """Get fault detect options""" + return self._get_iio_dev_attr_str("fault_detect_available") + + class _channel(attribute): + """LTC2672 channel""" + + def __init__(self, ctrl, channel_name): + self.name = channel_name + self._ctrl = ctrl + + @property + def raw(self): + """Get channel raw value""" + return self._get_iio_attr(self.name, "raw", True) + + @raw.setter + def raw(self, value): + """Set channel raw value""" + self._set_iio_attr(self.name, "raw", True, str(int(value))) + + @property + def scale(self): + """Get channel scale""" + return self._get_iio_attr(self.name, "scale", True) + + @property + def offset(self): + """get channel offset""" + return self._get_iio_attr(self.name, "offset", True) + + @property + def current(self): + """Get channel current value in mA""" + return self._get_iio_attr(self.name, "current", True) + + @current.setter + def current(self, value): + """Set channel current value in mA""" + self._set_iio_attr(self.name, "current", True, str(Decimal(value))) + + @property + def span(self): + """Get channel span value""" + return self._get_iio_attr_str(self.name, "span", True) + + @property + def span_avail(self): + """Get channel span options""" + return self._get_iio_attr_str(self.name, "span_available", True) + + @span.setter + def span(self, value): + """Set channel span setting""" + if value in self.span_avail: + self._set_iio_attr(self.name, "span", True, value) + else: + raise ValueError( + "Error: span setting not supported \nUse one of: " + "str(self.span_avail)" + ) + + @property + def powerdown(self): + """Get channel powerdown""" + return self._get_iio_attr_str(self.name, "powerdown", True) + + @property + def powerdown_avail(self): + """Get channel powerdown options""" + return self._get_iio_attr_str(self.name, "powerdown_available", True) + + @powerdown.setter + def powerdown(self, value): + """Set channel powerdown setting""" + if value in self.powerdown_avail: + self._set_iio_attr(self.name, "powerdown", True, value) + else: + raise ValueError( + "Error: powerdown setting not supported \nUse one of: " + "str(self.powerdown_avail)" + ) diff --git a/doc/source/devices/adi.ltc2672.rst b/doc/source/devices/adi.ltc2672.rst new file mode 100644 index 000000000..85fadaf06 --- /dev/null +++ b/doc/source/devices/adi.ltc2672.rst @@ -0,0 +1,7 @@ +ltc2672 +================== + +.. automodule:: adi.ltc2672 + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/devices/index.rst b/doc/source/devices/index.rst index 62d7396b9..07441c7d6 100644 --- a/doc/source/devices/index.rst +++ b/doc/source/devices/index.rst @@ -107,6 +107,7 @@ Supported Devices adi.ltc2499 adi.ltc2664 adi.ltc2688 + adi.ltc2672 adi.ltc2983 adi.max11205 adi.max14001 diff --git a/examples/ltc2672_example.py b/examples/ltc2672_example.py new file mode 100644 index 000000000..4e8087506 --- /dev/null +++ b/examples/ltc2672_example.py @@ -0,0 +1,58 @@ +# Copyright (C) 2024 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. + +from adi.ltc2672 import ltc2672 + +# Set up LTC2672 +ltc2672_dev = ltc2672(uri="serial:COM5,230400,8n1") + +# Set all channels span to 50mA +ltc2672_dev.all_chns_span = "50mA" + +# Configure channel 0 and update dac output using the 'raw' attribute +chn_num = 0 +ltc2672_chan = ltc2672_dev.channel[chn_num] +ltc2672_chan.raw = 25000 + +# Get the current value of channel 0 in mA for the corresponding raw value set +current_val_ma = ltc2672_chan.current +print(f"Channel{chn_num} current in mA: {current_val_ma}") + +# Set current value for channel 0 in mA +ltc2672_chan.current = 45 + +# Set mux value to "iout0" to monitor iout0 value on the mux_out pin +ltc2672_dev.mux = "iout0" + +# Powerdown all the channels of the DAC +ltc2672_dev.all_chns_powerdown = "powerdown" diff --git a/supported_parts.md b/supported_parts.md index b509c5512..1673d7740 100644 --- a/supported_parts.md +++ b/supported_parts.md @@ -163,6 +163,8 @@ - LTC2499 - LTC2664 - LTC2688 +- LTC2672-12 +- LTC2672-16 - LTC2983 - AD7606 - AD7745 diff --git a/test/emu/devices/ltc2672.xml b/test/emu/devices/ltc2672.xml new file mode 100644 index 000000000..114dcc15f --- /dev/null +++ b/test/emu/devices/ltc2672.xml @@ -0,0 +1 @@ +]> \ No newline at end of file diff --git a/test/emu/hardware_map.yml b/test/emu/hardware_map.yml index 8d31e0874..72e8aa140 100644 --- a/test/emu/hardware_map.yml +++ b/test/emu/hardware_map.yml @@ -412,6 +412,15 @@ ltc2664: - data_devices: - iio:device0 +ltc2672: + - ltc2672 + - pyadi_iio_class_support: + - ltc2672 + - emulate: + - filename: ltc2672.xml + - data_devices: + - iio:device0 + ltc2688: - ltc2688 - pyadi_iio_class_support: diff --git a/test/test_ltc2672.py b/test/test_ltc2672.py new file mode 100644 index 000000000..1debab786 --- /dev/null +++ b/test/test_ltc2672.py @@ -0,0 +1,70 @@ +import adi +import pytest +from adi.ltc2672 import * + +hardware = "ltc2672" +classname = "adi.ltc2672" + + +######################################### +@pytest.mark.iio_hardware(hardware) +@pytest.mark.parametrize("classname", [(classname)]) +@pytest.mark.parametrize( + "attr, val", + [ + ( + "all_chns_span", + [ + "off_mode", + "3.125mA", + "6.25mA", + "12.5mA", + "25mA", + "50mA", + "100mA", + "200mA", + "MVREF", + "300mA", + ], + ), + ("all_chns_powerdown", ["powerdown"],), + ], +) +def test_ltc2672_global_attr( + test_attribute_multiple_values, iio_uri, classname, attr, val +): + test_attribute_multiple_values(iio_uri, classname, attr, val, 0) + + +######################################### +@pytest.mark.iio_hardware(hardware) +@pytest.mark.parametrize("classname", [(classname)]) +@pytest.mark.parametrize("channel", [0, 1, 2, 3, 4]) +@pytest.mark.parametrize( + "attr, val", + [ + ( + "span", + [ + "off_mode", + "3.125mA", + "6.25mA", + "12.5mA", + "25mA", + "50mA", + "100mA", + "200mA", + "MVREF", + "300mA", + ], + ), + ("powerdown", ["powerdown"],), + ], +) +def test_ltc2672_channel_attr(iio_uri, classname, channel, attr, val): + dev = adi.ltc2672(iio_uri) + dev_chan = dev.channel[channel] + for value in val: + setattr(dev_chan, attr, value) + val_read = getattr(dev_chan, attr) + assert val_read == value