Jump to content
  • 0

Current Limiting IV Curves


joshna

Question

Hi all,

I created a script (below) using the Waveforms SDK to use an Analog Discovery 3 to sweep gate and drain voltages over a mosfet and generate IV curves. The plots look right but they seem to be limited by current and aren't showing the full plot. I was wondering if anyone knows why this is?

 

The plots:

Screenshot2024-03-26at17_35_38.thumb.png.471bb9ee7ebb7e82f1800ff46d914dd9.png

Screenshot 2024-03-26 at 17.34.41.png

 

This is the script I am runing:

from WF_SDK import device, scope, wavegen   # import instruments
 
import matplotlib.pyplot as plt   # needed for plotting

import csv #needed for generating CSV files for graphing later
 
"""-----------------------------------------------------------------------"""
 
#resistance = float(input("Enter the resistance in Ohms of the resistor in series with the mosfet: ")) TODO user inputs later
resistance = 100
gate_voltages = [0, 1, 2, 3, 4, 5] #gate voltages to sweep across
# name of csv files
filename_currents = "chip_currents.csv"
filename_voltages = "chip_voltages.csv"
# connect to the device
device_data = device.open() #TODO open again to get second device
 
"""-----------------------------------"""

# writing to csv file  
for filename in [filename_currents, filename_voltages]:
    with open(filename, 'w') as csvfile: # opens csv files
        csvwriter = csv.writer(csvfile)  # creating a csv writer object 
        csvwriter.writerow(gate_voltages) # writes header row (gate voltages)
 
# initialize the scope with default settings
scope.open(device_data, sampling_frequency=10e5)

# generate a 10KHz sine signal with 2V amplitude on channel 1
current_dict = {}
volt_dict = {}
for VG in gate_voltages:
    wavegen.generate(device_data, channel=2, function=wavegen.function.dc, offset=VG, frequency=10e2, amplitude=1) #generate dc signal to gate voltage at voltage i
    wavegen.generate(device_data, channel=1, function=wavegen.function.sine, offset=2.5, frequency=10e2, amplitude=2.5) #generation sine waveform to drain
    [mosfet_voltages, resistor_voltages] = scope.record2(device_data) # get data with AD3 oscilloscope
    mosfet_currents = []
    for v in resistor_voltages:
        mosfet_currents.append(v/resistance) # calculate current with ohms law
    for filename in [filename_currents, filename_voltages]: #outputs currents and voltages to csv
        with open(filename,'a') as csvfile:
            writer = csv.writer(csvfile)
            if "current" in filename:
                writer.writerow(mosfet_currents)
            elif "voltage" in filename:
                writer.writerow(mosfet_voltages)
    plt.plot(mosfet_voltages, mosfet_currents) #plot curve of mosfet voltages vs. mosfet currrents    

#plot labels and show
plt.xlabel("Voltage (V_DS) [V]")
plt.ylabel("Current (I_D) [A]")
plt.show()

# reset the scope
scope.close(device_data)
 
# reset the wavegen
wavegen.close(device_data)
 
# close the connection
device.close(device_data)

 

In case anyone asks, record2 which I use to record data from the oscilloscope is just a modified scope.record function in order to get data from both channels at the same time. Even if I use record, I still see current limiting graphs but they are also just wrong because of the time offset so I don't think that is the problem. Regardless, the code for record2 in scope.py looks like this:

def record2(device_data):
    """
        record an analog signal

        parameters: - device data
                    - the selected oscilloscope channel (1-2, or 1-4)

        returns:    - a list with the recorded voltages
    """
    # set up the instrument
    if dwf.FDwfAnalogInConfigure(device_data.handle, ctypes.c_bool(False), ctypes.c_bool(True)) == 0:
        check_error()
    
    # read data to an internal buffer
    while True:
        status = ctypes.c_byte()    # variable to store buffer status
        if dwf.FDwfAnalogInStatus(device_data.handle, ctypes.c_bool(True), ctypes.byref(status)) == 0:
            check_error()
    
        # check internal buffer status
        if status.value == constants.DwfStateDone.value:
                # exit loop when ready
                break
    
    # copy buffer
    buffer = (ctypes.c_double * data.buffer_size)()   # create an empty buffer
    buffer2 = (ctypes.c_double * data.buffer_size)()   # create an empty buffer
    if dwf.FDwfAnalogInStatusData(device_data.handle, ctypes.c_int(0), buffer, ctypes.c_int(data.buffer_size)) == 0:
        check_error()
    if dwf.FDwfAnalogInStatusData(device_data.handle, ctypes.c_int(1), buffer2, ctypes.c_int(data.buffer_size)) == 0:
        check_error()
    
    # convert into list
    buffer = [float(element) for element in buffer]
    buffer2 = [float(element) for element in buffer2]
    return [buffer, buffer2]

 

