diff --git a/adi/__init__.py b/adi/__init__.py index 9a40d4bcb..c91d7e96f 100644 --- a/adi/__init__.py +++ b/adi/__init__.py @@ -62,6 +62,7 @@ from adi.adf4159 import adf4159 from adi.adf4371 import adf4371 from adi.adf5610 import adf5610 +from adi.adg2128 import adg2128 from adi.adis16460 import adis16460 from adi.adis16495 import adis16495 from adi.adis16507 import adis16507 diff --git a/adi/adg2128.py b/adi/adg2128.py new file mode 100644 index 000000000..b45c0354a --- /dev/null +++ b/adi/adg2128.py @@ -0,0 +1,177 @@ +# Copyright (C) 2021 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.attribute import attribute +from adi.context_manager import context_manager + + +class yline(object): + def __init__(self, dev, x, line): + """Initializer for a Y line of switches.""" + self._line = line + self._x = x + self._dev = dev + + def __str__(self): + """String representation of a Y line of switches.""" + return "[" + ", ".join([str(num) for num in self._line]) + "]" + + def __getitem__(self, y): + """Read access a Y line using python array indexing.""" + return self._line[y] + + def __setitem__(self, y, value): + """Write access a Y line using python array indexing.""" + self._dev._switch(self._x, y, value, self._dev._ldsw) + self._line[y] = value + + +class adg2128(attribute, context_manager): + """ADG2128 cross point switch.""" + + def __init__(self, uri=""): + """Initializer for the adg2128 cross point switch device.""" + self._device_name = "adg2128" + context_manager.__init__(self, uri, self._device_name) + + self._i2c_devs = [] + self._xmax = 0 + self._xline = [] + self._ldsw = True + + self._ctrl = self._ctx.find_device(self._device_name) + + if not self._ctrl: + raise Exception(self._device_name + " device not found") + + @property + def immediate(self): + """Specify whether the writing of a switch is immediate or not. + + When it's not immediate, the new switch configuration is only + latched into the device (see LDSW in datasheet). + """ + return self._ldsw + + @immediate.setter + def immediate(self, value): + self._ldsw = value + + def add(self, addr): + """ + Add device by its i2c address. + + Multiple devices may be added provided they all have their Y terminals + connected (common Y configuration). + 1x adg2128 is represented by a 12 by 8 matrix + 2x adg2128 is represented by a 24 by 8 matrix + ... + + Arguments: + addr - device address on i2c bus + """ + self._i2c_devs.append(addr) + self._xmax += 12 + for x in range(self._xmax - 12, self._xmax): + y = yline(self, x, [False for y in range(8)]) + self._xline.append(y) + + def _read(self, addr): + """Direct Register Access via debugfs.""" + self._set_iio_debug_attr_str("direct_reg_access", addr, self._ctrl) + return self._get_iio_debug_attr_str("direct_reg_access", self._ctrl) + + def _write(self, addr, val): + """Direct Register Access via debugfs.""" + self._set_iio_debug_attr_str( + "direct_reg_access", "0x{:X} 0x{:X}".format(addr, val), self._ctrl + ) + + def _switch(self, x, y, closed, immediate): + data = closed << 15 + ax_lookup = [0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13] + ax = ax_lookup[x % 12] << 11 + ay = y << 8 + ldsw = immediate + addr = self._i2c_devs[x // 12] + val = data | ax | ay | ldsw + self._write(addr, val) + + def _read_x(self, x): + addr = self._i2c_devs[int(x) // 12] + x_lookup = [ + 0b00110100, + 0b00111100, + 0b01110100, + 0b01111100, + 0b00110101, + 0b00111101, + 0b01110101, + 0b01111101, + 0b00110110, + 0b00111110, + 0b01110110, + 0b01111110, + ] + val = x_lookup[x % 12] << 8 + self._write(addr, val) + return self._read(addr) + + def __str__(self): + """String representation of the cross point switch.""" + return "\n".join([f"x{idx}: {x}" for idx, x in enumerate(self._xline)]) + + def __getitem__(self, x): + """Read access a X line using python array indexing.""" + return self._xline[x] + + def __setitem__(self, x, value): + """Write access a X line using python array indexing.""" + for y, closed in enumerate(value): + self._xline[x][y] = closed + + def open_all(self): + """ + Open all switches. + + For each device, iterate all x-y combinations and + open all the switches at once. + """ + ldsw = self._ldsw + for x in range(self._xmax): + for y in range(8): + self._ldsw = False + if (y == 7) and (x % 12 == 11): + self._ldsw = True + self[x][y] = False + self._ldsw = ldsw diff --git a/doc/source/devices/adi.adg2128.rst b/doc/source/devices/adi.adg2128.rst new file mode 100644 index 000000000..dd2ea80ec --- /dev/null +++ b/doc/source/devices/adi.adg2128.rst @@ -0,0 +1,7 @@ +adi.adg2128 module +================== + +.. automodule:: adi.adg2128 + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/devices/index.rst b/doc/source/devices/index.rst index 784de3015..6e9052d77 100644 --- a/doc/source/devices/index.rst +++ b/doc/source/devices/index.rst @@ -42,6 +42,7 @@ Supported Devices adi.adf4159 adi.adf4371 adi.adf5610 + adi.adg2128 adi.adis16460 adi.adis16495 adi.adis16507 diff --git a/examples/adg2128.py b/examples/adg2128.py new file mode 100644 index 000000000..6fe4c7aef --- /dev/null +++ b/examples/adg2128.py @@ -0,0 +1,27 @@ +import adi + +# cross point switch +cps = adi.adg2128(uri="serial:/dev/ttyACM0,230400,8n1") + +# add two adg2128 cross point switches by their i2c addresses +cps.add(0x71) # add first device, x0...x11 <---> y0...y7 +cps.add(0x70) # add another device in common Y connection x0...x23 <---> y0...y7 + +# open all switches +cps.open_all() + +# set all x1 switches (y0...y7) with a list +cps[1] = [True, False, True, False, True, False, True, False] + +# set (x23, y7) switch individually +cps[23][7] = True + +# close (x5,y0) (x5,y1) (x5,y2) (x5,y3) switches together +cps.immediate = False +cps[5][0] = True +cps[5][1] = True +cps[5][2] = True +cps.immediate = True +cps[5][3] = True # close switches together when (x5, y3) is closed + +print(cps) diff --git a/supported_parts.md b/supported_parts.md index f62eb12b9..f8d6311cb 100644 --- a/supported_parts.md +++ b/supported_parts.md @@ -80,6 +80,7 @@ - ADF4159 - ADF4371 - ADF5610 +- ADG2128 - ADIS16460 - ADIS16495 - ADIS16507 diff --git a/test/test_adg2128.py b/test/test_adg2128.py new file mode 100644 index 000000000..24d65fe46 --- /dev/null +++ b/test/test_adg2128.py @@ -0,0 +1,31 @@ +import adi +import pytest + +hardware = ["adg2128"] +classname = "adi.adg2128" + + +@pytest.mark.iio_hardware(hardware) +@pytest.mark.parametrize( + "addr1, addr2", [(0x71, 0x70),], +) +def test_adg2128(context_desc, addr1, addr2): + cps = None + for ctx_desc in context_desc: + if ctx_desc["hw"] in hardware: + cps = adi.adg2128(uri=ctx_desc["uri"]) + break + if cps is None: + pytest.skip("No valid hardware found") + + cps.add(addr1) + cps.add(addr2) + + cps[0][0] = True + assert cps[0][0] == True + cps[0][0] = False + assert cps[0][0] == False + cps[23][7] = True + assert cps[23][7] == True + cps[23][7] = False + assert cps[23][7] == False