-
Notifications
You must be signed in to change notification settings - Fork 9
Instruments New
JISA
is an object-oriented (OO) library, especially when it comes to instruments. For this reason, if you are unfamiliar with OO (ie objects and classes), then please read the OO tutorial page here.
The basic idea of connecting to an instrument using JISA
is that you create (or "instantiate") an object to represent it. The class of object you need to create depends on the make/model of the instrument. For example, a Keithley 236 is connected to by creating a K236
object whereas a LakeShore 336 needs a LS336
object. These are all referred to as Instrument
objects.
When creating one of these objects you also need to provide it with an Address
object to specify how the device is connected to the computer. The type of object here depends on the connection, for example if it's over GPIB then you need a GPIBAddress
object whereas if it was over serial you'd need a SerialAddress
object. You then give this as an argument when creating your Instrument
object.
For example, connecting to a Keithley 236 over GPIB (address 24) and to a LakeShore 336 over serial (port 5):
Java
K236 smu = new K236(new GPIBAddress(24));
LS336 tc = new LS336(new SerialAddress(5));
Kotlin
val smu = K236(GPIBAddress(24))
val tc = LS336(SerialAddress(5))
Python
smu = K236(GPIBAddress(24))
tc = LS336(SerialAddress(5))
We can then control these instruments using these objects:
smu.setVoltage(5.0);
smu.turnOn();
tc.setTargetTemperature(300.0);
tc.useAutoHeater();
- Connection Examples
- Instrument Classes
- Instrument Addresses
- Basic Instrument Functionality
- Exceptions when Connecting
- Instrument Interfaces
- Channels as Virtual Instruments
Here's some examples of connecting to instruments, for a quick overview of what it should look like:
Java
// Keithley 2450 over TCP-IP at 192.168.0.5:
SMU k2450 = new K2450(new TCPIPAddress("192.168.0.5"));
// ITC503 over GPIB at address 24
MSTC itc503 = new ITC503(new GPIBAddress(24));
// Keithley 2200 over serial, port 4
DCPower k2200 = new K2200(new SerialAddress(4));
Kotlin
// Keithley 2450 over TCP-IP at 192.168.0.5:
val k2450 = K2450(TCPIPAddress("192.168.0.5"))
// ITC503 over GPIB at address 24
val itc503 = ITC503(GPIBAddress(24))
// Keithley 2200 over serial, port 4
val k2200 = K2200(SerialAddress(4))
Python
# Keithley 2450 over TCP-IP at 192.168.0.5:
k2450 = K2450(TCPIPAddress("192.168.0.5"))
# ITC503 over GPIB at address 24
itc503 = ITC503(GPIBAddress(24))
# Keithley 2200 over serial, port 4
k2200 = K2200(SerialAddress(4))
For reference, all supported instruments and their corresponding classes are listed below:
To connect to an instrument, JISA
needs to know where it is and how to talk to it. For example, it might be connected on address 20 over GPIB or it might be at 192.168.0.3
over TCP-IP.
To represent this, JISA
uses Address
objects. For each type of connection there is a relevant class. Below is listed all the available Address
types in JISA
. Parameters surrounded by [ ... ]
are optional.
While different instruments will obviously have different functions, they do all share a few. These are:
instrument.getIDN(); // Returns an identifying string
instrument.getAddress(); // Returns the address object used to connect
instrument.setTimeout(milliseconds); // Sets the timeout in milliseconds
instrument.close(); // Closes the connection to the instrument
The close()
method on each instrument will be automatically called when the object is no-longer being used (or if the program terminates normally) so you won't normally need to use it.
Connecting to an instrument can throw an exception. This will happen if something goes wrong with the connection. The most common example is if there is nothing to connect to, but other examples include using the wrong class to connect to an instrument or the connection breaking down before connection is fully established.
It is therefore advisable that you connect to your instrument within a try...catch
(or try...except
) block in your code. You could then use the GUI
package to display an error message in the catch
block and then exit the program. For example:
Java
K2450 keithley;
try {
// Attempt to connect
keithley = new K2450(new GPIBAddress(0, 20))
} catch (Exception e) {
// If it fails, show an error message and exit
GUI.errorAlert("Connection Error", e.getMessage());
System.exit(1);
}
Kotlin
var keithley: K2450
try {
keithley = K2450(GPIBAddress(0, 20))
} catch (e: Exception) {
GUI.errorAlert("Connection Error", e.message);
exitProcess(1);
}
Python
try:
keithley = K2450(GPIBAddress(0, 20))
except Exception as e:
GUI.errorAlert("Connection Error", e.getMessage())
quit()
Whilst each make/model of instrument is represented in JISA
with its own class, these classes are further organised around a set of interfaces corresponding to the type of instrument it controls. In general, an interface is like a class but all of its functions have no code in them. You can then create a class that "implements" your interface and as a result will be required to have all the functions that the interface has.
In the context of JISA
, this is used to define what standard functions Instrument
objects should have based on what type of instrument they control. For example, the K2450
and K236
classes both implement the SMU
interface (since they're both SMUs). This means that when we create K2450
and K236
objects:
K2450 smu1 = new K2450(new GPIBAddress(17));
K236 smu2 = new K236(new GPIBAddress(20));
We no-longer have to think about them as K2450
or K236
objects, but simply as SMU
objects:
SMU smu1 = new K2450(new GPIBAddress(17));
SMU smu2 = new K236(new GPIBAddress(20));
The obvious advantage of this is that you can write code to perform a measurement without needing to know what make/model of instrument you're using, just the type, for instance with two SMUs:
void doMeasurement(SMU smu1, SMU smu2) {
smu1.setCurrent(0.0);
smu2.setCurrent(0.0);
smu1.turnOn();
smu2.turnOn();
for (double I : Util.makeLinearArray(0.0, 50e-3, 11)) {
smu1.setCurrent(I);
double V = smu2.getVoltage();
double resistance = V / I;
System.out.println(resistance);
}
}
So now we can run this measurement with any two SMUs, since we know that whatever the make/model they will have the standard functions that we used in doMeasurement(...)
. For example using a Keithley 2450 and a Keithley 236:
SMU smu1 = new K2450(new GPIBAddress(17));
SMU smu2 = new K236(new GPIBAddress(20));
doMeasurement(smu1, smu2);
or if we wanted to run it using the two channels on a Keithley 2600B instead (more on this in the next section):
MCMSU smu = new K2600B(new TCPIPAddress("192.168.0.5"));
SMU smu1 = smu.getChannel(0);
SMU smu2 = smu.getChannel(1);
doMeasurement(smu1, smu2);
Both will result in the same measurement being performed but on different SMUs.
Below are listed all the instrument type interfaces:
Interface | Description |
---|---|
TMeter |
Thermometer |
MSTMeter |
Multi-Sensor Thermometer |
VMeter |
Voltmeter |
IMeter |
Ammeter |
VSource |
Voltage Source |
ISource |
Current Source |
IVMeter |
Multimeter |
IVSource |
Voltage/Current Source |
SMU |
Source-Measure Unit |
MCSMU |
Multi-Channel SMU |
TC |
Temperature Controller |
MSTC |
Multi-Sensor TC |
MSMOTC |
Multi-Sensor + Multi-Output TC |
LockIn |
Lock-In Amplifier |
DPLockIn |
Dual-Phase Lock-In Amplifier |
DCPower |
DC Power Supply |
VPreAmp |
Voltage Pre-Amplifier |
Some of these extend from each other. For instance IVMeter
is IMeter
and VMeter
combined, and SMU
is IVMeter
and IVSource
combined.
For this result, an SMU
object will be considered to be a valid VMeter
, IMeter
, VSource
, ISource
, IVMeter
or IVSource
as well as just an SMU
. So if you wrote some code that required a voltmeter (VMeter
), you can stick in an SMU (SMU
) to act as one instead.
Likewise, temperature controllers are considered to be thermometers with extra functionality. Therefore, a TC
/MSTC
/MSMOTC
can be considered to be a valid TMeter
object (since the functionality of a TMeter
is a sub-set of that of a TC
).
To further this theme of interchangeability, multi-channel interfaces (like MCSMU
, MSTMeter
, MSTC
etc) can be considered to be collections of their single-sensor counterparts. For example, an MCSMU
with 2 channels can be considered to be a collection of 2 single-channel SMU
objects. This is how you generally should treat multiple channels in JISA
. For instance, if we take the Keithley 2600B SMU (which has 2 channels), we can extract each as its own SMU
object like so:
MCSMU mcsmu = new K2600B(new TCPIPAddress("192.168.0.2"));
SMU channelA = mcsmu.getChannel(0);
SMU channelB = mcsmu.getChannel(1);
This can also be done with multi-output temperature controllers, splitting each PID output controller into individual TC
objects and for MSTC
and MSMOTC
objects you can extract each temperature sensor as a TMeter
object.
MSMOTC tc = new LS336(new SerialAddress(6));
TC base = tc.getOutput(0);
TC stage = tc.getOutput(1);
TMeter sample = tc.getSensor(2);
TMeter arm = tc.getSensor(3);
These individual virtual instruments will now control their respective channel as if you were giving the relevant channel-specific commands to the multi-channel instrument object itself.
For example:
channelA.setVoltage(15.5);
channelB.setCurrent(50e-3);
// == is the same as ==
mcsmu.setVoltage(0, 15.5);
mcmsu.setCurrent(1, 50e-3);
This lets you generalise your program so that it can be used with whatever combination of SMU channels the user wants. (ie two separate SMUs can be swapped out for a single 2-channel SMU and your code won't need to change).
It is recommended that you always interact with multi-channel instruments this way. Not only does it make your code more readable, but it also makes it easier to swap out instruments further down the road.
More specific information about this can be found on the relevant instrument type page.
- Getting Started
- Object Orientation
- Choosing a Language
- Using JISA in Java
- Using JISA in Python
- Using JISA in Kotlin
- Exceptions
- Functions as Objects
- Instrument Basics
- SMUs
- Thermometers (and old TCs)
- PID and Temperature Controllers
- Lock-Ins
- Power Supplies
- Pre-Amplifiers
- Writing New Drivers