HP/Agilent/Keysight E3632A Power Supply

The E3632A is a laboratory power supply with one floating output channel + sense terminals + ground terminal. It is able to output either up to 15V with up to 7A of current or up to 30V with up to 4A of current, both with a display resolution of 1mV/1mA.

The driver was written with both voltage ranges available. As the device will switch from one voltage range to the other fluently during a sweep even if the lower one was selected initially in the GUI, a check is performed on the self.value sweep step parameter if the pre-selected current compliance value is below the maximum limt of the lower voltage range, e.g. if going from 15V to 16V is requested with less than 4A of compliance. Otherwise, an exception will be thrown as the PSU would otherwise throw a notification that the current limit had to be adjusted, throwing off the SCPI command cue out of line due to a time out. Plus, the originally requested compliance limit would not be the same anymore, which the operator should be aware about, therefore the error message.

As there is no device class of power supplies, the driver was created as an SMU and sweep mode fixed for voltage use as the compliance is only a current limit.

@Axel: I don’t know how to retrieve all possible GUI parameters of a certain device class yet. Is there an option to switch off the display for SMUs like it is for the loggers or could we get one? The E3632A uses a VFD display and would benefit greatly from the option to switch it off and extend its lifetime.

# This Device Class is published under the terms of the MIT License.
# Required Third Party Libraries, which are included in the Device Class
# package for convenience purposes, may have a different license. You can
# find those in the corresponding folders or contact the maintainer.
#
# MIT License
# 
# Copyright (c) 2022-2023 SweepMe! GmbH (sweep-me.net)
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# SweepMe! device class
# Type: SMU
# Device: Agilent E3632A

import time #needed for waiting until PSU has set the requested voltage

#from pysweepme.ErrorMessage import error
from pysweepme.EmptyDeviceClass import EmptyDevice


