Have you ever found yourself in a situation where the characterization software did not support the hardware you were trying to use? Your tests may be complex, where the measurement conditions of your next data point depend on previous measurement results. I have found that using Python integrated with the IC-CAP software leads to a powerful and flexible solution that is supported by an open community. To help with the test development, I have found it useful to create a software layer that handles error checking and low-level function calls. In what follows, I'll describe the use of a "PyVISAWrapper" that can simplify your test suite development.
My Motivation
I found myself in a tricky situation when I started using IC-CAP to characterize and model my memristor devices. I needed to perform a quasi-static measurement to precisely control the ramp rate of the voltage sweep by adding a hold time and source delay time, and to control the delay between measurements. However, in some tests, the measurement conditions for the next measurement in IC-CAP depend on previously measured values. Because of that, IC-CAP’s built-in, general-purpose instrument drivers didn’t support my need to perform a quasi-static measurement.
The solution I found to my dilemma was to write Python code in IC-CAP to control my instruments. The process builds on my previous post, Extending the Power of IC-CAP with Python - PyVISA Instrument Control. Using this process, you too can write Python code to control your instruments and create highly flexible measurement routines.
The PyVISA library turned out to be a great way for me to meet these goals, plus it enables portability across several control interfaces (e.g., GPIB and LAN). And, it was tempting for me to dive right in and start writing instrument specific commands using the default GPIB interface. Instead though, I took a step back and thought about how to make my Python code more general. To that end, I decided to create a wrapper for PyVISA’s low-level library functions that would allow me to send the most common VISA calls to control an instrument (e.g., open(), write(), read(), query(), etc.).
PyVISA Wrapper Utility
You might ask, “Why employ a wrapper utility to use the PyVISA library for instrument control?” It’s a fair question and the answer is pretty straightforward. The wrapper utility may be considered a high-level API for controlling instruments. With carefully constructed API function calls, an engineer can more easily create a suite of tests leveraging smarter I/O function calls, including writing instrument commands, querying responses, checking for command complete events, parsing the measurement data, checking for errors, and providing meaningful troubleshooting information.
What follows are details on how you can use an open source PyVISA wrapper utility I wrote called pyvisawrapper.py to simplify writing custom Python code to control almost any instrument. The example code provides the same functionality as the _init_pyvisa macro presented in my last article, but it also adds some useful features described above. The pyvisawrapper API facilitates communication with any instrument that uses TCP/IP or USB, in addition to the standard GPIB. Using the E5270B analyzer as an example instrument, I’ll revisit the _init_pyvisa macro and illustrate the advantages of using the pyvisawrapper functions over low-level function calls.
Prerequisites
Since for this process, we’ll leverage the IC-CAP Python library, as well as the PyVISA library, using import statements, it’s important to make sure you have your Python environment configured correctly. Here’s a few of the basic prerequisites:
- Install IC-CAP_2016_01 or later, under Windows 7 in the default installation directory, which is typically C:\Keysight\ICCAP_2016_01.
- Install the Keysight IO Libraries 17.x software and configure it to use the VISA library visa32.dll. This dynamic link library will be installed in the C:\Windows\system32 directory.
- Configure and test communication with your instruments using the Keysight Connection Expert software.
- Before attempting this example, install and configure a virtual Python environment and install the PyVISA library presented in my aforementioned article, Extending the Power of IC-CAP with Python - PyVISA Instrument Control. Key to this process is the installation of a standalone Python interpreter, which will be great for developing experimental Python scripts and debugging in programming tools for making future modifications to your custom code, like pyvisawrapper itself, outside of the IC-CAP environment.
- Download and copy the pyvisawrapper.py script to your user directory. I personally like to create a sub-directory under my account C:/Users/username/iccap/python to store my own custom python code.
- Set the ICCAP_USER_PYTHON_PATH environment variable in your Windows 7 advanced systems settings to add this directory to the search path. This will allow IC-CAP to find your Python scripts.
- You will need some instruments that can be remotely controlled via GPIB or LAN. Any instrument will do.
Overview
The following is a high-level overview of the code we are going to implement:
- Test your new virtual Python environment installed as part of the prerequisites.
- Create a new IC-CAP macro named _pyvisa_run.
- Activate the virtual Python environment from your script.
- Import the iccap.py and visa.py modules from the IC-CAP and virtual Python 2.7 environments.
- Import the pyvisawrapper.py to use the high-level API functions in your macro.
- Use the visaOpenSession() function to access the PyVISA ResourceManager
and perform the VISA open() on the Keysight E5270B resource via the GPIB interface. - Use the visaCloseSession() to close the resource before exiting the script.
- Use the visaQuery() function to send the *IDN? command to the E5270B and read its response.
- Use the visaClear() function to perform a GPIB device clear and leave the GPIB interface in a initialized state.
Step-By-Step Process for Using pyvisawrapper
Step 1. Verify your new virtual Python environment
Verify the activated Python 2.7 environment by checking the system path 'sys.path' and system prefix 'sys.prefix.' To do this, open a Windows command shell and perform the following steps:
Activate the (icenv) virtual environment
C:\Users\username> workon icenv
(icenv) C:\Keysight\IC-CAP_2016_01\tools\win32_64>
Change the directory to virtual env (icenv)
C:\Users\username> cdvirtualenv
Check the Python interpreter version for (icenv)
(icenv) C:\Users\username\Envs\icenv> python -V
The version should return:
Python 2.7.3
Start the interactive Python interpreter for the virtual environment.
(icenv) C:\Users\username\Envs\icenv> python
You should see something like the following:
Python 2.7.3 (default, Feb 1 2013, 15:22:31) [MSC v.1700 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" from more information.
>>>
Now, enter the following commands at the Python >>> interactive prompt:
>>> import sys
>>> print sys.prefix
You should see something like the following:
C:\Users\username\Envs\icenv
Now type:
>>> print sys.path
You should see something like the following:
[''\,C:\\Users\\username\\Envs\\icenv\\Scripts\\python27.zip',
'C:\\Users\\username\\Envs\\icenv\\DLLs',
'C:\\Users\\username\\Envs\\icenv\\lib',
'C:\\Users\\username\\Envs\\icenv\\lib\plat-win',
'C:\\Users\\username\\Envs\\icenv\\lib-tk',
'C:\\Users\\username\\Envs\\icenv\\Scripts',
'C:\\Users\\username\\Envs\\icenv\\DLLs',
'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\Lib',
'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\DLLs',
'C:\\Keysight\\ICCAP_2016_\\tools\\win32_64\\lib-tk',
'C:\\Users\\username\\Envs\\icenv\\lib\\site-packages',
'C:\\Users\\username\\Envs\\icenv\\DLLs',
'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64']
To exit the virtual Python interpreter type:
>>> quit()
(icenv) C:\Users\username\Envs\icenv>
Leave the virtual environment by typing:
(icenv) C:\Users\username\Envs\icenv> deactivate
C:\Users\username\Envs\icenv>
If you made it this far then your icenv virtual Python environment is setup and ready to be used from IC-CAP.
Step 2. Start IC-CAP, then create a new model file and add a new macro
Go the the main IC-CAP menu and select File, then New. Name your model file ‘pypvisa_example’.
Select the Macros tab and then click New… to create a new macro.
Enter _pyvisa_run for the name of the new macro.
Step 3. Add variables to the IC-CAP Model Variable table
Select Model Variables and enter the following variables in the Model Variable table:
We'll describe each of these variables as follows.
- The
interface
variable should be set to the VISA resource string as listed under theMy Instruments
panel in Keysight Connection Expert or after performing ascan for instruments.
This is also the string returned from PyVISA after sending thelist_resources()
function to the default resource manager. - The
visaResponse
variable holds the response string from the latest command sent to the instrument. - The
error_status
is a list of values to look for in the status byte register that would indicate an error. This information should be available in the instrument's programming guide. In the case of the E5270B analyzer, the decimal value for the error status bit is32.
The error status could potentially be returned as48
if theSet Ready
bit is also active, or128
if an emergency error is reported after the last command sent. - The
error_command
is the list of instrument specific commands necessary for returning the errorcode
and the errormessage
string from the instrument's message buffer. In our E5270B example, the error commands are "ERR?"
and "EMG?"
, with "EMG?"
being the command that is sent after processing the list of codes returned from the "ERR?"
query. This command will return the error message string for the associated error code passed as a parameter when performing the query. "ERR?"
is the query that works with the B1500A to do extended error reporting.
The pyvisawrapper code performs all of this detailed error checking for you! This is incredibly handy. Simply setting the debug variable to 1 in the Model Variable table will enable debug prints in the macro and pyvisawrapper, and output verbose debug text that is displayed in the IC-CAP Output window. This information will aid you in troubleshooting your Python code.
Step 4. Activate the virtual Python environment
Add the following two lines to your macro
activate_this = "/Users/username/Envs/icenv/Scripts/activate_this.py"
execfile(activate_this, dict(__file__=activate_this))
The "username" in the first line is the name of the current user logged into your Windows 7 machine. This generic name is a place holder for the commands listed below and represent the current user's home directory.
Step 5. Import the iccap.py functions and pyvisawrapper.py
Add the following two lines to your macro
from iccap import icfuncs as f, MVar
from pyvisawrapper import *
Now we have access to all the functions in pyvisawrapper.py and iccap.py.
Step 6. Add some local variables and variables that access the Model Variable table
Add the following lines to your macro:
bOk = True # return status of the called function
cmd = "" # command string
instr = MVar("interface").get_val() # visa resource to use from model var table
rsp = MVar("visaResponse") # visa response string from read or query
stat = MVar("error_status").get_val().split(",") # status byte to check for error condition
err = MVar("error_command").get_val().split(",") # error command to send to query error event
debug = MVar("debug").get_val() # enable/disable debug prints
qdelay = 1.0 # delay for visa query
Here we are just reading variables from the IC-CAP model variable table and storing them to local Python variables.
Step 7. Use the high-level API function visaOpenSession to access the VISA Resource Manager and open a link to the E5270B resource
Add the following lines to your macro:
# open session and return visa resource link
vl = visaOpenSession(instr)
if debug: print vl
# close visa session
bOk = visaCloseSession(vl)
if debug: print "visaCloseSession: ",bOk
You should always close the VISA session when you are done with a resource. Not properly closing the session can cause errors if you attempt to re-open the same resource later.
Execute your macro and you should see something like the following displayed in the IC-CAP Output window:
default source manager: Resource Manager of Visa Library at C:\Windows\system32\visa32.dll
resources list: (u'ASRL10::INSTR', u'GPIB0::17::INSTR')pyvisawrapper::visaOpen: True inst: GPIBInstrument at GPIB0::17::INSTR
GPIBInstrument at GPIB0::17::INSTR
pyvisawrapper::closeSession: True
visaCloseSession: True
You did not need to import visa.py to access the VISA resources since pyvisawrapper.py handles that for you. It also handles calling the visa.ResourceManager() and visa.open() using your GPIB0:17::INSTR resource string specified in the Model Variable table.
If this step has errors, then you probably did not install and configure the GPIB VISA driver software application or the instrument is not on the bus. Otherwise the list will not return the GPIB resource with the address of your instrument. The communication drivers installed as part of the prerequisites allow you to 'scan for instruments.' National Instruments provides NI MAX (Measurement & Automation Explorer). Keysight interfaces provide Keysight Connection Expert. These software applications should be installed with your 488.2 and VISA drivers for your communications interface.
Step 8. Send your first command to the instrument to return its identifier string
Add the following lines to your macro:
cmd = "*IDN?"
if bOk: bOk = visaQuery(vl, cmd, rsp, 10000, stat, err)
print "visaQuery: {} cmd returned {}".format(cmd, rsp.get_val().split(","))
Execute your macro and you should see something like the following displayed in the IC-CAP Output window:
intr: GPIBInstrument at GPIB0::17::INSTR cmd: *IDN? rsp: <iccap.MVar instance at 0x000000000E11B608>
timo: 10000 stat: 48 err: <iccap.MVar instance at 0x000000000E11B488>pyvisawrapper::visaQuery() True read: Agilent Technologies,E5270B,0,B.01.10
bytes: 39
pyvisawrapper::visaQuery: True *IDN? returned ['Agilent Technologies', 'E5270B', '0', 'B.01.10\r\n']
NOTE: The instrument was found on interface GPIB0 at address 17 and returned its identifier string in the visaQuery response.
Step 9. Do some clean up and leave the system in an initialized state
It is good practice to send a GPIB device clear before closing the interface.
To do that, add the following lines to your macro:
bOk = visaClear(vl)
if debug: print "visaClear: ",bOK
Step 10. Test the completed _pyisa_run macro
Execute your macro and you should see something like the following displayed in the IC-CAP Output window:
default source manager: Resource Manager of Visa Library at C:\Windows\system32\visa32.dll
resources list: (u'ASRL10::INSTR', u'GPIB0::17::INSTR')pyvisawrapper::visaOpen: True inst: GPIBInstrument at GPIB0::17::INSTR
GPIBInstrument at GPIB0::17::INSTR
intr: GPIBInstrument at GPIB0::17::INSTR cmd: *IDN?
rsp: <iccap.MVar instance at 0x000000000C39DF08> timo: 10000 stat: 48 err: ERR?pyvisawrapper::visaQuery() True read: Agilent Technologies,E5270B,0,B.01.10
bytes: 39
pyvisawrapper::visaQuery: cmd *IDN? returned ['Agilent Technologies', 'E5270B', '0', 'B.01.10\r\n']
pyvisawrapper::clear: True inst: GPIBInstrument at GPIB0::17::INSTR
visaClear: True
pyvisawrapper::closeSession: True
visaCloseSession: True
Example of pyvisawrapper's Error Processing
Now, let's look at an example of the pyvisawrapper.py error processing, which will help you find errors occurring in your code. Again, this is one of the real advantages of using the pyvisawrapper API. Without it, you would have to write instrument specific code in your Python script to detect, query and display any errors.
Let's purposely create an example of a common type of error such as sending an illegal argument or command to the instrument, and see how it is handled by the pyvisawrapper. Let's change our macro slightly to send the command "ID?" instead of "*IDN?"
Change the following lines to your macro:
cmd = "ID?"
if bOk: bOk = visaQuery(vl, cmd, rsp, 10000, stat, err)
print "visaQuery: {} cmd returned {}".format(cmd, rsp.get_val().split(","))
See the error on line 20?
Execute your macro and you should see something like the following displayed in the IC-CAP Output window:
intr: GPIBInstrument at GPIB0::17::INSTR
cmd: ID?
rsp: <iccap.MVar instance at 0x000000000BFF0348>
timo: 10000
stat: 48
err: ERR?
Query error VI_ERROR_TMO (-1073807339): Timeout expired before operation completed. - last command: ID?
visaQuery: cmd ID? returned ['visaError : 100 : Undefined GPIB command.\r\n']
With no error checking, we would only get the timeout error "VI_ERROR_TMO (-1073807339)
". That doesn't say much, does it?
What's Next?
If you've successfully completed all of these steps, then you should now be able to access the most common functions of PyVISA, the pyvisawrapper API from IC-CAP. And that means you can now very simply create new transforms for instrument control and data acquisition over any supported interface.
In a future article, I’ll outline the steps for using the ideas presented here to create a series of transforms to perform an Id versus Vg quasi-static measurement on a discrete NMOS device. In other forthcoming articles, I'll show you how to use more of the powerful features included in IC-CAP and exposed through the iccap.py. I'll also use the pyvisawrapper.py again to write Python code for accessing an instrument and returning measured results. I'll even show you how to access more of the internal IC-CAP functions and data structures, which are provided to enable additional analysis and plotting functions in IC-CAP.
In the meantime, I hope you find this tutorial and the pyvisawrapper utility useful in exploring the various possible ways you can extend IC-CAPs powerful framework to characterize and model your most challenging devices. For more information on IC-CAP or Keysight IO Libraries, go to www.keysight.com/find/eesof-iccap and www.keysight.com/find/IOlibraries.
References
About Python If you are new to Python programming, here’s some information to help you follow the steps in this blog. Python is an "interpreted" language, which means it generally executes commands typed by the user in an interactive command shell. This is convenient for testing program statements to learn the Python syntax. However, a more common means of writing a Python program is to create a Python script file with the '.py' extension. Say you created an example program and save it as 'example.py.' To run the program, you simply type 'python example.py' at the command shell prompt. Python scripts, which are often called modules, can also be used for creating libraries of functionality, and are distributed along with program resources in packages. Packages can be installed and combined with user-generated code to extend a program's functionality. PIP is the Python package installer that integrates with PyPI.org—the Python Package Index. It is a repository of numerous free Python applications, utilities and libraries. PIP allows you to download and install packages from the package index without manually downloading, extracting and installing the package via the command 'python setup.py install'. PIP also checks for package dependencies and automatically downloads and installs those as well. An environment is a folder (directory) that contains everything a Python project (application) needs to run in an organized, isolated manner. When it’s initiated, it automatically comes with its own Python interpreter—a copy of the one used to create it—alongside its very own PIP.
Links
Extending the Power of IC-CAP with Python - PyVISA Instrument Control
Keysight IC-CAP Device Modeling Software
The Python Tutorial — Python 2.7.13 documentation
User Guide — virtualenv 15.1.0 documentation
PyVISA: Control your instruments with Python — PyVISA 1.8 documentation
This is great!
On my pyvisawrapper.py, I made a small change in the visaOpenSession function, as shown below.
def visaOpenSession(r):
bOk = True
rm = visa.ResourceManager()
if debug: print "default source manager:", rm
if debug: print "resources list:", rm.list_resources()
# open visa session on resource
print 'resource string: ' + r
sl = rm.open_resource(r, write_termination = '\n')
...