Thank you so much!

 

Edited by joshna
Link to comment
Share on other sites

3 answers to this question

Recommended Posts

  • 1

Hi @joshna

Probably the V_DS input voltage on the analog input channel is out the range. 
The analog inputs of AD3 have two input ranges, approximately 5V and 50V pk2pk, and adjustable offset.
Set offset or range dwf.FDwfAnalogInChannelOffsetSet(hdwf, #, c_double(2.5)) or dwf.FDwfAnalogInChannelRangeSet(hdwf, #, c_double(50.0))

Link to comment
Share on other sites

  • 0
On 4/2/2024 at 9:31 AM, attila said:

Hi @joshna

Probably the V_DS input voltage on the analog input channel is out the range. 
The analog inputs of AD3 have two input ranges, approximately 5V and 50V pk2pk, and adjustable offset.
Set offset or range dwf.FDwfAnalogInChannelOffsetSet(hdwf, #, c_double(2.5)) or dwf.FDwfAnalogInChannelRangeSet(hdwf, #, c_double(50.0))

@attila Thank you so much! This helped me a lot with increasing the input voltage.

 

However, I now see 2 other problems so I was wondering if you would be able to help me with that?

One problem is that the voltages I get now go up to 30 Volts, by the input waveform should only go from 0 to 10 volts so the channel should not be getting and plotting 30 Volts.

For reference, this is the new graph:

Screenshot2024-04-09at16_27_08.thumb.png.f858589617b43114eb4bf7b1434af75a.png

And this is the updated code:

from WF_SDK import device, scope, wavegen   # import instruments

import matplotlib.pyplot as plt   # needed for plotting

import csv #needed for generating CSV files for graphing later
import ctypes
from sys import platform, path    # this is needed to check the OS type and get the PATH
from os import sep                # OS specific file path separators
 
"""-----------------------------------------------------------------------"""
# assign dwf to be used later
# load the dynamic library, get constants path (the path is OS specific)
if platform.startswith("win"):
    # on Windows
    dwf = ctypes.cdll.dwf
    constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py"
elif platform.startswith("darwin"):
    # on macOS
    lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf"
    dwf = ctypes.cdll.LoadLibrary(lib_path)
    constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py"
else:
    # on Linux
    dwf = ctypes.cdll.LoadLibrary("libdwf.so")
    constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py"

# import constants
path.append(constants_path)
 
drain_resistance = 100#float(input("Enter the resistance in Ohms of the resistor in series with the mosfet DRAIN: "))
gate_resistance = 100#float(input("Enter the resistance in Ohms of the resistor in series with the mosfet GATE: "))
chip_number = "305"#input("Enter your chip number (ex. 305): ")
device_id = "2b1"#input("Enter the device being tested (ex. 2b1): ")
gate_voltages = input("Enter the gate voltages to test as a comma-separated list (Ex. 1, 1.5, 1.6, 3): ").replace(" ", "").split(",")
# name of csv files
filename_currents = f"csvfiles/{chip_number}_{device_id}_currents.csv"
filename_voltages = f"csvfiles/{chip_number}_{device_id}_voltages.csv"
# connect to the device
ad3_data1 = device.open() #open the first analog discovery 3 to measure Id and Vds
ad3_data2 = device.open() #open the second analog discovery 3 to measure Ig and Vds
#ad3_data3 = device.open() #open the third analog discovery 3 to measure Ib and Vds TODO
 
"""-----------------------------------"""

# writing to csv file  
for filename in [filename_currents, filename_voltages]:
    with open(filename, 'w') as csvfile: # opens csv files
        csvwriter = csv.writer(csvfile)  # creating a csv writer object 
        csvwriter.writerow(gate_voltages) # writes header row (gate voltages)
 
# initialize the scope with default settings
scope.open(ad3_data1, sampling_frequency=10e5)

wavegen.generate(ad3_data1, channel=1, function=wavegen.function.sine, offset=5, frequency=100, amplitude=5) #generation sine waveform to drain

#set voltage peak to peak input range to 50 V on both channels
dwf.FDwfAnalogInChannelRangeSet(ad3_data1.handle, 0, ctypes.c_double(50.0))
dwf.FDwfAnalogInChannelRangeSet(ad3_data1.handle, 1, ctypes.c_double(50.0))

# generate a 10KHz sine signal with 2V amplitude on channel 1
current_dict = {}
volt_dict = {}
#for VB in body_voltages: #TODO loop through body as well
for VG in gate_voltages:
    VG = float(VG)
    wavegen.generate(ad3_data1, channel=2, function=wavegen.function.dc, offset=VG, frequency=10e2, amplitude=1) #generate dc signal to gate voltage at voltage i
    [drain_voltages, ds_voltages1] = scope.record2(ad3_data1) # get data with first AD3 oscilloscope
    [gate_voltages, ds_voltages2] = scope.record2(ad3_data2) # get data with second AD3 oscilloscope
    drain_currents = []
    gate_currents = []
    for i in range(len(drain_voltages)):
        drain_currents.append(drain_voltages[i]/drain_resistance) # calculate current with ohms law
        gate_currents.append(gate_voltages[i]/gate_resistance) # calculate current with ohms law
    for filename in [filename_currents, filename_voltages]: #outputs currents and voltages to csv TODO add second AD3 data maybe to XLS
        with open(filename,'a') as csvfile:
            writer = csv.writer(csvfile)
            if "current" in filename:
                writer.writerow(drain_currents)
            elif "voltage" in filename:
                writer.writerow(ds_voltages1)
    plt.plot(ds_voltages1, drain_currents, label = f"Id with Vg = {VG}") #plot curve of Id vs. Vds    
    #plt.plot(ds_voltages2, gate_currents, label = f"Ig with Vg = {VG}") #plot curve of Id vs. Vds   
    #TODO figure out how to 3d plot agains Ib as well 


#plot labels and show
leg = plt.legend(loc='upper center')
#plt.xlim(0, 10)
#plt.ylim(-0.001, 0.03)
plt.xlabel("Voltage (V_DS) [V]")
plt.ylabel("Current [A]")
plt.show()

#TODO only save data to csv if plot looks good

# reset the scope
scope.close(ad3_data1)
 
# reset the wavegen
wavegen.close(ad3_data1)
 
# close the connection
device.close(ad3_data1)

 

The other problem I have, is that when I don't start off with a gate voltage of 1 V, I get really strange looking graphs. For example,

 Vg = 1, 2, 3, 4, 5 on 50V vs 5V analog inputScreenshot2024-04-09at16_27_08.thumb.png.7629e145c6c2c848834d8e2c03ebecd6.png:Screenshot2024-04-09at16_29_38.thumb.png.d91e1a886d4080ee4079f3badabe5a7f.png

 

 

Vg = 2, 3, 4, 5 on 50V vs 5V analog input:

Screenshot2024-04-09at16_27_24.thumb.png.30fcafc3a3b13e86d018545547159a82.pngScreenshot2024-04-09at16_29_51.thumb.png.58741db714d3f8396284ae00e8d40def.png

 

Vg = 2, 3, 4 on 50V vs 5V analog input:

Screenshot2024-04-09at16_27_58.thumb.png.a7f8a5c590beee36a786136dd7303bdd.pngScreenshot2024-04-09at16_30_14.thumb.png.e21c5c523f89c5141a297167fdc0d536.png

 

Vg = 3, 4, 5 on 50V vs 5V analog input:

Screenshot2024-04-09at16_28_16.thumb.png.97f83b57e4104a719b1db486f47059cf.pngScreenshot2024-04-09at16_30_46.thumb.png.f9f088c0c1ffbc2de6594fff7859e056.png

 

Screenshot 2024-04-09 at 16.27.08.png

Screenshot 2024-04-09 at 16.29.38.png

Link to comment
Share on other sites

  • 0

Hi @joshna

I don't recommend using this "WF_SDK"
Looking at the code: with scope.record you perform separate captures for each channel, although it would be better to retrieve the channels data from the same capture; the scope.open has 'amplitude_range' parameter
I don't know why you are reading 30V...

See the real WaveForms SDK manual and examples:

image.png

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...