From 895b81cb2e84e46bbbf2ec6426b3dc095b243a2a Mon Sep 17 00:00:00 2001 From: Brad Odhner Date: Wed, 1 Nov 2023 15:39:32 -0400 Subject: [PATCH 01/13] Update Maintainers --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 926cd1c..a3bd444 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,8 @@ If you're looking for officially supported software, we invite you to visit [tek ## Maintainers -Josh Brown: [jbrown1234](https://github.com/jbrown1234) +Liz Makley: [Little-LIZard](https://github.com/Little-LIZard) Brad Odhner: [Brad-O](https://github.com/Brad-O) -Liz Makley: [Little-LIZard](https://github.com/Little-LIZard) ## License From 96b23e0130f44fadfe8ce65e40d76c13b9fd55d4 Mon Sep 17 00:00:00 2001 From: Sebastian Arns Date: Thu, 23 Nov 2023 09:50:40 +0100 Subject: [PATCH 02/13] Fix erros in DM6500 Sockets Driver I couldn't run the sample script "DMM6500_01_DCV.py" due to some errors in "DMM6500_Sockets_Driver.py". --- .../DMM6500_01_DCV.py | 2 +- .../DMM6500_Sockets_Driver.py | 101 +++++++++--------- 2 files changed, 52 insertions(+), 51 deletions(-) diff --git a/Drivers/DMM6500_DAQ6510/DMM6500_Python_Sockets_Driver/DMM6500_01_DCV.py b/Drivers/DMM6500_DAQ6510/DMM6500_Python_Sockets_Driver/DMM6500_01_DCV.py index fd078c5..d395167 100644 --- a/Drivers/DMM6500_DAQ6510/DMM6500_Python_Sockets_Driver/DMM6500_01_DCV.py +++ b/Drivers/DMM6500_DAQ6510/DMM6500_Python_Sockets_Driver/DMM6500_01_DCV.py @@ -29,7 +29,7 @@ DMM6500.SetMeasure_NPLC(1.0) DMM6500.SetMeasure_InputImpedance(DMM6500.InputZ.Z_10M) DMM6500.SetMeasure_AutoZero(DMM6500.DmmState.ON) -DMM6500.SetMeasure_FilterType(DMM6500.FilterType.REP) +DMM6500.SetMeasure_FilterType(DMM6500.FilterType.REPEAT) DMM6500.SetMeasure_FilterCount(100) DMM6500.SetMeasure_FilterState(DMM6500.DmmState.ON) print(DMM6500.Measure(1)) diff --git a/Drivers/DMM6500_DAQ6510/DMM6500_Python_Sockets_Driver/DMM6500_Sockets_Driver.py b/Drivers/DMM6500_DAQ6510/DMM6500_Python_Sockets_Driver/DMM6500_Sockets_Driver.py index 1ed4d6b..99f8a47 100644 --- a/Drivers/DMM6500_DAQ6510/DMM6500_Python_Sockets_Driver/DMM6500_Sockets_Driver.py +++ b/Drivers/DMM6500_DAQ6510/DMM6500_Python_Sockets_Driver/DMM6500_Sockets_Driver.py @@ -28,7 +28,44 @@ def __init__(self): "dmm.FUNC_DCV_RATIO", "dmm.FUNC_DIGITIZE_VOLTAGE", "dmm.FUNC_DIGITIZE_CURRENT"] - + + self.trigger_edge_type = ["EDGE_FALLING", + "EDGE_RISING", + "EDGE_EITHER"] + + self.trigger_line_type = ["trigger.dig", + "trigger.ext", + "trigger.tsplink"] + + self.trigger_logic = ["LOGIC_NEGATIVE", + "LOGIC_POSITIVE"] + + self.stimulus = ["EVENT_NONE", + "EVENT_DISPLAY", + "EVENT_NOTIFYn", + "EVENT_COMMAND", + "EVENT_DIGIOn", + "EVENT_TSPLINKn", + "EVENT_LANn", + "EVENT_ANALOGTRIGGER", + "EVENT_TIMERn", + "EVENT_EXTERNAL", + "EVENT_SCAN_ALARM_LIMIT", + "EVENT_SCAN_CHANNEL_READY", + "EVENT_SCAN_COMPLETE", + "EVENT_SCAN_MEASURE_COMPLETE", + "EVENT_SCAN_ALARM_LIMIT"] + + self.dig_io_mode = ["digio.MODE_DIGITAL_IN", + "digio.MODE_DIGITAL_OUT", + "digio.MODE_DIGITAL_OPEN_DRAIN", + "digio.MODE_TRIGGER_IN", + "digio.MODE_TRIGGER_OUT", + "digio.MODE_TRIGGER_OPEN_DRAIN", + "digio.MODE_SYNCHRONOUS_MASTER", + "digio.MODE_SYNCHRONOUS_ACCEPTOR"] + + # ====================================================================== # DEFINE INSTRUMENT CONNECTION AND COMMUNICATIONS FUNCTIONS HERE # ====================================================================== @@ -203,9 +240,9 @@ def configure_filter(self, filter_enable=None, filter_type=None, filter_count=No # Set the filter type if filter_type is not None: - if filter_type == FilterType.REPEAT: + if filter_type == self.FilterType.REPEAT: self.SendCmd("dmm.measure.filter.type = dmm.FILTER_REPEAT_AVG") - elif filter_type == FilterType.MOVE: + elif filter_type == self.FilterType.MOVE: self.SendCmd("dmm.measure.filter.type = dmm.FILTER_MOVING_AVG") # Set the filter count @@ -223,9 +260,9 @@ def configure_filter(self, filter_enable=None, filter_type=None, filter_count=No # Set the filter type if filter_type is not None: - if filter_type == FilterType.REPEAT: + if filter_type == self.FilterType.REPEAT: self.SendCmd("channel.setdmm(\"{0}\", dmm.ATTR_MEAS_FILTER_TYPE, dmm.FILTER_REPEAT_AVG)") - elif filter_type == FilterType.MOVE: + elif filter_type == self.FilterType.MOVE: self.SendCmd("channel.setdmm(\"{0}\", dmm.ATTR_MEAS_FILTER_TYPE, dmm.FILTER_MOVING_AVG)") # Set the filter count @@ -311,7 +348,7 @@ def configure_digital_io_line_mode(self, dio_line=None, mode=None): def configure_trigger_input(self, input_type=None, line=None, edge_type=None, do_clear=None, do_wait=None): if input_type is None: - input_type = TriggerLineType.TSPLINK + input_type = self.TriggerLineType.TSPLINK if line is None: line = 1 @@ -355,22 +392,14 @@ class TriggerPolarity(Enum): RISING = 1 EITHER = 2 - self.trigger_edge_type = ["EDGE_FALLING", - "EDGE_RISING", - "EDGE_EITHER"] - class TriggerLineType(Enum): DIGITAL = 0 EXTERNAL = 1 TSPLINK = 2 - - self.trigger_line_type = ["trigger.dig", - "trigger.ext", - "trigger.tsplink"] def configure_trigger_output(self, output_type=None, logic_type=None, line=None, stimulus_type=None, pulse_width=None, do_assert=None, do_release=None): if output_type is None: - output_type = TriggerLineType.TSPLINK + output_type = self.TriggerLineType.TSPLINK if line is None: line = 1 @@ -379,7 +408,7 @@ def configure_trigger_output(self, output_type=None, logic_type=None, line=None, if line is not None: send_buffer = "{0}out[{1}].logic = trigger.{2}".format(self.trigger_line_type[logic_type.value], line, self.trigger_logic[logic_type.value]) else: - send_buffer = "{0}out.logic = trigger.{2}".format(self.trigger_line_type[input_type.value], line, self.trigger_logic[logic_type.value]) + send_buffer = "{0}out.logic = trigger.{2}".format(self.trigger_line_type[logic_type.value], line, self.trigger_logic[logic_type.value]) self.SendCmd(send_buffer) # trigger.tsplinkout[N].logic = trigger.LOGIC_POSITIVE trigger.extout.logic trigger.tsplinkout[N].logic if do_release is not None: @@ -415,9 +444,6 @@ class TriggerLogic(Enum): NEGATIVE = 0 POSITIVE = 1 - self.trigger_logic = ["LOGIC_NEGATIVE", - "LOGIC_POSITIVE"] - class Stimulus(Enum): NONE = 0 DISPLAY = 1 @@ -435,22 +461,6 @@ class Stimulus(Enum): SCAN_MEASURE_COMPLETE = 13 SCAN_ALARM_LIMIT_2 = 14 - self.stimulus = ["EVENT_NONE", - "EVENT_DISPLAY", - "EVENT_NOTIFYn", - "EVENT_COMMAND", - "EVENT_DIGIOn", - "EVENT_TSPLINKn", - "EVENT_LANn", - "EVENT_ANALOGTRIGGER", - "EVENT_TIMERn", - "EVENT_EXTERNAL", - "EVENT_SCAN_ALARM_LIMIT", - "EVENT_SCAN_CHANNEL_READY", - "EVENT_SCAN_COMPLETE", - "EVENT_SCAN_MEASURE_COMPLETE", - "EVENT_SCAN_ALARM_LIMIT"] - def trigger_reset(self): trigger.ext.reset() return @@ -502,16 +512,16 @@ def SeMeasure_LineSync(self, *args): def SetMeasure_InputImpedance(self, *args): if type(args[0]) != str: - if myZ == self.InputZ.Z_AUTO: + if args[0] == self.InputZ.Z_AUTO: funcStr = "dmm.IMPEDANCE_AUTO" - elif myZ == self.InputZ.Z_10M: + elif args[0] == self.InputZ.Z_10M: funcStr = "dmm.IMPEDANCE_10M" self.SendCmd("dmm.measure.inputimpedance = {}".format(funcStr)) else: setStr = "channel.setdmm(\"{}\", ".format(args[0]) - if myZ == self.InputZ.Z_AUTO: + if args[0] == self.InputZ.Z_AUTO: funcStr = "dmm.IMPEDANCE_AUTO" - elif myZ == self.InputZ.Z_10M: + elif args[0] == self.InputZ.Z_10M: funcStr = "dmm.IMPEDANCE_10M" self.SendCmd("{}dmm.ATTR_MEAS_INPUT_IMPEDANCE, {})".format(setStr, funcStr)) return @@ -567,9 +577,9 @@ def SetMeasure_Filter(self, *args): # THIS NEEDS FIXED - DOES NOT ACCOUNT FOR return def SetMeasure_FilterType(self, my_filter): - if my_filter == self.FilterType.REP: + if my_filter == self.FilterType.REPEAT: func_str = "dmm.FILTER_REPEAT_AVG" - elif myFilter == self.FilterType.MOV: + elif my_filter == self.FilterType.MOVE: func_str = "dmm.FILTER_MOVING_AVG" send_buffer = "dmm.measure.filter.type = {}".format(func_str) self.SendCmd(send_buffer) @@ -897,15 +907,6 @@ class DIOMode(Enum): TRIG_OPEN_DRAIN = 5 TRIG_SYNC_MASTER = 6 TRIG_SYNC_ACCEPTOR = 7 - - self.dig_io_mode = ["digio.MODE_DIGITAL_IN", - "digio.MODE_DIGITAL_OUT", - "digio.MODE_DIGITAL_OPEN_DRAIN", - "digio.MODE_TRIGGER_IN", - "digio.MODE_TRIGGER_OUT", - "digio.MODE_TRIGGER_OPEN_DRAIN", - "digio.MODE_SYNCHRONOUS_MASTER", - "digio.MODE_SYNCHRONOUS_ACCEPTOR"] def SetScan_BasicAttributes(self, *args): self.SendCmd("scan.create(\"{}\")".format(args[0])) From ccc753bf283d8b3353e33e7a1894d2a82e759f25 Mon Sep 17 00:00:00 2001 From: Brad Odhner Date: Wed, 29 Nov 2023 10:46:48 -0500 Subject: [PATCH 03/13] Update CODEOWNERS --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fafa665..c64ff34 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @Brad-O @jbrown1234 @Little-LIZard +* @Brad-O @Little-LIZard From 3bab3028fea845a93fb35d4ddc50dc1e7ea50dc3 Mon Sep 17 00:00:00 2001 From: Brad Odhner Date: Wed, 29 Nov 2023 10:47:30 -0500 Subject: [PATCH 04/13] Removed forced English version from links --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a3bd444..da2459a 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Code in any language for any instrument that fulfills a specific application. Th Code that makes up a partial or complete instrument driver library for any programming language. * **[KickStart Template Projects](./KickStart_Template_Projects/)** -Application specific template projects that can be loaded into [Keithley KickStart software](https://www.tek.com/en/products/keithley/keithley-control-software-bench-instruments/kickstart). +Application specific template projects that can be loaded into [Keithley KickStart software](https://www.tek.com/products/keithley/keithley-control-software-bench-instruments/kickstart). * **[TTI Apps](./TTI_Apps)** TSP® Apps for Touch, Test, Invent® instruments (i.e. Keithley's line of touch screen enabled instruments). These are not traditional TSP scripts. @@ -34,10 +34,10 @@ If you don't want to clone this entire repository, it's still possible to take i ## Relevant Keithley Software If you're looking for officially supported software, we invite you to visit [tek.com/software](https://www.tek.com/software). Here are some especially relevant software options for controlling Keithley equipment: -- [Test Script Builder](https://www.tek.com/en/keithley-test-script-builder), dedicated IDE for TSP code development. -- [KickStart](https://www.tek.com/en/products/keithley/keithley-control-software-bench-instruments/kickstart), Instrument control and automation software for both Keithley and Tektronix products. -- [Keithley Automated Characterization Suite (ACS)](https://www.tek.com/en/products/keithley/semiconductor-test-systems/automated-characterization-suite), Advanced characterization and automation software. -- [More Information on TSP](https://www.tek.com/en/solutions/application/test-automation/tsp-for-test-automation), our instrument control command set and programming language +- [Test Script Builder](https://www.tek.com/keithley-test-script-builder), dedicated IDE for TSP code development. +- [KickStart](https://www.tek.com/products/keithley/keithley-control-software-bench-instruments/kickstart), Instrument control and automation software for both Keithley and Tektronix products. +- [Keithley Automated Characterization Suite (ACS)](https://www.tek.com/products/keithley/semiconductor-test-systems/automated-characterization-suite), Advanced characterization and automation software. +- [More Information on TSP](https://www.tek.com/solutions/application/test-automation/tsp-for-test-automation), our instrument control command set and programming language ## Maintainers From 68ec33881fae9ddaf1cb16095683179ad75bc095 Mon Sep 17 00:00:00 2001 From: Brad Odhner Date: Wed, 29 Nov 2023 10:48:49 -0500 Subject: [PATCH 05/13] Update Repo Code of Conduct The Code of Conduct is hosted by Tektronix outside of this repo at https://github.com/tektronix/.github/blob/main/CODE_OF_CONDUCT.md --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- CODE_OF_CONDUCT.md | 3 --- CONTRIBUTING.md | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) delete mode 100644 CODE_OF_CONDUCT.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7c1c045..3dd69c1 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,7 @@ ### Pull Request Checklist -Please review the [Contribution Guidelines](https://github.com/tektronix/keithley/CONTRIBUTING.md) before submitting. +Please review the [Contribution Guidelines](/CONTRIBUTING.md) before submitting. - [ ] You have previously submitted a Contributor License Agreement or have contacted a maintainer to request one. - [ ] You have given the Pull Request a descriptive name above. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 19f4f49..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,3 +0,0 @@ -# Code of Conduct - -This repo adheres to the Tektronix [Code of Conduct](https://tektronix.github.io/Code-Of-Conduct/). Please follow the link to read the Code of Conduct. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9945d68..bb71c02 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ Thank you for your interest in contributing to the official example code repo for Keithley Instruments products! This code in this repo is not officially supported and is maintained on a volunteer basis by Keithley engineers. We welcome any input or additions from the community that meet these contributing guidelines. -First, please consult the Tektronix [Code of Conduct](https://tektronix.github.io/Code-Of-Conduct/) that governs the usage of this repo. +First, please consult the Tektronix [Code of Conduct](https://github.com/tektronix/.github/blob/main/CODE_OF_CONDUCT.md) that governs the usage of this repo. - [Contributing to the Keithley Instruments Example Repo](#contributing-to-the-keithley-instruments-example-repo) - [Contributor License Agreement](#contributor-license-agreement) From 312e7b629e6bdcd2c41ff7304afcc2e893236a8b Mon Sep 17 00:00:00 2001 From: Brad Odhner Date: Wed, 3 Jan 2024 08:23:46 -0500 Subject: [PATCH 06/13] Moved 2450 SMU Example --- Instrument_Examples/Series_2400_Graphical/2450-SMU/README.md | 3 +++ .../source_i_measure_v_compute_r.py | 0 Instrument_Examples/Series_2400_Graphical/README.md | 5 +---- 3 files changed, 4 insertions(+), 4 deletions(-) rename Instrument_Examples/Series_2400_Graphical/{Basic_Measurements => 2450-SMU/Simple_SourceI_MeasureV_CalcR}/source_i_measure_v_compute_r.py (100%) diff --git a/Instrument_Examples/Series_2400_Graphical/2450-SMU/README.md b/Instrument_Examples/Series_2400_Graphical/2450-SMU/README.md index e73ec5c..af3db51 100644 --- a/Instrument_Examples/Series_2400_Graphical/2450-SMU/README.md +++ b/Instrument_Examples/Series_2400_Graphical/2450-SMU/README.md @@ -28,3 +28,6 @@ See the description within the lower directory. * **[Other Devices](./Other_Devices)** A variety of TSP scripts that target specific applications. +* **[Simple Source I, Measure V, Calculate R](./Simple_SourceI_MeasureV_CalcR/)** +Python script to simply source I, measure V, and calculate resistance. Includes a simple tolerance band alert. + diff --git a/Instrument_Examples/Series_2400_Graphical/Basic_Measurements/source_i_measure_v_compute_r.py b/Instrument_Examples/Series_2400_Graphical/2450-SMU/Simple_SourceI_MeasureV_CalcR/source_i_measure_v_compute_r.py similarity index 100% rename from Instrument_Examples/Series_2400_Graphical/Basic_Measurements/source_i_measure_v_compute_r.py rename to Instrument_Examples/Series_2400_Graphical/2450-SMU/Simple_SourceI_MeasureV_CalcR/source_i_measure_v_compute_r.py diff --git a/Instrument_Examples/Series_2400_Graphical/README.md b/Instrument_Examples/Series_2400_Graphical/README.md index 4f564c3..df29f27 100644 --- a/Instrument_Examples/Series_2400_Graphical/README.md +++ b/Instrument_Examples/Series_2400_Graphical/README.md @@ -19,10 +19,7 @@ These examples include those found in the User Manual, Application Notes, and ot * **[2470-SMU](./2470-SMU/)** 1100V / 1A SMU with high-V triaxial outputs -* **[Basic Measurements(./Basic_Measurements/)** -A series of general purpose example providing insight on how to configure and execute the most common measurement operations. - -### Instrument agnostic: +### Instrument agnostic (In this directory): * **[Battery Cycler](./smu_battery_cycle_solution.py)** This Python script can be used to perform battery cycling (charge/discharge) testing. See comments in the file for details. From 16ad9b8fdca02515241b23ca92b809ef33f79315 Mon Sep 17 00:00:00 2001 From: Brad Odhner Date: Wed, 3 Jan 2024 08:29:33 -0500 Subject: [PATCH 07/13] Added Controlled Ramp Example Also cleaned up a few typos in the README --- .../Series_2400_Graphical/Controlled_Ramp.tsp | 161 ++++++++++++++++++ .../Series_2400_Graphical/README.md | 9 +- 2 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 Instrument_Examples/Series_2400_Graphical/Controlled_Ramp.tsp diff --git a/Instrument_Examples/Series_2400_Graphical/Controlled_Ramp.tsp b/Instrument_Examples/Series_2400_Graphical/Controlled_Ramp.tsp new file mode 100644 index 0000000..4047854 --- /dev/null +++ b/Instrument_Examples/Series_2400_Graphical/Controlled_Ramp.tsp @@ -0,0 +1,161 @@ +--[[ +Purpose: Use a Timer to control the rate of the Source + Measure. + Want a controlled V/sec ramp rate + Implements custom version of smu.source.sweeplinear() + As written: dual sweeps 100 pts from 0-1V with 100ms at each point + +Tested with: KEITHLEY INSTRUMENTS,MODEL 2450,04615364,1.7.12b +Written By: Keithley Applications Engineering (Andrea Clary) + +Keep in mind the source resolution of the voltage range. + 2V range has 50uV resolution + 20V range has 500uV resolution + +For best timing control: +-Use fixed source and measure ranges +-Turn off auto zero; perhaps update auto zero just before executing the +sweep +-Adjust the value of smu.measure.nplc to allow measurements to complete +within the allowed time at each source level +-Source read back is on by default = extra measurment. Turn off. + +-Set fixed current measure range based on current limit. +-Current range may need to be higher for faster scan rates; +-Lower ranges will impose more settling time. + +-Build a source config list containing the list of source levels for the +ramp. +-Build trigger blocks that will recall a value from source config list and +measure +-Use a timer object to control the rate at which the trigger blocks execute. +]] + +-- ************************* Functions Here ********************** + +function config_smu(src_range, current_limit, nplc) + smu.measure.func = smu.FUNC_DC_CURRENT + smu.source.func = smu.FUNC_DC_VOLTAGE + smu.source.range = src_range + smu.source.ilimit.level = current_limit + smu.source.readback = smu.OFF + smu.measure.range = current_limit + smu.measure.autorange = smu.OFF -- for best timing control, use fixed range + -- other measure related settings + smu.measure.nplc = nplc + smu.measure.sense = smu.SENSE_2WIRE + smu.measure.autozero.enable = smu.OFF +end -- function + +function build_config_list(start, stop, num_pts) + -- when building list, turn the output off else the source values will occur at the output + if smu.source.output == smu.ON then + smu.source.output = smu.OFF + end -- if + smu.source.configlist.create("MY_SOURCE_LIST") + smu.source.range = math.max(start, stop) + step_size = (stop - start)/(num_pts - 1) + -- ramp up + for i = 1, num_pts do + smu.source.level = start + step_size * (i-1) + smu.source.configlist.store("MY_SOURCE_LIST") + end -- loop + -- ramp down + for i = 1, num_pts do + smu.source.level = stop - step_size * (i-1) -- increase the value + smu.source.configlist.store("MY_SOURCE_LIST") + end -- loop +end -- function + +function build_trigger_blocks(num_src_values) + trigger.model.load("Empty") + + blockNumber = 1 + trigger.model.setblock(blockNumber, trigger.BLOCK_BUFFER_CLEAR, defbuffer1) + + blockNumber = blockNumber + 1 + trigger.model.setblock(blockNumber, trigger.BLOCK_CONFIG_RECALL, "MY_SOURCE_LIST", 1) + + blockNumber = blockNumber + 1 + trigger.model.setblock(blockNumber, trigger.BLOCK_SOURCE_OUTPUT, smu.ON) + + blockNumber = blockNumber + 1 + trigger.model.setblock(blockNumber, trigger.BLOCK_NOTIFY, trigger.EVENT_NOTIFY1) -- starts timer1 + + blockNumber = blockNumber + 1 + branchToHere = blockNumber -- ***** loop back to this block number ******* + trigger.model.setblock(blockNumber, trigger.BLOCK_WAIT, trigger.EVENT_TIMER1) + + blockNumber = blockNumber + 1 + -- acquire one sample into buffer + trigger.model.setblock(blockNumber, trigger.BLOCK_MEASURE_DIGITIZE, defbuffer1, 1) + + blockNumber = blockNumber + 1 + trigger.model.setblock(blockNumber, trigger.BLOCK_CONFIG_NEXT, "MY_SOURCE_LIST") + + blockNumber = blockNumber + 1 + -- branch back if still have values in source list + trigger.model.setblock(blockNumber, trigger.BLOCK_BRANCH_COUNTER, num_src_values, branchToHere) + + blockNumber = blockNumber + 1 + -- all done, turn blue light off + trigger.model.setblock(blockNumber, trigger.BLOCK_SOURCE_OUTPUT, smu.OFF) +end -- function + +function config_timer(timer_tic_interval, how_many_tics) + -- Timer to control sweep rate + local N = 1 + trigger.timer[N].enable = 0 + trigger.timer[N].reset() + trigger.timer[N].clear() + trigger.timer[N].enable = 1 + trigger.timer[N].delay = timer_tic_interval + trigger.timer[N].count = how_many_tics + trigger.timer[N].start.stimulus = trigger.EVENT_NOTIFY1 + trigger.timer[N].start.generate = trigger.OFF -- tic only when timer elapses +end -- function + +-- ************************* Main Program Here ********************** + reset() + eventlog.clear() + + start_voltage = 0 + stop_voltage = 1 + number_of_pts = 100 + SampleInterval = 0.1 -- need to compute this for desired V/sec ramp rate and number of points + AD_overhead = 10e-3 + AD_integration = (SampleInterval - AD_overhead) * localnode.linefreq + if AD_integration > 1 then AD_integration = 1 end + if AD_integration < 0.01 then + AD_integration = 0.01 + print("Logic error.... interval too fast") + end + + --print("integration: "..AD_integration) + + --config_smu(src_range, current_limit, nplc) + config_smu(2, 10e-3, AD_integration) + + --build_config_list(start, stop, num_pts) + build_config_list(start_voltage, stop_voltage, number_of_pts) + number_src_values = smu.source.configlist.size("MY_SOURCE_LIST") + + --config_timer(timer_tic_interval, how_many_tics) + config_timer(SampleInterval, number_src_values) + + --build_trigger_blocks(num_src_values) + build_trigger_blocks(number_src_values) + + -- update auto zero + smu.measure.autozero.once() + + -- run the trigger model + trigger.model.initiate() + waitcomplete() + print(string.format("Ramp Duration from Time Stamps: %0.2f seconds", defbuffer1.relativetimestamps[defbuffer1.n] - defbuffer1.relativetimestamps[1] )) + + print_data = false + if print_data then + for i = 1, defbuffer1.n do + print(defbuffer1.relativetimestamps[i]..","..defbuffer1.sourcevalues[i]..","..defbuffer1.readings[i]) + end -- for loop + end -- if \ No newline at end of file diff --git a/Instrument_Examples/Series_2400_Graphical/README.md b/Instrument_Examples/Series_2400_Graphical/README.md index df29f27..1c013dc 100644 --- a/Instrument_Examples/Series_2400_Graphical/README.md +++ b/Instrument_Examples/Series_2400_Graphical/README.md @@ -1,6 +1,6 @@ # Series 2400 Graphical Source Measure Units -These examples include those found in the User Manual, Application Notes, and other sources. The files in this directory will work with all [2400 Graphical SMUs](https://www.tek.com/en/products/keithley/source-measure-units/2400-graphical-series-sourcemeter), the sub directories will generally only work for those units, but might be able to be adapted. +These examples include those found in the User Manual, Application Notes, and other sources. The files in this directory will work with all [2400 Graphical SMUs](https://www.tek.com/products/keithley/source-measure-units/2400-graphical-series-sourcemeter), the sub directories will generally only work for those units, but might be able to be adapted. ## Directory @@ -8,10 +8,10 @@ These examples include those found in the User Manual, Application Notes, and ot [comment]: **[2450-SMU](./directory)** * **[2450-SMU](./2450-SMU/)** -200V / 1A SMU with Triaxial outputs +200V / 1A SMU with triaxial outputs * **[2460-SMU](./2460-SMU/)** -100V / 1A SMU +100V / 7A SMU * **[2461-SMU](./2461-SMU/)** 100V / 10A (pulsed) SMU @@ -21,6 +21,9 @@ These examples include those found in the User Manual, Application Notes, and ot ### Instrument agnostic (In this directory): +* **[Controlled Ramp](./Controlled_Ramp.tsp)** +A TSP script to sweep at a defined speed using a timer and the trigger model. + * **[Battery Cycler](./smu_battery_cycle_solution.py)** This Python script can be used to perform battery cycling (charge/discharge) testing. See comments in the file for details. From 18a0f4ddb64f45a4d594289780ed0e5cafed9311 Mon Sep 17 00:00:00 2001 From: Brad Odhner Date: Wed, 3 Jan 2024 08:51:47 -0500 Subject: [PATCH 08/13] Added 2600B TSP-Link Triggering Example --- .../Triggering/TSPLink_Trigger.tsp | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Instrument_Examples/Series_2600/Triggering/TSPLink_Trigger.tsp diff --git a/Instrument_Examples/Series_2600/Triggering/TSPLink_Trigger.tsp b/Instrument_Examples/Series_2600/Triggering/TSPLink_Trigger.tsp new file mode 100644 index 0000000..6ffdabb --- /dev/null +++ b/Instrument_Examples/Series_2600/Triggering/TSPLink_Trigger.tsp @@ -0,0 +1,79 @@ +--[[ + + Purpose: show how an event on one 2600B can be "echoed" to second 2600B + + Setup: need two 2600B that have TSP-LINK connectors. + Assign node 1 and 2 and use the cross-over cable to link them. + On front panel, use the reset under TSPLINK to verify the link. + + Method: Use a timer as stimulus for digital IO active lo pulse. + Cause 10 pulses from DIO line 1 on rear DB25 connector. + Echo the timer event_id on tsp-link for digital IO on second node to also pulse. + + Written By: Keithley Applications Engineering (Andrea Clary) + From the TekTalk Forum: https://my.tek.com/en/tektalk/source-measure-units/9b622205-b095-ee11-a81c-000d3a37f78a + + +]]-- + +function ConfigTSPLinkTriggers(nodenum) + node[nodenum].tsplink.trigger[1].clear() + node[nodenum].tsplink.trigger[1].mode = tsplink.TRIG_FALLING + node[nodenum].tsplink.trigger[2].clear() + node[nodenum].tsplink.trigger[2].mode = tsplink.TRIG_FALLING + node[nodenum].tsplink.trigger[3].clear() + node[nodenum].tsplink.trigger[3].mode = tsplink.TRIG_FALLING +end + +-- ********************* +-- +-- Main Program +-- +-- ********************* + +reset() +errorqueue.clear() + +if tsplink.state == "offline" then + nodesFound = tsplink.reset() + if nodesFound ~= 2 then + print(string.format("Error: Found %d Nodes. Expecting 2.", nodesFound)) + exit() + end +end + +-- for each tsplink node +ConfigTSPLinkTriggers(1) +ConfigTSPLinkTriggers(2) + +-- configure a timer on node 1 to issue 10 events +-- upon receipt of one tsplink event_id +nodenum = 1 +node[nodenum].trigger.timer[1].delay = 1e-3 +node[nodenum].trigger.timer[1].stimulus = node[nodenum].tsplink.trigger[1].EVENT_ID +node[nodenum].trigger.timer[1].count = 10 +node[nodenum].trigger.timer[1].passthrough = false +node[nodenum].trigger.timer[1].clear() + +-- echo the timer event on tsplink two +node[nodenum].tsplink.trigger[2].stimulus = node[nodenum].trigger.timer[1].EVENT_ID + +-- digital IO *could* use timer event directly on this same node +-- but it can also make use of the locally available tsplink two +node[nodenum].digio.trigger[1].clear() +node[nodenum].digio.trigger[1].mode = digio.TRIG_FALLING +node[nodenum].digio.trigger[1].pulsewidth = 100e-6 +--node[nodenum].digio.trigger[1].stimulus = node[nodenum].trigger.timer[1].EVENT_ID +node[nodenum].digio.trigger[1].stimulus = node[nodenum].tsplink.trigger[2].EVENT_ID + +-- this remote node cannot "see" the timer events on other node +-- this remote node must use the echoed event on tsplink two +nodenum = 2 +node[nodenum].digio.trigger[1].clear() +node[nodenum].digio.trigger[1].mode = digio.TRIG_FALLING +node[nodenum].digio.trigger[1].pulsewidth = 100e-6 +node[nodenum].digio.trigger[1].stimulus = node[nodenum].tsplink.trigger[2].EVENT_ID + + +-- issue an assert to get the party started +node[1].tsplink.trigger[1].assert() \ No newline at end of file From 478fb7bfdbe459fe029a4341d90a729be7738796 Mon Sep 17 00:00:00 2001 From: Odhner Date: Fri, 2 Feb 2024 17:08:07 -0500 Subject: [PATCH 09/13] Added 2651A and 2657A examples Including several examples for paralleling SMUs for up to 100A of current, and a script for checking for status model overrruns. --- .../265xA/2651A_-_IdVd_Sweep_Step_Vg.tsp | 553 +++++++ .../2651A_-_Power_FET_IGBT_IV_Curves.tsp | 231 +++ .../265xA/2651A_-_Pulse_Width_Modulation.tsp | 585 +++++++ .../Series_2600/265xA/2651A_100A_Pulse.tsp | 466 ++++++ .../Series_2600/265xA/2651A_100A_RdsOn.tsp | 256 +++ .../Series_2600/265xA/2651A_Demo_Script.tsp | 605 +++++++ .../2651A_IdVgSweep_ParallelDrainSMUstsp | 1403 +++++++++++++++++ .../265xA/2651A_Parallel_IV_Simple.tsp | 491 ++++++ .../265xA/2657A_current_sine_wave.tsp | 156 ++ .../265xA/265xA_Fast_ADC_Pulse_Sweep.tsp | 142 ++ .../Series_2600/26xxA_Check_for_Overrun.tsp | 74 + 11 files changed, 4962 insertions(+) create mode 100644 Instrument_Examples/Series_2600/265xA/2651A_-_IdVd_Sweep_Step_Vg.tsp create mode 100644 Instrument_Examples/Series_2600/265xA/2651A_-_Power_FET_IGBT_IV_Curves.tsp create mode 100644 Instrument_Examples/Series_2600/265xA/2651A_-_Pulse_Width_Modulation.tsp create mode 100644 Instrument_Examples/Series_2600/265xA/2651A_100A_Pulse.tsp create mode 100644 Instrument_Examples/Series_2600/265xA/2651A_100A_RdsOn.tsp create mode 100644 Instrument_Examples/Series_2600/265xA/2651A_Demo_Script.tsp create mode 100644 Instrument_Examples/Series_2600/265xA/2651A_IdVgSweep_ParallelDrainSMUstsp create mode 100644 Instrument_Examples/Series_2600/265xA/2651A_Parallel_IV_Simple.tsp create mode 100644 Instrument_Examples/Series_2600/265xA/2657A_current_sine_wave.tsp create mode 100644 Instrument_Examples/Series_2600/265xA/265xA_Fast_ADC_Pulse_Sweep.tsp create mode 100644 Instrument_Examples/Series_2600/26xxA_Check_for_Overrun.tsp diff --git a/Instrument_Examples/Series_2600/265xA/2651A_-_IdVd_Sweep_Step_Vg.tsp b/Instrument_Examples/Series_2600/265xA/2651A_-_IdVd_Sweep_Step_Vg.tsp new file mode 100644 index 0000000..7838011 --- /dev/null +++ b/Instrument_Examples/Series_2600/265xA/2651A_-_IdVd_Sweep_Step_Vg.tsp @@ -0,0 +1,553 @@ +--[[ + Title: IV Curves Example Script + Date: 12/6/2010 + Description: This script will perform a IdVd Sweep Step Vg test to + collect a series of characteristic IV Curves on a MOSFET or IGBT device + and will return the data in an Excel compatible format for graphing and + analysis. + + TSP-Link Configuration: + Node 1: 2651A + Node 2: 26xxA + Node 3: 2651A + + Master Node: Node 1 + + Revision History: + 12/6/2010 - Version 1.0 + David Wyban + Initial Revision +]] + +--[[ + IV_Curves(gstart, gstop, gsteps, dstart, dstop, dsteps, pulseWidth, pulsePeriod, pulseLimit) + + Description: This function will perform a series of pulsed sweeps + on a MOSFET or IGBT device to generate a series of IV Curves that + characterize the device. Note: To avoid device oscillations, a + series resistor on the gate terminal of the device may be required. + + Parameters: + gstart: The starting voltage of the gate sweep + gstop: The ending voltage of the gate sweep + gsteps: The number of steps in the gate sweep + dstart: The starting voltage of the drain sweep + dstop: The ending voltage of the drain sweep + dsteps: The number of steps in the drain sweep + pulseWidth: The width of the drain pulse in seconds + pulsePeriod:The time in seconds between the start of consecutive drain pulses in the sweep + pulseLimit: The current limit in Amps of the drain pulse + + Example Usage: + IV_Curves(5, 9, 5, 0, 10, 21, 300e-6, 30e-3, 50) + IRFP240 + IV_Curves(4, 10, 13, 0, 10, 101, 300e-6, 30e-3, 50, 0.001) + IV_Curves(4, 10, 13, 0, 20, 101, 300e-6, 30e-3, 50, 0.001) + IGBT + IV_Curves(6, 12, 7, 0, 10, 101, 300e-6, 30e-3, 50, 0.005) + IV_Curves(6, 12, 7, 0, 10, 101, 1e-3, 100e-3, 50, 0.01) +--]] +function IV_Curves(gstart, gstop, gsteps, dstart, dstop, dsteps, pulseWidth, pulsePeriod, pulseLimit, nplc) + if (nplc == nil) then nplc = 0.001 end + reset() + tsplink.reset() + + -- Configure the Drain SMU(2651A) + --------------------------------- + smua.reset() + smua.source.func = smua.OUTPUT_DCVOLTS + smua.sense = smua.SENSE_REMOTE + + smua.source.rangev = math.max(math.abs(dstart), math.abs(dstop)) + -- Select the source range that is large enough to fit all values of the sweep + smua.source.levelv = 0 -- Sets the drain bias level + smua.source.limiti = 5 + + smua.measure.rangev = smua.source.rangev + smua.measure.rangei = (pulseLimit == "off") and 50 or pulseLimit + -- Select a measure range large enough to fit pulses up to the current limit + + smua.measure.nplc = nplc + smua.measure.delay = (pulseWidth - ((1/localnode.linefreq) * smua.measure.nplc)) - 60e-6 + -- Set the measure delay so that the measurement is + -- taken at the end of the pulse before the falling edge + smua.measure.autozero = smua.AUTOZERO_ONCE + + -- Timer 1 controls the pulse period + trigger.timer[1].count = (dsteps <= 1) and 1 or (dsteps - 1) + -- If dsteps <= 1 then use 1 for the count else use dsteps - 1 + trigger.timer[1].delay = pulsePeriod + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = tsplink.trigger[1].EVENT_ID + trigger.timer[1].clear() + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = pulseWidth -- 2e-6 + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = smua.trigger.SOURCE_COMPLETE_EVENT_ID--trigger.timer[1].EVENT_ID + trigger.timer[2].clear() + + -- Configure Drain SMU(2651A) trigger model + smua.trigger.source.linearv(dstart, dstop, dsteps) + smua.trigger.source.limiti = pulseLimit + 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.arm.count = gsteps + smua.trigger.count = dsteps + smua.trigger.arm.stimulus = 0 + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = 0 + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + -- Configure TSP-Link Triggers + tsplink.trigger[1].clear() + tsplink.trigger[1].mode = tsplink.TRIG_SYNCHRONOUSM + tsplink.trigger[1].stimulus = smua.trigger.ARMED_EVENT_ID + -- TSP-Link Trigger 1 is used by the 2651A to tell the 26xxA to step + -- the gate and for the 26xxA to tell the 2651A when it has completed + -- the step. + + tsplink.trigger[2].clear() + tsplink.trigger[2].mode = tsplink.TRIG_FALLING + tsplink.trigger[2].stimulus = smua.trigger.SWEEP_COMPLETE_EVENT_ID + -- TSP-Link Trigger 2 is used by the 2651A to tell the 26xxA that it + -- has completed the drain sweep and it is OK for the 26xxA to continue. + + -- Debug code + ------------------------ + trigger.blender[1].orenable = true + trigger.blender[1].stimulus[1] = trigger.timer[1].EVENT_ID + trigger.blender[1].stimulus[2] = smua.trigger.SOURCE_COMPLETE_EVENT_ID + trigger.blender[1].stimulus[3] = smua.trigger.MEASURE_COMPLETE_EVENT_ID + trigger.blender[1].stimulus[4] = trigger.timer[2].EVENT_ID + --trigger.blender[1].stimulus[4] = smua.trigger.PULSE_COMPLETE_EVENT_ID + trigger.blender[1].clear() + + digio.trigger[1].mode = digio.TRIG_FALLING + digio.trigger[1].pulsewidth = 3e-6 + digio.trigger[1].stimulus = trigger.blender[1].EVENT_ID + digio.trigger[1].clear() + ------------------------- + -- End Debug Code + + -- Prepare the Drain SMU (2651A) reading buffers + smua.nvbuffer1.clear() + smua.nvbuffer1.collectsourcevalues = 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.collectsourcevalues = 1 + + + -- Configure the Gate SMU(26xxA) + -------------------------------- + node[2].smua.reset() + node[2].smua.source.func = node[2].smua.OUTPUT_DCVOLTS + node[2].smua.sense = node[2].smua.SENSE_REMOTE + node[2].smua.source.levelv = 0 + node[2].smua.source.limiti = 100e-3 + node[2].smua.measure.delay = 300e-6 -- Give gate 300us to settle + -- Do not need to configure any additional measure settings. + -- Timing is not critical on the gate so autorange will do. + node[2].smua.source.highc = 1 + + -- Configure Gate SMU(26xxA) Trigger Model + node[2].smua.trigger.source.linearv(gstart, gstop, gsteps) + node[2].smua.trigger.source.limiti = 100e-3 + node[2].smua.trigger.measure.action = node[2].smua.ENABLE + node[2].smua.trigger.measure.iv(node[2].smua.nvbuffer1, node[2].smua.nvbuffer2) + node[2].smua.trigger.endpulse.action = smua.SOURCE_HOLD + node[2].smua.trigger.endsweep.action = smua.SOURCE_IDLE + node[2].smua.trigger.count = gsteps + node[2].smua.trigger.arm.stimulus = 0 + node[2].smua.trigger.source.stimulus = node[2].tsplink.trigger[1].EVENT_ID + node[2].smua.trigger.measure.stimulus = 0 + node[2].smua.trigger.endpulse.stimulus = node[2].tsplink.trigger[2].EVENT_ID + node[2].smua.trigger.source.action = smua.ENABLE + + -- Configure 26xxA TSP-Link Triggers + node[2].tsplink.trigger[1].clear() + node[2].tsplink.trigger[1].mode = node[2].tsplink.TRIG_SYNCHRONOUSA + node[2].tsplink.trigger[1].stimulus = node[2].smua.trigger.MEASURE_COMPLETE_EVENT_ID + + node[2].tsplink.trigger[2].clear() + node[2].tsplink.trigger[2].mode = node[2].tsplink.TRIG_FALLING + + -- Prepare the Gate SMU (26xxA) reading buffers + node[2].smua.nvbuffer1.clear() + node[2].smua.nvbuffer1.collectsourcevalues = 1 + node[2].smua.nvbuffer2.clear() + node[2].smua.nvbuffer2.collectsourcevalues = 1 + + -- The SMUs are configured and ready to run the test + + -- Outputs on + node[2].smua.source.output = 1 smua.source.output = 1 + + node[2].smua.trigger.initiate() -- Start the 26xxA's trigger model + smua.trigger.initiate() -- Start the 2651A's trigger model + + waitcomplete() -- Wait until the sweeps are complete + + -- Outputs off + smua.source.output = 0 + node[2].smua.source.output = 0 + + -- Return the data + PrintIVcurveData(gsteps, dsteps) +end + + +--[[ + IV_CurvesDual(gstart, gstop, gsteps, dstart, dstop, dsteps, pulseWidth, pulsePeriod, pulseLimit) + + Description: This function will perform a series of pulsed sweeps + on a MOSFET or IGBT device to generate a series of IV Curves that + characterize the device. It will use two 2651A SMUs in parallel to + reach currents of up to 100A. Note: To avoid device oscillations, a + series resistor on the gate terminal of the device may be required. + + Parameters: + gstart: The starting voltage of the gate sweep + gstop: The ending voltage of the gate sweep + gsteps: The number of steps in the gate sweep + dstart: The starting voltage of the drain sweep + dstop: The ending voltage of the drain sweep + dsteps: The number of steps in the drain sweep + pulseWidth: The width of the drain pulse in seconds + pulsePeriod:The time in seconds between the start of consecutive drain pulses in the sweep + pulseLimit: The current limit in Amps of the drain pulse + + Example Usage: + IV_CurvesDual(6, 8.5, 6, 0, 5, 42, 300e-6, 30e-3, 100) +--]] +function IV_CurvesDual(gstart, gstop, gsteps, dstart, dstop, dsteps, pulseWidth, pulsePeriod, pulseLimit) + local _nplc = 0.001 + reset() + tsplink.reset() + + -- Configure the Drain V-SMU(2651A) + --------------------------------- + smua.reset() + smua.source.func = smua.OUTPUT_DCVOLTS + smua.sense = smua.SENSE_REMOTE + + smua.source.rangev = math.max(math.abs(dstart), math.abs(dstop)) + -- Select the source range that is large enough to fit all values of the sweep + smua.source.levelv = 0 -- Sets the drain bias level + smua.source.limiti = 5 + + smua.measure.rangev = smua.source.rangev + smua.measure.rangei = pulseLimit/2 --(pulseLimit == "off") and 50 or pulseLimit + -- Select a measure range large enough to fit pulses up to the current limit + + smua.measure.nplc = _nplc + smua.measure.delay = (pulseWidth - ((1/localnode.linefreq) * smua.measure.nplc)) - 60e-6 + -- Set the measure delay so that the measurement is + -- taken at the end of the pulse before the falling edge + smua.measure.autozero = smua.AUTOZERO_ONCE + + -- Timer 1 controls the pulse period + trigger.timer[1].count = (dsteps <= 1) and 1 or (dsteps - 1) + -- If dsteps <= 1 then use 1 for the count else use dsteps - 1 + trigger.timer[1].delay = pulsePeriod + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = tsplink.trigger[1].EVENT_ID + trigger.timer[1].clear() + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = pulseWidth - 4e-6 + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = smua.trigger.SOURCE_COMPLETE_EVENT_ID--trigger.timer[1].EVENT_ID + trigger.timer[2].clear() + + -- Configure Drain V-SMU(2651A #1) trigger model + smua.trigger.source.linearv(dstart, dstop, dsteps) + smua.trigger.source.limiti = pulseLimit/2 + 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.arm.count = gsteps + smua.trigger.count = dsteps + smua.trigger.arm.stimulus = 0 + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = 0 + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + -- Configure TSP-Link Triggers + tsplink.trigger[1].clear() + tsplink.trigger[1].mode = tsplink.TRIG_SYNCHRONOUSM + tsplink.trigger[1].stimulus = smua.trigger.ARMED_EVENT_ID + -- TSP-Link Trigger 1 is used by the 2651A to tell the 26xxA to step + -- the gate and for the 26xxA to tell the 2651A when it has completed + -- the step. + + tsplink.trigger[2].clear() + tsplink.trigger[2].mode = tsplink.TRIG_FALLING + tsplink.trigger[2].stimulus = smua.trigger.SWEEP_COMPLETE_EVENT_ID + -- TSP-Link Trigger 2 is used by the 2651A to tell the 26xxA that it + -- has completed the drain sweep and it is OK for the 26xxA to continue. + + tsplink.trigger[3].clear() + tsplink.trigger[3].mode = tsplink.TRIG_FALLING + tsplink.trigger[3].stimulus = trigger.timer[1].EVENT_ID + -- TSP-Link Trigger 3 is used by 2651A #1 to tell 2651A #2 to output the pulse + + + -- Debug code + ------------------------ + trigger.blender[1].orenable = true + trigger.blender[1].stimulus[1] = trigger.timer[1].EVENT_ID + trigger.blender[1].stimulus[2] = smua.trigger.SOURCE_COMPLETE_EVENT_ID + trigger.blender[1].stimulus[3] = smua.trigger.MEASURE_COMPLETE_EVENT_ID + trigger.blender[1].stimulus[4] = trigger.timer[2].EVENT_ID + --trigger.blender[1].stimulus[4] = smua.trigger.PULSE_COMPLETE_EVENT_ID + trigger.blender[1].clear() + + digio.trigger[1].mode = digio.TRIG_FALLING + digio.trigger[1].pulsewidth = 3e-6 + digio.trigger[1].stimulus = trigger.blender[1].EVENT_ID + digio.trigger[1].clear() + ------------------------- + -- End Debug Code + + + -- Prepare the Drain V-SMU (2651A) reading buffers + smua.nvbuffer1.clear() + smua.nvbuffer1.collectsourcevalues = 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.collectsourcevalues = 1 + + + +--=================================================================================== +-- Configure the Drain I-SMU(2651A #2) + --------------------------------- + node[3].smua.reset() + node[3].smua.source.func = node[3].smua.OUTPUT_DCAMPS + node[3].smua.sense = node[3].smua.SENSE_REMOTE + + node[3].smua.source.rangei = pulseLimit/2--math.max(math.abs(dstart), math.abs(dstop)) + -- Select the source range that is large enough to fit all values of the sweep + node[3].smua.source.leveli = 0 -- Sets the drain bias level + node[3].smua.source.limitv = 10 + + node[3].smua.measure.rangei = node[3].smua.source.rangei + node[3].smua.measure.rangev = 10 + -- Select a measure range large enough to fit pulses up to the current limit + + node[3].smua.measure.nplc = _nplc + node[3].smua.measure.delay = (pulseWidth - ((1/node[3].linefreq) * node[3].smua.measure.nplc)) - 60e-6 + -- Set the measure delay so that the measurement is + -- taken at the end of the pulse before the falling edge + node[3].smua.measure.autozero = node[3].smua.AUTOZERO_ONCE + + -- Timer 2 controls the pulse width + node[3].trigger.timer[2].count = 1 + node[3].trigger.timer[2].delay = pulseWidth - 4e-6 + node[3].trigger.timer[2].passthrough = false + node[3].trigger.timer[2].stimulus = node[3].smua.trigger.SOURCE_COMPLETE_EVENT_ID + node[3].trigger.timer[2].clear() + + -- Configure Drain I-SMU(2651A #2) trigger model + node[3].smua.trigger.source.lineari(pulseLimit/2, pulseLimit/2, dsteps) + node[3].smua.trigger.source.limitv = 10 + node[3].smua.trigger.measure.action = node[3].smua.ENABLE + node[3].smua.trigger.measure.iv(node[3].smua.nvbuffer1, node[3].smua.nvbuffer2) + node[3].smua.trigger.endpulse.action = node[3].smua.SOURCE_IDLE + node[3].smua.trigger.endsweep.action = node[3].smua.SOURCE_IDLE + node[3].smua.trigger.arm.count = gsteps + node[3].smua.trigger.count = dsteps + node[3].smua.trigger.arm.stimulus = 0 + node[3].smua.trigger.source.stimulus = node[3].tsplink.trigger[3].EVENT_ID + node[3].smua.trigger.measure.stimulus = 0 + node[3].smua.trigger.endpulse.stimulus = node[3].trigger.timer[2].EVENT_ID + node[3].smua.trigger.source.action = node[3].smua.ENABLE + + -- Configure TSP-Link Triggers + node[3].tsplink.trigger[3].clear() + node[3].tsplink.trigger[3].mode = node[3].tsplink.TRIG_FALLING +-- node[3].tsplink.trigger[3].stimulus = node[3].smua.trigger.ARMED_EVENT_ID + -- TSP-Link Trigger 3 is used by 2651A #2 to receive triggers from 2651A #1 + -- to tell it when to start the pulse + + -- Prepare the Drain I-SMU (2651A #2) reading buffers + node[3].smua.nvbuffer1.clear() + node[3].smua.nvbuffer1.collectsourcevalues = 1 + node[3].smua.nvbuffer2.clear() + node[3].smua.nvbuffer2.collectsourcevalues = 1 +--=================================================================================== + + + -- Configure the Gate SMU(26xxA) + -------------------------------- + node[2].smua.reset() + node[2].smua.source.func = node[2].smua.OUTPUT_DCVOLTS + node[2].smua.sense = node[2].smua.SENSE_REMOTE + node[2].smua.source.levelv = 0 + node[2].smua.source.limiti = 100e-3 + node[2].smua.measure.delay = 300e-6 -- Give gate 300us to settle + -- Do not need to configure any additional measure settings. + -- Timing is not critical on the gate so autorange will do. + node[2].smua.source.highc = 1 + + -- Configure Gate SMU(26xxA) Trigger Model + node[2].smua.trigger.source.linearv(gstart, gstop, gsteps) + node[2].smua.trigger.source.limiti = 100e-3 + node[2].smua.trigger.measure.action = node[2].smua.ENABLE + node[2].smua.trigger.measure.iv(node[2].smua.nvbuffer1, node[2].smua.nvbuffer2) + node[2].smua.trigger.endpulse.action = smua.SOURCE_HOLD + node[2].smua.trigger.endsweep.action = smua.SOURCE_IDLE + node[2].smua.trigger.count = gsteps + node[2].smua.trigger.arm.stimulus = 0 + node[2].smua.trigger.source.stimulus = node[2].tsplink.trigger[1].EVENT_ID + node[2].smua.trigger.measure.stimulus = 0 + node[2].smua.trigger.endpulse.stimulus = node[2].tsplink.trigger[2].EVENT_ID + node[2].smua.trigger.source.action = smua.ENABLE + + -- Configure 26xxA TSP-Link Triggers + node[2].tsplink.trigger[1].clear() + node[2].tsplink.trigger[1].mode = node[2].tsplink.TRIG_SYNCHRONOUSA + node[2].tsplink.trigger[1].stimulus = node[2].smua.trigger.MEASURE_COMPLETE_EVENT_ID + + node[2].tsplink.trigger[2].clear() + node[2].tsplink.trigger[2].mode = node[2].tsplink.TRIG_FALLING + + -- Prepare the Gate SMU (26xxA) reading buffers + node[2].smua.nvbuffer1.clear() + node[2].smua.nvbuffer1.collectsourcevalues = 1 + node[2].smua.nvbuffer2.clear() + node[2].smua.nvbuffer2.collectsourcevalues = 1 + + -- The SMUs are configured and ready to run the test + + -- Outputs on + node[2].smua.source.output = 1 + node[3].smua.source.output = 1 + smua.source.output = 1 + + node[2].smua.trigger.initiate() -- Start the 26xxA's trigger model + node[3].smua.trigger.initiate() -- Start the 2651A #2s trigger model + smua.trigger.initiate() -- Start the 2651A #1s trigger model + + waitcomplete() -- Wait until the sweeps are complete + + -- Outputs off + smua.source.output = 0 + node[3].smua.source.output = 0 + node[2].smua.source.output = 0 + + -- Return the data + PrintIVcurveDataDual(gsteps, dsteps) +end + + +--[[ + PrintIVcurveData(gsteps, dsteps) + + Description: This function will output the data collected by the + IV_Curves() function in an Excel compatible format. For each step of + the gate, this function will output three columns containing the + drain sweep data as well as the gate data in the first row. + + Parameters: + gsteps: The number of steps in the gate sweep + dsteps: The number of steps in the drain sweep + + Example Usage: + PrintIVcurveData(5, 21) +--]] +function PrintIVcurveData(gsteps, dsteps) + line1 = "" + line2 = "" + for i=1,gsteps do + line1 = line1 .. string.format("Vgs = %0.2f\t%g\t%g\t", node[2].smua.nvbuffer1.sourcevalues[i], node[2].smua.nvbuffer2[i], node[2].smua.nvbuffer1[i]) + line2 = line2 .. "Source Value\tVoltage\tCurrent\t" + end + print(line1) + print(line2) + for i=1, dsteps do + line = "" + for j=1, gsteps do + line = line .. string.format("%g\t%g\t%g\t", smua.nvbuffer1.sourcevalues[(j-1)*dsteps + i], smua.nvbuffer2[(j-1)*dsteps + i], smua.nvbuffer1[(j-1)*dsteps + i]) + end + print(line) + end +end + +function PrintIVcurveDataDual(gsteps, dsteps) + line1 = "" + line2 = "" + for i=1,gsteps do + line1 = line1 .. string.format("Vgs = %0.2f\t%g\t%g\t", node[2].smua.nvbuffer1.sourcevalues[i], node[2].smua.nvbuffer2[i], node[2].smua.nvbuffer1[i]) + line2 = line2 .. "Source Value\tVoltage\tCurrent\t" + end + print(line1) + print(line2) + for i=1, dsteps do + line = "" + for j=1, gsteps do + line = line .. string.format("%g\t%g\t%g\t", smua.nvbuffer1.sourcevalues[(j-1)*dsteps + i], (smua.nvbuffer2[(j-1)*dsteps + i] + node[3].smua.nvbuffer2[(j-1)*dsteps + i])/2, (smua.nvbuffer1[(j-1)*dsteps + i] + node[3].smua.nvbuffer1[(j-1)*dsteps + i])) + end + print(line) + end +end + +function Print2(gsteps, dsteps) + line1 = "" + line2 = "" + for i=1,gsteps do + line1 = line1 .. string.format("Vgs = %0.2f\t%g\t", node[2].smua.nvbuffer1.sourcevalues[i], node[2].smua.nvbuffer1[i]) + line2 = line2 .. "Voltage\tCurrent\t" + end + print(line1) + print(line2) + for i=1, dsteps do + line = "" + for j=1, gsteps do + line = line .. string.format("%g\t%g\t", smua.nvbuffer2[(j-1)*dsteps + i], smua.nvbuffer1[(j-1)*dsteps + i]) + end + print(line) + end +end + +function Print2Dual(gsteps, dsteps) + line1 = "" + line2 = "" + for i=1,gsteps do + line1 = line1 .. string.format("Vgs = %0.2f\t%g\t", node[2].smua.nvbuffer1.sourcevalues[i], node[2].smua.nvbuffer1[i]) + line2 = line2 .. "Voltage\tCurrent\t" + end + print(line1) + print(line2) + for i=1, dsteps do + line = "" + for j=1, gsteps do + line = line .. string.format("%g\t%g\t", (smua.nvbuffer2[(j-1)*dsteps + i] + node[3].smua.nvbuffer2[(j-1)*dsteps + i])/2, (smua.nvbuffer1[(j-1)*dsteps + i] + node[3].smua.nvbuffer1[(j-1)*dsteps + i])) + end + print(line) + end +end + +function DumpBuffers(gsteps, dsteps) + line1 = "" + line2 = "" + for i=1,gsteps do + line1 = line1 .. string.format("Vgs = %0.2f\t%g\t\t\t\t\t", node[2].smua.nvbuffer1.sourcevalues[i], node[2].smua.nvbuffer1[i]) + line2 = line2 .. "V1\tI1\tV2\tI2\tVdual\tIdual\t" + end + print(line1) + print(line2) + for i=1, dsteps do + line = "" + for j=1, gsteps do + line = line .. string.format("%g\t%g\t%g\t%g\t%g\t%g\t", smua.nvbuffer2[(j-1)*dsteps + i], smua.nvbuffer1[(j-1)*dsteps + i], node[3].smua.nvbuffer2[(j-1)*dsteps + i], node[3].smua.nvbuffer1[(j-1)*dsteps + i], (smua.nvbuffer2[(j-1)*dsteps + i] + node[3].smua.nvbuffer2[(j-1)*dsteps + i])/2, (smua.nvbuffer1[(j-1)*dsteps + i] + node[3].smua.nvbuffer1[(j-1)*dsteps + i])) + end + print(line) + end +end diff --git a/Instrument_Examples/Series_2600/265xA/2651A_-_Power_FET_IGBT_IV_Curves.tsp b/Instrument_Examples/Series_2600/265xA/2651A_-_Power_FET_IGBT_IV_Curves.tsp new file mode 100644 index 0000000..882663c --- /dev/null +++ b/Instrument_Examples/Series_2600/265xA/2651A_-_Power_FET_IGBT_IV_Curves.tsp @@ -0,0 +1,231 @@ +--[[ + Title: IV Curves Example Script + Date: 12/23/2010 + Description: This script will perform a series of IV Curves on a + MOSFET or IGBT device and will return the data in a Microsoft Excel + compatible format for graphing and analysis. + + TSP-Link Configuration: + Node 1: 2651A + Node 2: 26xxA + + Master Node: Node 1 + + Revision History: + 12/23/2010 - Version 1.01 + David Wyban + • Changed Timer 2 to be triggered from SOURCE_COMPLETE event to avoid + issue with the first pulse in a sweep being shorter than the rest + • Added a line to turn on highc mode on the gate SMU for stability + + 12/6/2010 - Version 1.0 + David Wyban + Initial Revision +]] + +--[[ + IV_Curves(gstart, gstop, gsteps, dstart, dstop, dsteps, pulseWidth, pulsePeriod, pulseLimit) + + Description: This function will perform a series of pulsed sweeps + on a MOSFET or IGBT device to generate a series of IV Curves that + characterize the device. Note: To avoid device oscillations, a + series resistor on the gate terminal of the device may be required. + + Parameters: + gstart: The starting voltage of the gate sweep + gstop: The ending voltage of the gate sweep + gsteps: The number of steps in the gate sweep + dstart: The starting voltage of the drain sweep + dstop: The ending voltage of the drain sweep + dsteps: The number of steps in the drain sweep + pulseWidth: The width of the drain pulse in seconds + pulsePeriod:The time in seconds between the start of consecutive drain pulses in the sweep + pulseLimit: The current limit in Amps of the drain pulse + + Example Usage: + IV_Curves(5, 9, 5, 0, 10, 21, 300e-6, 30e-3, 50) +--]] +function IV_Curves(gstart, gstop, gsteps, dstart, dstop, dsteps, pulseWidth, pulsePeriod, pulseLimit) + reset() + tsplink.reset() + + -- Configure the Drain SMU(2651A) + --------------------------------- + smua.reset() + smua.source.func = smua.OUTPUT_DCVOLTS + smua.sense = smua.SENSE_REMOTE + + smua.source.rangev = math.max(math.abs(dstart), math.abs(dstop)) + -- Select the source range that is large enough to fit all values of the sweep + smua.source.levelv = 0 -- Sets the drain bias level + smua.source.limiti = 5 + + smua.measure.rangev = smua.source.rangev + smua.measure.rangei = (pulseLimit == "off") and 50 or pulseLimit + -- Select a measure range large enough to fit pulses up to the current limit + + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.nplc = 0.005 + -- NPLC can be increased to improve measurement accuracy. + -- However, it should remain small enough to fit the measurement + -- within the width of the settled part of the pulse. + smua.measure.delay = (pulseWidth - ((1/localnode.linefreq) * smua.measure.nplc)) - 20e-6 + -- Set the measure delay so that the measurement is + -- taken at the end of the pulse before the falling edge + + -- Timer 1 controls the pulse period + trigger.timer[1].count = (dsteps <= 1) and 1 or (dsteps - 1) + -- If dsteps <= 1 then use 1 for the count else use dsteps - 1 + trigger.timer[1].delay = pulsePeriod + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = tsplink.trigger[1].EVENT_ID + trigger.timer[1].clear() + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = pulseWidth - 3e-6 + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = smua.trigger.SOURCE_COMPLETE_EVENT_ID + trigger.timer[2].clear() + + -- Configure Drain SMU(2651A) trigger model + smua.trigger.source.linearv(dstart, dstop, dsteps) + smua.trigger.source.limiti = pulseLimit + 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.arm.count = gsteps + smua.trigger.count = dsteps + smua.trigger.arm.stimulus = 0 + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = 0 + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + -- Configure TSP-Link Triggers + tsplink.trigger[1].clear() + tsplink.trigger[1].mode = tsplink.TRIG_SYNCHRONOUSM + tsplink.trigger[1].stimulus = smua.trigger.ARMED_EVENT_ID + -- TSP-Link Trigger 1 is used by the 2651A to command the 26xxA + -- to step the gate and for the 26xxA to report to the 2651A + -- that it has completed the step. + + tsplink.trigger[2].clear() + tsplink.trigger[2].mode = tsplink.TRIG_FALLING + tsplink.trigger[2].stimulus = smua.trigger.SWEEP_COMPLETE_EVENT_ID + -- TSP-Link Trigger 2 is used by the 2651A to command the 26xxA that + -- it has completed the drain sweep and that the 26xxA continue. + + -- Prepare the Drain SMU (2651A) reading buffers + smua.nvbuffer1.clear() + smua.nvbuffer1.collectsourcevalues = 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.collectsourcevalues = 1 + + + -- Configure the Gate SMU(26xxA) + -------------------------------- + node[2].smua.reset() + node[2].smua.source.func = node[2].smua.OUTPUT_DCVOLTS + node[2].smua.sense = node[2].smua.SENSE_REMOTE + node[2].smua.source.levelv = 0 + node[2].smua.source.limiti = 100e-3 + node[2].smua.measure.delay = 300e-6 -- Give gate 300us to settle + -- Do not need to configure any additional measure settings. + -- Timing is not critical on the gate so autorange will do. + + node[2].smua.source.highc = 0 + -- If you find the gate to be unstable even with a gate resistor in place + -- changing highc to 1 can improve stability. + + -- Configure Gate SMU(26xxA) Trigger Model + node[2].smua.trigger.source.linearv(gstart, gstop, gsteps) + node[2].smua.trigger.source.limiti = 100e-3 + node[2].smua.trigger.measure.action = node[2].smua.ENABLE + node[2].smua.trigger.measure.iv(node[2].smua.nvbuffer1, node[2].smua.nvbuffer2) + node[2].smua.trigger.endpulse.action = smua.SOURCE_HOLD + node[2].smua.trigger.endsweep.action = smua.SOURCE_IDLE + node[2].smua.trigger.count = gsteps + node[2].smua.trigger.arm.stimulus = 0 + node[2].smua.trigger.source.stimulus = node[2].tsplink.trigger[1].EVENT_ID + node[2].smua.trigger.measure.stimulus = 0 + node[2].smua.trigger.endpulse.stimulus = node[2].tsplink.trigger[2].EVENT_ID + node[2].smua.trigger.source.action = smua.ENABLE + + -- Configure Model 26xxA TSP-Link Triggers + node[2].tsplink.trigger[1].clear() + node[2].tsplink.trigger[1].mode = node[2].tsplink.TRIG_SYNCHRONOUSA + node[2].tsplink.trigger[1].stimulus = node[2].smua.trigger.MEASURE_COMPLETE_EVENT_ID + + node[2].tsplink.trigger[2].clear() + node[2].tsplink.trigger[2].mode = node[2].tsplink.TRIG_FALLING + + -- Prepare the Gate SMU (26xxA) reading buffers + node[2].smua.nvbuffer1.clear() + node[2].smua.nvbuffer1.collectsourcevalues = 1 + node[2].smua.nvbuffer2.clear() + node[2].smua.nvbuffer2.collectsourcevalues = 1 + + -- The SMUs are configured and ready to run the test + + -- Outputs on + node[2].smua.source.output = 1 + smua.source.output = 1 + + -- Start the 26xxA's trigger model + node[2].smua.trigger.initiate() + + -- Start the 2651A's trigger model + smua.trigger.initiate() + + waitcomplete() -- Wait until the sweeps are complete + + -- Outputs off + smua.source.output = 0 + node[2].smua.source.output = 0 + + -- Return the data + PrintIVcurveData(gsteps, dsteps) +end + + +--[[ + PrintIVcurveData(gsteps, dsteps) + + Description: This function will output the data collected by + the IV_Curves() function in a Microsoft Excel compatible format. + For each step of the gate, this function will output three + columns containing the drain sweep data as well as the gate data + in the first row. + + Parameters: + gsteps: The number of steps in the gate sweep + dsteps: The number of steps in the drain sweep + + Example Usage: + PrintIVcurveData(5, 21) +--]] +function PrintIVcurveData(gsteps, dsteps) + line1 = "" + line2 = "" + for i = 1,gsteps do + line1 = line1 .. string.format("Vgs = %0.2f\t%g\t%g\t", + node[2].smua.nvbuffer1.sourcevalues[i], + node[2].smua.nvbuffer2[i], + node[2].smua.nvbuffer1[i]) + line2 = line2 .. "Source Value\tVoltage\tCurrent\t" + end + print(line1) + print(line2) + for i = 1, dsteps do + line = "" + for j = 1, gsteps do + line = line .. string.format("%g\t%g\t%g\t", + smua.nvbuffer1.sourcevalues[(j - 1) * dsteps + i], + smua.nvbuffer2[(j - 1) * dsteps + i], + smua.nvbuffer1[(j - 1) * dsteps + i]) + end + print(line) + end +end diff --git a/Instrument_Examples/Series_2600/265xA/2651A_-_Pulse_Width_Modulation.tsp b/Instrument_Examples/Series_2600/265xA/2651A_-_Pulse_Width_Modulation.tsp new file mode 100644 index 0000000..55e5d6e --- /dev/null +++ b/Instrument_Examples/Series_2600/265xA/2651A_-_Pulse_Width_Modulation.tsp @@ -0,0 +1,585 @@ +--[[ + Title: Pulse Width Modulation Script + Author: David Wyban + Date: 6/24/2011 + Desription: The purpose of this script is to generate a pulse width + modulated waveform for use in testing High Brightness LED modules. + Users of this script should call the functions in the User Functions + section. Functions in the Utility Functions section are used by the + User Functions to execute the test. + + System Setup: + PWM_Test_Single() + 1x Model 2651A + PWM_TEST_Dual() + 2x Model 2651A + 1x TSP-Link Cable + + Node 1: 2651A #1 (Master) + Node 2: 2651A #2 (Slave) + + Revision History + Ver. 1.00 6/24/2011 + Modified by: David Wyban + Original Revision +]]-- + +--================ +-- User Functions +--================ +--[[ PWM_Test_Single() + + This function uses a single SMU to output a pulse width modulated waveform. +--]] +function PWM_Test_Single(pulseLevel, pulseLimit, frequency, dutyCycle, numPulses, specDelay) + if (pulseLevel == nil) then pulseLevel = 1 end + if (pulseLimit == nil) then pulseLimit = 1 end + if (frequency == nil) then frequency = 100 end + if (dutyCycle == nil) then dutyCycle = 1 end + if (numPulses == nil) then numPulses = 10 end + if (specDelay == nil) then specDelay = 0 end + + local pulsePeriod + local pulseWidth + local measDelay + -- Calculate the timing parameters from the frequency and duty cycle + pulsePeriod,pulseWidth,measDelay = CalculateTiming(frequency, dutyCycle) + + -- Do a quick check on the input parameters + f,msg = SimpleRegionCheck(pulseLevel, pulseLimit, dutyCycle, 1) + if (f == false) then + print(msg) + quit() + end + + reset() + smua.reset() + smua.source.func = smua.OUTPUT_DCAMPS + smua.sense = smua.SENSE_REMOTE + smua.source.autorangei = 0 + smua.source.rangei = pulseLevel + smua.source.leveli = 0 + -- Set the DC bias limit. This is not the limit used during the pulses. + smua.source.limitv = 1 + + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.autorangev = 0 + smua.measure.rangev = pulseLimit + -- The fast ADC allows us to place the measurements very close to the falling edge of + -- the pulse allowing for settled measurements even when pulse widths are very small + smua.measure.adc = smua.ADC_FAST + smua.measure.count = 1 + smua.measure.interval = 1e-6 + -- Uncomment the following lines to turn on measure filtering. When enabled, the SMU + -- will take multiple measurements and average them to produce a single reading. + -- Because the Fast ADC can take one measurement every microsecond, several measurements + -- can be aquired in a small time to produce an averaged reading. + --smua.measure.filter.count = 5 + --smua.measure.filter.enable = smua.FILTER_ON + + -- This measure delay sets the delay between the measurement trigger being received + -- and when the actual measurement(s) start. This is set to 0 because we will be + -- delaying the trigger itself and do not need additional delay. + smua.measure.delay = 0 + + -- Setup the Reading Buffers + smua.nvbuffer1.clear() + smua.nvbuffer1.appendmode = 1 + smua.nvbuffer1.collecttimestamps= 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.appendmode = 1 + smua.nvbuffer2.collecttimestamps= 1 + + -- Configure the Trigger Model + --============================ + + -- Timer 1 controls the pulse period + trigger.timer[1].count = numPulses > 1 and numPulses - 1 or 1 + trigger.timer[1].delay = pulsePeriod + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = smua.trigger.ARMED_EVENT_ID + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + if (type(pulseWidth) == "table") then + -- Use a delay list if the duty cycle will vary for each pulse + trigger.timer[2].delaylist = pulseWidth + else + -- else every pulse will be the same duty cycle + trigger.timer[2].delay = pulseWidth + end + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = smua.trigger.SOURCE_COMPLETE_EVENT_ID + + -- Timer 3 controls the measurement + trigger.timer[3].count = 1 + if (type(measDelay) == "table") then + -- If the duty cycle is variable then the measure delay will be as well + trigger.timer[3].delaylist = measDelay + else + trigger.timer[3].delay = measDelay + end + trigger.timer[3].passthrough = false + trigger.timer[3].stimulus = smua.trigger.SOURCE_COMPLETE_EVENT_ID + + -- Configure SMU Trigger Model for Sweep + smua.trigger.source.lineari(pulseLevel, pulseLevel, numPulses) + smua.trigger.source.limitv = pulseLimit + smua.trigger.measure.action = smua.ASYNC + 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 = numPulses + smua.trigger.arm.stimulus = 0 + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = trigger.timer[3].EVENT_ID + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + -- Configure the Digital I/O trigger + ConfigureSpectrometerTrigger(specDelay) + + -- Start the Test + --=============== + -- Turn the output on + smua.source.output = 1 + -- Start the trigger model execution + smua.trigger.initiate() + + -- While the trigger model is outputing the waveform and collecting the + -- measurements, the script will scan the status model for any overruns + -- that may occur as a result of using impropper settings. + local ovr = false + local msg = "" + while ((status.operation.sweeping.condition ~= 0) and (ovr == false)) do + ovr, msg = CheckForOverRun(localnode) + end + if (ovr == true) then + smua.abort() + print(msg) + end + -- Turn the output off + smua.source.output = 0 + -- Return the data + PrintData() +end + +--[[ PWM_Test_Dual() + + This function uses two SMUs connected together in parallel to ouput a pulse width + modulated wavform. By using two SMUs higher current levels/duty cycles can be achieved. +--]] +function PWM_Test_Dual(pulseLevel, pulseLimit, frequency, dutyCycle, numPulses, specDelay) + if (pulseLevel == nil) then pulseLevel = 1 end + if (pulseLimit == nil) then pulseLimit = 1 end + if (frequency == nil) then frequency = 100 end + if (dutyCycle == nil) then dutyCycle = 1 end + if (numPulses == nil) then numPulses = 10 end + if (specDelay == nil) then specDelay = 0 end + + local pulsePeriod + local pulseWidth + local measDelay + + -- Calculate the timing parameters from the frequency and duty cycle + pulsePeriod,pulseWidth,measDelay = CalculateTiming(frequency, dutyCycle) + + -- Do a quick check on the input parameters + f,msg = SimpleRegionCheck(pulseLevel, pulseLimit, dutyCycle, 2) + if (f == false) then + print(msg) + quit() + end + + -- Initialize the TSP-Link + errorqueue.clear() + tsplink.reset() + errcode,errmsg,stat = errorqueue.next() + if (errcode ~= 0) then + print(errmsg) + exit() + end + reset() + ConfigureLocalSMU(pulseLevel, pulseLimit, pulsePeriod, pulseWidth, measDelay, numPulses) + ConfigureRemoteSMU(pulseLevel, pulseLimit, pulsePeriod, pulseWidth, measDelay, numPulses) + + -- Start the Test + --=============== + -- Turn the output on + smua.source.output = 1 + node[2].smua.source.output = 1 + -- Start the trigger model execution + node[2].smua.trigger.initiate() + smua.trigger.initiate() + + -- While the trigger model is outputing the waveform and collecting the + -- measurements, the script will scan the status model for any overruns + -- that may occur as a result of using impropper settings. + local ovr1 = false + local ovr2 = false + local msg1 = "" + local msg2 = "" + -- Loop until the sweep is either complete, or an overrun condition is detected + while (((status.operation.sweeping.condition ~= 0) or (node[2].status.operation.sweeping.condition ~= 0)) and (ovr1 == false) and (ovr2 == false)) do + ovr1, msg1 = CheckForOverRun(localnode) + ovr2, msg2 = CheckForOverRun(node[2]) + end + if ((ovr1 == true) or (ovr2 == true)) then + smua.abort() + node[2].smua.abort() + print("SMU#1:", msg1) + print("SMU#2:", msg2) + end + -- Turn the output off + node[2].smua.source.output = 0 + smua.source.output = 0 + -- Return the data + PrintDataDual() +end + + +--=================== +-- Utility Functions +--=================== +function ConfigureLocalSMU(pulseLevel, pulseLimit, pulsePeriod, pulseWidth, measDelay, numPulses) + smua.reset() + smua.source.func = smua.OUTPUT_DCAMPS + smua.sense = smua.SENSE_REMOTE + smua.source.autorangei = 0 + smua.source.rangei = pulseLevel/2 + smua.source.leveli = 0 + -- Set the DC bias limit. This is not the limit used during the pulses. + smua.source.limitv = 1 + smua.source.offmode = smua.OUTPUT_NORMAL + smua.source.offfunc = smua.OUTPUT_DCVOLTS + smua.source.offlimiti = 1e-3 + + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.autorangev = 0 + smua.measure.rangev = pulseLimit + -- The fast ADC allows us to place the measurements very close to the falling edge of + -- the pulse allowing for settled measurements even when pulse widths are very small + smua.measure.adc = smua.ADC_FAST + smua.measure.count = 1 + smua.measure.interval = 1e-6 + -- Uncomment the following lines to turn on measure filtering. When enabled, the SMU + -- will take multiple measurements and average them to produce a single reading. + -- Because the Fast ADC can take one measurement every microsecond, several measurements + -- can be aquired in a small time to produce an averaged reading. + --smua.measure.filter.count = 5 + --smua.measure.filter.enable = smua.FILTER_ON + + -- This measure delay sets the delay between the measurement trigger being received + -- and when the actual measurement(s) start. This is set to 0 because we will be + -- delaying the trigger itself and do not need additional delay. + smua.measure.delay = 0 + + -- Setup the Reading Buffers + smua.nvbuffer1.clear() + smua.nvbuffer1.appendmode = 1 + smua.nvbuffer1.collecttimestamps= 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.appendmode = 1 + smua.nvbuffer2.collecttimestamps= 1 + + -- Configure the Trigger Model + --============================ + + -- Timer 1 controls the pulse period + trigger.timer[1].count = (numPulses > 1) and numPulses - 1 or 1 + trigger.timer[1].delay = pulsePeriod + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = smua.trigger.ARMED_EVENT_ID + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + if (type(pulseWidth) == "table") then + -- Use a delay list if the duty cycle will vary for each pulse + trigger.timer[2].delaylist = pulseWidth + else + -- else every pulse will be the same duty cycle + trigger.timer[2].delay = pulseWidth + end + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = smua.trigger.SOURCE_COMPLETE_EVENT_ID + + -- Timer 3 controls the measurement delay + trigger.timer[3].count = 1 + if (type(measDelay) == "table") then + -- If the duty cycle is variable then the measure delay will be as well + trigger.timer[3].delaylist = measDelay + else + trigger.timer[3].delay = measDelay + end + trigger.timer[3].passthrough = false + trigger.timer[3].stimulus = smua.trigger.SOURCE_COMPLETE_EVENT_ID + + -- TSP-Link Trigger 1 is used to synchronize the SMUs by telling + -- the second SMU when to pulse. + tsplink.trigger[1].clear() + tsplink.trigger[1].mode = tsplink.TRIG_FALLING + tsplink.trigger[1].stimulus = trigger.timer[1].EVENT_ID + + -- Configure SMU Trigger Model for Sweep + smua.trigger.source.lineari(pulseLevel/2, pulseLevel/2, numPulses) + smua.trigger.source.limitv = pulseLimit + smua.trigger.measure.action = smua.ASYNC + 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 = numPulses + smua.trigger.arm.stimulus = 0 + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = trigger.timer[3].EVENT_ID + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE +end + +function ConfigureRemoteSMU(pulseLevel, pulseLimit, pulsePeriod, pulseWidth, measDelay, numPulses) + node[2].smua.reset() + node[2].smua.source.func = node[2].smua.OUTPUT_DCAMPS + node[2].smua.sense = node[2].smua.SENSE_REMOTE + node[2].smua.source.autorangei = 0 + node[2].smua.source.rangei = pulseLevel/2 + node[2].smua.source.leveli = 0 + -- Set the DC bias limit. This is not the limit used during the pulses. + node[2].smua.source.limitv = 1 + node[2].smua.source.offmode = node[2].smua.OUTPUT_NORMAL + node[2].smua.source.offfunc = node[2].smua.OUTPUT_DCAMPS + node[2].smua.source.offlimitv = 40 + + node[2].smua.measure.autozero = node[2].smua.AUTOZERO_ONCE + node[2].smua.measure.autorangev = 0 + node[2].smua.measure.rangev = pulseLimit + -- The fast ADC allows us to place the measurements very close to the falling edge of + -- the pulse allowing for settled measurements even when pulse widths are very small + node[2].smua.measure.adc = node[2].smua.ADC_FAST + node[2].smua.measure.count = 1 + node[2].smua.measure.interval = 1e-6 + -- Uncomment the following lines to turn on measure filtering. When enabled, the SMU + -- will take multiple measurements and average them to produce a single reading. + -- Because the Fast ADC can take one measurement every microsecond, several measurements + -- can be aquired in a small time to produce an averaged reading. + --node[2].smua.measure.filter.count = 5 + --node[2].smua.measure.filter.enable = node[2].smua.FILTER_ON + + -- This measure delay sets the delay between the measurement trigger being received + -- and when the actual measurement(s) start. This is set to 0 because we will be + -- delaying the trigger itself and do not need additional delay. + node[2].smua.measure.delay = 0 + + -- Setup the Reading Buffers + node[2].smua.nvbuffer1.clear() + node[2].smua.nvbuffer1.appendmode = 1 + node[2].smua.nvbuffer1.collecttimestamps= 1 + node[2].smua.nvbuffer2.clear() + node[2].smua.nvbuffer2.appendmode = 1 + node[2].smua.nvbuffer2.collecttimestamps= 1 + + -- Configure the Trigger Model + --============================ + + -- Timer 2 controls the pulse width + node[2].trigger.timer[2].count = 1 + if (type(pulseWidth) == "table") then + -- Use a delay list if the duty cycle will vary for each pulse + node[2].trigger.timer[2].delaylist = pulseWidth + else + -- else every pulse will be the same duty cycle + node[2].trigger.timer[2].delay = pulseWidth + end + node[2].trigger.timer[2].passthrough = false + node[2].trigger.timer[2].stimulus = node[2].smua.trigger.SOURCE_COMPLETE_EVENT_ID + + -- Timer 3 controls the measurement delay + node[2].trigger.timer[3].count = 1 + if (type(measDelay) == "table") then + -- If the duty cycle is variable then the measure delay will be as well + node[2].trigger.timer[3].delaylist = measDelay + else + node[2].trigger.timer[3].delay = measDelay + end + node[2].trigger.timer[3].passthrough = false + node[2].trigger.timer[3].stimulus = node[2].smua.trigger.SOURCE_COMPLETE_EVENT_ID + + -- TSP-Link Trigger 1 is used to synchronize the SMUs. SMU #2 receives + -- its trigger to pulse from SMU #1 + node[2].tsplink.trigger[1].clear() + node[2].tsplink.trigger[1].mode = node[2].tsplink.TRIG_FALLING + -- Release the trigger line when the pulse is complete + node[2].tsplink.trigger[1].stimulus = 0 + + -- Configure SMU Trigger Model for Sweep + node[2].smua.trigger.source.lineari(pulseLevel/2, pulseLevel/2, numPulses) + node[2].smua.trigger.source.limitv = pulseLimit + node[2].smua.trigger.measure.action = node[2].smua.ASYNC + node[2].smua.trigger.measure.iv(node[2].smua.nvbuffer1, node[2].smua.nvbuffer2) + node[2].smua.trigger.endpulse.action = node[2].smua.SOURCE_IDLE + node[2].smua.trigger.endsweep.action = node[2].smua.SOURCE_IDLE + node[2].smua.trigger.count = numPulses + node[2].smua.trigger.arm.stimulus = 0 + node[2].smua.trigger.source.stimulus = node[2].tsplink.trigger[1].EVENT_ID + node[2].smua.trigger.measure.stimulus = node[2].trigger.timer[3].EVENT_ID + node[2].smua.trigger.endpulse.stimulus = node[2].trigger.timer[2].EVENT_ID + node[2].smua.trigger.source.action = node[2].smua.ENABLE +end + +function ConfigureSpectrometerTrigger(specDelay) + -- Digital I/O line 1 triggers the spectrometer measurements + -- Timer 4 puts a delay between the start of the pulse train and the + -- output of the digital IO trigger on Digital I/O line 1 + digio.trigger[1].clear() + digio.trigger[1].mode = digio.TRIG_FALLING + + -- If the delay value is > 0 then configure a timer to provide the delay + if specDelay > 0 then + trigger.timer[4].count = 1 + trigger.timer[4].delay = specDelay + trigger.timer[4].passthrough = false + trigger.timer[4].stimulus = smua.trigger.ARMED_EVENT_ID + + digio.trigger[1].stimulus = trigger.timer[4].EVENT_ID + else + -- Else bypass the timer and trigger the digital I/O immediately + -- Configure the Digital I/O pin that will trigger the spectrometer + digio.trigger[1].stimulus = smua.trigger.ARMED_EVENT_ID + end +end + +function CheckForOverRun(pNode) + -- Check SMUA Trigger Overruns + if (bit.bitand(pNode.status.operation.instrument.smua.trigger_overrun.condition, 2) == 2) then + return true, "smua arm trigger is overrun" + end + if (bit.bitand(pNode.status.operation.instrument.smua.trigger_overrun.condition, 4) == 4) then + return true, "smua source trigger is overrun" + end + if (bit.bitand(pNode.status.operation.instrument.smua.trigger_overrun.condition, 8) == 8) then + return true, "smua measure trigger is overrun" + end + if (bit.bitand(pNode.status.operation.instrument.smua.trigger_overrun.condition, 16) == 16) then + return true, "smua endpulse trigger is overrun" + end + + local CFORi = 0 + -- Check Timers for Overrun + if (pNode.status.operation.instrument.trigger_timer.trigger_overrun.condition > 0) then + return true, string.format("Timer trigger is overrun: 0x%x", CFORi) + end + + -- Check Blenders for Overrun + if (pNode.status.operation.instrument.trigger_blender.trigger_overrun.condition > 0) then + return true, string.format("blender trigger is overrun: 0x%x", CFORi) + end + + -- Check TSP-Link Triggers for Overrun + if (pNode.status.operation.instrument.tsplink.trigger_overrun.condition > 0) then + return true, string.format("TSP-Link trigger is overrun: 0x%x", CFORi) + end + + -- Check DIGIO Triggers for Overrun + if (pNode.status.operation.instrument.digio.trigger_overrun.condition > 0) then + return true, string.format("digio trigger is overrun: 0x%x", CFORi) + end + + -- Check LAN Triggers for Overrun + if (pNode.status.operation.instrument.lan.trigger_overrun.condition > 0) then + return true, string.format("LAN trigger is overrun: 0x%x", CFORi) + end + + return false, "no overrun detected" +end + +function PrintData() + print("Timestamp\tVoltage\tCurrent") + for i=1,smua.nvbuffer1.n do + print(smua.nvbuffer1.timestamps[i], smua.nvbuffer2[i], smua.nvbuffer1[i]) + end +end + +function PrintDataDual() + local voltage + local current + print("Timestamp\tVoltage\tCurrent") + for i=1,smua.nvbuffer1.n do + voltage = (smua.nvbuffer2[i] + node[2].smua.nvbuffer2[i])/2 + current = smua.nvbuffer1[i] + node[2].smua.nvbuffer1[i] + print(smua.nvbuffer1.timestamps[i], voltage, current) + end +end + +function CalculateTiming(frequency, dutyCycle) + local pulsePeriod = 1/frequency + local pulseWidth + local measDelay + + -- If duty cycle was a table then we need to create delay lists for the timers + if (type(dutyCycle)=="table") then + pulseWidth = {} + measDelay = {} + for i=1,table.getn(dutyCycle) do + if ((dutyCycle[i] > 99) or (dutyCycle[i] < 0.01)) then + print(string.format("Error: dutyCycle[%d] must be between 0.01% and 99%.", i)) + exit() + end + -- Calculate pulse width from period and duty cycle. Subtract 3us of overhead + pulseWidth[i] = pulsePeriod * (dutyCycle[i]/100) - 3e-6 + -- Set measure delay so measurement happen 10us before the falling edge of the pulse + measDelay[i] = pulseWidth[i] - 10e-6 + end + else -- Duty cycle was a single value so we only need a single delay value for the timers + if ((dutyCycle > 99) or (dutyCycle < 0.01)) then + print("Error: dutyCycle must be between 0.01% and 99%.") + exit() + end + pulseWidth = pulsePeriod * (dutyCycle/100) - 3e-6 + measDelay = pulseWidth - 10e-6 + end + return pulsePeriod, pulseWidth, measDelay +end + +function SimpleRegionCheck(pulseLevel, pulseLimit, dutyCycle, SMUs) + -- This function only serves as a quick check that the entered parameters are + -- within the max allowable duty cycles for the operating regions. This function + -- does not check that the pulse widths are within the maximums as well. + + local pLev = math.abs(pulseLevel) + f = true + msg = "Checks passed." + if ((pulseLimit >= 10e-3) and (pulseLimit <= 10)) then + if ((pLev > 30*SMUs) and (dutyCycle > 35)) then + msg = string.format("Duty Cycle too high for pulse region 5. Duty cycle must be 35%% or less for pulse levels above %dA.", 30*SMUs) + f = false + elseif (((pLev > 20*SMUs) and (pLev <= 30*SMUs)) and (dutyCycle > 50)) then + msg = string.format("Duty Cycle too high for pulse region 2. Duty cycle must be 50%% or less for pulse levels between %dA and %dA.", 20*SMUs, 30*SMUs) + f = false + end + elseif ((pulseLimit > 10) and (pulseLimit <= 20)) then + if ((pLev > 20*SMUs) and (dutyCycle > 10)) then + msg = string.format("Duty Cycle too high for pulse region 6. Duty cycle must be 10%% or less for pulse levels above %dA.", 20*SMUs) + f = false + elseif (((pLev > 10*SMUs) and (pLev <= 20*SMUs)) and (dutyCycle > 40)) then + msg = string.format("Duty Cycle too high for pulse region 3. Duty cycle must be 40%% or less for pulse levels between %dA and %dA.", 10*SMUs, 20*SMUs) + f = false + end + elseif (pulseLimit > 20) and (pulseLimit <= 40) then + if ((pLev > 10*SMUs) and (dutyCycle > 1)) then + msg = string.format("Duty Cycle too high for pulse region 7. Duty cycle must be 1%% or less for pulse levels above %dA.", 10*SMUs) + f = false + elseif (((pLev > 5*SMUs) and (pLev <= 10*SMUs)) and (dutyCycle > 40)) then + msg = string.format("Duty Cycle too high for pulse region 4. Duty cycle must be 40%% or less for pulse levels between %dA and %dA.", 5*SMUs, 10*SMUs) + f = false + end + else + msg = "Error: pulseLimit out of range. pulseLimit must be between 10mV and 40V." + f = false + end + + return f,msg +end + + +--PWM_Test_Single(1, 2, 100, 1, 10, 0) +-- duty = {20, 40, 60, 80, 60, 40, 20, 40, 60} +--PWM_Test_Single(20, 10, 1000, duty, 9, 1e-3) +--PWM_Test_Dual(40, 10, 1000, duty, 9, 1e-3) \ No newline at end of file diff --git a/Instrument_Examples/Series_2600/265xA/2651A_100A_Pulse.tsp b/Instrument_Examples/Series_2600/265xA/2651A_100A_Pulse.tsp new file mode 100644 index 0000000..ffc52e7 --- /dev/null +++ b/Instrument_Examples/Series_2600/265xA/2651A_100A_Pulse.tsp @@ -0,0 +1,466 @@ +--[[ + Title: 2651A 100A Pulse + Description: The purpose of this script is to combine two 2651A + units to produce 100A Pulses. This script assumes you have 2x + 2651A units in a TSP-Link as nodes 1 and 2 and their outputs are + connected in parallel. + + If you are not familiar with high current pulsing, consult Keithley Applications Engineering before attempting this test. It could damage you or your equipment. + + Written By: Keithley Applications Engineering + +]] + +function DualPulseInit() + tsplink.reset() + master = smua + slave = node[2].smua +end + + + +-- DualPulseI(60, 10, 700e-6, 2.8e-3, 1, 4) +function DualPulseI(level, limit, ton, period, numpulse, wiremode) + if (level == nil) then level = 1e-3 end + if (limit == nil) then limit = 1 end + if (ton == nil) then ton = 1e-3 end + if (period == nil) then period = 100e-3 end + if (nplc == nil) then nplc = 0.001 end + if (numpulse == nil) then numpulse = 1 end + if (wiremode == 4) then wiremode = smua.SENSE_REMOTE + else wiremode = smua.SENSE_LOCAL end + measCount = ton * 1.5e6 + + reset() + + -- Configure the Master SMU + --========================= + smua.reset() + smua.source.func = smua.OUTPUT_DCAMPS + smua.sense = wiremode + smua.source.autorangev = 0 + smua.source.autorangei = 0 + smua.source.rangei = level/2 + smua.source.leveli = 0 + smua.source.limitv = limit + + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.autorangev = 0 + smua.measure.autorangei = 0 + smua.measure.rangev = limit + smua.measure.rangei = level/2 +-- smua.measure.nplc = nplc + smua.measure.adc = smua.ADC_FAST + smua.measure.interval = 0 + smua.measure.count = measCount +-- smua.measure.delay = (ton - ((1/60) * smua.measure.nplc)) - 60e-6 + + smua.nvbuffer1.clear() + smua.nvbuffer1.appendmode = 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.appendmode = 1 + + -- Configure Trigger Model + -------------------------- + tsplink.trigger[1].clear() + tsplink.trigger[1].mode = tsplink.TRIG_EITHER + + -- Configure timers + -- Timer 1 controls the pulse period + trigger.timer[1].count = numpulse - 1 + trigger.timer[1].delay = period + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = smua.trigger.ARMED_EVENT_ID + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = ton + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = trigger.timer[1].EVENT_ID + + -- Configure SMU Trigger Model for Sweep + smua.trigger.source.lineari(level/2, level/2, 2) + smua.trigger.source.limitv = limit + smua.trigger.measure.action = smua.ASYNC--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 = numpulse + smua.trigger.arm.stimulus = tsplink.trigger[1].EVENT_ID + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + --======================== + -- Configure the Salve SMU + --======================== + slave.reset() + slave.source.func = slave.OUTPUT_DCAMPS + slave.sense = slave.SENSE_LOCAL--wiremode + slave.source.autorangev = 0 + slave.source.autorangei = 0 + slave.source.rangei = level/2 + slave.source.leveli = 0 + slave.source.limitv = limit + + slave.measure.autozero = slave.AUTOZERO_ONCE + slave.measure.autorangev = 0 + slave.measure.autorangei = 0 + slave.measure.rangev = limit + slave.measure.rangei = level/2 +-- slave.measure.nplc = nplc + slave.measure.adc = slave.ADC_FAST + slave.measure.interval = 0 + slave.measure.count = measCount +-- slave.measure.delay = (ton - ((1/60) * slave.measure.nplc)) - 60e-6 + + slave.nvbuffer1.clear() + slave.nvbuffer1.appendmode = 1 + slave.nvbuffer2.clear() + slave.nvbuffer2.appendmode = 1 + + -- Configure Trigger Model + -------------------------- + node[2].tsplink.trigger[1].clear() + node[2].tsplink.trigger[1].mode = node[2].tsplink.TRIG_EITHER + -- Configure timers + -- Timer 1 controls the pulse period + node[2].trigger.timer[1].count = numpulse - 1 + node[2].trigger.timer[1].delay = period + node[2].trigger.timer[1].passthrough = true + node[2].trigger.timer[1].stimulus = node[2].smua.trigger.ARMED_EVENT_ID + + -- Timer 2 controls the pulse width + node[2].trigger.timer[2].count = 1 + node[2].trigger.timer[2].delay = ton + node[2].trigger.timer[2].passthrough = false + node[2].trigger.timer[2].stimulus = node[2].trigger.timer[1].EVENT_ID + + -- Configure SMU Trigger Model for Sweep + slave.trigger.source.lineari(level/2, level/2, 2) + slave.trigger.source.limitv = limit + slave.trigger.measure.action = slave.ASYNC--slave.ENABLE + slave.trigger.measure.iv(slave.nvbuffer1, slave.nvbuffer2) + slave.trigger.endpulse.action = slave.SOURCE_IDLE + slave.trigger.endsweep.action = slave.SOURCE_IDLE + slave.trigger.count = numpulse + slave.trigger.arm.stimulus = node[2].tsplink.trigger[1].EVENT_ID + slave.trigger.source.stimulus = node[2].trigger.timer[1].EVENT_ID + slave.trigger.measure.stimulus = node[2].trigger.timer[1].EVENT_ID + slave.trigger.endpulse.stimulus = node[2].trigger.timer[2].EVENT_ID + slave.trigger.source.action = slave.ENABLE + + + smua.source.output = 1 + slave.source.output = 1 + smua.trigger.initiate() + slave.trigger.initiate() + tsplink.trigger[1].assert() + waitcomplete() + smua.source.output = 0 + slave.source.output = 0 + + -- for x=1,smua.nvbuffer1.n do + -- print(smua.nvbuffer1[x], smua.nvbuffer2[x]) + -- end +end + + +-- DualPulseI_Train(48, 10, 200e-6, 1e-3, 5, 4) +function DualPulseI_Train(level, limit, ton, period, numpulse, wiremode) + if (level == nil) then level = 1e-3 end + if (limit == nil) then limit = 1 end + if (ton == nil) then ton = 1e-3 end + if (period == nil) then period = 100e-3 end + if (nplc == nil) then nplc = 0.001 end + if (numpulse == nil) then numpulse = 1 end + if (wiremode == 4) then wiremode = smua.SENSE_REMOTE + else wiremode = smua.SENSE_LOCAL end + + local measCount = 5000 + + reset() + + -- Configure the Master SMU + --========================= + smua.reset() + smua.source.func = smua.OUTPUT_DCAMPS + smua.sense = smua.SENSE_REMOTE + smua.source.autorangev = 0 + smua.source.autorangei = 0 + smua.source.rangei = level/2 + smua.source.leveli = 0 + smua.source.limitv = limit + + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.autorangev = 0 + smua.measure.autorangei = 0 + smua.measure.rangev = limit + smua.measure.rangei = level/2 +-- smua.measure.nplc = nplc + smua.measure.adc = smua.ADC_FAST + smua.measure.interval = 0 + smua.measure.count = measCount +-- smua.measure.delay = (ton - ((1/60) * smua.measure.nplc)) - 60e-6 + + smua.nvbuffer1.clear() + smua.nvbuffer1.appendmode = 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.appendmode = 1 + + -- Configure Trigger Model + -------------------------- + tsplink.trigger[1].clear() + tsplink.trigger[1].mode = tsplink.TRIG_EITHER + + -- Timer 1 controls the pulse period + trigger.timer[1].count = numpulse + trigger.timer[1].delay = period + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = smua.trigger.ARMED_EVENT_ID + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = ton + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = trigger.timer[1].EVENT_ID + + -- Configure SMU Trigger Model for Sweep + smua.trigger.source.lineari(level/2, level/2, 2) + smua.trigger.source.limitv = limit + smua.trigger.measure.action = smua.ASYNC--smua.ENABLE + smua.trigger.measure.v(smua.nvbuffer1)--, smua.nvbuffer2) + smua.trigger.endpulse.action = smua.SOURCE_IDLE + smua.trigger.endsweep.action = smua.SOURCE_IDLE + smua.trigger.count = numpulse + smua.trigger.arm.stimulus = tsplink.trigger[1].EVENT_ID + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = smua.trigger.ARMED_EVENT_ID + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + --======================== + -- Configure the Salve SMU + --======================== + slave.reset() + slave.source.func = slave.OUTPUT_DCAMPS + slave.sense = wiremode + slave.source.autorangev = 0 + slave.source.autorangei = 0 + slave.source.rangei = level/2 + slave.source.leveli = 0 + slave.source.limitv = limit + + slave.measure.autozero = slave.AUTOZERO_ONCE + slave.measure.autorangev = 0 + slave.measure.autorangei = 0 + slave.measure.rangev = limit + slave.measure.rangei = level/2 +-- slave.measure.nplc = nplc + slave.measure.adc = slave.ADC_FAST + slave.measure.interval = 0 + slave.measure.count = measCount +-- slave.measure.delay = (ton - ((1/60) * slave.measure.nplc)) - 60e-6 + + slave.nvbuffer1.clear() + slave.nvbuffer1.appendmode = 1 + slave.nvbuffer2.clear() + slave.nvbuffer2.appendmode = 1 + + -- Configure Trigger Model + -------------------------- + node[2].tsplink.trigger[1].clear() + node[2].tsplink.trigger[1].mode = node[2].tsplink.TRIG_EITHER + + -- Timer 1 controls the pulse period + node[2].trigger.timer[1].count = numpulse + node[2].trigger.timer[1].delay = period + node[2].trigger.timer[1].passthrough = true + node[2].trigger.timer[1].stimulus = slave.trigger.ARMED_EVENT_ID + + -- Timer 2 controls the pulse width + node[2].trigger.timer[2].count = 1 + node[2].trigger.timer[2].delay = ton + node[2].trigger.timer[2].passthrough = false + node[2].trigger.timer[2].stimulus = node[2].trigger.timer[1].EVENT_ID + + -- Configure SMU Trigger Model for Sweep + slave.trigger.source.lineari(level/2, level/2, 2) + slave.trigger.source.limitv = limit + slave.trigger.measure.action = slave.ASYNC--slave.ENABLE + slave.trigger.measure.v(slave.nvbuffer1)--, slave.nvbuffer2) + slave.trigger.endpulse.action = slave.SOURCE_IDLE + slave.trigger.endsweep.action = slave.SOURCE_IDLE + slave.trigger.count = numpulse + slave.trigger.arm.stimulus = node[2].tsplink.trigger[1].EVENT_ID + slave.trigger.source.stimulus = node[2].trigger.timer[1].EVENT_ID + slave.trigger.measure.stimulus = slave.trigger.ARMED_EVENT_ID + slave.trigger.endpulse.stimulus = node[2].trigger.timer[2].EVENT_ID + slave.trigger.source.action = slave.ENABLE + + + smua.source.output = 1 + slave.source.output = 1 + smua.trigger.initiate() + slave.trigger.initiate() + tsplink.trigger[1].assert() + waitcomplete() + smua.source.output = 0 + slave.source.output = 0 + + -- for x=1,smua.nvbuffer1.n do + -- print(smua.nvbuffer1[x], smua.nvbuffer2[x]) + -- end +end + + + +function PrintData() + for i=1,smua.nvbuffer1.n do + print(smua.nvbuffer1[i]) + end +end + + +-- TriggerTestV(5, 1e-3, 500e-6, 10e-3, 2, 2) +-- This function was meant to try a new method of trigger timing and it doesn't seem +-- to work so don't bother using this function unless you modify it first. +function TriggerTestV(level, limit, ton, period, numpulse, wiremode) + if (level == nil) then level = 1e-3 end + if (limit == nil) then limit = 1 end + if (ton == nil) then ton = 1e-3 end + if (period == nil) then period = 100e-3 end + if (nplc == nil) then nplc = 0.001 end + if (numpulse == nil) then numpulse = 1 end + if (wiremode == 4) then wiremode = smua.SENSE_REMOTE + else wiremode = smua.SENSE_LOCAL end + + DualPulseInit() + reset() + + -- Configure the Master SMU + --========================= + smua.reset() + smua.source.func = smua.OUTPUT_DCAMPS + smua.sense = smua.SENSE_REMOTE + smua.source.autorangev = 0 + smua.source.autorangei = 0 + smua.source.rangev = level + smua.source.levelv = 0 + smua.source.limiti = limit + + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.autorangev = 0 + smua.measure.autorangei = 0 + smua.measure.rangei = limit + smua.measure.rangev = level + smua.measure.nplc = nplc + smua.measure.delay = (ton - ((1/60) * smua.measure.nplc)) - 60e-6 + + smua.nvbuffer1.clear() + smua.nvbuffer1.appendmode = 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.appendmode = 1 + + -- Configure Trigger Model + -------------------------- + tsplink.trigger[1].mode = tsplink.TRIG_RISINGM + tsplink.trigger[1].clear() + tsplink.trigger[1].release() + tsplink.trigger[1].stimulus = trigger.timer[1].EVENT_ID + + -- Timer 1 controls the pulse period + trigger.timer[1].count = numpulse + trigger.timer[1].delay = period + trigger.timer[1].passthrough = false + trigger.timer[1].stimulus = smua.trigger.ARMED_EVENT_ID + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = ton + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = tsplink.trigger[1].EVENT_ID + + -- Configure SMU Trigger Model for Sweep + smua.trigger.source.linearv(level, level, 2) + smua.trigger.source.limiti = limit + 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 = numpulse + smua.trigger.arm.stimulus = display.trigger.EVENT_ID + smua.trigger.source.stimulus = tsplink.trigger[1].EVENT_ID + smua.trigger.measure.stimulus = 0 + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + --======================== + -- Configure the Salve SMU + --======================== + slave.reset() + slave.source.func = slave.OUTPUT_DCVOLTS + slave.sense = wiremode + slave.source.autorangev = 0 + slave.source.autorangei = 0 + slave.source.rangev = level + slave.source.levelv = 0 + slave.source.limiti = limit + + slave.measure.autozero = slave.AUTOZERO_ONCE + slave.measure.autorangev = 0 + slave.measure.autorangei = 0 + slave.measure.rangei = limit + slave.measure.rangev = level + slave.measure.nplc = nplc + slave.measure.delay = (ton - ((1/60) * slave.measure.nplc)) - 60e-6 + + slave.nvbuffer1.clear() + slave.nvbuffer1.appendmode = 1 + slave.nvbuffer2.clear() + slave.nvbuffer2.appendmode = 1 + + -- Configure Trigger Model + -------------------------- + node[2].tsplink.trigger[1].mode = node[2].tsplink.TRIG_RISINGA + node[2].tsplink.trigger[1].clear() + node[2].tsplink.trigger[1].release() + + -- Timer 1 controls the pulse width. (Period is done by the TSP-Link Trigger) + node[2].trigger.timer[1].count = 1 + node[2].trigger.timer[1].delay = period + node[2].trigger.timer[1].passthrough = true + node[2].trigger.timer[1].stimulus = node[2].tsplink.trigger[1].EVENT_ID + + + -- Configure SMU Trigger Model for Sweep + slave.trigger.source.linearv(level, level, 2) + slave.trigger.source.limiti = limit + slave.trigger.measure.action = slave.ENABLE + slave.trigger.measure.iv(slave.nvbuffer1, slave.nvbuffer2) + slave.trigger.endpulse.action = slave.SOURCE_IDLE + slave.trigger.endsweep.action = slave.SOURCE_IDLE + slave.trigger.count = numpulse + slave.trigger.arm.stimulus = 0 + slave.trigger.source.stimulus = node[2].tsplink.trigger[1].EVENT_ID + slave.trigger.measure.stimulus = 0 + slave.trigger.endpulse.stimulus = node[2].trigger.timer[1].EVENT_ID + slave.trigger.source.action = slave.ENABLE + + + smua.source.output = 1 + slave.source.output = 1 + smua.trigger.initiate() + slave.trigger.initiate() +-- delay(0.1) +-- tsplink.trigger[1].assert() + waitcomplete() + smua.source.output = 0 + slave.source.output = 0 + + -- for x=1,smua.nvbuffer1.n do + -- print(smua.nvbuffer1[x], smua.nvbuffer2[x]) + -- end +end + diff --git a/Instrument_Examples/Series_2600/265xA/2651A_100A_RdsOn.tsp b/Instrument_Examples/Series_2600/265xA/2651A_100A_RdsOn.tsp new file mode 100644 index 0000000..eb3343f --- /dev/null +++ b/Instrument_Examples/Series_2600/265xA/2651A_100A_RdsOn.tsp @@ -0,0 +1,256 @@ +--[[ + Title: Combining SMUs for 100A Example + Description: This script is designed to perform an Rds(on)sweep on a power + MOSFET device. It combines two 2651A SMUs in parallel to perform a current + sweep up to 100A. Data collected from the sweep is then returned in a + Microsoft Excel compatible format for plotting and analysis. + Equipment needed: + 2x 2651A + 1x 26xxA + 2x TSP-Link Cable + TSP-Link Configuration: + ----------------------- + Unit | Node # + 2651A #1 | 1 + 2651A #2 | 2 + 26xxA | 3 + Master Node (PC Interface): Node 1 + + If you are not familiar with high current, consult Keithley Applications Engineering before attempting this test. It could damage you or your equipment. + + Written By: Keithley Applications Engineering +]] +--[[ + Name: DualSmuRdson(gateLevel, dstart, dstop, dsteps, pulseWidth, pulsePeriod, pulseLimit) + Description: This function uses two 2651A SMUs to perform a pulsed Rds(on) + sweep with currents up to 100A. + Parameters: + gateLevel: The gate level to be used during the sweep + dstart: The starting current level of the drain sweep + dstop: The ending current level of the drain sweep + dsteps: The number of steps in the drain sweep + pulseWidth: The width of the drain pulse in seconds + pulsePeriod:The time from the start of one drain pulse to + the next in seconds + pulseLimit: The voltage limit of the drain pulse in volts + Note: Actual pulse limit will be 10% lower than setting + to protect SMUs in a compliance condition + + Example Usage: + DualSmuRdson(10, 1, 100, 100, 500e-6, 50e-3, 10) +]] +function DualSmuRdson(gateLevel, dstart, dstop, dsteps, pulseWidth, pulsePeriod, pulseLimit) + tsplink.reset(3) -- Verify that at least three nodes are present + reset() + -- Configure 2651A #1 (Drain SMU 1) + ----------------------------------- + smua.reset() + smua.source.func = smua.OUTPUT_DCAMPS + smua.sense = smua.SENSE_REMOTE + smua.source.offmode = smua.OUTPUT_NORMAL + smua.source.offfunc = smua.OUTPUT_DCVOLTS + smua.source.offlimiti = 1e-3 -- Set off limit + -- SMU #1 will be a 0V voltage source with 1mA limit when its + -- output is turned off. SMU #2 will be a 0A current source with + -- a 10V limit when the output is turned off. These settings keep + -- the parallel combination safe in case one SMU is turned off. + smua.source.rangei = math.max(math.abs(dstart / 2), math.abs(dstop / 2)) + smua.source.leveli = 0 -- Sets the DC bias level + smua.source.limitv = 9 -- Sets the DC bias limit + -- SMU #2 will have a voltage limit of 10V. By setting the voltage + -- limit 10% lower than that of SMU #2, we can ensure that only + -- one of the two SMUs will ever go into compliance and become a + -- voltage source. This is desirable, because if both SMUs went + -- into compliance, there would be two voltage sources in parallel, + -- which is an unsafe condition. + smua.measure.nplc = 0.005 + smua.measure.rangev = pulseLimit + + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.delay = pulseWidth - ((1 / localnode.linefreq) * smua.measure.nplc) - 20e-6 + -- Set the delay so that the measurement is near the end of the pulse + + -- Prepare the reading buffers + smua.nvbuffer1.clear() + smua.nvbuffer1.collecttimestamps = 1 + smua.nvbuffer1.collectsourcevalues = 1 + smua.nvbuffer1.fillmode = smua.FILL_ONCE + smua.nvbuffer2.clear() + smua.nvbuffer2.collecttimestamps = 1 + smua.nvbuffer2.collectsourcevalues = 1 + smua.nvbuffer2.fillmode = smua.FILL_ONCE + + -- Configure TSP-Link Trigger 1 + tsplink.trigger[1].clear() + tsplink.trigger[1].mode = tsplink.TRIG_FALLING + tsplink.trigger[1].stimulus = trigger.timer[1].EVENT_ID + -- TSP-Link Trigger 1 signals 2651A #2 to pulse + + -- Timer 1 controls the pulse period by triggering the pulse to begin + trigger.timer[1].count = dsteps - 1 + trigger.timer[1].delay = pulsePeriod + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = smua.trigger.ARMED_EVENT_ID + trigger.timer[1].clear() + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = pulseWidth - 3e-6 + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = smua.trigger.SOURCE_COMPLETE_EVENT_ID + trigger.timer[2].clear() + + -- Configure SMU Trigger Model for Sweep + -- Each unit will source half the current, so divide the start + -- and stop values by 2 + smua.trigger.source.lineari(dstart / 2, dstop / 2, dsteps) + smua.trigger.source.limitv = pulseLimit - (pulseLimit * 0.1) + -- Again, keep the limit SMU #1 lower than the limit of SMU #2 + -- to prevent parallel V-sources + smua.trigger.measure.iv(smua.nvbuffer1, smua.nvbuffer2) + smua.trigger.measure.action = smua.ENABLE + -- Return to the bias level at the end of the pulse/sweep + smua.trigger.endpulse.action = smua.SOURCE_IDLE + smua.trigger.endsweep.action = smua.SOURCE_IDLE + smua.trigger.count = dsteps + smua.trigger.arm.stimulus = 0 + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = 0 + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + -- Configure 2651A #2 (Drain SMU 2) + ----------------------------------- + node[2].smua.reset() + node[2].smua.source.func = node[2].smua.OUTPUT_DCAMPS + node[2].smua.sense = node[2].smua.SENSE_REMOTE + node[2].smua.source.offmode = node[2].smua.OUTPUT_NORMAL + node[2].smua.source.offfunc = node[2].smua.OUTPUT_DCAMPS + node[2].smua.source.offlimitv = 10 -- Set off limit + -- SMU will be a 0A current source with 10V limit when output is turned off + node[2].smua.source.rangei = math.max(math.abs(dstart / 2), math.abs(dstop / 2)) + node[2].smua.source.leveli = 0 -- Sets the DC bias level + node[2].smua.source.limitv = 10 -- Sets the DC bias limit + node[2].smua.measure.nplc = 0.005 + node[2].smua.measure.rangev = pulseLimit + node[2].smua.measure.autozero = node[2].smua.AUTOZERO_ONCE + node[2].smua.measure.delay = (pulseWidth - ((1 / node[2].linefreq) * node[2].smua.measure.nplc)) - 20e-6 + + -- Set the delay so that the measurement is near the end of the pulse + -- Prepare the reading buffers + node[2].smua.nvbuffer1.clear() + node[2].smua.nvbuffer1.collecttimestamps = 1 + node[2].smua.nvbuffer1.collectsourcevalues = 1 + node[2].smua.nvbuffer1.fillmode = node[2].smua.FILL_ONCE + node[2].smua.nvbuffer2.clear() + node[2].smua.nvbuffer2.collecttimestamps = 1 + node[2].smua.nvbuffer2.collectsourcevalues = 1 + node[2]. smua.nvbuffer2.fillmode = node[2].smua.FILL_ONCE + + -- Configure TSP-Link Trigger 1 + node[2].tsplink.trigger[1].clear() + node[2].tsplink.trigger[1].mode = node[2].tsplink.TRIG_FALLING + + -- Timer 1 controls the pulse width + node[2].trigger.timer[1].count = 1 + node[2].trigger.timer[1].delay = pulseWidth - 3e-6 + node[2].trigger.timer[1].passthrough = false + node[2].trigger.timer[1].stimulus = node[2].smua.trigger.SOURCE_COMPLETE_EVENT_ID + node[2].trigger.timer[1].clear() + + -- Configure SMU Trigger Model for Sweep + node[2].smua.trigger.source.lineari(dstart / 2, dstop / 2, dsteps) + node[2].smua.trigger.source.limitv = pulseLimit + node[2].smua.trigger.measure.iv(node[2].smua.nvbuffer1, node[2].smua.nvbuffer2) + node[2].smua.trigger.measure.action = node[2].smua.ENABLE + + -- Return the output to the bias level at the end of the pulse/sweep + node[2].smua.trigger.endpulse.action = node[2].smua.SOURCE_IDLE + node[2].smua.trigger.endsweep.action = node[2].smua.SOURCE_IDLE + node[2].smua.trigger.count = dsteps + node[2].smua.trigger.arm.stimulus = 0 + node[2].smua.trigger.source.stimulus = node[2].tsplink.trigger[1].EVENT_ID + node[2].smua.trigger.measure.stimulus = 0 + node[2].smua.trigger.endpulse.stimulus = node[2].trigger.timer[1].EVENT_ID + node[2].smua.trigger.source.action = node[2].smua.ENABLE + + -- Configure the 26xxA (Gate SMU) + --------------------------------- + node[3].smua.reset() + node[3].smua.source.func = node[3].smua.OUTPUT_DCVOLTS + node[3].smua.sense = node[3].smua.SENSE_REMOTE + node[3].smua.source.levelv = gateLevel + node[3].smua.source.highc = node[3].smua.ENABLE + -- If you find your gate oscillating even with a dampening resistor + -- in place, try enabling high-C mode to help stabilize the gate. + + -- Prepare the reading buffers + node[3].smua.nvbuffer1.clear() + node[3].smua.nvbuffer1.collectsourcevalues = 1 + if node[3].smua.nvbuffer1.fillmode ~= nil then + node[3].smua.nvbuffer1.fillmode = node[3].smua.FILL_ONCE + end + node[3].smua.nvbuffer2.clear() + node[3].smua.nvbuffer2.collectsourcevalues = 1 + if node[3].smua.nvbuffer2.fillmode ~= nil then + node[3].smua.nvbuffer2.fillmode = node[3].smua.FILL_ONCE + end + -------------------------- + -- Ready to begin the test + -------------------------- + -- Outputs on + node[3].smua.source.output = node[3].smua.OUTPUT_ON + node[2].smua.source.output = node[2].smua.OUTPUT_ON + smua.source.output = smua.OUTPUT_ON + if errorqueue.count > 0 then + print(�Errors were encountered�) + reset() + return + end + + -- Give the gate some time to settle before starting the sweep + delay(0.001) + node[3].smua.measure.iv(node[3].smua.nvbuffer1, node[3].smua.nvbuffer2) + -- Start the 2651A #2 trigger model + node[2].smua.trigger.initiate() + -- Start the 2651A #1 trigger model + smua.trigger.initiate() + -- Wait until test is complete + waitcomplete() + -- Outputs off + node[3].smua.source.output = node[3].smua.OUTPUT_OFF + smua.source.output = smua.OUTPUT_OFF + node[2].smua.source.output = node[2].smua.OUTPUT_OFF + -- Print back data + PrintDualSmuRdsonData() +end + +--[[ + Function: PrintDualSmuRdsonData() + Description: + This function processes the data stored in the SMU reading buffers by + function DualSmuRdson() and prints back the individual SMU data and the + combined SMU data and Rds(on) readings in a format that is copy and paste + compatible with Microsoft Excel. +]] +function PrintDualSmuRdsonData() + -- Print the gate SMU readings + print("Gate SMU\r\nSource Value\tVoltage\tCurrent") + print(string.format("%0.2f\t%g\t%g\r\n", node[3].smua.nvbuffer1.sourcevalues[1], node[3].smua.nvbuffer2[1], node[3].smua.nvbuffer1[1])) + + -- Print column headers + print("Timestamp\tSource Value\tVoltage 1\tCurrent 1\tVoltage 2\tCurrent 2\tVoltage\tCurrent\tRds(on)") + -- Loop through the reading buffer printing one row at a time + for i = 1,smua.nvbuffer1.n do + -- Combined Source Level = SMU1 source level + SMU2 source level + sourceLevel = smua.nvbuffer1.sourcevalues[i] + node[2].smua.nvbuffer1.sourcevalues[i] + -- Combined Voltage = Average(SMU1 Voltage reading, SMU2 Voltage reading) + combinedVoltage = (smua.nvbuffer2[i] + node[2].smua.nvbuffer2[i]) / 2 + -- Combined Current = SMU1 Current reading + SMU2 Current reading + combinedCurrent = smua.nvbuffer1[i] + node[2].smua.nvbuffer1[i] + -- Rds(on) = Combined Voltage / Combined Current + rdson = combinedVoltage / combinedCurrent + -- Print a row of data + print(string.format("%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g", smua.nvbuffer1.timestamps[i], sourceLevel, smua.nvbuffer2[i], smua.nvbuffer1[i], node[2].smua.nvbuffer2[i], node[2].smua.nvbuffer1[i], combinedVoltage, combinedCurrent, rdson)) + end +end \ No newline at end of file diff --git a/Instrument_Examples/Series_2600/265xA/2651A_Demo_Script.tsp b/Instrument_Examples/Series_2600/265xA/2651A_Demo_Script.tsp new file mode 100644 index 0000000..f36a6e6 --- /dev/null +++ b/Instrument_Examples/Series_2600/265xA/2651A_Demo_Script.tsp @@ -0,0 +1,605 @@ +--[[ + Title: 2651A Demo Script + Author: David Wyban + Date: 6/29/2010 + + Description: The purpose of this script is to get up and running with the 2651A + as quickly as possible. It contains several functions to output pulses using + either the integrating ADC or the new fast ADC. +]] + +--[[ + Function: PulseI(start, stop, numpulse, limit, ton, period, nplc, wiremode) + Summary: This function outputs current pulses and takes a single measurement + per pulse using the integrating ADC. Set start = stop to output pulses of a + single level. + + Parameters: + start: The current level for the first pulse in the sweep + stop: The current level for the last pulse in the sweep + numpulse: The number of consecutive pulses to output + limit: The voltage limit of the pulses + ton: The width of the pulse in seconds + period: The length of a single pulse cycle in seconds + nplc: The NPLC setting for the integrating ADC to use + wiremode: Lets you select between local and remote sense. Use 2 for local and 4 for remote. + Example Usage: + PulseI(50, 50, 1, 10, 500e-6, 100e-3, 0.01, 4) +]] +function PulseI(start, stop, numpulse, limit, ton, period, nplc, wiremode) + if (start == nil) then start = 1e-3 end + if (stop == nil) then stop = 1e-3 end + if (numpulse == nil) then numpulse = 1 end + if (limit == nil) then limit = 1 end + if (ton == nil) then ton = 1e-3 end + if (period == nil) then period = 100e-3 end + if (nplc == nil) then nplc = 0.001 end + if (wiremode == 4) then wiremode = smua.SENSE_REMOTE + else wiremode = smua.SENSE_LOCAL end + + reset() + smua.reset() + smua.source.func = smua.OUTPUT_DCAMPS + smua.sense = wiremode + smua.source.autorangev = 0 + smua.source.autorangei = 0 + smua.source.rangei = math.max(math.abs(start), math.abs(stop)) + smua.source.leveli = 0 + smua.source.limitv = 1 + + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.autorangev = 0 + smua.measure.autorangei = 0 + smua.measure.rangev = limit + smua.measure.rangei = math.max(math.abs(start), math.abs(stop)) + smua.measure.nplc = nplc + smua.measure.delay = (ton - ((1/60) * smua.measure.nplc)) - 60e-6 + + smua.nvbuffer1.clear() + smua.nvbuffer1.appendmode = 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.appendmode = 1 + + -- Configure the Pulsed Sweep setup + --============================= + + -- Configure timers + -- Timer 1 controls the pulse period + trigger.timer[1].count = numpulse + trigger.timer[1].delay = period + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = smua.trigger.ARMED_EVENT_ID + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = ton + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = trigger.timer[1].EVENT_ID + + -- Configure SMU Trigger Model for Sweep + smua.trigger.source.lineari(start, stop, (numpulse >= 2) and numpulse or 2) + smua.trigger.source.limitv = limit + 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 = numpulse + smua.trigger.arm.stimulus = 0 + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = 0 + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + smua.source.output = 1 + smua.trigger.initiate() + waitcomplete() + smua.source.output = 0 + + PrintData2() +end + + + +--[[ + Function: PulseIFast(start, stop, numpulse, limit, ton, period, measCount, wiremode) + Summary: This function outputs current pulses and takes a measurements using the + high speed adc. The high speed ADC will be configured for a 1 MS/s sample rate. + Set start = stop to output pulses of a single level. + + Parameters: + start: The current level for the first pulse in the sweep + stop: The current level for the last pulse in the sweep + numpulse: The number of consecutive pulses to output + limit: The voltage limit of the pulse + ton: The width of the pulse in seconds + period: The length of a single pulse cycle in seconds + measCount: Sets th number of samples to take per pulse + wiremode: Lets you select between local and remote sense. Use 2 for local and 4 for remote. + Example Usage: + PulseIFast(0, 50, 6, 10, 500e-6, 100e-3, 600, 4) + PulseIFast(50, 50, 1, 10, 300e-6, 100e-3, 400, 4) + PulseIFast(50, 50, 1, 10, 200e-6, 100e-3, 300, 4) +]] + +function PulseIFast(start, stop, numpulse, limit, ton, period, measCount, wiremode) + if (start == nil) then start = 1e-3 end + if (stop == nil) then stop = 1e-3 end + if (numpulse == nil) then numpulse = 1 end + if (limit == nil) then limit = 1 end + if (ton == nil) then ton = 1e-3 end + if (period == nil) then period = 100e-3 end + if (measCount == nil) then measCount = ton*1e6 + 100 end + if (wiremode == 4) then wiremode = smua.SENSE_REMOTE + else wiremode = smua.SENSE_LOCAL end + + reset() + smua.reset() + smua.source.func = smua.OUTPUT_DCAMPS + smua.sense = wiremode + smua.source.autorangev = 0 + smua.source.autorangei = 0 + smua.source.rangei = math.max(math.abs(start), math.abs(stop)) + smua.source.leveli = 0 + smua.source.limitv = limit + + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.autorangev = 0 + smua.measure.autorangei = 0 + smua.measure.rangev = limit + smua.measure.rangei = math.max(math.abs(start), math.abs(stop)) + smua.measure.nplc = 0.001 + smua.measure.interval = 0 + smua.measure.adc = smua.ADC_FAST + smua.measure.count = measCount + + smua.nvbuffer1.clear() + smua.nvbuffer1.appendmode = 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.appendmode = 1 + + -- Configure the Pulsed Sweep setup + --============================= + + -- Configure timers + -- Timer 1 controls the pulse period + trigger.timer[1].count = numpulse + trigger.timer[1].delay = period + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = smua.trigger.ARMED_EVENT_ID + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = ton + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = trigger.timer[1].EVENT_ID + + -- Configure SMU Trigger Model for Sweep + smua.trigger.source.lineari(start, stop, (numpulse >= 2) and numpulse or 2) + smua.trigger.source.limitv = limit + smua.trigger.measure.action = smua.ASYNC + 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 = numpulse + smua.trigger.arm.stimulus = 0 + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + smua.source.output = 1 + smua.trigger.initiate() + waitcomplete() + smua.source.output = 0 + + PrintData2() +end + + + +--[[ + Function: PulseV(start, stop, numpulse, limit, ton, period, nplc, wiremode) + Summary: This function outputs voltage pulses and takes a single measurement + per pulse using the integrating ADC. Set start = stop to output pulses of a + single level. + + Parameters: + start: The voltage level for the first pulse in the sweep + stop: The voltage level for the last pulse in the sweep + numpulse: The number of consecutive pulses to output + limit: The current limit of the pulse + ton: The width of the pulse in seconds + period: The length of a single pulse cycle in seconds + nplc: The NPLC setting for the integrating ADC to use + wiremode: Lets you select between local and remote sense. Use 2 for local and 4 for remote. + Example Usage: + PulseV(7, 7, 1, 50, 500e-6, 100e-3, 0.01, 4) +]] +function PulseV(start, stop, numpulse, limit, ton, period, nplc, wiremode) + if (start == nil) then start = 1 end + if (stop == nil) then stop = 1 end + if (numpulse == nil) then numpulse = 1 end + if (limit == nil) then limit = 1e-3 end + if (ton == nil) then ton = 1e-3 end + if (period == nil) then period = 100e-3 end + if (nplc == nil) then nplc = 0.001 end + if (wiremode == 4) then wiremode = smua.SENSE_REMOTE + else wiremode = smua.SENSE_LOCAL end + + reset() + smua.reset() + smua.source.func = smua.OUTPUT_DCVOLTS + smua.sense = wiremode + smua.source.autorangev = 0 + smua.source.autorangei = 0 + smua.source.rangev = math.max(math.abs(start), math.abs(stop)) + smua.source.levelv = 0 + smua.source.limiti = 5 + + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.autorangev = 0 + smua.measure.autorangei = 0 + smua.measure.rangev = math.max(math.abs(start), math.abs(stop)) + smua.measure.rangei = limit + smua.measure.nplc = nplc + smua.measure.delay = (ton - ((1/60) * smua.measure.nplc)) - 60e-6 + + smua.nvbuffer1.clear() + smua.nvbuffer1.appendmode = 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.appendmode = 1 + + -- Configure the Pulsed Sweep setup + --============================= + + -- Configure timers + -- Timer 1 controls the pulse period + trigger.timer[1].count = numpulse + trigger.timer[1].delay = period + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = smua.trigger.ARMED_EVENT_ID + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = ton + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = trigger.timer[1].EVENT_ID + + -- Configure SMU Trigger Model for Sweep + smua.trigger.source.linearv(start, stop, (numpulse >= 2) and numpulse or 2) + smua.trigger.source.limiti = limit + 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 = numpulse + smua.trigger.arm.stimulus = 0 + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = 0 + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + smua.source.output = 1 + smua.trigger.initiate() + waitcomplete() + smua.source.output = 0 + + PrintData2() +end + + + +--[[ + Function: PulseVFast(start, stop, numpulse, limit, ton, period, measCount, wiremode) + Summary: This function outputs voltage pulses and takes a measurements using the + high speed adc. The high speed ADC will be configured for a 1 MS/s sample rate. + Set start = stop to output pulses of a single level. + + Parameters: + start: The voltage level for the first pulse in the sweep + stop: The voltage level for the last pulse in the sweep + numpulse: The number of consecutive pulses to output + limit: The current limit of the pulse + ton: The width of the pulse in seconds + period: The length of a single pulse cycle in seconds + measCount: Sets th number of samples to take per pulse + wiremode: Lets you select between local and remote sense. Use 2 for local and 4 for remote. + Example Usage: + PulseVFast(0, 7, 8, 50, 500e-6, 100e-3, 600, 4) + PulseVFast(7, 7, 1, 50, 300e-6, 100e-3, 400, 4) + PulseVFast(7, 7, 1, 50, 200e-6, 100e-3, 300, 4) +]] +function PulseVFast(start, stop, limit, ton, period, measCount, numpulse, wiremode) + if (start == nil) then start = 1 end + if (stop == nil) then stop = 1 end + if (limit == nil) then limit = 1 end + if (ton == nil) then ton = 1e-3 end + if (period == nil) then period = 100e-3 end + if (measCount == nil) then measCount = ton*1e6 + 100 end + if (numpulse == nil) then numpulse = 1 end + if (wiremode == 4) then wiremode = smua.SENSE_REMOTE + else wiremode = smua.SENSE_LOCAL end + + reset() + smua.reset() + smua.source.func = smua.OUTPUT_DCVOLTS + smua.sense = wiremode + smua.source.autorangev = 0 + smua.source.autorangei = 0 + smua.source.rangev = math.max(math.abs(start), math.abs(stop)) + smua.source.levelv = 0 + smua.source.limiti = 5 + + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.autorangev = 0 + smua.measure.autorangei = 0 + smua.measure.rangei = limit + smua.measure.rangev = math.max(math.abs(start), math.abs(stop)) + smua.measure.nplc = 0.001 + smua.measure.interval = 0 + smua.measure.adc = smua.ADC_FAST + smua.measure.count = measCount + + smua.nvbuffer1.clear() + smua.nvbuffer1.appendmode = 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.appendmode = 1 + + -- Configure the Pulsed Sweep setup + --============================= + + -- Configure timers + -- Timer 1 controls the pulse period + trigger.timer[1].count = numpulse + trigger.timer[1].delay = period + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = smua.trigger.ARMED_EVENT_ID + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = ton + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = trigger.timer[1].EVENT_ID + + -- Configure SMU Trigger Model for Sweep + smua.trigger.source.linearv(start, stop, (numpulse >= 2) and numpulse or 2) + smua.trigger.source.limiti = limit + smua.trigger.measure.action = smua.ASYNC + 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 = numpulse + smua.trigger.arm.stimulus = 0 + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + smua.source.output = 1 + smua.trigger.initiate() +-- waitcomplete() + local ovr = false + local msg = "" + while ((status.operation.sweeping.condition ~= 0) and (ovr == false)) do + ovr, msg = CheckForOverRun() + end + if (ovr == true) then smua.abort() print(msg) end + + smua.source.output = 0 + + PrintData2() +end + + + +--[[ + Function: SweepFET(gstart, gstop, gsteps, start, stop, steps, ton, period, limit, nplc) + Summary: This function performs a IV test on a FET, IGBT or other transitor type semiconductor + device. This function will collect the data necessary to generate a series of IV curves for + varying gate voltages. + This function requires smua of a 2602A to connect to the gate of the device while the 2651A + connects to the drain. The 2602A should be configured as node 2 while the 2651A is configured + as node 1. The 2651A should be used as the master node. + This function requires 4-Wire connections. + + Parameters: + gtart: The voltage level to set the gate at for the first IV curve + gstop: The voltage level to set the gate at for the last IV curve + gsteps: The number of gate voltage steps in the test sequence (the number of IV curves) + start: The voltage level for the first pulse in the sweep across the drain + stop: The voltage level for the last pulse in the sweep across the drain + steps: The number of steps in the sweep across the drain + ton: The width of the drain pulse in seconds + period: The length of a single pulse cycle in seconds + limit: The current limit of the pulses across the drain + nplc: The NPLC setting to use for measurements on the drain + Example Usage: + SweepFET(1, 9, 9, 0, 10, 51, 500e-6, 100e-3, 50, 0.01) + SweepFET(5, 6.5, 7, 0, 20, 21, 500e-6, 100e-3, 10, 0.01) +]] +function SweepFET(gstart, gstop, gsteps, start, stop, steps, ton, period, limit, nplc) + if (gstart == nil) then start = 0 end + if (gstop == nil) then stop = 5 end + if (gsteps == nil) then steps = 6 end + if (start == nil) then start = 0 end + if (stop == nil) then stop = 1 end + if (steps == nil) then steps = 11 end + if (ton == nil) then ton = 1e-3 end + if (period == nil) then period = 100e-3 end + if (limit == nil) then limit = 1 end + if (limit > 20) then dclimit = 20 else dclimit = limit end + if (nplc == nil) then nplc = 0.001 end + + tsplink.reset() + reset() + node[2].reset() + node[2].display.screen = node[2].display.SMUA + node[2].display.smua.measure.func = node[2].display.MEASURE_DCAMPS + gate = node[2].smua + + -- Config Gate SMU + gate.reset() + gate.source.func = gate.OUTPUT_DCVOLTS + stepg = (gstop-gstart)/(gsteps-1) + + -- Prep Buffers + smua.nvbuffer1.clear() + smua.nvbuffer1.appendmode = 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.appendmode = 1 + + for x=1,gsteps do + + -- Config Sweep SMU + smua.reset() + smua.source.func = smua.OUTPUT_DCVOLTS + smua.sense = smua.SENSE_REMOTE + smua.source.autorangev = 0 + smua.source.autorangei = 0 + smua.source.rangev = math.max(math.abs(start), math.abs(stop)) + smua.source.rangei = limit + smua.source.levelv = 0 + smua.source.leveli = 0 + smua.source.limiti = dclimit + smua.source.limitv = 20 + + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.autorangev = 0 + smua.measure.autorangei = 0 + smua.measure.rangev = math.max(math.abs(start), math.abs(stop)) + smua.measure.rangei = limit + smua.measure.nplc = nplc + smua.measure.delay = (ton - ((1/60) * smua.measure.nplc)) - 60e-6 + + -- Configure the Pulsed Sweep setup + --================================= + + -- Configure timers + -- Timer 1 controls the pulse period + trigger.timer[1].count = steps + trigger.timer[1].delay = period + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = smua.trigger.ARMED_EVENT_ID + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = ton + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = trigger.timer[1].EVENT_ID + + -- Configure SMU Trigger Model for Sweep + smua.trigger.source.linearv(start, stop, steps) + smua.trigger.source.limiti = limit + 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 = steps + smua.trigger.arm.stimulus = 0 + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = 0 + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + gate.source.output = 1 + smua.source.output = 1 + + gate.source.levelv = gstart + stepg*(x-1) + gate.measure.i() + smua.trigger.initiate() + waitcomplete() + + gate.source.levelv = 0 + smua.source.output = 0 + gate.source.output = 0 + end + + PrintData2() +end + + + + + +function PrintData() + for i=1,smua.nvbuffer1.n do + print(smua.nvbuffer1[i]) + end +end + +function PrintData2() + print("Current Readings\tVoltage Readings") + for i=1,smua.nvbuffer1.n do + print(smua.nvbuffer1[i], smua.nvbuffer2[i]) + end +end + + + +-- Utility Functions +function CheckForOverRun() + -- Check SMUA Trigger Overruns + if (bit.bitand(status.operation.instrument.smua.trigger_overrun.condition, 2) == 2) then + return true, "smua arm trigger is overrun" + end + if (bit.bitand(status.operation.instrument.smua.trigger_overrun.condition, 4) == 4) then + return true, "smua source trigger is overrun" + end + if (bit.bitand(status.operation.instrument.smua.trigger_overrun.condition, 8) == 8) then + return true, "smua measure trigger is overrun" + end + if (bit.bitand(status.operation.instrument.smua.trigger_overrun.condition, 16) == 16) then + return true, "smua endpulse trigger is overrun" + end + + -- Check SMUB Trigger Overruns + -- if (bit.bitand(status.operation.instrument.smub.trigger_overrun.condition, 2) == 2) then + -- return true, "smub arm trigger is overrun" + -- end + -- if (bit.bitand(status.operation.instrument.smub.trigger_overrun.condition, 4) == 4) then + -- return true, "smub source trigger is overrun" + -- end + -- if (bit.bitand(status.operation.instrument.smub.trigger_overrun.condition, 8) == 8) then + -- return true, "smub measure trigger is overrun" + -- end + -- if (bit.bitand(status.operation.instrument.smub.trigger_overrun.condition, 16) == 16) then + -- return true, "smub endpulse trigger is overrun" + -- end + + -- Check Timers for Overrun + for CFORi=1,8 do + if (trigger.timer[CFORi].overrun == true) then + return true, string.format("Timer %d trigger is overrun", CFORi) + end + end + + -- Check Blenders for Overrun + for CFORi=1,4 do + if (trigger.blender[CFORi].overrun == true) then + return true, string.format("blender %d trigger is overrun", CFORi) + end + end + + -- Check TSP-Link Triggers for Overrun + for CFORi=1,3 do + if (tsplink.trigger[CFORi].overrun == true) then + return true, string.format("TSP-Link trigger %d is overrun", CFORi) + end + end + + -- Check DIGIO Triggers for Overrun + for CFORi=1,14 do + if (digio.trigger[CFORi].overrun == true) then + return true, string.format("digio trigger %d is overrun", CFORi) + end + end + + -- Check LAN Triggers for Overrun + for CFORi=1,8 do + if (lan.trigger[CFORi].overrun == true) then + return true, string.format("LAN trigger %d is overrun", CFORi) + end + end + + return false, "no overrun detected" +end \ No newline at end of file diff --git a/Instrument_Examples/Series_2600/265xA/2651A_IdVgSweep_ParallelDrainSMUstsp b/Instrument_Examples/Series_2600/265xA/2651A_IdVgSweep_ParallelDrainSMUstsp new file mode 100644 index 0000000..646561e --- /dev/null +++ b/Instrument_Examples/Series_2600/265xA/2651A_IdVgSweep_ParallelDrainSMUstsp @@ -0,0 +1,1403 @@ + +--[[ + +This example requires a Model 2612A (or similar)and 2 Model 2651A. One channel (SMU A) of the 2612A is used to drive the gate +of a transistor with a linear staircase voltage sweep. The two outputs of the 2651As are connected and operated in +parallel to drive the drain with one fixed amplitude voltage pulse for each step of the gate sweep. One 2651A is configured as +a V-source, and the other 2561A is configured as an I-source. The I-source is used to effectively increase the current +compliance of the V-source. + +Written By: Keithley Facotry Applications (Al Ivons) + +Requirements (TBD): + + - Sweep the gate from X to Y in Z steps; compliance = TBD. + - Must turn on gate BEFORE pulsing the drain (Check if necessary for Fairchild FCH35N60 + - Pulse the drain with up to 40V max; compliance of both SMUs set to TBD for a total of TBD. Max pulse width is region-dependent. + - Measure Ig and Id + - All SMUs should have their high-C mode enabled (Don't believe this is required, but leave high-C stuff in from original function) + +Some hardware driven requirements: + + - 2651A SMUs will be be used in an Extended Operating Area (EOA). Pulse width and duty cycle are + limited as follows: + - Max pulse width = region-dependent + - Max duty cycle = region-dependent +]] + +--[[ + +The basic approach used is as follows: + 1. Specify the 2651A operating region to be used for test (Ivons renumbered regions for this test); + Region = 0 will automatically pick the region based on Drain Voltage and corresponding I-limit + 2. Specify drain pulse width (PW). If PW is too large for the specified region, it will automatically + be reduced to 98% of the maximum pulse width allowed for the selected operating region (allow 2% timing + margin). Note: Program DOES NOT force margin if user specifies max PW allowed for region. + 3. Specify the duty cycle (DC) for the drain pulse. If DC is too large for the specified region, it + will automatically be reduced to the 98% maximum duty cycle allowed for the selected operating region + (allow 2% timing margin). Note: Program DOES NOT force margin if user specifies max DC allowed for region. + 4. Calculate drain pulse period (PERIOD = PW/DC). + 5. Use PERIOD as the time interval per step for Gate staircase sweep. + 6. Initiate the drain voltage pulse "sweep," which will sit waiting to be synchronized with gate sweep. + 7. Initiate the gate voltage sweep. Note: Gate sweep waits for confirmation that drain pulse completed. + 8. Assert a Dig I/O line to generate digio.trigger[N].EVENT_ID to start the PERIOD timer with passthrough enabled. + 9. Use the PERIOD timer EVENT_ID to start the GATE MEASURE timer with interval set to PULSE DELAY + PW/2, and + also to drive TSP-Link trigger line #1. + 10. Use TSP-Link trigger line #1 to start the PULSE DELAY timer on the drain 2602A with interval set to PERIOD/2. + 11. Use PULSE DELAY timer EVENT_ID to trigger the source actions on both drain SMUs and to start the PW timer with + interval set to PW, and also to start DRAIN MEASURE timer with interval set (at least initially) to PW/2. + 12. Use DRAIN MEASURE timer and GATE MEASURE timer EVENT_IDs to start measure actions on drain and gate SMUs, + respectively. Use NPLC=0.001 to start. + 13. Use PW timer to trigger the endpulse action for drain SMUs. +]] + +--[[ + +This demo is set up to work with Keithley's ACS Basic software. It requires 1 Model 2612A (or similar) set as Node 01, and +2 Model 2651A, one set as Node 02 and the other set as Node 3. +]] +-- It is assumed that TSP Link is reset by ACS Basic prior to running this script +-- tsplink.reset() +-- tsplink_status = tsplink.state +-- print(tsplink_status) + +-- ACS Basic should automatically set up the following global aliases: (NOW DOES NOT APPEAR TO BE TRUE 02/11/2009) +-- Note added by Ivons on 12/16/2010: SMUX is a table; SMUX[1] is node[N].smuY, e.g. SMU1[1] is node[1].smua +-- SMU1 = node[1].smua +-- SMU2 = node[1].smub +-- SMU3 = node[2].smua + + --============================================================================ + -- + -- Pulse Region definitions - From KIPulse Factory Script + -- Ivons added 2651A info on 12/16/2010 + -- Ivons modified 2651A region numbers to be consistent with other models + -- Ivons corrected limit errors for other models based on his table of values + -- + --============================================================================ + + local PULSE_REGIONS = -- These regions are local to the script + { + -- ilimit = max DC and/or PULSE current + -- vlimit = max DC and/or PULSE voltage + -- mbi = max bias (DC) level i + -- mblv = max bias (DC) limit v + -- mbv = max bias (DC) level v + -- mbli = max bias (DC) limit i + + ["2601A"] = + { + [1] = { ilimit= 1.0, vlimit=40, mpw=4200, dc=1.00, mbi=1., mblv=40, mbv=40, mbli=1 }, -- SOA + [2] = { ilimit= 3.0, vlimit= 6, mpw=4200, dc=1.00, mbi=3., mblv= 6, mbv= 6, mbli=3 }, -- SOA + [3] = { ilimit= 1.5, vlimit=40, mpw=100.0e-3, dc=0.25, mbi=3, mblv= 6, mbv=40, mbli=1 }, -- EOA + [4] = { ilimit= 5.0, vlimit=35, mpw= 4.0e-3, dc=0.04, mbi=3, mblv= 6, mbv=40, mbli=1 }, -- EOA + [5] = { ilimit=10.0, vlimit=20, mpw= 1.8e-3, dc=0.01, mbi=3, mblv= 6, mbv=40, mbli=1 } -- EOA + }, + ["2611A"] = + { + [1] = { ilimit= 0.1, vlimit=200, mpw=4200, dc=1.000, mbi=0.1, mblv=200, mbv=200, mbli=0.1 }, -- SOA + [2] = { ilimit= 1.5, vlimit= 20, mpw=4200, dc=1.000, mbi=1.5, mblv=20, mbv= 20, mbli=1.5 }, -- SOA + [3] = { ilimit= 1.0, vlimit=180, mpw=8.5e-3, dc=0.010, mbi=1.0, mblv=20, mbv=200, mbli=0.1 }, -- EOA + [4] = { ilimit= 1.0, vlimit=200, mpw=2.2e-3, dc=0.010, mbi=0.0, mblv=0, mbv=200, mbli=0.1 }, -- EOA + [5] = { ilimit=10.0, vlimit= 5, mpw=1.0e-3, dc=0.022, mbi=1.5, mblv=20, mbv= 20, mbli=1.5 } -- EOA + }, + ["2651A"] = + { + [1] = { ilimit= 5, vlimit=40, mpw=4200, dc=1.00, mbi=5, mblv=40, mbv=40, mbli=5 }, -- SOA + [2] = { ilimit=10, vlimit=20, mpw=4200, dc=1.00, mbi=10, mblv=20, mbv=20, mbli=10 }, -- SOA + [3] = { ilimit=20, vlimit=10, mpw=4200, dc=1.00, mbi=20, mblv=10, mbv=10, mbli=20 }, -- SOA + [4] = { ilimit=10, vlimit=40, mpw=1.5e-3, dc=0.40, mbi=10, mblv=20, mbv=40, mbli=5 }, -- EOA + [5] = { ilimit=20, vlimit=20, mpw=1.5e-3, dc=0.40, mbi=20, mblv=10, mbv=20, mbli=10 }, -- EOA + [6] = { ilimit=50, vlimit=10, mpw=1.0e-3, dc=0.35, mbi=20, mblv=10, mbv=10, mbli=20 }, -- EOA + [7] = { ilimit=50, vlimit=20, mpw=330e-6, dc=0.10, mbi=20, mblv=10, mbv=20, mbli=10 }, -- EOA + [8] = { ilimit=50, vlimit=40, mpw=300e-6, dc=0.01, mbi=20, mblv=10, mbv=40, mbli=5 } -- EOA; was mpw=165e-6; changed on 01/05/2011 + }, + } + + PULSE_REGIONS["2602A"] = PULSE_REGIONS["2601A"] -- Cannot use PULSE_REGIONS.2601A because 2601A is not a proper + PULSE_REGIONS["2612A"] = PULSE_REGIONS["2611A"] -- Lua identifier; must start with a letter; + PULSE_REGIONS["2635A"] = PULSE_REGIONS["2611A"] -- e.g PULSE_REGIONS.A2601 is OK + PULSE_REGIONS["2636A"] = PULSE_REGIONS["2611A"] + + --============================================================================ + -- HELPER FUNCTIONS + --============================================================================ + +function ResetTspLink(Print_TsplinkState) + + local status + local msg + + tsplink.reset() + + if tsplink.state == "online" then + status = true + else + status = false + end --if + + msg = "TSP-Link is "..tsplink.state + + if Print_TsplinkState == true then + print(msg) + end --if + + return status, msg + +end --function ResetTspLink + +function PrintUnitInfo() + + if tsplink.state == "online" then + + for i = 1, 64 do + if node[i] ~= nil then + print() + print("Node Number:|".."node["..i.."]") + print("Model Number:|"..node[i].model) + print("S/N:|"..node[i].serialno) + print("FW Rev:|"..node[i].revision) + end --if + end --for + + else + + print() + print("Node Number:|".."Localnode Only") + print("Model Number:|"..localnode.model) + print("S/N:|"..localnode.serialno) + print("FW Rev:|"..localnode.revision) + + end --if + + print() + +end --function CheckUnitInfo + + ------------------------------------------------------------------------------ + -- + -- CheckPulseRegionI - Borrowed from KIPulse Factory Script (20 Dec 2010) + -- + -- Check pulse region for a current pulse. The bias and level are in Amps, + -- the limit in Volts. + -- + ------------------------------------------------------------------------------ + +function CheckPulseRegionI(bias, level, limit, rg) + local b = math.abs(bias) + local lv = math.abs(level) + local lm = math.abs(limit) + local l = math.max(b, lv) + local r, region + local ret = nil + for r, region in ipairs(rg) do + if (l <= region.ilimit) and (lm <= region.vlimit) then + ret = r + break + end --if + end --for + return ret +end --function CheckPulseRegionI + + ------------------------------------------------------------------------------ + -- + -- CheckPulseRegionV - Borrowed from KIPulse Factory Script (20 Dec 2010) + -- + -- Check pulse region for a voltage pulse. The bias and level are in Volts, + -- the limit in Amps. + -- + ------------------------------------------------------------------------------ + +function CheckPulseRegionV(bias, level, limit, rg) + local b = math.abs(bias) + local lv = math.abs(level) + local lm = math.abs(limit) + local l = math.max(b, lv) + local r, region + local ret = nil + for r, region in ipairs(rg) do + if (l <= region.vlimit) and (lm <= region.ilimit) then + ret = r + break + end --if + end --for + return ret +end --function CheckPulseRegionV + + ------------------------------------------------------------------------------ + -- + -- CheckBiasRegionI - Borrowed from KIPulse Factory Script (20 Dec 2010) + -- + -- Check bias region for a current pulse. Check bias level and limit + -- during bias conditions. Determine the voltage limit to set during bias. + -- The bias is in Amps, the limit in Volts. r is a region. + -- + ------------------------------------------------------------------------------ + +function CheckBiasRegionI(bias, limit, r) + local b = math.abs(bias) + local lm = math.abs(limit) + + if (b > r.mbi) then + return nil + end --if + lm = math.min(lm, r.mblv) + return lm +end --function CheckBiasRegionI + + ------------------------------------------------------------------------------ + -- + -- CheckBiasRegionV - Borrowed from KIPulse Factory Script (20 Dec 2010) + -- + -- Check bias region for voltage pulse. Check bias level and limit + -- during bias conditions. Determine the voltage limit to set during bias. + -- The bias is in Volts, the limit in Amps. r is a region. + ------------------------------------------------------------------------------ + +function CheckBiasRegionV(bias, limit, r) + local b = math.abs(bias) + local lm = math.abs(limit) + + if (b > r.mbv) then + return nil + end --if + lm = math.min(lm, r.mbli) + return lm +end --function CheckBiasRegionV + + ------------------------------------------------------------------------------ + -- + -- CheckRegionLimits - Borrowed from KIPulse Factory Script (20 Dec 2010) + -- + -- Check the pulse region against the specified ON and OFF times to + -- verify pulse width and duty cycle limits. + ------------------------------------------------------------------------------ +--[[ + function CheckRegionLimits(region, ton, tofftable) + local dc + local p = table.getn(tofftable) + local i + + if (ton < 200e-6) then + return false, "Pulse too short" + end --if + + if (ton > region.mpw) then + return false, "Pulse too long" + end --if + + for i=1,p do + if (tofftable[i] < 0) then + return false, "Invalid off time" + end --if + + dc = ton / (ton + tofftable[i]) + if (dc > region.dc) then + return false, "Duty cycle exceeded" + end --if + end --for + + return true, "" + end --function CheckRegionLimits +]] +function CheckRegionLimits_IvonsMod(region, pulse_width, duty_cycle) + --local dc + --local p = table.getn(tofftable) + --local i + + if (pulse_width < 100e-6) then + return false, "Pulse too short" + end --if + + if (pulse_width > region.mpw) then + return false, "Pulse too long" + end --if + + if (duty_cycle > region.dc) then + return false, "Duty cycle exceeded" + end --if + + return true, "" +end --function CheckRegionLimits_IvonsMod + +function TurnOffAll() + node[3].smua.source.output = 0 + node[2].smua.source.output = 0 + node[1].smua.source.output = 0 +end --function TurnOffAll + +function SweepGate_PulseDrain(GateStartV, GateStopV, GateStepV, GateIlimit, DrainSmuMode, DrainSmuRegionIndex, DrainVpulseLevel, DrainIlimit, DrainIsrc_ZeroAmpMode, DrainPulseWidth, DrainDutyCycle, Nplc, EnableHighC, + Digitize, DigInterval, DigCount, PreTrigPercent, CheckParamOnly, VsrcCmplOff, VsrcLagDelay, IsrcPulseWidth, Use2wire) + + -- DrainSmuMode = 1 or 2 + -- 1: Always split current between Drain V-source and I-source + -- 2: Only use Drain I-source if V-source can't supply enough current + -- "else" condition will select mode 1 + + -- DrainSmuRegionIndex = 0, 1, 2, 3, 4, 5, 6, 7, or 8 + -- 0: Will automatically select appropriate SMU operating region based on DrainVpulseLevel and DrainIlimit + -- 1: Region 1: 40V @ 5A or 5A @ 40V (SOA, i.e Safe Operating Area) + -- 2: Region 2: 20V @ 10A or 10A @ 20V (SOA) + -- 3: Region 3: 10V @ 20A or 20A @ 10V (SOA) + -- 4: Region 4: 40V @ 10A or 10A @ 40V (EOA, i.e. Extended Operating Area, Pulsed Operation Only) + -- 5: Region 5: 20V @ 20A or 20A @ 20V (EOA) + -- 6: Region 6: 10V @ 50A or 50A @ 10V (EOA) + -- 7: Region 7: 20V @ 50A or 50A @ 20V (EOA) + -- 8: Region 8: 40V @ 50A or 50A @ 40V (EOA) + -- Entries less than zero are set to zero + -- Entries greater than 8 are set to 8 + -- Other entries set rounded down to closest integer (math.floor) + -- For Index = 0, the function will exit if an appropriate operating region is not found + -- For Index ~= 0, the function will exit if either DrainVpulseLevel or DrainIlimit is outside the specified region + -- Once specified, the same operating region will be used for both the Drain V-source and I-source + -- NOTE: IT IS STILL TO BE DETERMINED IF I CAN FORCE THE SMUS TO OPERATE IN A SPECIFIED OPERATING REGION + + -- DrainIsrc_ZeroAmpMode = "ZeroAmp" or "HighZ" + -- Comes into play if DrainSmuMode = 2, and the required Drain I-source level is zero amps + -- "ZeroAmp": Set I-source I-level = 0 if not required, and turn output on. + -- "HighZ": If Drain I-source is not required, set its off-state impedance = High-Z, and leave its output turned off + -- "else" condition will select "ZeroAmp" mode + + print() + print("GateStartV:|"..GateStartV) + print("GateStopV:|"..GateStopV) + print("GateStepV:|"..GateStepV) + print("GateIlimit:|"..GateIlimit) + print("DrainSmuMode:|"..DrainSmuMode) + print("DrainSmuRegionIndex:|"..DrainSmuRegionIndex) + print("DrainVpulseLevel:|"..DrainVpulseLevel) + print("DrainIlimit:|"..DrainIlimit) + print("DrainIsrc_ZeroAmpMode:|"..DrainIsrc_ZeroAmpMode) + print("DrainPulseWidth:|"..DrainPulseWidth) + print("DrainDutyCycle:|"..DrainDutyCycle) + print("Nplc:|"..Nplc) + print("EnableHighC:|"..tostring(EnableHighC)) + print("Digitize:|"..tostring(Digitize)) + print("DigInterval:|"..tostring(DigInterval)) + print("DigCount:|"..tostring(DigCount)) + print("PreTrigPercent:|"..tostring(PreTrigPercent)) + print("VsrcCmplOff:|"..tostring(VsrcCmplOff)) -- Ivons added on 21 Jan 2011 + print("VsrcLagDelay:|"..VsrcLagDelay) -- Ivons added on 21 Jan 2011 + print("IsrcPulseWidth:|"..IsrcPulseWidth) -- Ivons added on 21 Jan 2011 + print("Use2wire:|"..tostring(Use2wire)) -- Ivons added on 21 Jan 2011 + + if tsplink.state ~= "online" then + if not(ResetTspLink(true)) then + return + end --if + end --if + + -- Create local aliases for NODEs and SMUs + + -- Create aliases for nodes + local N1 = node[1] + local N2 = node[2] + local N3 = node[3] + + -- The following SMUX aliases are equivalent, but not identical, to ACS's SMUX aliases. + -- That is, node[1].smua is the same physical SMU as referenced by ACS SMU1, but ACS SMU1<>node[1].smua + local GATE = N1.smua + local DRAIN_V = N2.smua + local DRAIN_I = N3.smua + + -- Assign the Pulse or Operating Region based on the model number + local GATE_PULSE_REGIONS = PULSE_REGIONS[N1.model] -- Set up for now, but probably won't need because low power only + local DRAIN_PULSE_REGIONS = PULSE_REGIONS[N2.model] -- The same region will be used for both the V-source and the I-source + + -- Specify/check SMU source levels (including passed values) and select operating region(s) + + local drain_smu_mode + local drain_pulse_ilimit + + if DrainSmuMode == 1 then + drain_smu_mode = 1 + drain_pulse_ilimit = DrainIlimit / 2 -- Drain SMUs will each supply maximum of half of necessary current + elseif DrainSmuMode == 2 then + drain_smu_mode = 2 + drain_pulse_ilimit = DrainIlimit -- DRAIN_I SMU will only supply excess current required by DRAIN_V SMU + else + drain_smu_mode = 1 + drain_pulse_ilimit = DrainIlimit / 2 -- Drain SMUs will each supply maximum of half of necessary current + end --if + + print() + print("drain_smu_mode:|"..drain_smu_mode) + print("Max current per Drain SMU:|"..drain_pulse_ilimit) + + local max_index = table.getn(DRAIN_PULSE_REGIONS) + local drain_smu_region_index = math.floor(DrainSmuRegionIndex) + + drain_smu_region_index = math.max(drain_smu_region_index, 0) + drain_smu_region_index = math.min(drain_smu_region_index, max_index) -- was math.min(drain_smu_region_index, 8) + +-- local region_index + if drain_smu_region_index == 0 then -- Automatically select operating region + -- function CheckPulseRegionV(bias, level, limit, rg) + drain_smu_region_index = CheckPulseRegionV(0, DrainVpulseLevel, drain_pulse_ilimit, DRAIN_PULSE_REGIONS) + if (drain_smu_region_index == nil) then + if drain_smu_mode == 1 then + print() + print("Levels incompatible with available pulse regions") + return + else + drain_smu_region_index = CheckPulseRegionV(0, DrainVpulseLevel, drain_pulse_ilimit/2, DRAIN_PULSE_REGIONS) + if (drain_smu_region_index == nil) then + print() + print("Levels incompatible with available pulse regions") + return + end --if + end --if + end --if + end --if + + local DRAIN_SMU_REGION = DRAIN_PULSE_REGIONS[drain_smu_region_index] + print() + print("max_index:|"..max_index) + print("drain_smu_region_index:|"..drain_smu_region_index) + + -- Note: The following inspection does NOT consider any margin for I-src V-limit or V-src I-limit + if drain_smu_mode == 1 then + if (DrainVpulseLevel > DRAIN_SMU_REGION.vlimit) or (drain_pulse_ilimit > DRAIN_SMU_REGION.ilimit) then + print() + print("Voltage and/or current level incompatible with selected operating region") + return + end --if + else + if (DrainVpulseLevel > DRAIN_SMU_REGION.vlimit) then + print() + print("Voltage level incompatible with selected operating region") + return + end --if + if (drain_pulse_ilimit > DRAIN_SMU_REGION.ilimit) and ((drain_pulse_ilimit / 2) > DRAIN_SMU_REGION.ilimit) then + print() + print("Current level incompatible with selected operating region") + return + end --if + end --if + + local DRAIN_V_PULSE_VLIMIT = DRAIN_SMU_REGION.vlimit + local DRAIN_V_PULSE_ILIMIT = DRAIN_SMU_REGION.ilimit + local DRAIN_V_IDLE_VLIMIT = DRAIN_SMU_REGION.mbv + local DRAIN_V_IDLE_ILIMIT = DRAIN_SMU_REGION.mbli + + local DRAIN_I_PULSE_ILIMIT = DRAIN_SMU_REGION.ilimit + local DRAIN_I_PULSE_VLIMIT = DRAIN_SMU_REGION.vlimit + local DRAIN_I_IDLE_ILIMIT = DRAIN_SMU_REGION.mbi + local DRAIN_I_IDLE_VLIMIT = DRAIN_SMU_REGION.mblv + + print() + print("DRAIN_V_PULSE_VLIMIT:|"..DRAIN_V_PULSE_VLIMIT) + print("DRAIN_V_PULSE_ILIMIT:|"..DRAIN_V_PULSE_ILIMIT) + print("DRAIN_V_IDLE_VLIMIT:|"..DRAIN_V_IDLE_VLIMIT) + print("DRAIN_V_IDLE_ILIMIT:|"..DRAIN_V_IDLE_ILIMIT) + print() + print("DRAIN_I_PULSE_ILIMIT:|"..DRAIN_I_PULSE_ILIMIT) + print("DRAIN_I_PULSE_VLIMIT:|"..DRAIN_I_PULSE_VLIMIT) + print("DRAIN_I_IDLE_ILIMIT:|"..DRAIN_I_IDLE_ILIMIT) + print("DRAIN_I_IDLE_VLIMIT:|"..DRAIN_I_IDLE_VLIMIT) + + local drain_v_idle_vlevel = 0 + local drain_v_pulse_vlevel = DrainVpulseLevel + + local drain_i_idle_ilevel = 0 + local drain_i_pulse_ilevel + + if drain_smu_mode == 1 then + drain_i_pulse_ilevel = math.min(drain_pulse_ilimit, DRAIN_I_PULSE_ILIMIT) -- DRAIN_V and DRAIN_I SMUs will both supply current; limit is a redundant check + else + drain_i_pulse_ilevel = math.max(drain_pulse_ilimit - DRAIN_V_PULSE_ILIMIT, 0) -- DRAIN_I SMU will supply current only if DRAIN_V SMU alone can't + end --if + + print() + print("drain_i_pulse_ilevel:|"..drain_i_pulse_ilevel) + + -- Specify/check timing parameters including passed parameters + + local drain_v_pulse_width = DrainPulseWidth + local drain_v_pulse_delay = 0 + + local drain_i_pulse_width = IsrcPulseWidth + local drain_i_pulse_delay = 0 + + if VsrcCmplOff then + drain_v_pulse_width = drain_v_pulse_width + 50e-6 -- In compliance-off mode, there is a 50us holdoff after setting the compliance DAC + -- before before setting the source level DAC; pulse width will be shorter than expected + -- so I am trying to account for this. On the other hand, Greg Roberts believes that the + -- low-level timer that keeps track of source ON-time starts when the complinace DAC is + -- set and the source level DAC, which means that the max pulse width for a given region + -- is effectively reduced by 50us. Greg told me to let him know if I observe something + -- different (this was as of 24 Jan 2011) + drain_i_pulse_delay = drain_i_pulse_delay + 50e-6 -- Want to hold off Isrc so its source level changes at the same time that the Vsrc level changes + end --if + + -- function CheckRegionLimits_IvonsMod(region, pulse_width, duty_cycle) + local flag, msg + local ChkPulseWidth = math.max(drain_v_pulse_width, drain_i_pulse_width) -- was math.max(DrainPulseWidth, IsrcPulseWidth) + -- flag, msg = CheckRegionLimits_IvonsMod(DRAIN_SMU_REGION, DrainPulseWidth, DrainDutyCycle) -- Borrowed from KIPulse Factory Script (20 Dec 2010) + flag, msg = CheckRegionLimits_IvonsMod(DRAIN_SMU_REGION, ChkPulseWidth, DrainDutyCycle) -- Ivons changed from above on 21 Jan 2011 + if (flag == false) then + print() + print(msg) -- msg is a message + return + end --if + + if VsrcLagDelay > 0 then + drain_v_pulse_delay = drain_v_pulse_delay + VsrcLagDelay + elseif VsrcLagDelay < 0 then + drain_i_pulse_delay = drain_i_pulse_delay + math.abs(VsrcLagDelay) + else + -- Do nothing + end --if + + -- Set up drain pulse and related timing parameters + --local drain_pulse_ilimit = DrainIlimit / 2 -- Moved earlier + local period = 1.00 * DrainPulseWidth / DrainDutyCycle -- Change 1.00 to 1.02 for 2% margin + + local pulse_delay = period / 2 -- Start Drain Pulse in middle of Period, i.e. Gate ON time + + if localnode.linefreq ~= 60 then localnode.linefreq = 60 end -- Ivons added added on 23 Dec 2010 beccause of issues with line freq autodetect + -- Clearly this is not appropriate for countries where line freq = 50 Hz + local measure_aperture = Nplc / localnode.linefreq + + local measure_delay = DrainPulseWidth - measure_aperture - 50e-6 -- Measure at end of pulse; allow 50us of overhead at end of pulse + if measure_delay < 0 then + print() + print("NPLC setting too big") + return + end --if + + local digitize_delay + if Digitize == true then -- Ivons added on 05 Jan 2010 + digitize_delay = pulse_delay - DigInterval * math.floor(DigCount * PreTrigPercent / 100) + if digitize_delay < 0 then + print("Pre-Trigger interval is too big") + return + end --if + end --if + + print() + print("DrainPulseWidth:|"..DrainPulseWidth) + print("drain_v_pulse_width:|"..drain_v_pulse_width) -- Ivons added on 24 Jan 2011 + print("drain_i_pulse_width:|"..drain_i_pulse_width) -- Ivons added on 24 Jan 2011 + print("DrainDutyCycle:|"..DrainDutyCycle) -- Fixed on 05 Jan 2010; had been concatenating "period" instead of "DrainDutyCycle" + print("period:|"..period) + print("pulse_delay:|"..pulse_delay) + print("drain_v_pulse_delay:|"..drain_v_pulse_delay) -- Ivons added on 24 Jan 2011 + print("drain_i_pulse_delay:|"..drain_i_pulse_delay) -- Ivons added on 24 Jan 2011 + print("Line Frequency:|"..localnode.linefreq) + print("measure_aperture:|"..measure_aperture) + print("Overhead allowed at end of pulse:|"..50e-6) + print("measure_delay:|"..measure_delay) + + print("digitize_delay:|"..tostring(digitize_delay)) -- Ivons added on 05 Jan 2011; digitize_delay will be nil if Digitize flag ~= true + + --local gate_measure_delay = pulse_delay + pulse_measure_delay + + --Create aliases for the timers used for the sweep + local PERIOD_TIMER = N1.trigger.timer[1] + + local PULSE_DELAY_TIMER = N1.trigger.timer[2] + + local VSRC_LAG_DELAY_TIMER -- Ivons added VSRC_LAG_DELAY_TIMER on 24 Jan 2011 + local ISRC_LAG_DELAY_TIMER -- Ivons added ISRC_LAG_DELAY_TIMER on 24 Jan 2011 + if drain_v_pulse_delay > drain_i_pulse_delay then + VSRC_LAG_DELAY_TIMER = N2.trigger.timer[2] -- Delay start of V-pulse re I-Pulse + ISRC_LAG_DELAY_TIMER = nil + elseif drain_i_pulse_delay > drain_v_pulse_delay then + ISRC_LAG_DELAY_TIMER = N3.trigger.timer[2] -- Delay start of I-pulse re V-Pulse + VSRC_LAG_DELAY_TIMER = nil + else + VSRC_LAG_DELAY_TIMER = nil + ISRC_LAG_DELAY_TIMER = nil + end --if + + local PULSE_WIDTH_TIMER = N1.trigger.timer[3] -- Ivons commented out on 24 Jan 2011, but then decided to keep it running on Node 1 + local DRAIN_V_PULSE_WIDTH_TIMER = N2.trigger.timer[3] + local DRAIN_I_PULSE_WIDTH_TIMER = N3.trigger.timer[3] + + local MEASURE_DELAY_TIMER = N1.trigger.timer[4] + local DIGITIZE_DELAY_TIMER = N1.trigger.timer[5] -- Ivons added on 05 Jan 2011 to support use of ASYNC measurements + --local DRAIN_MEASURE_TIMER = node[2].trigger.timer[3] + --local GATE_MEASURE_TIMER = node[1].trigger.timer[2] + + --Create aliases for the blenders used for the sweep + local OK_TO_MEASURE_BLENDER = N1.trigger.blender[1] + local OK_TO_END_PULSE_BLENDER = N1.trigger.blender[2] + + local ZEROAMP_OFFSTATE_Z + if string.upper(DrainIsrc_ZeroAmpMode) == "HIGHZ" then + ZEROAMP_OFFSTATE_Z = DRAIN_I.OUTPUT_HIGH_Z + else + ZEROAMP_OFFSTATE_Z = DRAIN_I.OUTPUT_NORMAL -- Need 1.0.0RC29 to use DRAIN_I.OUTPUT_ACTIVE_LOAD -- Ivons changed on 21 Jan 2011; was DRAIN_I.OUTPUT_NORMAL + end --if + + -- Check passed parameters + + if EnableHighC == nil then EnableHighC = false end + + -- Set up gate sweep parameters + local gate_max_vmag = math.max(math.abs(GateStartV), math.abs(GateStopV)) + local gate_sweep_npoints = math.abs((GateStopV-GateStartV) / GateStepV + 1) + print() + print("gate_max_vmag:|"..gate_max_vmag) + print("gate_sweep_npoints:|"..gate_sweep_npoints) + + --Reset the system and clear the errorqueue + reset() + errorqueue.clear() -- On the local node + + -- Configure 26XXA Displays + N1.display.screen = display.SMUA -- Display SMU A only + N1.display.smua.measure.func = display.MEASURE_DCVOLTS -- Display the SMU A current reading + N2.display.smua.measure.func = display.MEASURE_DCAMPS -- Display the SMU A current reading + N3.display.smua.measure.func = display.MEASURE_DCVOLTS -- Display the SMU B voltage reading + + -- Setup general SMU source parameters + + --Configure general source parameters for GATE (Gate, 2612A) + if Use2wire == true then + GATE.sense = GATE.SENSE_LOCAL -- Use local (2-wire) sensing + else + GATE.sense = GATE.SENSE_REMOTE -- Use remote (4-wire) sensing + end --if + GATE.source.func = GATE.OUTPUT_DCVOLTS -- DC Volts is default source function + GATE.source.rangev = gate_max_vmag -- Set GATE source voltage range; use fixed range to guarantee timing + GATE.source.limiti = GateIlimit -- Idle compliance limit + GATE.source.delay = 0 -- Source Delay disabled by default + GATE.source.offmode = GATE.OUTPUT_NORMAL -- Set output-off impedance state to NORMAL mode (default setting) + GATE.source.offlimiti = 0.001 -- Default is 1mA +--[[ -- Ivons commented out this block on 18 Jan 2010 + if EnableHighC then + GATE.source.highc = GATE.ENABLE -- Enable HIGH Capacitance mode + GATE.measure.highcrangedelayfactor = 1 + else + GATE.source.highc = GATE.DISABLE + end --if +]] + print() + print("GATE sense mode:|"..GATE.sense) -- Ivons added on 21 Jan 2011 + print("GATE V-source range:|"..GATE.source.rangev) + + --Configure source ranges and limits for DRAIN_V and DRAIN_I SMUs (2651A} + if Use2wire == true then -- Ivons added option for 2-wire for James Niemann test on 20 Jan 2010; had been exclusively 4-wire + DRAIN_V.sense = DRAIN_V.SENSE_LOCAL -- Use local (2-wire) sensing + DRAIN_I.sense = DRAIN_I.SENSE_LOCAL -- Use local (2-wire) sensing + else + DRAIN_V.sense = DRAIN_V.SENSE_REMOTE -- Use remote (4-wire) sensing + DRAIN_I.sense = DRAIN_I.SENSE_REMOTE -- Use remote (4-wire) sensing + end --if + + DRAIN_V.source.func = DRAIN_V.OUTPUT_DCVOLTS -- DC Volts is default source function + DRAIN_I.source.func = DRAIN_I.OUTPUT_DCAMPS -- Use I-source to effectively increase the I-compliance of V-source (DRAIN_V) + +-- local drain_i_pulse_ilevel +-- if drain_smu_mode == 1 then +-- drain_i_pulse_ilevel = math.min(drain_pulse_ilimit, DRAIN_I_PULSE_ILIMIT) -- DRAIN_V and DRAIN_I SMUs will both supply current; limit is a redundant check +-- else +-- drain_i_pulse_ilevel = math.max(drain_pulse_ilimit - DRAIN_V_PULSE_ILIMIT, 0) -- DRAIN_I SMU will supply current only if DRAIN_V SMU alone can't +-- end --if + + DRAIN_V.source.rangev = math.min(DrainVpulseLevel, DRAIN_V_PULSE_VLIMIT) -- Set DRAIN_V source voltage range; limit is a redundant check + DRAIN_V.source.limiti = DRAIN_V_IDLE_ILIMIT -- was math.min(0.1, DRAIN_V_IDLE_ILIMIT) -- Idle compliance limit; this is a redundant check + if VsrcCmplOff then -- Ivons added on 25 Jan 2011 + DRAIN_V.trigger.source.limiti = "off" -- This compliance setting is used when SMU goes into compliance during pulse rise/fall when the compliance circuit can't keep up with the slew rate + else + DRAIN_V.trigger.source.limiti = DRAIN_V_PULSE_ILIMIT -- was drain_pulse_ilimit -- This compliance setting is used to enable EOA operation when needed + end --if + DRAIN_I.source.rangei = DRAIN_I_PULSE_ILIMIT -- was math.max(drain_i_pulse_ilevel, 0.1) -- Ivons added 12/28/2010; don't use I-source range less than 100mA + DRAIN_I.source.limitv = DRAIN_I_IDLE_VLIMIT -- Idle compliance limit + DRAIN_I.trigger.source.limitv = DRAIN_I_PULSE_VLIMIT -- was DRAIN_V.source.rangev -- was math.min(1.2 * math.abs(DrainVpulseLevel), DRAIN_SMU_REGION.vlimit) -- This compliance setting is used to enable EOA operation when needed + + print() + print("DRAIN_V sense mode:|"..DRAIN_V.sense) -- Ivons added on 21 Jan 2011 + print("DRAIN_V V-source range:|"..DRAIN_V.source.rangev) + print("DRAIN_V Idle I-limit:|"..DRAIN_V.source.limiti) -- fixed after Test10 on 29 Dec 2010; was DRAIN_I.source.limiti + print("DRAIN_V Triggered I-limit:|"..DRAIN_V.trigger.source.limiti) + print() +-- print("drain_i_pulse_ilevel:|"..drain_i_pulse_ilevel) + print("DRAIN_I sense mode:|"..DRAIN_I.sense) -- Ivons added on 21 Jan 2011 + print("DRAIN_I I-source range:|"..DRAIN_I.source.rangei) + print("DRAIN_I Idle V-limit:|"..DRAIN_I.source.limitv) -- fixed after Test10 on 29 Dec 2010; was DRAIN_V.source.limitv + print("DRAIN_I Triggered V-limit:|"..DRAIN_I.trigger.source.limitv) + + DRAIN_V.source.delay = 0 -- Zero is default + DRAIN_I.source.delay = 0 -- Zero is default + + local v_fw = string.upper(string.sub(node[2].revision,-4)) -- Ivons added on 25 Jan 2011 + if v_fw >= "RC29" then -- Ivons added this TEMPORARY conditional on 25 Jan 2011; need to remove when final FW is released + DRAIN_V.source.offmode = DRAIN_V.OUTPUT_ACTIVE_LOAD -- Ivons changed on 21 Jan 2011 (requires 1.0.0RC29 FW or later); was DRAIN_V.OUTPUT_NORMAL + else + DRAIN_V.source.offmode = DRAIN_V.OUTPUT_NORMAL -- Set output-off impedance state to NORMAL mode (default setting) + end --if + + local i_fw = string.upper(string.sub(node[3].revision,-4)) -- Ivons added on 25 Jan 2011 + if i_fw >= "RC29" then -- Ivons added this TEMPORARY conditional on 25 Jan 2011; need to remove when final FW is released + DRAIN_I.source.offmode = DRAIN_I.OUTPUT_ACTIVE_LOAD -- Ivons changed on 21 Jan 2011 (requires 1.0.0RC29 FW or later); was DRAIN_V.OUTPUT_NORMAL + else + DRAIN_I.source.offmode = DRAIN_I.OUTPUT_NORMAL -- Set output-off impedance state to NORMAL mode (default setting) + end --if + +--[[ -- Ivons commented out this block on 25 Jan 2011 + DRAIN_V.source.offmode = DRAIN_V.OUTPUT_NORMAL -- DRAIN_V.OUTPUT_ACTIVE_LOAD -- Ivons changed on 21 Jan 2011 (requires 1.0.0RC29 FW or later); was DRAIN_V.OUTPUT_NORMAL -- Set output-off impedance state to NORMAL mode (default setting) + if drain_i_pulse_ilevel == 0 then + -- DRAIN_I.source.offmode = ZEROAMP_OFFSTATE_Z -- Either NORMAL or HIGH-Z depending on value of DrainIsrc_ZeroAmpMode + DRAIN_I.source.offmode = DRAIN_I.OUTPUT_NORMAL -- DRAIN_I.OUTPUT_ACTIVE_LOAD -- Ivons changed on 21 Jan 2011 (requires 1.0.0RC29 FW or later) + else + -- DRAIN_I.source.offmode = DRAIN_I.OUTPUT_NORMAL -- Set output-off impedance state to NORMAL mode (default setting) + DRAIN_I.source.offmode = DRAIN_I.OUTPUT_NORMAL -- DRAIN_I.OUTPUT_ACTIVE_LOAD -- Ivons changed on 21 Jan 2011 (requires 1.0.0RC29 FW or later) + end --if +]] + print() + print("DRAIN_V Output-off Impedance State:|"..DRAIN_V.source.offmode) -- Ivons added on 21 Jan 2011 + print("DRAIN_I Output-off Impedance State:|"..DRAIN_I.source.offmode) + + DRAIN_V.source.offlimiti = 0.001 -- Compliance limit when output is OFF and Off-State Impedance is NORMAL; default is 1mA + DRAIN_I.source.offlimiti = 0.001 -- Compliance limit when output is OFF and Off-State Impedance is NORMAL; default is 1mA + + if EnableHighC then + DRAIN_V.source.highc = DRAIN_V.ENABLE -- Enable HIGH Capacitance mode +-- DRAIN_I.source.highc = DRAIN_I.ENABLE -- Enable HIGH Capacitance mode; Ivons commented out on 17 Jan 2010 + DRAIN_V.measure.highcrangedelayfactor = 1 +-- DRAIN_I.measure.highcrangedelayfactor = 1 -- Ivons commented out on 17 Jan 2010 + else + DRAIN_V.source.highc = DRAIN_V.DISABLE + DRAIN_I.source.highc = DRAIN_I.DISABLE + end --if + + --[[ NOTE: In the following several lines, 50e-6 is an estimate of + measurement overhead. ALL forms of instrument measure + autoranging source must be disabled for this to be valid. This + includes measure autoranging for the source function, which is + counterintuitive because the source parameter can only be + measured on the source range. It has been determined that the + latter requirement is due to a bug and NCM06284 was submitted + on 18 Mar 2010. + + Timing calculations also assume that all source and meaure delays + are zero, and that autozero is disabled before the measurements. + ]] + + -- Setup general SMU measure parameters (e.g. NPLC, Range, etc) and buffers + + --Configure general measure parameters for GATE + GATE.measure.count = 1 -- Number of measurements made when smuX.measure.Y() or + -- smuX.trigger.measure.Y()functions are invoked. Default is 1. +-- GATE.measure.rangei = 100e-6 -- Use 100uA for now + GATE.measure.rangei = GateIlimit -- Set I-measure range (use compliance setting); use fixed range for optimum timing; V-measure range determined by source range +-- GATE.measure.autorangei = GATE.AUTORANGE_ON -- Use autorange until know value to set fixed range for measuring gate current (Note: this causes error -221, Settings Conflict if High-C is enabled) + GATE.measure.autorangev = GATE.AUTORANGE_OFF + GATE.measure.nplc = Nplc -- Set integration time + + print() + print("GATE I-measure range:|"..GATE.measure.rangei) + + GATE.nvbuffer1.clear() + GATE.nvbuffer1.appendmode = 1 + GATE.nvbuffer1.collectsourcevalues = 1 + GATE.nvbuffer1.collecttimestamps = 1 + + GATE.nvbuffer2.clear() + GATE.nvbuffer2.appendmode = 1 + GATE.nvbuffer2.collectsourcevalues = 1 + GATE.nvbuffer2.collecttimestamps = 1 + + --Configure general measure parameters for DRAIN_V + + DRAIN_V.measure.rangei = DRAIN_V_PULSE_ILIMIT -- was drain_pulse_ilimit -- Set I-measure range (use compliance setting); use fixed range for optimum timing; V-measure range determined by source range + DRAIN_V.measure.autorangev = DRAIN_V.AUTORANGE_OFF -- Added on 23 Dec 2010 because buffer status indicated that autorange occured for source readback + + print() + print("DRAIN_V I-measure range:|"..DRAIN_V.measure.rangei) + + if Digitize == true then -- Ivons added on 05 Jan 2010 + DRAIN_V.measure.interval = DigInterval + DRAIN_V.measure.count = DigCount + else + DRAIN_V.measure.interval = 0 + DRAIN_V.measure.count = 1 -- Number of measurements made when smuX.measure.Y() or + -- smuX.trigger.measure.Y()functions are invoked. Default is 1. + end --if + + DRAIN_V.measure.nplc = Nplc -- Set integration time; does not apply if using the high-speed ADC + + DRAIN_V.nvbuffer1.clear() + DRAIN_V.nvbuffer1.appendmode = 1 + DRAIN_V.nvbuffer1.collecttimestamps = 1 + if Digitize == true then -- Ivons added on 06 Jan 2011 + DRAIN_V.nvbuffer1.collectsourcevalues = 0 -- was getting Error 5064, "SMU A: Cannot use reading buffer that collects source values" + -- not sure if this is because using high-speed ADC or because using asynchronous measurements + else + DRAIN_V.nvbuffer1.collectsourcevalues = 1 + end --if + + DRAIN_V.nvbuffer2.clear() + DRAIN_V.nvbuffer2.appendmode = 1 + DRAIN_V.nvbuffer2.collecttimestamps = 1 + if Digitize == true then -- Ivons added on 06 Jan 2011 + DRAIN_V.nvbuffer2.collectsourcevalues = 0 -- was getting Error 5064, "SMU A: Cannot use reading buffer that collects source values" + -- not sure if this is because using high-speed ADC or because using asynchronous measurements + else + DRAIN_V.nvbuffer2.collectsourcevalues = 1 + end --if + + --Configure general measure parameters for DRAIN_I + DRAIN_I.measure.autorangei = DRAIN_I.AUTORANGE_OFF -- Added on 23 Dec 2010 because buffer status indicated that autorange occured for source readback + DRAIN_I.measure.rangev = DRAIN_V.source.rangev -- was DRAIN_I_PULSE_VLIMIT -- was wrongly set to DRAIN_I.source.limitv -- Set V-measure range (use compliance setting); was math.min(1.2 * math.abs(DrainVpulseLevel), DRAIN_SMU_REGION.mblv) + -- Use fixed range for optimum timing; I-measure range determined by source range + print("DRAIN_I V-measure range:|"..DRAIN_I.measure.rangev) -- Should I use DRAIN_I_PULSE_VLIMIT instead? + + if Digitize == true then -- Ivons added on 05 Jan 2010 + DRAIN_I.measure.interval = DigInterval + DRAIN_I.measure.count = DigCount + else + DRAIN_I.measure.interval = 0 + DRAIN_I.measure.count = 1 -- Number of measurements made when smuX.measure.Y() or + -- smuX.trigger.measure.Y()functions are invoked. Default is 1. + end --if + + DRAIN_I.measure.nplc = Nplc -- Set integration time; does not apply if using the high-speed ADC + + DRAIN_I.nvbuffer1.clear() + DRAIN_I.nvbuffer1.appendmode = 1 + DRAIN_I.nvbuffer1.collecttimestamps = 1 + if Digitize == true then -- Ivons added on 06 Jan 2011 + DRAIN_I.nvbuffer1.collectsourcevalues = 0 -- was getting Error 5064, "SMU A: Cannot use reading buffer that collects source values" + -- not sure if this is because using high-speed ADC or because using asynchronous measurements + else + DRAIN_I.nvbuffer1.collectsourcevalues = 1 + end --if + + DRAIN_I.nvbuffer2.clear() + DRAIN_I.nvbuffer2.appendmode = 1 + DRAIN_I.nvbuffer2.collecttimestamps = 1 + if Digitize == true then -- Ivons added on 06 Jan 2011 + DRAIN_I.nvbuffer2.collectsourcevalues = 0 -- was getting Error 5064, "SMU A: Cannot use reading buffer that collects source values" + -- not sure if this is because using high-speed ADC or because using asynchronous measurements + else + DRAIN_I.nvbuffer2.collectsourcevalues = 1 + end --if + + --Configure sweep-related and pulse-related source and measure settings + + --[[ + Note: Use the smuX.trigger.source.limitY attribute to perform extended operating area (EOA) pulse mode sweeps. + If the attribute is set to smuX.LIMIT_AUTO, the SMU will use the normal limit setting during sweeping. + If this attribute is set to any other value, the SMU will switch in this limit at the start of the + source action and then switch back to the normal limit setting at the end of the end pulse action. + + When using the EOA, the SMU will automatically start the end pulse action if the SMU is not triggered + before its maximum pulse width. It will also delay the source action if necessary to limit the pulse + duty cycle to stay within the capabilities of the SMU + ]] + + -- Set up gate voltage sweep (Vgs) + GATE.trigger.source.linearv(GateStartV, GateStopV, gate_sweep_npoints) -- linearX (start, stop, points); define the source action for GATE + + GATE.trigger.source.limiti = GATE.LIMIT_AUTO -- Use the idle limit value (previously explicitly was GateIlimit); this compliance setting is used to enable EOA operation when needed + GATE.trigger.source.action = GATE.ENABLE -- Enable source action in GATE trigger model + + GATE.trigger.endpulse.action = GATE.SOURCE_HOLD -- Controls end pulse source action + GATE.trigger.endsweep.action = GATE.SOURCE_HOLD -- Controls source action at end of sweep + -- Stay at last set level (HOLD) or go to idle level (IDLE) + + GATE.trigger.measure.iv(GATE.nvbuffer1,GATE.nvbuffer2) --Configures measurements to be made in subsequent sweep; define measure action for GATE + GATE.trigger.measure.action = GATE.ENABLE -- Enable measure action in GATE trigger model + + -- Use 2 SMUs in parallel to pulse the drain (Vds) at DrainVpulseLevel with DrainIlimit total compliance; + -- drain_pulse_limit = DrainIlimit/2. Pulsewidth is DrainPulseWidth. + + DRAIN_V.trigger.source.listv({DrainVpulseLevel}) -- Do a single point list sweep; define source action for DRAIN_V + -- Actually a list N points will be generated where N = Trigger Count +-- DRAIN_V.trigger.source.limiti = DRAIN_V_PULSE_ILIMIT -- was drain_pulse_ilimit -- This compliance setting is used to enable EOA operation when needed + DRAIN_V.trigger.source.action = DRAIN_V.ENABLE -- Enable source action in DRAIN_V trigger model + + DRAIN_V.trigger.endpulse.action = DRAIN_V.SOURCE_IDLE -- Controls source action at endpulse action block + -- Stay at last set level (HOLD) or go to idle level (IDLE) + -- Set to SOURCE_IDLE to generate pulses (i.e. pulse sweep) + + DRAIN_V.trigger.endsweep.action = DRAIN_V.SOURCE_HOLD -- Controls source action at end of sweep + -- Stay at last set level (HOLD) or go to idle level (IDLE) + + if Digitize == true then -- Ivons added on 05 Jan 2011 + DRAIN_V.measure.adc = DRAIN_V.ADC_FAST -- Use high-speed ADC for measurements; new to Krabs + DRAIN_V.trigger.measure.iv(DRAIN_V.nvbuffer1, DRAIN_V.nvbuffer2) -- Configures measurements to be made in subsequent sweep; define measure action for DRAIN_V + DRAIN_V.trigger.measure.action = DRAIN_V.ASYNC -- Enable ASYNChronous measurements in DRAIN_V trigger model + else + DRAIN_V.measure.adc = DRAIN_V.ADC_INTEGRATE -- Use integrating ADC for measurements; default; option new to Krabs + DRAIN_V.trigger.measure.iv(DRAIN_V.nvbuffer1, DRAIN_V.nvbuffer2) -- Configures measurements to be made in subsequent sweep; define measure action for DRAIN_V + DRAIN_V.trigger.measure.action = DRAIN_V.ENABLE -- Enable synchronous (or normal) measure action in DRAIN_V trigger model + end --if + +-- DRAIN_I.trigger.source.listi({drain_i_pulse_ilevel}) -- Do a single point list sweep; define source action for DRAIN_I + DRAIN_I.trigger.source.lineari(drain_i_pulse_ilevel,drain_i_pulse_ilevel,gate_sweep_npoints) -- Alternate way to generate a list of N identical + -- points, where N = gate_sweep_npoints +-- DRAIN_I.trigger.source.limitv = DRAIN_V.source.rangev -- was math.min(1.2 * math.abs(DrainVpulseLevel), DRAIN_SMU_REGION.vlimit) -- This compliance setting is used to enable EOA operation when needed + DRAIN_I.trigger.source.action = DRAIN_I.ENABLE -- Enable source action in DRAIN_I trigger model + + DRAIN_I.trigger.endpulse.action = DRAIN_I.SOURCE_IDLE -- Controls source action at endpulse action block + DRAIN_I.trigger.endsweep.action = DRAIN_I.SOURCE_HOLD -- Controls source action at end of sweep + + if Digitize == true then -- Ivons added on 05 Jan 2011 + DRAIN_I.measure.adc = DRAIN_I.ADC_FAST -- Use high-speed ADC for measurements; new to Krabs + DRAIN_I.trigger.measure.iv(DRAIN_I.nvbuffer1, DRAIN_I.nvbuffer2) --Configures measurements to be made in subsequent sweep + DRAIN_I.trigger.measure.action = DRAIN_I.ASYNC -- Enable ASYNChronous measurements in DRAIN_I trigger model + else + DRAIN_I.measure.adc = DRAIN_I.ADC_INTEGRATE -- Use integrating ADC for measurements; default; option new to Krabs + DRAIN_I.trigger.measure.iv(DRAIN_I.nvbuffer1, DRAIN_I.nvbuffer2) --Configures measurements to be made in subsequent sweep + DRAIN_I.trigger.measure.action = DRAIN_I.ENABLE -- Enable synchronous (or normal) measure action in DRAIN_I trigger model + end --if + + print() + print("DRAIN_V Pulse I-limit:|"..DRAIN_V.trigger.source.limiti) + print("DRAIN_I Pulse V-limit:|"..DRAIN_I.trigger.source.limitv) + + if CheckParamOnly then + return -99, "Check Parameters Only" -- Function returns -99 if exits here because only checking values of parameters + end --if + + -- Set up Digital I/O and TSP-link Trigger Line(s) and Mode on Each Node + N1.digio.trigger[1].mode = N1.digio.TRIG_FALLING --digio.TRIG_SYNCHRONOUSA + + N1.digio.trigger[2].mode = N1.digio.TRIG_FALLING + N1.digio.trigger[3].mode = N1.digio.TRIG_FALLING + N1.digio.trigger[4].mode = N1.digio.TRIG_FALLING + + N1.digio.trigger[2].stimulus = PERIOD_TIMER.EVENT_ID + N1.digio.trigger[3].stimulus = OK_TO_MEASURE_BLENDER.EVENT_ID + N1.digio.trigger[4].stimulus = OK_TO_END_PULSE_BLENDER.EVENT_ID --GATE.trigger.SOURCE_COMPLETE_EVENT_ID --PULSE_DELAY_TIMER.EVENT_ID + + if Digitize == true then -- Ivons added on 06 Jan 2011 + + -- digio.trigger[N].mode = digio.TRIG_SYNCHRONOUSM + N1.tsplink.trigger[1].mode = N1.tsplink.TRIG_FALLING -- Trigger on falling edge signal + N2.tsplink.trigger[1].mode = N2.tsplink.TRIG_FALLING -- Trigger on falling edge signal + N3.tsplink.trigger[1].mode = N3.tsplink.TRIG_FALLING -- Trigger on falling edge signal + + N1.tsplink.trigger[2].mode = N1.tsplink.TRIG_FALLING -- Trigger on falling edge signal + N2.tsplink.trigger[2].mode = N2.tsplink.TRIG_FALLING -- Trigger on falling edge signal + N3.tsplink.trigger[2].mode = N3.tsplink.TRIG_FALLING -- Trigger on falling edge signal + + N1.tsplink.trigger[3].mode = N1.tsplink.TRIG_FALLING -- Trigger on falling edge signal + N2.tsplink.trigger[3].mode = N2.tsplink.TRIG_FALLING -- Trigger on falling edge signal + N3.tsplink.trigger[3].mode = N3.tsplink.TRIG_FALLING -- Trigger on falling edge signal + + else + + -- digio.trigger[N].mode = digio.TRIG_SYNCHRONOUSM + N1.tsplink.trigger[1].mode = N1.tsplink.TRIG_SYNCHRONOUSM -- was node[1].tsplink.TRIG_FALLING -- Trigger on falling edge signal + N2.tsplink.trigger[1].mode = N2.tsplink.TRIG_SYNCHRONOUSA + N3.tsplink.trigger[1].mode = N3.tsplink.TRIG_SYNCHRONOUSA + + N1.tsplink.trigger[2].mode = N1.tsplink.TRIG_SYNCHRONOUSM -- was node[1].tsplink.TRIG_FALLING -- Trigger on falling edge signal + N2.tsplink.trigger[2].mode = N2.tsplink.TRIG_SYNCHRONOUSA + N3.tsplink.trigger[2].mode = N3.tsplink.TRIG_SYNCHRONOUSA + + N1.tsplink.trigger[3].mode = N1.tsplink.TRIG_FALLING -- Trigger on falling edge signal + N2.tsplink.trigger[3].mode = N2.tsplink.TRIG_FALLING -- Trigger on falling edge signal + N3.tsplink.trigger[3].mode = N3.tsplink.TRIG_FALLING -- Trigger on falling edge signal + + end --if + + --Configure timer attributes + + PERIOD_TIMER.delay = period + PERIOD_TIMER.stimulus = N1.digio.trigger[1].EVENT_ID -- was GATE.trigger.ARMED_EVENT_ID + PERIOD_TIMER.count = gate_sweep_npoints - 1 -- Effectively gate_sweep_npoints because passthrough = true + PERIOD_TIMER.passthrough = true -- Output an "event" immediately when timer is triggered + + PULSE_DELAY_TIMER.delay = pulse_delay + PULSE_DELAY_TIMER.stimulus = PERIOD_TIMER.EVENT_ID -- was node[2].tsplink.trigger[1].EVENT_ID + PULSE_DELAY_TIMER.count = 1 + PULSE_DELAY_TIMER.passthrough = false + + PULSE_WIDTH_TIMER.delay = drain_v_pulse_width -- was DrainPulseWidth; Ivons changed on 21 Jan 2011 and again on 24 Jan 2011 + PULSE_WIDTH_TIMER.stimulus = PULSE_DELAY_TIMER.EVENT_ID + PULSE_WIDTH_TIMER.count = 1 + PULSE_WIDTH_TIMER.passthrough = false + + if VSRC_LAG_DELAY_TIMER then + + VSRC_LAG_DELAY_TIMER.delay = drain_v_pulse_delay + VSRC_LAG_DELAY_TIMER.stimulus = N2.tsplink.trigger[1].EVENT_ID + VSRC_LAG_DELAY_TIMER.count = 1 + VSRC_LAG_DELAY_TIMER.passthrough = false + + DRAIN_V_PULSE_WIDTH_TIMER.delay = drain_v_pulse_width -- was DrainPulseWidth; Ivons changed on 21 Jan 2011 and again on 24 Jan 2011 + DRAIN_V_PULSE_WIDTH_TIMER.stimulus = VSRC_LAG_DELAY_TIMER.EVENT_ID -- was PULSE_DELAY_TIMER.EVENT_ID + DRAIN_V_PULSE_WIDTH_TIMER.count = 1 + DRAIN_V_PULSE_WIDTH_TIMER.passthrough = false + + else + + DRAIN_V_PULSE_WIDTH_TIMER.delay = drain_v_pulse_width -- was DrainPulseWidth; Ivons changed on 21 Jan 2011 and again on 24 Jan 2011 + DRAIN_V_PULSE_WIDTH_TIMER.stimulus = N2.tsplink.trigger[1].EVENT_ID -- was PULSE_DELAY_TIMER.EVENT_ID + DRAIN_V_PULSE_WIDTH_TIMER.count = 1 + DRAIN_V_PULSE_WIDTH_TIMER.passthrough = false + + end --if + + if ISRC_LAG_DELAY_TIMER then + + ISRC_LAG_DELAY_TIMER.delay = drain_i_pulse_delay + ISRC_LAG_DELAY_TIMER.stimulus = N3.tsplink.trigger[1].EVENT_ID + ISRC_LAG_DELAY_TIMER.count = 1 + ISRC_LAG_DELAY_TIMER.passthrough = false + + DRAIN_I_PULSE_WIDTH_TIMER.delay = drain_i_pulse_width -- was DrainPulseWidth; Ivons changed on 21 Jan 2011 and again on 24 Jan 2011 + DRAIN_I_PULSE_WIDTH_TIMER.stimulus = ISRC_LAG_DELAY_TIMER.EVENT_ID -- was PULSE_DELAY_TIMER.EVENT_ID + DRAIN_I_PULSE_WIDTH_TIMER.count = 1 + DRAIN_I_PULSE_WIDTH_TIMER.passthrough = false + + else + + DRAIN_I_PULSE_WIDTH_TIMER.delay = drain_i_pulse_width -- was DrainPulseWidth; Ivons changed on 21 Jan 2011 and again on 24 Jan 2011 + DRAIN_I_PULSE_WIDTH_TIMER.stimulus = N3.tsplink.trigger[1].EVENT_ID -- was PULSE_DELAY_TIMER.EVENT_ID + DRAIN_I_PULSE_WIDTH_TIMER.count = 1 + DRAIN_I_PULSE_WIDTH_TIMER.passthrough = false + + end --if + + if Digitize == true then -- Ivons added on 06 Jan 2011 + + DIGITIZE_DELAY_TIMER.delay = digitize_delay + DIGITIZE_DELAY_TIMER.stimulus = PERIOD_TIMER.EVENT_ID + DIGITIZE_DELAY_TIMER.count = 1 + DIGITIZE_DELAY_TIMER.passthrough = false + + else + + -- Do nothing + + end --if + + MEASURE_DELAY_TIMER.delay = measure_delay -- was pulse_measure_delay + MEASURE_DELAY_TIMER.stimulus = PULSE_DELAY_TIMER.EVENT_ID + MEASURE_DELAY_TIMER.count = 1 + MEASURE_DELAY_TIMER.passthrough = false + + + +-- DRAIN_MEASURE_TIMER.delay = pulse_measure_delay +-- DRAIN_MEASURE_TIMER.stimulus = PULSE_DELAY_TIMER.EVENT_ID +-- DRAIN_MEASURE_TIMER.count = 1 +-- DRAIN_MEASURE_TIMER.passthrough = false + +-- GATE_MEASURE_TIMER.delay = gate_measure_delay +-- GATE_MEASURE_TIMER.stimulus = PERIOD_TIMER.EVENT_ID +-- GATE_MEASURE_TIMER.count = 1 +-- GATE_MEASURE_TIMER.passthrough = false + + if Digitize == true then -- Ivons added on 06 Jan 2011 + + OK_TO_MEASURE_BLENDER.stimulus[1] = MEASURE_DELAY_TIMER.EVENT_ID + OK_TO_MEASURE_BLENDER.stimulus[2] = GATE.trigger.SOURCE_COMPLETE_EVENT_ID + OK_TO_MEASURE_BLENDER.stimulus[3] = 0 -- Disable input; default setting + + OK_TO_END_PULSE_BLENDER.stimulus[1] = PULSE_WIDTH_TIMER.EVENT_ID + OK_TO_END_PULSE_BLENDER.stimulus[2] = GATE.trigger.MEASURE_COMPLETE_EVENT_ID + OK_TO_END_PULSE_BLENDER.stimulus[3] = 0 -- Disable input; default setting + + else + + OK_TO_MEASURE_BLENDER.stimulus[1] = MEASURE_DELAY_TIMER.EVENT_ID + OK_TO_MEASURE_BLENDER.stimulus[2] = GATE.trigger.SOURCE_COMPLETE_EVENT_ID + OK_TO_MEASURE_BLENDER.stimulus[3] = N1.tsplink.trigger[1].EVENT_ID + + OK_TO_END_PULSE_BLENDER.stimulus[1] = PULSE_WIDTH_TIMER.EVENT_ID + OK_TO_END_PULSE_BLENDER.stimulus[2] = GATE.trigger.MEASURE_COMPLETE_EVENT_ID + OK_TO_END_PULSE_BLENDER.stimulus[3] = N1.tsplink.trigger[2].EVENT_ID + + end --if + + if Digitize == true then -- Ivons added on 06 Jan 2011 + + -- Configure TSP-LINK trigger line stimulus + N1.tsplink.trigger[1].stimulus = PULSE_DELAY_TIMER.EVENT_ID -- was PERIOD_TIMER.EVENT_ID + N1.tsplink.trigger[2].stimulus = DIGITIZE_DELAY_TIMER.EVENT_ID + N1.tsplink.trigger[3].stimulus = OK_TO_END_PULSE_BLENDER.EVENT_ID + + else + + -- Configure TSP-LINK trigger line stimulus + N1.tsplink.trigger[1].stimulus = PULSE_DELAY_TIMER.EVENT_ID -- was PERIOD_TIMER.EVENT_ID + N1.tsplink.trigger[2].stimulus = OK_TO_MEASURE_BLENDER.EVENT_ID + N1.tsplink.trigger[3].stimulus = OK_TO_END_PULSE_BLENDER.EVENT_ID + + N2.tsplink.trigger[1].stimulus = DRAIN_V.trigger.SOURCE_COMPLETE_EVENT_ID + N2.tsplink.trigger[2].stimulus = DRAIN_V.trigger.MEASURE_COMPLETE_EVENT_ID + + N3.tsplink.trigger[1].stimulus = DRAIN_I.trigger.SOURCE_COMPLETE_EVENT_ID + N3.tsplink.trigger[2].stimulus = DRAIN_I.trigger.MEASURE_COMPLETE_EVENT_ID + + end --if + + --Configure SMU trigger models + + GATE.trigger.arm.count = 1 + GATE.trigger.arm.stimulus = 0 -- Zero is equivalent to Immediate + GATE.trigger.count = gate_sweep_npoints -- Sweep will execute gate_sweep_npoints source values + GATE.trigger.source.stimulus = PERIOD_TIMER.EVENT_ID + GATE.trigger.measure.stimulus = OK_TO_MEASURE_BLENDER.EVENT_ID -- was GATE_MEASURE_TIMER.EVENT_ID + GATE.trigger.endpulse.stimulus = OK_TO_END_PULSE_BLENDER.EVENT_ID -- was 0 + + DRAIN_V.trigger.arm.count = 1 -- was gate_sweep_npoints -- Also the number of pulses + DRAIN_V.trigger.arm.stimulus = 0 + DRAIN_V.trigger.count = gate_sweep_npoints -- was 1 + if VSRC_LAG_DELAY_TIMER then -- Ivons added on 24 Jan 2011 + DRAIN_V.trigger.source.stimulus = VSRC_LAG_DELAY_TIMER.EVENT_ID -- Ivons changed on 24 Jan 2011; was N2.tsplink.trigger[1].EVENT_ID -- was PULSE_DELAY_TIMER.EVENT_ID + else + DRAIN_V.trigger.source.stimulus = N2.tsplink.trigger[1].EVENT_ID + end --if + DRAIN_V.trigger.measure.stimulus = N2.tsplink.trigger[2].EVENT_ID -- was DRAIN_MEASURE_TIMER.EVENT_ID + DRAIN_V.trigger.endpulse.stimulus = DRAIN_V_PULSE_WIDTH_TIMER.EVENT_ID -- Ivons changed on 24 Jan 2011; was N2.tsplink.trigger[3].EVENT_ID -- was PULSE_WIDTH_TIMER.EVENT_ID + + DRAIN_I.trigger.arm.count = 1 -- was gate_sweep_npoints -- Also the number of pulses + DRAIN_I.trigger.arm.stimulus = 0 + DRAIN_I.trigger.count = gate_sweep_npoints --was 1 + if ISRC_LAG_DELAY_TIMER then -- Ivons added on 24 Jan 2011 + DRAIN_I.trigger.source.stimulus = ISRC_LAG_DELAY_TIMER.EVENT_ID -- Ivons changed on 24 Jan 2011; was N2.tsplink.trigger[1].EVENT_ID -- was PULSE_DELAY_TIMER.EVENT_ID + else + DRAIN_I.trigger.source.stimulus = N2.tsplink.trigger[1].EVENT_ID + end --if + DRAIN_I.trigger.measure.stimulus = N3.tsplink.trigger[2].EVENT_ID -- was DRAIN_MEASURE_TIMER.EVENT_ID + DRAIN_I.trigger.endpulse.stimulus = DRAIN_I_PULSE_WIDTH_TIMER.EVENT_ID -- Ivons changed on 24 Jan 2011; was N3.tsplink.trigger[3].EVENT_ID -- was PULSE_WIDTH_TIMER.EVENT_ID + + -- Perform test + + -- Execute autozero function once and then disable for duration of test + GATE.measure.autozero = GATE.AUTOZERO_ONCE --(Value is 1) + DRAIN_V.measure.autozero = DRAIN_V.AUTOZERO_ONCE --(Value is 1) + DRAIN_I.measure.autozero = DRAIN_I.AUTOZERO_ONCE --(Value is 1) + + -- Clear all timer event and overrun detectors; unclear if this step is really necessary + PERIOD_TIMER.clear() + PULSE_DELAY_TIMER.clear() + MEASURE_DELAY_TIMER.clear() + PULSE_WIDTH_TIMER.clear() + DIGITIZE_DELAY_TIMER.clear() -- Ivons added on 06 Jan 2010 +-- GATE_MEASURE_TIMER.clear() +-- DRAIN_MEASURE_TIMER.clear() + + -- Turn ON all SMUs used for test + GATE.source.output = GATE.OUTPUT_ON + DRAIN_V.source.output = DRAIN_V.OUTPUT_ON + if drain_i_pulse_ilevel == 0 and string.upper(DrainIsrc_ZeroAmpMode) == "HIGHZ" then + DRAIN_I.source.output = DRAIN_I.OUTPUT_OFF + else + DRAIN_I.source.output = DRAIN_I.OUTPUT_ON + end --if + + waitcomplete() -- Make sure all operations have completed before intiating trigger models + + --Initiate the SMU trigger models + DRAIN_V.trigger.initiate() -- Won't proceed until GATE is initiated + + if drain_i_pulse_ilevel == 0 and string.upper(DrainIsrc_ZeroAmpMode) == "HIGHZ" then + -- Do nothing + else + DRAIN_I.trigger.initiate() -- Won't proceed until GATE is initiated + end --if + + +-- delay(0.01) -- Make sure that DRAIN_V and DRAIN_I are ready and waiting before initiating GATE; + -- can't use waitcomplete(); not sure exactly how much time is required + + GATE.trigger.initiate() + + delay(0.01) + + N1.digio.trigger[1].assert() + + -- Wait for test to complete; trigger model operation is an overlapped operation + waitcomplete() + + -- Turn OFF all SMUs used for test + DRAIN_I.source.output = DRAIN_I.OUTPUT_OFF + DRAIN_V.source.output = DRAIN_V.OUTPUT_OFF +-- DRAIN_I.source.output = DRAIN_I.OUTPUT_OFF + GATE.source.output = GATE.OUTPUT_OFF + + -- Print out data (Use "print" loop for TSB and "postbuftime" and "postbuffer" for ACS + +local headings = "i".."|".."GateSwpSrcVal".."|".."Gate_Imeas".."|".."Gate_Vmeas".. + "|".."DRAIN_V_Imeas".."|".."DRAIN_V_Vmeas".. + "|".."DRAIN_I_Imeas".."|".."DRAIN_I_Vmeas".."|".."Drain_TotalCurrent" +local dig_headings = "j".."|".."GateSwpSrcVal".."|".."Gate_Imeas_Tstamp".."|".."Gate_Vmeas_Tstamp".. + "|".."DRAIN_V_Imeas_Tstamp".."|".."DRAIN_V_Vmeas_Tstamp".. + "|".."DRAIN_I_Imeas_Tstamp".."|".."DRAIN_I_Vmeas_Tstamp".. + "|".."j".."|".."GateSwpSrcVal".."|".."Gate_Imeas".."|".."Gate_Vmeas".. + "|".."DRAIN_V_Imeas".."|".."DRAIN_V_Vmeas".. + "|".."DRAIN_I_Imeas".."|".."DRAIN_I_Vmeas".."|".."Drain_TotalCurrent".. + "|".."j".."|".."GateSwpSrcVal".."|".."Gate_Imeas_Status".."|".."Gate_Vmeas_Status".. + "|".."DRAIN_V_Imeas_Status".."|".."DRAIN_V_Vmeas_Status".. + "|".."DRAIN_I_Imeas_Status".."|".."DRAIN_I_Vmeas_Status" +local readings = {} +local timestamps = {} +local statuses = {} + +if Digitize == true then -- Ivons added on 06 Jan 2011 + + local start_count + for i = 1, GATE.nvbuffer1.n do + + start_count = (i-1) * DigCount + 1 + for j = start_count, (i * DigCount) do + readings[j] = j.."|"..GATE.nvbuffer1.sourcevalues[i].."|"..GATE.nvbuffer1[i].."|"..GATE.nvbuffer2[i].. + "|"..DRAIN_V.nvbuffer1[j].."|"..DRAIN_V.nvbuffer2[j].. + "|"..DRAIN_I.nvbuffer1[j].."|"..DRAIN_I.nvbuffer2[j].."|"..(DRAIN_V.nvbuffer1[j]+DRAIN_I.nvbuffer1[j]) + timestamps[j] = j.."|"..GATE.nvbuffer1.sourcevalues[i].."|"..GATE.nvbuffer1.timestamps[i].."|"..GATE.nvbuffer2.timestamps[i].. + "|"..DRAIN_V.nvbuffer1.timestamps[j].."|"..DRAIN_V.nvbuffer2.timestamps[j].. + "|"..DRAIN_I.nvbuffer1.timestamps[j].."|"..DRAIN_I.nvbuffer2.timestamps[j] + statuses[j] = j.."|"..GATE.nvbuffer1.sourcevalues[i].."|"..GATE.nvbuffer1.statuses[i].."|"..GATE.nvbuffer2.statuses[i].. + "|"..DRAIN_V.nvbuffer1.statuses[j].."|"..DRAIN_V.nvbuffer2.statuses[j].. + "|"..DRAIN_I.nvbuffer1.statuses[j].."|"..DRAIN_I.nvbuffer2.statuses[j] + end --for + end --for + +else + + for i = 1, GATE.nvbuffer1.n do + readings[i] = i.."|"..GATE.nvbuffer1.sourcevalues[i].."|"..GATE.nvbuffer1[i].."|"..GATE.nvbuffer2[i].. + "|"..DRAIN_V.nvbuffer1[i].."|"..DRAIN_V.nvbuffer2[i].. + "|"..DRAIN_I.nvbuffer1[i].."|"..DRAIN_I.nvbuffer2[i].."|"..(DRAIN_V.nvbuffer1[i]+DRAIN_I.nvbuffer1[i]) + timestamps[i] = i.."|"..GATE.nvbuffer1.sourcevalues[i].."|"..GATE.nvbuffer1.timestamps[i].."|"..GATE.nvbuffer2.timestamps[i].. + "|"..DRAIN_V.nvbuffer1.timestamps[i].."|"..DRAIN_V.nvbuffer2.timestamps[i].. + "|"..DRAIN_I.nvbuffer1.timestamps[i].."|"..DRAIN_I.nvbuffer2.timestamps[i] + statuses[i] = i.."|"..GATE.nvbuffer1.sourcevalues[i].."|"..GATE.nvbuffer1.statuses[i].."|"..GATE.nvbuffer2.statuses[i].. + "|"..DRAIN_V.nvbuffer1.statuses[i].."|"..DRAIN_V.nvbuffer2.statuses[i].. + "|"..DRAIN_I.nvbuffer1.statuses[i].."|"..DRAIN_I.nvbuffer2.statuses[i] + end --for + +end --if + +print() +if Digitize == true then -- Ivons added on 06 Jan 2011 + print(dig_headings) + -- print() + for i = 1, table.getn(readings) do + print(timestamps[i].."|"..readings[i].."|"..statuses[i]) + end --for +else + + print(headings) + print() + for i = 1, table.getn(readings) do + print(readings[i]) + end --for + + print() + for i = 1, table.getn(timestamps) do + print(timestamps[i]) + end --for + + print() + for i = 1, table.getn(statuses) do + print(statuses[i]) + end --for +end --if + +--[[ + print() + print(" i "," GateSwpSrcVal"," Gate_Imeas"," Gate_Vmeas"," DRAIN_V_Imeas"," DRAIN_V_Vmeas"," DRAIN_I_Imeas"," DRAIN_I_Vmeas"," Drain_TotalCurrent") + for i = 1, GATE.nvbuffer1.n do + print(i,GATE.nvbuffer1.sourcevalues[i],GATE.nvbuffer1[i],GATE.nvbuffer2[i],DRAIN_V.nvbuffer1[i]," ",DRAIN_V.nvbuffer2[i]," ",DRAIN_I.nvbuffer1[i]," ",DRAIN_I.nvbuffer2[i]," ",DRAIN_V.nvbuffer1[i]+DRAIN_I.nvbuffer1[i]) + end --for + + print() + print(" i "," GateSwpSrcVal"," Gate_Imeas"," Gate_Vmeas"," DRAIN_V_Imeas"," DRAIN_V_Vmeas"," DRAIN_I_Imeas"," DRAIN_I_Vmeas"," Drain_TotalCurrent") + for i = 1, GATE.nvbuffer1.n do + print(i,GATE.nvbuffer1.sourcevalues[i],GATE.nvbuffer1.timestamps[i],GATE.nvbuffer2.timestamps[i],DRAIN_V.nvbuffer1.timestamps[i]," ",DRAIN_V.nvbuffer2.timestamps[i]," ",DRAIN_I.nvbuffer1.timestamps[i]," ",DRAIN_I.nvbuffer2.timestamps[i]) + end --for + + print() + print(" i "," GateSwpSrcVal"," Gate_Imeas"," Gate_Vmeas"," DRAIN_V_Imeas"," DRAIN_V_Vmeas"," DRAIN_I_Imeas"," DRAIN_I_Vmeas"," Drain_TotalCurrent") + for i = 1, GATE.nvbuffer1.n do + print(i,GATE.nvbuffer1.sourcevalues[i],GATE.nvbuffer1.statuses[i],GATE.nvbuffer2.statuses[i],DRAIN_V.nvbuffer1.statuses[i]," ",DRAIN_V.nvbuffer2.statuses[i]," ",DRAIN_I.nvbuffer1.statuses[i]," ",DRAIN_I.nvbuffer2.statuses[i]) + end --for +]] +--[[ + + -- postbuftime(�name�, start_index, end_index, buff_name, avg_num) + postbuftime("GateSwpTimestamps",1,GATE.nvbuffer1.n,GATE.nvbuffer1,1) + + -- postbuffer(�name�, start_index, end_index, buff_name, avg_num) + postbuffer("GateSwpSrcValues",1,GATE.nvbuffer1.n,GATE.nvbuffer1.sourcevalues,1) + postbuffer("Gate_Vmeas",1,GATE.nvbuffer2.n,GATE.nvbuffer2,1) + postbuffer("Gate_Imeas",1,GATE.nvbuffer1.n,GATE.nvbuffer1,1) + postbuffer("Drain_DRAIN_V_Vmeas",1,DRAIN_V.nvbuffer2.n,DRAIN_V.nvbuffer2,1) + postbuffer("Drain_DRAIN_V_Imeas",1,DRAIN_V.nvbuffer1.n,DRAIN_V.nvbuffer1,1) + postbuffer("Drain_DRAIN_I_Vmeas",1,DRAIN_I.nvbuffer2.n,DRAIN_I.nvbuffer2,1) + postbuffer("Drain_DRAIN_I_Imeas",1,DRAIN_I.nvbuffer1.n,DRAIN_I.nvbuffer1,1) +]] + + return 0, "Ran OK" -- Function returns zero if runs to completion + +end --function + +ResetTspLink(true) + +PrintUnitInfo() + +--print("SweepGate_PulseDrain(GateStartV, GateStopV, GateStepV, GateIlimit, DrainSmuMode, DrainSmuRegionIndex, DrainVpulseLevel, DrainIlimit, DrainIsrc_ZeroAmpMode, DrainPulseWidth, DrainDutyCycle, Nplc, EnableHighC)") + + +-- SweepGate_PulseDrain(GateStartV, GateStopV, GateStepV, GateIlimit, DrainSmuMode, DrainSmuRegionIndex, DrainVpulseLevel, DrainIlimit, DrainIsrc_ZeroAmpMode, DrainPulseWidth, DrainDutyCycle, Nplc, EnableHighC) + +local VoltageLevels = {1,2,4,5,6,8,9,10,12,14,15,16,18,19,20,22,24,25,26,28,30,32,34,35,36,38,39,40} -- Ivons added on 11 Jan 2011 +local VoltageLevelToTest = 10 -- Ivons added on 12 Jan 2011 ******************************************* +--local VoltageLevelsToTest = {1,2,5,9} -- Ivons added on 11 Jan 2011 +--local VoltageLevelsToTest = {10,12,18,19} -- Ivons added on 11 Jan 2011 +--local VoltageLevelsToTest = {20,22,25,30} -- Ivons added on 11 Jan 2011 +--local VoltageLevelsToTest = {35,38,39,40} -- Ivons added on 11 Jan 2011 +local CmplMultiplier = {1, 1.2} -- Ivons added on 11 Jan 2011 +local SmuMode = {1,2} -- Ivons added on 11 Jan 2011 + +for i = 1, table.getn(VoltageLevels) do -- Ivons added on 11 Jan 2011 +-- for m = 1, table.getn(VoltageLevelsToTest) do -- Ivons added on 11 Jan 2011; Ivons commented out on 12 Jan 2011 +-- if VoltageLevels[i] == VoltageLevelsToTest[m] then -- Ivons added on 11 Jan 2011; Ivons commented out on 12 Jan 2011 + if VoltageLevels[i] == VoltageLevelToTest then -- Ivons added on 12 Jan 2011 + for j = 1, 2 do -- Ivons added on 11 Jan 2011 + for k = 1, 2 do -- Ivons added on 11 Jan 2011 + +print() -- Ivons added on 11 Jan 2011 +--print("i:|"..i.."|j:|"..j.."|k:|"..k) -- Ivons added on 11 Jan 2011 +print("i,j,k:|"..i..","..j..","..k) -- Ivons added on 11 Jan 2011 +print() -- Ivons added on 11 Jan 2011 + +GateStartV = 0 +GateStopV = 1 -- was 5, , Ivons changed to 1 beginning with Test 27 +GateStepV = 1 +GateIlimit = 0.1 +DrainSmuMode = SmuMode[k] -- was 1 or 2 +DrainSmuRegionIndex = 0 -- Zero (0) is pick automatically +DrainVpulseLevel = VoltageLevels[i] -- was 10 +DrainIlimit = CmplMultiplier[j] * VoltageLevels[i] -- was 10 -- Enter TOTAL compliance for both SMUs; script will divide in half for each individual SMU +DrainIsrc_ZeroAmpMode = "ZeroAmp" -- Set to "ZeroAmp" or "HighZ" +DrainPulseWidth = 300e-6 --was 300e-6 +DrainDutyCycle = 0.001 -- was 0.003 +Nplc = 0.003 +EnableHighC = false +Digitize = true -- Ivons added on 06 Jan 2011 +DigInterval = 1e-6 -- Ivons added on 06 Jan 2011 +DigCount = 800 -- Ivons added on 06 Jan 2011; originally was 500, Ivons increased to 600 beginning with Test 27; changed to 800 starting with Test 28 +PreTrigPercent = 10 -- Ivons added on 06 Jan 2011 +CheckParamOnly = false -- Ivons added on 11 Jan 2011 +VsrcCmplOff = false +VsrcLagDelay = 0 +IsrcPulseWidth = 300e-6 +Use2wire = true +-- status = SweepGate_PulseDrain(GateStartV, GateStopV, GateStepV, GateIlimit, DrainVpulseLevel, DrainIlimit, DrainPulseWidth, EnableHighC) + +Status, Msg = SweepGate_PulseDrain(GateStartV, GateStopV, GateStepV, GateIlimit, DrainSmuMode, DrainSmuRegionIndex, DrainVpulseLevel, DrainIlimit, DrainIsrc_ZeroAmpMode, DrainPulseWidth, DrainDutyCycle, Nplc, EnableHighC, + Digitize, DigInterval, DigCount, PreTrigPercent, CheckParamOnly, VsrcCmplOff, VsrcLagDelay, IsrcPulseWidth, Use2wire) +print("Status:|"..Status) +print(Msg) +if not(CheckParamOnly) then delay(35) end -- Ivons added on 11 Jan 2011 + end --for k -- Ivons added on 11 Jan 2011 + end --for j -- Ivons added on 11 Jan 2011 + end --if -- Ivons added on 11 Jan 2011 +-- end --for m -- Ivons added on 11 Jan 2011; Ivons commented out on 12 Jan 2011 +end --for i -- Ivons added on 11 Jan 2011 + + +--postdata("Status",status) diff --git a/Instrument_Examples/Series_2600/265xA/2651A_Parallel_IV_Simple.tsp b/Instrument_Examples/Series_2600/265xA/2651A_Parallel_IV_Simple.tsp new file mode 100644 index 0000000..5393335 --- /dev/null +++ b/Instrument_Examples/Series_2600/265xA/2651A_Parallel_IV_Simple.tsp @@ -0,0 +1,491 @@ +--[[ + Title: Parallel IV Open Lab Script + Date: 5/13/2011 + + Description: This script uses two 2651As + wired together in parallel to source voltage with currents up to 95A. + + If you are not familiar with high current, consult Keithley Applications + Engineering before attempting this test. It could damage you or your equipment. + + !!! Warning !!!! + This script is an incredibly simplified version of a complete Parallel IV + script. It is not gaurenteed to work with all input values passed to it + not does it incorporate any major parameter or error checking therefore + SMU/DUT safety cannot be ensured with this script. Because of these + things it is therefore not recommended that this script be given to + customers without appropriate modification/customer attention from a + Keithley Applications Engineer. + + TSP-Link Configuration: + Node 1: 2651A + Node 2: 2651A + Node 3: 26XXA + + Master Node: Node 1 + + Revision History: + 5/13/2011 - Version 1.0 + David Wyban + Initial Revision +]] + + +--[[ + DualSMUIVcurves(gstart, gstop, gsteps, dstart, dstop, dsteps, pulseWidth, pulsePeriod) + + Description: This function will perform a series of pulsed sweeps + on a MOSFET or IGBT device to generate a series of IV Curves that + characterize the device. It will use two 2651A SMUs in parallel to + reach currents of up to 95A. Note: This function uses only the + 100A(50A x 2) range for source and measure. + Note: To avoid device oscillations, a series resistor on the gate + terminal of the device may be required. + + Parameters: + gstart: The starting voltage of the gate sweep + gstop: The ending voltage of the gate sweep + gsteps: The number of steps in the gate sweep + dstart: The starting voltage of the drain sweep + dstop: The ending voltage of the drain sweep + dsteps: The number of steps in the drain sweep + pulseWidth: The width of the drain pulse in seconds + pulsePeriod:The time in seconds between the start of consecutive drain pulses in the sweep + + Example Usage: + DualSMUIVcurves(6, 8.5, 6, 0, 5, 51, 300e-6, 30e-3) +--]] +function DualSMUIVcurves(gstart, gstop, gsteps, dstart, dstop, dsteps, pulseWidth, pulsePeriod) + local _nplc = 0.005 + reset() + tsplink.reset() + + -- Configure the Drain V-SMU(2651A) + --------------------------------- + smua.reset() + smua.source.func = smua.OUTPUT_DCVOLTS + + -- Configure the proper output off configuration + smua.source.offmode = smua.OUTPUT_NORMAL + smua.source.offfunc = smua.OUTPUT_DCVOLTS + smua.source.offlimiti = 1e-3 + smua.sense = smua.SENSE_REMOTE + + smua.source.rangev = math.max(math.abs(dstart), math.abs(dstop)) + -- Select the source range that is large enough to fit all values of the sweep + smua.source.levelv = 0 -- Sets the drain bias level + smua.source.limiti = 5 + + smua.measure.rangev = smua.source.rangev + smua.measure.rangei = 50 + -- Select a measure range large enough to fit pulses up to the current limit + + smua.measure.nplc = _nplc + smua.measure.delay = (pulseWidth - ((1/localnode.linefreq) * smua.measure.nplc)) - 20e-6 + -- Set the measure delay so that the measurement is + -- taken at the end of the pulse before the falling edge + smua.measure.autozero = smua.AUTOZERO_ONCE + + -- Timer 1 controls the pulse period + trigger.timer[1].count = (dsteps <= 1) and 1 or (dsteps - 1) + -- If dsteps <= 1 then use 1 for the count else use dsteps - 1 + trigger.timer[1].delay = pulsePeriod + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = tsplink.trigger[1].EVENT_ID + trigger.timer[1].clear() + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = pulseWidth - 4e-6 + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = smua.trigger.SOURCE_COMPLETE_EVENT_ID + trigger.timer[2].clear() + + -- Configure Drain V-SMU(2651A #1) trigger model + smua.trigger.source.linearv(dstart, dstop, dsteps) + smua.trigger.source.limiti = 50 + 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.arm.count = gsteps + smua.trigger.count = dsteps + smua.trigger.arm.stimulus = 0 + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = 0 + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + -- Configure TSP-Link Triggers + tsplink.trigger[1].clear() + tsplink.trigger[1].mode = tsplink.TRIG_SYNCHRONOUSM + tsplink.trigger[1].stimulus = smua.trigger.ARMED_EVENT_ID + -- TSP-Link Trigger 1 is used by the 2651A to tell the 26xxA to step + -- the gate and for the 26xxA to tell the 2651A when it has completed + -- the step. + + tsplink.trigger[2].clear() + tsplink.trigger[2].mode = tsplink.TRIG_FALLING + tsplink.trigger[2].stimulus = smua.trigger.SWEEP_COMPLETE_EVENT_ID + -- TSP-Link Trigger 2 is used by the 2651A to tell the 26xxA that it + -- has completed the drain sweep and it is OK for the 26xxA to continue. + + tsplink.trigger[3].clear() + tsplink.trigger[3].mode = tsplink.TRIG_FALLING + tsplink.trigger[3].stimulus = trigger.timer[1].EVENT_ID + -- TSP-Link Trigger 3 is used by 2651A #1 to tell 2651A #2 to output the pulse + + + -- Debug code + --************************* + trigger.blender[1].orenable = true + trigger.blender[1].stimulus[1] = trigger.timer[1].EVENT_ID + trigger.blender[1].stimulus[2] = smua.trigger.SOURCE_COMPLETE_EVENT_ID + trigger.blender[1].stimulus[3] = smua.trigger.MEASURE_COMPLETE_EVENT_ID + trigger.blender[1].stimulus[4] = trigger.timer[2].EVENT_ID + --trigger.blender[1].stimulus[4] = smua.trigger.PULSE_COMPLETE_EVENT_ID + trigger.blender[1].clear() + + digio.trigger[1].mode = digio.TRIG_FALLING + digio.trigger[1].pulsewidth = 3e-6 + digio.trigger[1].stimulus = trigger.blender[1].EVENT_ID + digio.trigger[1].clear() + --************************** + -- End Debug Code + + + -- Prepare the Drain V-SMU (2651A) reading buffers + smua.nvbuffer1.clear() + smua.nvbuffer1.collectsourcevalues = 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.collectsourcevalues = 1 +--=================================================================================== + + +-- Configure the Drain I-SMU(2651A #2) + --------------------------------- + node[2].smua.reset() + node[2].smua.source.func = node[2].smua.OUTPUT_DCAMPS + + -- Configure the proper output off configuration + node[2].smua.source.offmode = node[2].smua.OUTPUT_NORMAL + node[2].smua.source.offfunc = node[2].smua.OUTPUT_DCAMPS + node[2].smua.source.offlimitv = 40 + node[2].smua.sense = node[2].smua.SENSE_REMOTE + + node[2].smua.source.rangei = 50 + -- Select the source range that is large enough to fit all values of the sweep + node[2].smua.source.leveli = 0 -- Sets the drain bias level + node[2].smua.source.limitv = 10 + + node[2].smua.measure.rangei = node[2].smua.source.rangei + node[2].smua.measure.rangev = 10 + -- Select a measure range large enough to fit pulses up to the current limit + + node[2].smua.measure.nplc = _nplc + node[2].smua.measure.delay = (pulseWidth - ((1/node[2].linefreq) * node[2].smua.measure.nplc)) - 20e-6 + -- Set the measure delay so that the measurement is + -- taken at the end of the pulse before the falling edge + node[2].smua.measure.autozero = node[2].smua.AUTOZERO_ONCE + + -- Timer 2 controls the pulse width + node[2].trigger.timer[2].count = 1 + node[2].trigger.timer[2].delay = pulseWidth - 4e-6 + node[2].trigger.timer[2].passthrough = false + node[2].trigger.timer[2].stimulus = node[2].smua.trigger.SOURCE_COMPLETE_EVENT_ID + node[2].trigger.timer[2].clear() + + -- Configure Drain I-SMU(2651A #2) trigger model + node[2].smua.trigger.source.lineari(45, 45, dsteps) + node[2].smua.trigger.source.limitv = 10 + node[2].smua.trigger.measure.action = node[2].smua.ENABLE + node[2].smua.trigger.measure.iv(node[2].smua.nvbuffer1, node[2].smua.nvbuffer2) + node[2].smua.trigger.endpulse.action = node[2].smua.SOURCE_IDLE + node[2].smua.trigger.endsweep.action = node[2].smua.SOURCE_IDLE + node[2].smua.trigger.arm.count = gsteps + node[2].smua.trigger.count = dsteps + node[2].smua.trigger.arm.stimulus = 0 + node[2].smua.trigger.source.stimulus = node[2].tsplink.trigger[3].EVENT_ID + node[2].smua.trigger.measure.stimulus = 0 + node[2].smua.trigger.endpulse.stimulus = node[2].trigger.timer[2].EVENT_ID + node[2].smua.trigger.source.action = node[2].smua.ENABLE + + -- Configure TSP-Link Triggers + node[2].tsplink.trigger[3].clear() + node[2].tsplink.trigger[3].mode = node[2].tsplink.TRIG_FALLING +-- node[3].tsplink.trigger[3].stimulus = node[3].smua.trigger.ARMED_EVENT_ID + -- TSP-Link Trigger 3 is used by 2651A #2 to receive triggers from 2651A #1 + -- to tell it when to start the pulse + + -- Prepare the Drain I-SMU (2651A #2) reading buffers + node[2].smua.nvbuffer1.clear() + node[2].smua.nvbuffer1.collectsourcevalues = 1 + node[2].smua.nvbuffer2.clear() + node[2].smua.nvbuffer2.collectsourcevalues = 1 +--=================================================================================== + + + -- Configure the Gate SMU(26xxA) + -------------------------------- + node[3].smua.reset() + node[3].smua.source.func = node[3].smua.OUTPUT_DCVOLTS + node[3].smua.sense = node[3].smua.SENSE_REMOTE + node[3].smua.source.levelv = 0 + node[3].smua.source.limiti = 100e-3 + node[3].smua.measure.delay = 300e-6 -- Give gate 300us to settle + -- Do not need to configure any additional measure settings. + -- Timing is not critical on the gate so autorange will do. + node[3].smua.source.highc = 0 + + -- Configure Gate SMU(26xxA) Trigger Model + node[3].smua.trigger.source.linearv(gstart, gstop, gsteps) + node[3].smua.trigger.source.limiti = 100e-3 + node[3].smua.trigger.measure.action = node[3].smua.ENABLE + node[3].smua.trigger.measure.iv(node[3].smua.nvbuffer1, node[3].smua.nvbuffer2) + node[3].smua.trigger.endpulse.action = smua.SOURCE_HOLD + node[3].smua.trigger.endsweep.action = smua.SOURCE_IDLE + node[3].smua.trigger.count = gsteps + node[3].smua.trigger.arm.stimulus = 0 + node[3].smua.trigger.source.stimulus = node[3].tsplink.trigger[1].EVENT_ID + node[3].smua.trigger.measure.stimulus = 0 + node[3].smua.trigger.endpulse.stimulus = node[3].tsplink.trigger[2].EVENT_ID + node[3].smua.trigger.source.action = smua.ENABLE + + -- Configure 26xxA TSP-Link Triggers + node[3].tsplink.trigger[1].clear() + node[3].tsplink.trigger[1].mode = node[3].tsplink.TRIG_SYNCHRONOUSA + node[3].tsplink.trigger[1].stimulus = node[3].smua.trigger.MEASURE_COMPLETE_EVENT_ID + + node[3].tsplink.trigger[2].clear() + node[3].tsplink.trigger[2].mode = node[3].tsplink.TRIG_FALLING + + -- Prepare the Gate SMU (26xxA) reading buffers + node[3].smua.nvbuffer1.clear() + node[3].smua.nvbuffer1.collectsourcevalues = 1 + node[3].smua.nvbuffer2.clear() + node[3].smua.nvbuffer2.collectsourcevalues = 1 + + -- The SMUs are configured and ready to run the test + + -- Outputs on + node[3].smua.source.output = 1 + node[2].smua.source.output = 1 + smua.source.output = 1 + + node[3].smua.trigger.initiate() -- Start the 26xxA's trigger model + node[2].smua.trigger.initiate() -- Start the 2651A #2s trigger model + smua.trigger.initiate() -- Start the 2651A #1s trigger model + + waitcomplete() -- Wait until the sweeps are complete + + -- Outputs off + smua.source.output = 0 + node[2].smua.source.output = 0 + node[3].smua.source.output = 0 + + -- Return the data + PrintIVcurveDataDual(gsteps, dsteps) +end + +--[[ + DualSMUPulseV(pulseLevel, pulseWidth) + + Description: This function will output a single voltage pulse and + capture the pulse with the Fast ADC then return the readings in a + format that is cut-and-paste compatible with Microsoft Excel. This + function uses two 2651A SMUs in parallel to reach currents of up to 95A. + Note: This function uses only the 100A(50A x 2) range for source and + measure. + + Parameters: + pulseLevel: The voltage to pulse + pulseWidth: The width of the drain pulse in seconds + + Example Usage: + DualSMUPulseV(6, 300e-6) +--]] +function DualSMUPulseV(pulseLevel, pulseWidth) + reset() + tsplink.reset() + + -- Configure the V-SMU(2651A #1) + -------------------------------- + smua.reset() + smua.source.func = smua.OUTPUT_DCVOLTS + + -- Configure the proper output off configuration + smua.source.offmode = smua.OUTPUT_NORMAL + smua.source.offfunc = smua.OUTPUT_DCVOLTS + smua.source.offlimiti = 1e-3 + smua.sense = smua.SENSE_REMOTE +-- smua.sense = smua.SENSE_LOCAL + + smua.source.rangev = pulseLevel + smua.source.levelv = 0 -- Sets the bias level + smua.source.limiti = 5 + + smua.measure.rangev = smua.source.rangev + smua.measure.rangei = 50 + + smua.measure.adc = smua.ADC_FAST + smua.measure.delay = 0 + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.interval = 1e-6 + smua.measure.count = pulseWidth * 1e6 + 200 + + -- Timer 1 controls the pulse period + trigger.timer[1].count = 1 + trigger.timer[1].delay = 100e-3 + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = tsplink.trigger[1].EVENT_ID + trigger.timer[1].clear() + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = pulseWidth - 4e-6 + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = smua.trigger.SOURCE_COMPLETE_EVENT_ID + trigger.timer[2].clear() + + -- Configure V-SMU(2651A #1) trigger model + smua.trigger.source.linearv(pulseLevel, pulseLevel, 2) + smua.trigger.source.limiti = 50 + smua.trigger.measure.action = smua.ASYNC + smua.trigger.measure.iv(smua.nvbuffer1, smua.nvbuffer2) + smua.trigger.endpulse.action = smua.SOURCE_IDLE + smua.trigger.endsweep.action = smua.SOURCE_IDLE + smua.trigger.arm.count = 1 + smua.trigger.count = 1 + smua.trigger.arm.stimulus = 0 + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + -- Configure TSP-Link Triggers + tsplink.trigger[1].clear() + tsplink.trigger[1].mode = tsplink.TRIG_FALLING + tsplink.trigger[1].stimulus = smua.trigger.ARMED_EVENT_ID + -- TSP-Link Trigger 1 is used by the 2651A #1 to tell the 2651A #2 + -- to start the pulse. + + -- Prepare the V-SMU (2651A) reading buffers + smua.nvbuffer1.clear() + smua.nvbuffer1.collectsourcevalues = 0 + smua.nvbuffer1.collecttimestamps = 1 + smua.nvbuffer2.clear() + smua.nvbuffer2.collectsourcevalues = 0 + smua.nvbuffer2.collecttimestamps = 1 +--=================================================================================== + + +-- Configure the I-SMU(2651A #2) + ---------------------------- + node[2].smua.reset() + node[2].smua.source.func = node[2].smua.OUTPUT_DCAMPS + + -- Configure the proper output off configuration + node[2].smua.source.offmode = node[2].smua.OUTPUT_NORMAL + node[2].smua.source.offfunc = node[2].smua.OUTPUT_DCAMPS + node[2].smua.source.offlimitv = 40 + node[2].smua.sense = node[2].smua.SENSE_REMOTE +-- node[2].smua.sense = node[2].smua.SENSE_LOCAL + + node[2].smua.source.rangei = 50 + node[2].smua.source.leveli = 0 -- Sets the bias level + node[2].smua.source.limitv = 10 + + node[2].smua.measure.rangei = node[2].smua.source.rangei + node[2].smua.measure.rangev = 10 + + node[2].smua.measure.adc = node[2].smua.ADC_FAST + node[2].smua.measure.delay = 0 + node[2].smua.measure.autozero = node[2].smua.AUTOZERO_ONCE + node[2].smua.measure.interval = 1e-6 + node[2].smua.measure.count = pulseWidth * 1e6 + 200 + + -- Timer 2 controls the pulse width + node[2].trigger.timer[2].count = 1 + node[2].trigger.timer[2].delay = pulseWidth - 4e-6 + node[2].trigger.timer[2].passthrough = false + node[2].trigger.timer[2].stimulus = node[2].smua.trigger.SOURCE_COMPLETE_EVENT_ID + node[2].trigger.timer[2].clear() + + -- Configure I-SMU(2651A #2) trigger model + node[2].smua.trigger.source.lineari(45, 45, 2) + node[2].smua.trigger.source.limitv = 10 + node[2].smua.trigger.measure.action = node[2].smua.ASYNC + node[2].smua.trigger.measure.iv(node[2].smua.nvbuffer1, node[2].smua.nvbuffer2) + node[2].smua.trigger.endpulse.action = node[2].smua.SOURCE_IDLE + node[2].smua.trigger.endsweep.action = node[2].smua.SOURCE_IDLE + node[2].smua.trigger.arm.count = 1 + node[2].smua.trigger.count = 1 + node[2].smua.trigger.arm.stimulus = 0 + node[2].smua.trigger.source.stimulus = node[2].tsplink.trigger[1].EVENT_ID + node[2].smua.trigger.measure.stimulus = node[2].tsplink.trigger[1].EVENT_ID + node[2].smua.trigger.endpulse.stimulus = node[2].trigger.timer[2].EVENT_ID + node[2].smua.trigger.source.action = node[2].smua.ENABLE + + -- Configure TSP-Link Triggers + node[2].tsplink.trigger[1].clear() + node[2].tsplink.trigger[1].mode = node[2].tsplink.TRIG_FALLING + -- TSP-Link Trigger 1 is used by 2651A #2 to receive triggers from 2651A #1 + -- to tell it when to start the pulse + + -- Prepare the I-SMU (2651A #2) reading buffers + node[2].smua.nvbuffer1.clear() + node[2].smua.nvbuffer1.collectsourcevalues = 0 + node[2].smua.nvbuffer1.collecttimestamps = 1 + node[2].smua.nvbuffer2.clear() + node[2].smua.nvbuffer2.collectsourcevalues = 0 + node[2].smua.nvbuffer2.collecttimestamps = 1 +--=================================================================================== + + -- The SMUs are configured and ready to run the test + + -- Outputs on + node[2].smua.source.output = 1 + smua.source.output = 1 + + node[2].smua.trigger.initiate() -- Start the 2651A #2s trigger model + smua.trigger.initiate() -- Start the 2651A #1s trigger model + + waitcomplete() -- Wait until the sweeps are complete + + -- Outputs off + smua.source.output = 0 + node[2].smua.source.output = 0 + + -- Return the data + PrintFastIVDataDual() +end + +function PrintIVcurveDataDual(gsteps, dsteps) + line1 = "" + line2 = "" + for i=1,gsteps do + line1 = line1 .. string.format("Vgs = %0.2f\t%g\t%g\t", node[3].smua.nvbuffer1.sourcevalues[i], node[3].smua.nvbuffer2[i], node[3].smua.nvbuffer1[i]) + line2 = line2 .. "Source Value\tVoltage\tCurrent\t" + end + print(line1) + print(line2) + for i=1, dsteps do + line = "" + for j=1, gsteps do + line = line .. string.format("%g\t%g\t%g\t", smua.nvbuffer1.sourcevalues[(j-1)*dsteps + i], (smua.nvbuffer2[(j-1)*dsteps + i] + node[2].smua.nvbuffer2[(j-1)*dsteps + i])/2, (smua.nvbuffer1[(j-1)*dsteps + i] + node[2].smua.nvbuffer1[(j-1)*dsteps + i])) + end + print(line) + end +end + +function PrintFastIVDataDual() + print("\r\nTimestamps\tCombined Voltage\tCombined Current\tVoltage SMU1\tCurrent SMU1\tVoltage SMU2\tCurrent SMU2") + local combinedV + local combinedI + for i=1,smua.nvbuffer1.n do + combinedV = (smua.nvbuffer2[i] + node[2].smua.nvbuffer2[i])/2 + combinedI = smua.nvbuffer1[i] + node[2].smua.nvbuffer1[i] + print(string.format("%g\t%g\t%g\t%g\t%g\t%g\t%g", smua.nvbuffer1.timestamps[i], combinedV, combinedI, smua.nvbuffer2[i], smua.nvbuffer1[i], node[2].smua.nvbuffer2[i], node[2].smua.nvbuffer1[i])) + end +end + diff --git a/Instrument_Examples/Series_2600/265xA/2657A_current_sine_wave.tsp b/Instrument_Examples/Series_2600/265xA/2657A_current_sine_wave.tsp new file mode 100644 index 0000000..bf3ae1d --- /dev/null +++ b/Instrument_Examples/Series_2600/265xA/2657A_current_sine_wave.tsp @@ -0,0 +1,156 @@ +--[[ + + model: Keithley Instruments Inc.,Model 2657A,4098545,1.1.8 + + Purpose: generate a current sinewave with DC offset + + + +]] + + +-- Helper functions + +function compute_waveform_cycle(Arms, DCOffset, PtsPerCycle) + + -- Generate the source values + --local Vpp = Vrms * math.sqrt(2) / 2 + + for i=1, PtsPerCycle do --numDataPoints do + sourceValues[i] = (Arms/2 * math.sin(i * 2 * math.pi / PtsPerCycle)) + DCOffset + end + +end -- function + + +function config_timer(smu, frequency, PtsPerCycle, numCycles) + + trigger.timer[1].delay = (1/frequency) / PtsPerCycle + trigger.timer[1].stimulus = smu.trigger.ARMED_EVENT_ID + trigger.timer[1].count = PtsPerCycle * numCycles + trigger.timer[1].passthrough = false + trigger.timer[1].clear() + +end -- function + + +function config_smu(smu, IRange, limitV, numCycles, remoteSense) + + +-- config a fixed range list sweep + smu.source.func = smu.OUTPUT_DCAMPS + + if (remoteSense == true) then + smu.sense = smu.SENSE_REMOTE + else + smu.sense = smu.SENSE_LOCAL + end + smu.source.autorangei = smu.AUTORANGE_OFF + smu.source.rangei = IRange + smu.source.leveli = 0 + smu.source.limitv = limitV + + -- allow limited auto ranging + smu.measure.autozero = smu.AUTOZERO_ONCE + smu.measure.autorangei = smu.AUTORANGE_OFF + smu.measure.rangei = smu.source.rangei + smu.measure.autorangev = smu.AUTORANGE_OFF + smu.measure.rangev = smu.source.limitv -- measure range to follow source limit + smu.measure.nplc = 0.001 --nplc + smu.measure.delay = 0 + + -- Configure SMU Trigger Model for Sweep + smu.trigger.source.listi(sourceValues) -- sourceValues is global table + smu.trigger.source.limitv = limitV + smu.trigger.measure.action = smu.ENABLE + smu.trigger.measure.iv(smu.nvbuffer1, smu.nvbuffer2) + smu.trigger.endpulse.action = smu.SOURCE_HOLD + smu.trigger.endsweep.action = smu.SOURCE_IDLE + smu.trigger.count = table.getn(sourceValues) * numCycles + smu.trigger.arm.stimulus = 0 + smu.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smu.trigger.measure.stimulus = 0 + smu.trigger.endpulse.stimulus = 0 + smu.trigger.source.action = smu.ENABLE + +end -- function + + + +function reset_buffers(smu) + -- Prepare the Reading Buffers + smu.nvbuffer1.clear() + smu.nvbuffer1.appendmode = 1 + smu.nvbuffer1.collecttimestamps = 1 + smu.nvbuffer2.clear() + smu.nvbuffer2.appendmode = 1 + smu.nvbuffer2.collecttimestamps = 1 + +end + + + +-- ****************** Main Program ***************** + +reset() +errorqueue.clear() + + +local numberCyclesToPlay = 2 +local sineWaveFreq = 100 +local numberPtsPerCycle = 60 +local AC_waveform_height = 0.5 +local DC_offset = 0 +local VoltageLimit = 40 + +local smu_iRange = DC_offset + AC_waveform_height -- divide by 2? + +sourceValues = {} -- empty table, global var + +--compute_waveform_cycle(Vrms, DCOffset, PtsPerCycle) +compute_waveform_cycle(AC_waveform_height, DC_offset, numberPtsPerCycle) + +--[[ +for i = 1, table.getn(sourceValues) do + print(sourceValues[i]) +end -- for loop +]] + +--config_timer(smu, frequency, PtsPerCycle, numCycles) +config_timer(smua, sineWaveFreq, numberPtsPerCycle, numberCyclesToPlay) + +--config_smu(smu, IRange, limitV, numCycles, remoteSense) +config_smu(smua, smu_iRange, VoltageLimit, numberCyclesToPlay, false) + + + -- clear the reading buffers + reset_buffers(smua) + + + + -- Turn the outputs on + smua.source.output = smua.OUTPUT_ON + + + -- Start the trigger model execution + smua.trigger.initiate() + + -- Wait until the sweep has completed + waitcomplete() + + delay(0.1) + + smua.source.output = smua.OUTPUT_OFF + + show_data = true + if show_data == true then + -- Print the data back to the Console in tabular format + print("Time\tVoltage\tCurrent") + for x=1,smua.nvbuffer1.n do + -- Voltage readings are in nvbuffer2. Current readings are in nvbuffer1. + print(smua.nvbuffer1.timestamps[x], smua.nvbuffer2[x], smua.nvbuffer1[x]) + end + end -- if + +-- on exit, free the memory used by table +sourceValues = nil \ No newline at end of file diff --git a/Instrument_Examples/Series_2600/265xA/265xA_Fast_ADC_Pulse_Sweep.tsp b/Instrument_Examples/Series_2600/265xA/265xA_Fast_ADC_Pulse_Sweep.tsp new file mode 100644 index 0000000..49d77aa --- /dev/null +++ b/Instrument_Examples/Series_2600/265xA/265xA_Fast_ADC_Pulse_Sweep.tsp @@ -0,0 +1,142 @@ +--[[ + Title: Fast ADC Pulse Sweep + + Description: This script performs a high power pulse sweep using the + fast ADC to measure. + +]] + +-- SweepV(0, 5, 11, 200e-6, 20e-3, 20) +function SweepV(start, stop, steps, ton, period, limit) + if (start == nil) then start = 0 end + if (stop == nil) then stop = 1 end + if (steps == nil) then steps = 11 end + if (ton == nil) then ton = 1e-3 end + if (period == nil) then period = 100e-3 end + if (limit == nil) then limit = 1 end + if (limit > 20) then dclimit = 20 else dclimit = limit end + + reset() + smua.reset() + smua.source.func = smua.OUTPUT_DCVOLTS + smua.sense = smua.SENSE_REMOTE + smua.source.autorangev = 0 + smua.source.autorangei = 0 + smua.source.rangev = 10 + smua.source.rangei = limit + smua.source.levelv = 0 + smua.source.leveli = 0 + smua.source.limiti = dclimit + smua.source.limitv = 10 + + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.autorangev = 0 + smua.measure.autorangei = 0 + smua.measure.rangev = 10 + smua.measure.rangei = limit + smua.measure.adc = smua.ADC_FAST + smua.measure.interval = 0 + smua.measure.count = 300 + + smua.nvbuffer1.clear() + smua.nvbuffer1.appendmode = 1 + smua.nvbuffer2.clear() + + -- Configure the Pulsed Sweep setup + --============================= + + -- Configure timers + -- Timer 1 controls the pulse period + trigger.timer[1].count = steps + trigger.timer[1].delay = period + trigger.timer[1].passthrough = true + trigger.timer[1].stimulus = smua.trigger.ARMED_EVENT_ID + + -- Timer 2 controls the pulse width + trigger.timer[2].count = 1 + trigger.timer[2].delay = ton + trigger.timer[2].passthrough = false + trigger.timer[2].stimulus = trigger.timer[1].EVENT_ID + + -- Configure SMU Trigger Model for Sweep + smua.trigger.source.linearv(start, stop, steps) + smua.trigger.source.limiti = limit + smua.trigger.measure.action = smua.ASYNC--smua.ENABLE + smua.trigger.measure.i(smua.nvbuffer1)--, smua.nvbuffer2) + smua.trigger.endpulse.action = smua.SOURCE_IDLE + smua.trigger.endsweep.action = smua.SOURCE_IDLE + smua.trigger.count = steps + smua.trigger.arm.stimulus = 0 + smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.measure.stimulus = trigger.timer[1].EVENT_ID + smua.trigger.endpulse.stimulus = trigger.timer[2].EVENT_ID + smua.trigger.source.action = smua.ENABLE + + + smua.source.output = 1 + smua.trigger.initiate() + waitcomplete() + smua.source.output = 0 +end + + +-- SimpleSweepV(0, 5, 11, 200e-6, 20e-3) +function SimpleSweepV(start, stop, steps, ton, period) + if (start == nil) then start = 0 end + if (stop == nil) then stop = 5 end + if (steps == nil) then steps = 11 end + if (ton == nil) then ton = 1e-3 end + if (period == nil) then period = 100e-3 end + + stepv = (stop-start)/(steps-1) + + smua.reset() + smua.source.func = smua.OUTPUT_DCVOLTS + smua.sense = smua.SENSE_LOCAL + smua.source.autorangev = 0 + smua.source.autorangei = 0 + smua.source.rangev = 20 + smua.source.rangei = 10 + smua.source.levelv = 0 + smua.source.leveli = 0 + smua.source.limiti = 10 + smua.source.limitv = 20 + + smua.measure.autozero = smua.AUTOZERO_ONCE + smua.measure.autorangev = 0 + smua.measure.autorangei = 0 + smua.measure.rangev = 20 + smua.measure.rangei = 10 + smua.measure.adc = smua.ADC_FAST + smua.measure.interval = 0 + smua.measure.count = 300 + + smua.nvbuffer1.clear() + smua.nvbuffer1.appendmode = 1 +-- smua.nvbuffer2.clear() + + smua.source.output = 1 + print("Output on!") + delay(0.1) + for x=1,steps do + print("x=", x, "Level=", start+(x*stepv)) + smua.source.levelv = start + stepv*(x-1) + delay(ton) + smua.measure.i(smua.nvbuffer1) + smua.source.levelv = 0 + delay(period - ton) + end + smua.source.output = 0 +end + + + +function PrintBuffer() + printbuffer(1, smua.nvbuffer1.n, smua.nvbuffer1) +end + +function PrintData() + for i=1,smua.nvbuffer1.n do + print(smua.nvbuffer1[i]) + end +end diff --git a/Instrument_Examples/Series_2600/26xxA_Check_for_Overrun.tsp b/Instrument_Examples/Series_2600/26xxA_Check_for_Overrun.tsp new file mode 100644 index 0000000..8a717c4 --- /dev/null +++ b/Instrument_Examples/Series_2600/26xxA_Check_for_Overrun.tsp @@ -0,0 +1,74 @@ +--[[ + CheckForOverRun(pNode) + + This script contains a function that scans the 26XXA status model to + detect an overrun condition in the trigger model. If no overrun is + detected, the function returns false. If overrun is detected, the + function returns true along with a message describing the overrun. + + The Overrun attribute is not available on the Models 2604B/2614B/2634B. + + Parameters: + pNode: The node whose status model to scan. For the SMU + executing the script use localnode for pNode. +]] + +function CheckForOverRun(pNode) + -- Check SMUA Trigger Overruns + if (bit.bitand(pNode.status.operation.instrument.smua.trigger_overrun.condition, 2) == 2) then + return true, "smua arm trigger is overrun" + end + if (bit.bitand(pNode.status.operation.instrument.smua.trigger_overrun.condition, 4) == 4) then + return true, "smua source trigger is overrun" + end + if (bit.bitand(pNode.status.operation.instrument.smua.trigger_overrun.condition, 8) == 8) then + return true, "smua measure trigger is overrun" + end + if (bit.bitand(pNode.status.operation.instrument.smua.trigger_overrun.condition, 16) == 16) then + return true, "smua endpulse trigger is overrun" + end + +--[[ + -- Check SMUB Trigger Overruns + if (bit.bitand(status.operation.instrument.smub.trigger_overrun.condition, 2) == 2) then + return true, "smub arm trigger is overrun" + end + if (bit.bitand(status.operation.instrument.smub.trigger_overrun.condition, 4) == 4) then + return true, "smub source trigger is overrun" + end + if (bit.bitand(status.operation.instrument.smub.trigger_overrun.condition, 8) == 8) then + return true, "smub measure trigger is overrun" + end + if (bit.bitand(status.operation.instrument.smub.trigger_overrun.condition, 16) == 16) then + return true, "smub endpulse trigger is overrun" + end +--]] + + local CFORi = 0 + -- Check Timers for Overrun + if (pNode.status.operation.instrument.trigger_timer.trigger_overrun.condition > 0) then + return true, string.format("Timer trigger is overrun: 0x%x", CFORi) + end + + -- Check Blenders for Overrun + if (pNode.status.operation.instrument.trigger_blender.trigger_overrun.condition > 0) then + return true, string.format("blender trigger is overrun: 0x%x", CFORi) + end + + -- Check TSP-Link Triggers for Overrun + if (pNode.status.operation.instrument.tsplink.trigger_overrun.condition > 0) then + return true, string.format("TSP-Link trigger is overrun: 0x%x", CFORi) + end + + -- Check DIGIO Triggers for Overrun + if (pNode.status.operation.instrument.digio.trigger_overrun.condition > 0) then + return true, string.format("digio trigger is overrun: 0x%x", CFORi) + end + + -- Check LAN Triggers for Overrun + if (pNode.status.operation.instrument.lan.trigger_overrun.condition > 0) then + return true, string.format("LAN trigger is overrun: 0x%x", CFORi) + end + + return false, "no overrun detected" +end \ No newline at end of file From d2ec6fa726d6ae814770b84eaa3c4f40b0383aa3 Mon Sep 17 00:00:00 2001 From: Brad Odhner Date: Wed, 21 Feb 2024 10:24:26 -0500 Subject: [PATCH 10/13] #58 Fix Add missing function and function description. Removed some unneeded line breaks. --- ...nt_Control_Example_Power_Supply_Control.go | 21 ++++-------- ..._Control_Example_Power_Supply_Control_2.go | 12 +++---- ...nstrument_Control_Example_Send_a_Script.go | 33 +++++++++++++++++-- ...l_Example_Single_Threshold_Edge_Trigger.go | 18 ++++++++++ 4 files changed, 60 insertions(+), 24 deletions(-) diff --git a/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Power_Supply_Control.go b/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Power_Supply_Control.go index 7abf106..a4f9540 100644 --- a/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Power_Supply_Control.go +++ b/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Power_Supply_Control.go @@ -165,8 +165,7 @@ func instrument_read(conn *net.TCPConn, anticipated_receive_size int) (string){ Revisions: 2019-07-04 JJB Initial revision. *********************************************************************************/ -func instrument_query(conn *net.TCPConn, my_command string, - anticipated_receive_size int) (string) { +func instrument_query(conn *net.TCPConn, my_command string, anticipated_receive_size int) (string) { // A query is the same as a question: send an "ask" and receive // a reply. instrument_write(conn, my_command + "\n") @@ -193,8 +192,7 @@ func instrument_query(conn *net.TCPConn, my_command string, Revisions: 2019-07-04 JJB Initial revision. *********************************************************************************/ -func load_script_file_onto_keithley_instrument(my_script_file string, - conn *net.TCPConn){ +func load_script_file_onto_keithley_instrument(my_script_file string, conn *net.TCPConn){ var my_response_receive_size = 128 // Read the entire script file into the computer's memory... @@ -203,8 +201,7 @@ func load_script_file_onto_keithley_instrument(my_script_file string, instrument_write(conn, "if loadfuncs ~= nil then script.delete('loadfuncs') end") instrument_write(conn, "loadscript loadfuncs\n" + string(dat) + "\nendscript") - println("Reply from instrument = ", string(instrument_query(conn, "loadfuncs()", - my_response_receive_size))) + println("Reply from instrument = ", string(instrument_query(conn, "loadfuncs()", my_response_receive_size))) } func check(e error) { @@ -230,8 +227,7 @@ func main() { conn, _ := instrument_connect(my_ip_address, my_port) // Ask the instrument to identify itself.... - println("Reply from instrument = ", string(instrument_query(conn, - "*IDN?", my_id_receive_size))) + println("Reply from instrument = ", string(instrument_query(conn, "*IDN?", my_id_receive_size))) instrument_write(conn, "OUTPut:MODE 0") // set for constant voltage instrument_write(conn, "VOLT 0.0") // set voltage level to start at 0V @@ -247,12 +243,9 @@ func main() { instrument_write(conn, command_string) time.Sleep(1 * time.Second) - println("Voltage = ", string(instrument_query(conn, - "MEAS:VOLT?", my_id_receive_size))) - println("Voltage = ", string(instrument_query(conn, - "MEAS:CURR?", my_id_receive_size))) - println("Voltage = ", string(instrument_query(conn, - "MEAS:POW?", my_id_receive_size))) + println("Voltage = ", string(instrument_query(conn, "MEAS:VOLT?", my_id_receive_size))) + println("Voltage = ", string(instrument_query(conn, "MEAS:CURR?", my_id_receive_size))) + println("Voltage = ", string(instrument_query(conn, "MEAS:POW?", my_id_receive_size))) } instrument_write(conn, "OUTP OFF") // turn the output off diff --git a/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Power_Supply_Control_2.go b/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Power_Supply_Control_2.go index 9ec3ed1..212d0f4 100644 --- a/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Power_Supply_Control_2.go +++ b/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Power_Supply_Control_2.go @@ -165,8 +165,7 @@ func instrument_read(conn *net.TCPConn, anticipated_receive_size int) (string){ Revisions: 2019-07-04 JJB Initial revision. *********************************************************************************/ -func instrument_query(conn *net.TCPConn, my_command string, - anticipated_receive_size int) (string) { +func instrument_query(conn *net.TCPConn, my_command string, anticipated_receive_size int) (string) { // A query is the same as a question: send an "ask" and receive // a reply. instrument_write(conn, my_command + "\n") @@ -193,8 +192,7 @@ func instrument_query(conn *net.TCPConn, my_command string, Revisions: 2019-07-04 JJB Initial revision. *********************************************************************************/ -func load_script_file_onto_keithley_instrument(my_script_file string, - conn *net.TCPConn){ +func load_script_file_onto_keithley_instrument(my_script_file string, conn *net.TCPConn){ var my_response_receive_size = 128 // Read the entire script file into the computer's memory... @@ -203,8 +201,7 @@ func load_script_file_onto_keithley_instrument(my_script_file string, instrument_write(conn, "if loadfuncs ~= nil then script.delete('loadfuncs') end") instrument_write(conn, "loadscript loadfuncs\n" + string(dat) + "\nendscript") - println("Reply from instrument = ", string(instrument_query(conn, "loadfuncs()", - my_response_receive_size))) + println("Reply from instrument = ", string(instrument_query(conn, "loadfuncs()", my_response_receive_size))) } func check(e error) { @@ -231,8 +228,7 @@ func main() { conn, _ := instrument_connect(my_ip_address, my_port) // Ask the instrument to identify itself.... - println("Reply from instrument = ", string(instrument_query(conn, - "*IDN?", my_id_receive_size))) + println("Reply from instrument = ", string(instrument_query(conn, "*IDN?", my_id_receive_size))) instrument_write(conn, "OUTPut:MODE 1") // set for constant current command_string = fmt.Sprintf("CURR %f", source_value) diff --git a/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Send_a_Script.go b/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Send_a_Script.go index 651ae0d..868aae1 100644 --- a/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Send_a_Script.go +++ b/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Send_a_Script.go @@ -164,14 +164,43 @@ func instrument_read(conn *net.TCPConn, anticipated_receive_size int) (string){ Revisions: 2019-07-04 JJB Initial revision. *********************************************************************************/ -func instrument_query(conn *net.TCPConn, my_command string, - anticipated_receive_size int) (string) { +func instrument_query(conn *net.TCPConn, my_command string, anticipated_receive_size int) (string) { // A query is the same as a question: send an "ask" and receive // a reply. instrument_write(conn, my_command + "\n") return instrument_read(conn, anticipated_receive_size) } +/********************************************************************************* + Function: load_script_file_onto_keithley_instrument(my_script_file string, + conn *net.TCPConn) + + Purpose: Copy the contents of a specific script file off of the computer + and upload onto the target instrument. + + Parameters: + my_script_file (string) - The script file/path (ASCII text format) that + will be read from the computer and sent to the + instrument. + conn (*net.TCPConn) - The TCP instrument connection object used for + sending and receiving data. + Returns: + None + + Revisions: + 2019-07-04 JJB Initial revision. +*********************************************************************************/ +func load_script_file_onto_keithley_instrument(my_script_file string, conn *net.TCPConn){ + var my_response_receive_size = 128 + + // Read the entire script file into the computer's memory... + dat, err := ioutil.ReadFile(my_script_file) + check(err) + + instrument_write(conn, "if loadfuncs ~= nil then script.delete('loadfuncs') end") + instrument_write(conn, "loadscript loadfuncs\n" + string(dat) + "\nendscript") + println("Reply from instrument = ", string(instrument_query(conn, "loadfuncs()", my_response_receive_size))) +} /********************************************************************************* This example copies the contents of a script file on the host computer and diff --git a/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Single_Threshold_Edge_Trigger.go b/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Single_Threshold_Edge_Trigger.go index 061d81f..614bcd8 100644 --- a/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Single_Threshold_Edge_Trigger.go +++ b/Instrument_Examples/General/Instructables/Get_Started_with_Instr_Control_Go/Instrument_Control_Example_Single_Threshold_Edge_Trigger.go @@ -171,7 +171,25 @@ func instrument_query(conn *net.TCPConn, my_command string, anticipated_receive_ } +/********************************************************************************* + Function: load_script_file_onto_keithley_instrument(my_script_file string, + conn *net.TCPConn) + + Purpose: Copy the contents of a specific script file off of the computer + and upload onto the target instrument. + Parameters: + my_script_file (string) - The script file/path (ASCII text format) that + will be read from the computer and sent to the + instrument. + conn (*net.TCPConn) - The TCP instrument connection object used for + sending and receiving data. + Returns: + None + + Revisions: + 2019-07-04 JJB Initial revision. +*********************************************************************************/ func load_script_file_onto_keithley_instrument(my_script_file string, conn *net.TCPConn){ var my_response_receive_size = 128 From 84145c495b5ca10bca846039ed0334dc357a8d81 Mon Sep 17 00:00:00 2001 From: Odhner Date: Thu, 28 Mar 2024 15:09:05 -0400 Subject: [PATCH 11/13] Add Link to new TSP Toolkit extension TSP Toolkit is now the recommended way to write for and communicate with TSP instruments. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index da2459a..71ab742 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,11 @@ If you don't want to clone this entire repository, it's still possible to take i ## Relevant Keithley Software -If you're looking for officially supported software, we invite you to visit [tek.com/software](https://www.tek.com/software). Here are some especially relevant software options for controlling Keithley equipment: -- [Test Script Builder](https://www.tek.com/keithley-test-script-builder), dedicated IDE for TSP code development. +If you're looking for officially supported software, we invite you to visit [tek.com/software](https://www.tek.com/software). Here are some relevant software options for controlling Keithley equipment: +- [Keithley TSP Toolkit (Beta)](https://github.com/tektronix/tsp-toolkit), An open source [Visual Studio Code](https://code.visualstudio.com/) extension that facilitates communicating with TSP instruments from VS Code, adds command auto-complete and syntax checking, and provides TSP command documentation from within VS Code. - [KickStart](https://www.tek.com/products/keithley/keithley-control-software-bench-instruments/kickstart), Instrument control and automation software for both Keithley and Tektronix products. - [Keithley Automated Characterization Suite (ACS)](https://www.tek.com/products/keithley/semiconductor-test-systems/automated-characterization-suite), Advanced characterization and automation software. +- [Test Script Builder](https://www.tek.com/keithley-test-script-builder), dedicated IDE for TSP code development. Consider using the Visual Studio Code extension, TSP Toolkit, instead. - [More Information on TSP](https://www.tek.com/solutions/application/test-automation/tsp-for-test-automation), our instrument control command set and programming language ## Maintainers From 62946825cf9ce1a6545a5dfe5d1480dd78be5ea2 Mon Sep 17 00:00:00 2001 From: Brad Odhner Date: Wed, 10 Apr 2024 17:33:26 -0400 Subject: [PATCH 12/13] Revise comments to include only code languages --- Instrument_Examples/README.md | 42 +++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/Instrument_Examples/README.md b/Instrument_Examples/README.md index f0a4957..d24b7ce 100644 --- a/Instrument_Examples/README.md +++ b/Instrument_Examples/README.md @@ -6,60 +6,64 @@ These code examples are a good place to start learning how to work with your ins ## Directory [comment]: **[Instrument](./directory)** +[comment]: The order of included languages is: TSP, Python, Matlab, Visual Basic, C#, LabVIEW, (others) * **[General](./General)** -Instrument examples which might be applicable to across an instrument platform, series, or instrument independent. +TSP, Python, Visual Basic, C#, Go * **[DAQ6510](./DAQ6510)** -Examples include those from the module card manuals (7700, 7701, 7702, 7703, 7705, 7706, 7707, 7708, 7709, 7710, 7711, and 7712) where applicable. +TSP, Python, Matlab, Visual Basic, LabVIEW * **[DMM6500](./DMM6500)** -Some examples work with all graphical Keithley instruments. Includes examples from the DMM6500 User's Manual. +TSP, Python * **[DMM7510](./DMM7510)** -Python, TSP, and C# code examples. +TSP, Python, C# * **[Model 2000 DMM](./Model_2000/)** -Visual basic example for controlling 2000-SCAN cards. Also works with DMM6500 in Model 2000 emulation mode. +Visual Basic * **[Model 2001 DMM](./Model_2001/)** -Visual basic examples for the 2001 and 2002 Multimeters. +Visual Basic * **[Model 2015 THD Audio Analyzer DMM](./Model_2015/)** -Python code example. +Python * **[Model 2182A Nanovoltmeter](./Model_2182A/)** -Visual basic example for delta mode with a 2450 SMU. +Visual Basic + +* **[Model 2790 SourceMeter Airbag Test System](./Model_2790/)** +Python * **[Model 4200A-SCS](./Model_4200A/)** -Examples for controlling the 4200A-SCS via the KXCI command set. +Python * **[Models 622x and 2182A](./Model_622x_2182A/)** -Examples of using the 6220 or 6221 current source to measure low resistance with the 2182A Nanovoltmeter. +Python * **[Model 6430 Electrometer](./Model_6430/)** -C# examples. +C# * **[2200 Series Power Supplies](./Series_2200/)** -Python and C# examples. +Python, C# * **[2260B Series Power Supplies](./Series_2260B)** -General examples to show basic and advanced features. +Python * **[2280S Series Power Supplies](./Series_2280S)** -Assorted examples for remotely operating the PSU in different modes and connecting/controlling as a slave instrument using a Keithley TSP-enabled instrument. +TSP, Python * **[2380 Series Electronic Loads](./Series_2380)** -General examples to show basic and advanced features. +Python * **[2400 Series SourceMeter® Source Measure Units](./Series_2400/)** -Code examples for the non-graphical (classic VFD display) 2400 Series Source Measure Units. +Python * **[2400 Series Graphical SourceMeter® Source Measure Units](./Series_2400_Graphical/)** -Code examples for the graphical 2400 Series Source Measure Units. Some examples are written for specific models. +TSP, Python * **[2600B Series SourceMeter® Source Measure Units](./Series_2600/)** -Many examples will work with non-A and A versions of the 2600 Series +TSP, Python * **[3706A Series System Switch/Multimeter](./Series_3706A)** -A variety of scanning and switching examples. \ No newline at end of file +TSP, Python, Visual Basic, C# \ No newline at end of file From 7f09ac6b4371141fb0fde19eda6114274af73974 Mon Sep 17 00:00:00 2001 From: Odhner Date: Mon, 15 Apr 2024 09:35:34 -0400 Subject: [PATCH 13/13] Fix minor README errors --- Instrument_Examples/Model_2790/README.md | 2 +- Instrument_Examples/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Instrument_Examples/Model_2790/README.md b/Instrument_Examples/Model_2790/README.md index a6c1545..9500d7a 100644 --- a/Instrument_Examples/Model_2790/README.md +++ b/Instrument_Examples/Model_2790/README.md @@ -1,6 +1,6 @@ # Model 2790 SourceMeter Airbag Test System -These examples include those found in the User Manual, Application Notes, and other sources. The files in this directory will work with [Model 2790 SourceMeter Airbag Test System](https://www.tek.com/en/products/keithley/switching-and-data-acquisition-systems/2790-airbag-and-electrical-device-test-system), the sub directories will generally only work for those units, but might be able to be adapted. +These examples include those found in the User Manual, Application Notes, and other sources. The files in this directory will work with [Model 2790 SourceMeter Airbag Test System](https://www.tek.com/en/products/keithley/switching-and-data-acquisition-systems/2790-airbag-and-electrical-device-test-system). ## Directory diff --git a/Instrument_Examples/README.md b/Instrument_Examples/README.md index d24b7ce..ceb0508 100644 --- a/Instrument_Examples/README.md +++ b/Instrument_Examples/README.md @@ -6,7 +6,7 @@ These code examples are a good place to start learning how to work with your ins ## Directory [comment]: **[Instrument](./directory)** -[comment]: The order of included languages is: TSP, Python, Matlab, Visual Basic, C#, LabVIEW, (others) + * **[General](./General)** TSP, Python, Visual Basic, C#, Go