Re: OT: I/V curve tracing made easy with Python and PyVISA

Jim Ford
 

Thanks, Magnus, for posting.  I, too, am interested in automating my test bench using Python.  This after seeing the nightmare at work of piles of Labview code tweaked by many engineers over several decades.  You know there's a problem when tests have to pass 2 out of 3 times because of race conditions and other nasties in the code!  We asked one of the software jocks for an Abort Test button that actually gets out of a test, but he said "Well, it's not that simple.  Which module should it jump out of, and where should it land?"  Uh, never mind...!With the VISA wrappers it *should* be easy to write test code for both my old (mostly HP) instruments with their GPIB interfaces and newer gear with USB.  Hoping to get a GPIB card for my garage lab computer and get programming in Python over the summer (of which year remains to be seen!)Jim Ford Sent from my Verizon, Samsung Galaxy smartphone

-------- Original message --------From: "magnustoelle via Groups.Io" <magnustoelle=yahoo.com@groups.io> Date: 5/16/19 8:26 AM (GMT-08:00) To: TekScopes@groups.io Subject: [TekScopes] OT: I/V curve tracing made easy with Python and PyVISA Good Day to the group,this is completely off-topic, but as several of you seemed always interested in curve-tracing applications, let me share my excitement with you...I know that I am very late into this, but allow me to say that I have found the use of the Python scripting language in combination with libraries & packages such as PyVISA very powerful.To cut a long story short: I was curious to see, if I can replace my NI LabView projects with something simpler, less costly and more portable between Operating Systems such as Windows, Linux etc. And after playing with Python for a few days, it seems that Python offers such an alternative.I still like how easy it is to generate appealing GUIs with LabView, but even when using hierarchical designs, LabView "code" can be hard to read and comprehend - just try to decipher your own VIs after a few months!I have used PyVISA https://pyvisa.readthedocs.io/en/master/ for easy communication with T&M equipment and libraries such as mathlibplot https://matplotlib.org/ for plotting and numpy https://www.numpy.org/ for a few math operations. The documentation and tools come for free, and there are tons and tons of on-line training courses on Python as well as code examples etc. which are available at 0 costs.Curve-tracing application:I have used a GRUNDIG/digimess PN300 programmable power supply & a common NI USB-GPIB adapter; then created some simple Python-scripts which allow the I/V-curve tracing of two-terminal devices such as diodes and of three-terminal devices such as transistors. For the latter, output A from the power supply provided the Collector-Emitter sweep voltage, and output B and a 22kOhm resistor provided the base current sweep.I have never really had any formal education in programming/coding, but I have found it fairly easy to create the attached PNG diagram, to save the current and voltage data to a local file for post-processing with MS Excel etc.While this is nothing to "write home about" - my heartful recommendation is: If you have the interest and leisure, give Python and a stab into PyVISA a try!I am including my simple Python code below . I am sure there are many things which could be improved - however,  please let's do not discuss here...Cheers,MagnusP.S. I am aware that the GRUNDIG PN300 power supply is not a precision instrument, but I have chosen it for simplicity - upgrading to precision DMMs and more precise current/voltage sources and the like would be easy.# Setup Digimess/GRUNDIG PN300 power supply# Testing three-terminal electronic devices such as transistors# Set-up for independent operation, Voltage and Currents for output A and readback Current measurements# Set-up Voltage and Currents for output B and readback Current measurements and turn off outputs# Hardware setup: Connect emitter to common GND, collector to pos. terminal output A, connect base to output B using a 22k series resistor for base currents.# Magnus Tölle, last edit: 13th May 2019# Setup: Easyinstall from https://pypi.org/project/setuptools/0.6c11/#files first, and "easyinstall pip" second# Install Python, pip, matplotlib, numpy, pandas, seaborn and NI VISA from ni.com# References: https://github.com/demisjohn/Keithley-I-V-Sweep/blob/master/Keithley%20I-V%20Sweep%20v2.py # Import libraries for plottingimport matplotlib.pyplot as pltimport numpy as np# import pandas as pd# import seaborn as sns# Import libraries for VISA controlimport visa# Optional logging of PyVISA# visa.log_to_screen()# Import other librariesimport time # as to allow pause between measurementsimport csv # as to allow saving data file# For UTF Coding to avoid encoding issues:# -*- coding: utf-8 -*-# Program runs on Windows only:import sysimport os # Filesystem manipulation# Disable printdef blockPrint():     sys.stdout = open(os.devnull, 'w')# Restore printdef enablePrint():     sys.stdout = sys.__stdout__def windows_interaction():     assert ('win' in sys.platform), "This program only runs on Windows systems."     try: windows_interaction()     except AssertionError as error:         print(error)         print('This script was not executed')# open resource Digimess/GRUNDIG PN300 power supplyrm = visa.ResourceManager()# rm.list_resources()res = rm.list_resources()print ("Please connect and power-up GPIB adapter and PN300 power supply")print ("Please configure PN300 for GPIB and note address - default is address 7")PN300_GPIB_address = input("Please enter GPIB address:")print ("Please connect DUT as follows:")print ("Emitter to common GND, collector to pos. terminal output A, connect base to output B using a 22k series resistor")print("Found the following equipment / resources:")print(res)     # print("Opening " + res[-1])PN300 = rm.get_instrument ('GPIB::' + str(PN300_GPIB_address))  # Query to confirm that Digimess/GRUNDIG PN300 is presentprint (PN300.query('*IDN?'))  # Initialize and setup Digimess/GRUNDIG PN300 power supply for constant voltage, independent operationPN300.write('*RST')PN300.write('OPER_IND')PN300.write('CONT_CV')# Ask user for voltage setup, current limits for output AVstartA = input("Enter min. voltage sweep setting for output A in Volt: ")VstopA = input("Enter max. voltage sweep setting for output A in Volt: ")VstepA = input("Enter number of sweep steps for output A: ")VoltsA = np.linspace(int(VstartA),int(VstopA),(int(VstepA)+1))Current_setA = input("Enter current limit for output A in Ampere: ")Current_setA = 'ISET ' + Current_setA# Ask user for voltage setup, current limits for output BVstartB = input("Enter min. current sweep setting for output B in µA: ")VstopB = input("Enter max. current sweep setting for output B in µA (max. 1300µA): ")VstepB = input("Enter number of sweep steps for output B: ")Current_setB = input("Enter current limit for output B in Ampere: ")Current_setB = 'ISET ' + Current_setB# Ask user for base current resistor valueResistor = input("Enter measured series resistor value in kOhm: ")Resistor = float(Resistor)*1E3VoltsB = np.linspace(float(float(VstartB)*1E-6*Resistor),float(float(VstopB)*1E-6*Resistor),int(VstepB)+1) # Ask user for DUT nameDUTname = input("Enter name of the device under test or DUT: ")# Set timeout to 10 secondsPN300.timeout = 10000# Enable outputs, and set output A and B to 0V, inform user that measurements start nowPN300.write('SEL_A')PN300.write('VSET 0.00')PN300.write(Current_setA)PN300.write('SEL_B')PN300.write('VSET 0.00')PN300.write(Current_setB)PN300.write('OUT_ON')print("Starting with measurements now")VoltageDataA = []CurrentDataA = []CurrentDataB = []for Voltage2 in VoltsB:     PN300.write('SEL_B')     PN300.write('VSET '+(str(Voltage2)))     Voltage2A = PN300.query('VOUT?')     Voltage_formatted2A = float(Voltage2A[2:7])     CurrentDataB.append(Voltage_formatted2A/Resistor)     for Voltage1 in VoltsA:         PN300.write('SEL_A')         PN300.write('VSET '+(str(Voltage1)))         Voltage1A = PN300.query('VOUT?')         Voltage_formatted1A = float(Voltage1A[2:7])         VoltageDataA.append(Voltage_formatted1A)         # time.sleep(0.1) optional delay time of 0.1 seconds         Current1A = PN300.query('IOUT?')         Current_formatted1A = float(Current1A[2:7])         CurrentDataA.append(Current_formatted1A)         print ("Emitter voltage and current: ", Voltage1A, Current1A)         print ("Base current in µA: ", (Voltage_formatted2A/Resistor*1E6))DataArrayA = np.asarray((VoltageDataA, CurrentDataA, CurrentDataB))  # Set output A to 0V and turn outputs offPN300.write('OUT_OFF')Voltage_setA = 'VSET 0.00'PN300.write(Voltage_setA)# Notify userprint("Finished measurement and outputs turned off")plt.style.use('seaborn-whitegrid')for IndexA in range(0, int(VstepA)+1):     IndexB = IndexA+1     IndexC = IndexA+2plt.plot(VoltageDataA[(IndexA*int(VstepA)+IndexA):(IndexC*int(VstepA)+IndexC-1)], CurrentDataA[(IndexA*int(VstepA)+IndexA):(IndexC*int(VstepA)+IndexC-1)], linestyle='--', marker='x', markersize=8, color='g')plt.xlabel('Voltage in Volt')plt.ylabel('Current in Ampere')plt.legend([DUTname + ' I/V curve'], loc='best', shadow=True)plt.show()# Save the Data to a csv-file with the DUT name# with appended rows for VoltageA, CurrentA, CurrentB dataNewFile = open(DUTname+'.csv', 'w')with open(DUTname+'.csv', 'w', newline='') as fp:     wr = csv.writer(fp, dialect='excel')     wr.writerow(VoltageDataA)with open(DUTname+'.csv', 'a', newline='') as fp:     wr = csv.writer(fp, dialect='excel')     wr.writerow(CurrentDataA)with open(DUTname+'.csv', 'a', newline='') as fp:     wr = csv.writer(fp, dialect='excel')     wr.writerow(CurrentDataB)# Close the file - end of python scriptNewFile.close()

Join TekScopes@groups.io to automatically receive all group messages.