class Device(EmptyDevice):
    
    """
    description = 
    <p><strong>Notes:</strong></p>
    <ul>
    <li>COM Port untested as of 20240619</li>
    <li>Calibration commands not implemented</li>
    <li>-</li>
    </ul> 
    """

    def __init__(self):

        EmptyDevice.__init__(self)

        self.shortname = "E3632A"

        self.variables = ["Voltage", "Current"]
        self.units = ["V", "A"]
        self.plottype = [True, True]  # True to plot data
        self.savetype = [True, True]  # True to save data

        self.port_manager = True
        self.port_types = ["COM", "GPIB"]

        self.port_properties = {"timeout": 1,
                                # "delay": 0.05,
                                # "Exception": False,
                                }

    #       self.commands = {
    #                      "Voltage in V" : "VOLT",
    #                      "Current in A" : "CURR",
    #                      }

    def set_GUIparameter(self):

        gui_parameter = {
            "SweepMode": ["Voltage in V"],
            #"Channel": [1], NOT NEEDED with E3632A as it is a single channel instrument but will be used for other E36xxA instruments later.
            "RouteOut": ["Front"],
            "Compliance": 1,
            "RangeVoltage": ["15 V / 7 A", "30 V / 4 A"],
        }

        return gui_parameter

    def get_GUIparameter(self, parameter={}):
        self.port_string = parameter["Port"]
        self.source = parameter["SweepMode"]
        self.route_out = parameter["RouteOut"]
        self.currentlimit = parameter["Compliance"]
        #self.channel = int(parameter["Channel"]) #NOT NEEDED with E3632A as it is a single channel instrument but will be used for other E36xxA instruments later.
        self.voltage_range = parameter["RangeVoltage"]

    def connect(self):
        pass # No LAN port on E3632A
        #if self.port_string.startswith("TCPIP"):
        #    if self.port_string.endswith("SOCKET"):
        #        self.port.port.write_termination = '\n'
        #        self.port.port.read_termination = '\n'

    def initialize(self):
        self.port.write("*IDN?")
        identifier = self.port.read()
        print("Identifier:", identifier)  # can be used to check the instrument
        self.port.write("*RST")

    def configure(self):
        #self.port.write("VOLT:PROT:STAT OFF") # output voltage protection disabled
        #self.port.write("CURR:PROT:STAT OFF") # output current protection disabled
        self.port.write("VOLT:PROT:LEV 32") #hardcoded overvoltage protection limit, causes switch-off of channel; set to PSU protection default value
        self.port.write("CURR:PROT:LEV 7.5") #hardcoded overcurrent protection limit, causes switch-off of channel; set to PSU protection default value
    
        if self.voltage_range.startswith("30 V"):
            if float(self.currentlimit) > 4.12:
                raise Exception("Lower compliance limit to max 4.12A")
            self.port.write("CURR:LEV:IMM %1.4f" % float(self.currentlimit))
            self.port.write("SOUR:VOLT:RANG P30V")
        elif self.voltage_range.startswith("15 V"):
            if float(self.currentlimit) > 7.21:
                raise Exception("Lower compliance limit to max 7.21A")
            self.port.write("CURR:LEV:IMM %1.4f" % float(self.currentlimit))
            self.port.write("SOUR:VOLT:RANG P15V")
        else:
            raise Exception("The input voltage range is not valid.")
        
        self.port.write("SOUR:VOLT MIN")
    
    def unconfigure(self):
        self.port.write("SOUR:VOLT MIN")
        #self.port.write("SYST:LOC") #On the E3632A, ONLY ALLOWED WITH RS232

    def deinitialize(self):
        pass

    def poweron(self):
        self.port.write("OUTP:STAT ON")

    def poweroff(self):
        self.port.write("OUTP:STAT OFF")

    def apply(self):
        if self.value > 15.45 and float(self.currentlimit) > 4.12:
            raise Exception("The next requested step would exceed 15.45 V with current limit higher than 4.12 A.\n\nPlease either stop at 15.45 V max or lower current compliance limit to 4.12 A max.")
        else:
            self.port.write("VOLT:LEV:IMM %1.4f" % float(self.value))

    def measure(self):
        self.port.write("MEAS:VOLT?")
        self.v = float(self.port.read())
        self.port.write("MEAS:CURR?")
        self.i = float(self.port.read())
        #print(self.v, self.i)

    def call(self):
        return [self.v, self.i]
       
    def display_off(self):
        #For further use.

        self.port.write("DISP:STAT OFF")
        # wait for display shutdown procedure to complete
        # time.sleep(0.5)
        
    def display_on(self):
        #For further use.

        self.port.write("DISP:STAT ON")
        # wait for display switch-on procedure to complete
        # time.sleep(0.5)

The driver has been in extensive use lately and showed no error. It should be pretty stable.

The driver can be used as a basis for all other models of the E36xx line and might be compatibel with the newer line of E366xx PSUs as well. I intend to write a version for the 3-Channel E3631A soon as this is a model I can test the driver on.

Best wishes,
Christian

1 Like

Hi Christian,

thanks a lot for the further contribution!

In order to see all available GUI parameters/keys, you can use the following code:

def get_GUIparameter(self, parameter={}):
    print(parameter)
    

This will show you the entire dictionary and all existing code independent from whether they have been activated with the set_GUIparameter function.

I have also updated the Wiki article of the SMU module where all parameters are explained:

As you probably have noticed, the SMU module has a fixed number of options. The idea is to provide the users a similar look&feel independent from the selected instrument driver. This way you can change devices without getting used to a different GUI or other parameters.

However, this sometimes makes problems when an instrument has options that other instruments don’t have.

In your case I could imagine that it would be a good solution to switch off the display during the measurement as a hard coded feature.

When new parameters pop up that are common for SMUs we can extend the modules by providing new versions. For example, we did so to provide the options for pulses measurements or for list sweeps.

In general we try to not add every option to keep the GUI simple and understandable.

Other modules like Logger, Switch, Robot, and WaferProber have a parameters section where the driver can define and add own keys. These modules are typically used for instruments that always come with their own specific features and properties. Especially, the modules Logger and Switch are used for all kind of instruments that do not fit to any other category.

