I coded a TSP script to measure the IV curve of a solar cell with my Keithley 2601B (I will attach it in case is useful for someone else). The cell may deliver a current higher than 3A, therefore I need to use the pulsed method to be in the safe range of the instrument. It works perfectly fine, even when measuring high currents (around 9A). I never measure voltages beyond 20V.
Code: Select all
function IV(irradiance)
start=-1
stop=2
numPoints=5
area=3682.5
remoteSense=true
linear=true
pulseWidth=0.001
pulsePeriod=0.2
limitI=9.8
reset()
smua.reset()
smua.source.func = smua.OUTPUT_DCVOLTS
if remoteSense == true then
smua.sense = smua.SENSE_REMOTE
else
smua.sense = smua.SENSE_LOCAL
end
smua.source.autorangev = smua.AUTORANGE_OFF
smua.source.rangev = math.max(math.abs(start), math.abs(stop))
smua.source.levelv = 0
-- Set the DC bias limit. This is not the limit used during the pulses.
smua.source.limiti = 0.1
-- Disabling Auto-Ranging and Auto-Zero ensures accurate and consistent timing
smua.measure.autozero = smua.AUTOZERO_ONCE
smua.measure.autorangei = smua.AUTORANGE_OFF
smua.measure.rangei = limitI
measDelay = 300e-6
nplc=(localnode.linefreq)*(pulseWidth-measDelay)
smua.measure.nplc = nplc
-- A timer will be used to control the measure delay so set the built-in delay to 0
smua.measure.delay = 0
-- Prepare the Reading Buffers
smua.nvbuffer1.clear()
smua.nvbuffer1.collecttimestamps= 1
smua.nvbuffer2.clear()
smua.nvbuffer2.collecttimestamps= 1
-- Configure the Trigger Model
--============================
-- Pressing the TRIG button on the front panel will trigger the sweep to start
display.trigger.clear()
-- Timer 1 controls the pulse period
trigger.timer[1].count = numPoints > 1 and numPoints - 1 or 1
trigger.timer[1].delay = pulsePeriod
trigger.timer[1].passthrough = true
trigger.timer[1].stimulus = display.trigger.EVENT_ID
-- Timer 2 controls the measure delay
trigger.timer[2].count = 1
-- Set the measure delay long enough so that measurements start after the pulse
-- has settled, but short enough that it fits within the width of the pulse.
trigger.timer[2].delay = measDelay
trigger.timer[2].passthrough = false
trigger.timer[2].stimulus = trigger.timer[1].EVENT_ID
-- Timer 3 controls the pulse width
trigger.timer[3].count = 1
trigger.timer[3].delay = pulseWidth
trigger.timer[3].passthrough = false
trigger.timer[3].stimulus = trigger.timer[1].EVENT_ID
-- Configure SMU Trigger Model for Sweep
if linear == true then
smua.trigger.source.linearv(start, stop, numPoints)
else
smua.trigger.source.logv(start, stop, numPoints,stop+0.01)
end
smua.trigger.source.limiti = limitI
smua.trigger.measure.action = smua.ENABLE
smua.trigger.measure.iv(smua.nvbuffer1, smua.nvbuffer2)
smua.trigger.endpulse.action = smua.SOURCE_IDLE
smua.trigger.endsweep.action = smua.SOURCE_IDLE
smua.trigger.count = numPoints
smua.trigger.arm.stimulus = 0
smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID
smua.trigger.measure.stimulus = trigger.timer[2].EVENT_ID
smua.trigger.endpulse.stimulus = trigger.timer[3].EVENT_ID
smua.trigger.source.action = smua.ENABLE
--==============================
-- End Trigger Model Configuration
smua.source.output = smua.OUTPUT_ON
-- Start the trigger model execution
smua.trigger.initiate()
-- Sweep will not start TRIG button is pressed
-- Wait until the sweep has completed
waitcomplete()
smua.source.output = smua.OUTPUT_OFF
power={}
-- Print the data back to the Console in tabular format
for x=1,smua.nvbuffer1.n do
power[x]=smua.nvbuffer2[x]*smua.nvbuffer1[x]
end
pmax=0
for x=1,smua.nvbuffer1.n do
if x>1 then
if smua.nvbuffer2[x]>0 and smua.nvbuffer2[x-1]<0 then
Isc=smua.nvbuffer1[x-1]-(smua.nvbuffer1[x]-smua.nvbuffer1[x- 1])*smua.nvbuffer2[x-1]/(smua.nvbuffer2[x]-smua.nvbuffer2[x-1])
end
if smua.nvbuffer1[x]>0 and smua.nvbuffer1[x-1]<0 then
Voc=smua.nvbuffer2[x-1]-smua.nvbuffer1[x-1]*(smua.nvbuffer2[x]- smua.nvbuffer2[x-1])/(smua.nvbuffer1[x]-smua.nvbuffer1[x-1])
end
end
if smua.nvbuffer2[x]>0 and smua.nvbuffer1[x]<0 and math.abs(power[x])>pmax then
pmax=math.abs(power[x])
Vmpp=smua.nvbuffer2[x]
Impp=smua.nvbuffer1[x]
end
end
Pmpp=Vmpp*Impp
FF=math.abs(100*pmax/(Voc*Isc))
eta=math.abs(Voc*Isc*FF/(area/10000*irradiance))
print("Voc(V)", "Isc(A)","FF(%)","Vmpp(V)","Impp(A)","efficiency(%)","area(cm2)","irradiance(W/m2)","pulse width(s)","pulse period(s)","NPLC(s)","meas. delay (us)","limitI (A)","Pmpp(W)","Isc1sun(A)","Jsc1sun (mA/cm2")
print(Voc,-Isc,FF,Vmpp,-Impp,eta,area,irradiance,pulseWidth,pulsePeriod,nplc,measDelay*10^6,limitI,-Pmpp,-Isc*1000/irradiance,-Isc*1000/area*1000/irradiance)
print()
print("Time(s)\tVoltage(V)\tCurrent(A)\tPower(W)")
for x=1,smua.nvbuffer1.n do
print(smua.nvbuffer1.timestamps[x], smua.nvbuffer2[x], -smua.nvbuffer1[x],-power[x])
end
end
The problem: The interface works great as long as I set a limitI below 3A (I can initialize the instrument, measure, plot, save, etc..). However, once I set a limitI above 3A, it tells me "Operation would exceed safe operating area of the instrument" and I cannot measure.
It is weird, because the exactly same code written in the TSB gives no "out of safe operating area" error (I am pulsing the IV exactly for this reason). For me it seems as if when initializing the instrument in C#, something is going wrong and it may not get that my 2601B can go beyond 3A when pulsing (always below 20V). The way I initialize in C# is:
Code: Select all
private void initializeButton_Click(object sender, System.EventArgs e)
{
try
{
// Disable the Initialize button to provide visual feedback
initializeButton.Enabled = false;
// Close the driver if it's already been initialized
if (_driver.Initialized)
{
_driver.Close();
statusBar.Text = "Instrument not initialized.";
}
statusBar.Text = "Initializing...";
_driver.Initialize(IPaddress.Text, true, true, "QueryInstrStatus=True");
statusBar.Text = "Instrument initialized.";
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message, "Ke26XX Simple Source Measure", MessageBoxButtons.OK, MessageBoxIcon.Error);
statusBar.Text = "Initialize failed.";
}
finally
{
// Re-enable the Initialize button
initializeButton.Enabled = true;
}
}
_driver.Initialize(IPaddress.Text, true, true, "QueryInstrStatus=True, Simulate=True, DriverSetup= Model=Model 2601B");
but does not work either.
Any ideas?
Thank you very much in advance!
Jorge