One solution to define a custom design of a driver, is to create a driver by inheriting from an existing driver. For example, a driver can use our library pysweepme to load a driver. This can also be done in a driver itself. This way, one could for example load the standard E3632 driver but extend just configure/unconfigure to switch off/on the display.

This way one can benefit from the driver improvements but still doing custom things.

Implementing a power supply for the SMU module is what we also do as many options are very similar. For example, we use the compliance/protection to either set the voltage or current limit while the other limit can be swept.

I will have a look at your driver and check whether we can make a E36xx driver out of it that covers all models. Do you already have any objections? If there models with different channel numbers, it would not hurt to provide all channels and models with only one channel throw an error if a high channel number is selected.

Thanks and best, Axel

1 Like

Ich have added the driver to the repository

The changes can be seen here:

As the E3632A has very specific ranges it could indeed make sense to create an individual driver and make further new drivers for other models.

This is why I have not uploaded a version to the server yet.

If the different models still have many commands in common, it could be also a solution to create a base class that all drivers inherit from and then only things like the ranges or channels are adapted for each.

A good example are the Eurotherm temperature controller drivers that all use the same base class and make minor adaptions in their main.py files.

Thanks and best, Axel

Hi Axel.

Thanks for the polishing of the driver, looks good.

The only objectional aspect of a multi-model E36xx libary would be that the driver would contain lots of blocks for all the different exception messages due to their vastly different capabilities. Other than that, solely from a functional point of view, this will work just fine as they should run on pretty much the similar command set.

Best wishes,
Christian

1 Like

Hi Christian,

as I do not know in detail how the other E36xx models work, I suggest to go ahead with a new driver for each model. As far as I have seen there are not too much different models.

If we see in future that there are more and more similar drivers, we can also create a new driver, e.g. “SMU-Keysight_E36xxA” to cover all models and withdraw the other drivers. Then, one can also introduce a base class if needed.

One missing feature of the SweepMe! driver programming API is that the GUI parameters cannot be set after retrieving the identification of the instrument. So it would be difficult to set the different range options for each model.

The problem that you mention regarding many if-blocks for each model, can sometimes be circumvented by using nested dictionaries:

self.models_properties = {
    "E3631A": {
        "Range": ["1_first", "1_second"],
        "Other parameter": "other value 1",
        ...
    }
    "E3632A": {
        "Range": ["2_first", "2_second"],
        "Other parameter": "other value 2",
        ...
    }
}

This approach allows to specify all model-dependent properties at one place and in the code one can use something like

self.model_properties[self.model_id]["Range"]

to insert the range options of the model.

Another approach would be to create a class for each model that summarizes all model features.

e.g.

class E3632A():
    # static attributes
    range = ["2_first", "2_second"]
    other_parameter = "other value 2"

In the code one could then use

my_model = E3632A
my_model.range

to again use the range of a certain model. There would be only an if-block once at the beginning of the semantic standard functions in the SweepMe! driver to select the right class depending on the model.

1 Like

The driver is now uploaded to our server in state “testing”. You can test it once more by downloading the driver after login with your SweepMe! account. If everything works, I will make it publicly available.
Thanks and best, Axel

1 Like

I’ve used it now for over a day and did not find an issue. Looks good to me.

But as long as we cannot make the display switch-off optional, we should leave it on by default and leave it to the user to apply a switch-off via a custom version, if required.

1 Like

Thanks for the feedback. The driver is now available to all users. I have not changed the display handling for now. In case of further enquiries, we can change it if needed.

Regarding the display option: We could easily add it, but in general I try to avoid to add to many buttons/options to each module to keep them simple to use and understand. There is always the debate whether the “instrument modules”, i.e. the ones that can load instrument drivers should have an extra parameter section as it can be found for the modules Logger, Switch, Robot, or WaferProber. This would allow to add additional parameters in the driver, when a GUI key is not yet defined by the module.

So far we tried to have the same GUI for each driver of the SMU module and other instrument modules which in our oppinion helps with quickly changing between instruments without the need to understand the user interface again.

1 Like