-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathKeithleyV15.py
1073 lines (869 loc) · 40.9 KB
/
KeithleyV15.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"""
Library to access the basic functionality of the Keithley SourceMeter 2600 series using pyvisa for communication.
written by: Peter Luidolt @ TUGraz
last modified: 2016-12-21
"""
import pyvisa
# noinspection PyProtectedMember
class _SMUChannel:
# variables to store the ranges that have been selected
# we need this information to check if the limit value is valid
__current_range = 0
__voltage_range = 0
def __init__(self, smu_object, smu_channel):
"""
Implements the functionality for one individual channel of the SMU.
Args:
smu_object (SMU26xx): the SMU the channel belongs to
smu_channel: the channel you want to connect to
Returns:
an "channel" object that has methods to control the channel
"""
# store the parameters in variables that can be accessed from other methods
self.__smu = smu_object
self.__channel = smu_channel
"""
#####################################################################################
commands for setting the mode / ranges / limits / levels
#####################################################################################
"""
def identify(self):
"""
returns a string with model and channel identification
"""
model = self.__smu.identify_model()
if self.__channel is SMU26xx.CHANNEL_A:
channel = "Channel A"
else:
channel = "Channel B"
identification_string = str(model) + " " + str(channel)
return identification_string
def reset(self):
"""
Resets the channel to the default setting of the SMU.
"""
self.__smu._reset(self.__channel)
def set_mode_voltage_source(self):
"""
Sets the channel into voltage source mode.
In this mode you set the voltage and can measure current, resistance and power.
"""
self.__smu._set_mode(self.__channel, SMU26xx.VOLTAGE_MODE)
def set_mode_current_source(self):
"""
Sets the channel into current source mode.
In this mode you set the current and can measure voltage, resistance and power.
"""
self.__smu._set_mode(self.__channel, SMU26xx.CURRENT_MODE)
def enable_voltage_autorange(self):
"""
Enables the autorange feature for the voltage source and measurement
"""
self.__smu._set_autorange(self.__channel, SMU26xx.UNIT_VOLTAGE, SMU26xx.STATE_ON)
def disable_voltage_autorange(self):
"""
Disables the autorange feature for the voltage source and measurement
"""
self.__smu._set_autorange(self.__channel, SMU26xx.UNIT_VOLTAGE, SMU26xx.STATE_OFF)
def enable_current_autorange(self):
"""
Enables the autorange feature for the current source and measurement
"""
self.__smu._set_autorange(self.__channel, SMU26xx.UNIT_CURRENT, SMU26xx.STATE_ON)
def disable_current_autorange(self):
"""
Disables the autorange feature for the current source and measurement
"""
self.__smu._set_autorange(self.__channel, SMU26xx.UNIT_CURRENT, SMU26xx.STATE_OFF)
def set_voltage_range(self, value):
"""
Sets the range for the voltage.
Args:
value: set to the maximum expected voltage be sourced or measured
Examples:
to set the voltage range to 2 V use:
>>> self.set_voltage_range(2)
Note:
The range is applied to the source function as well as the measurement function.
"""
# store the requested voltage range; we check it when the limit is set
self.__voltage_range = value
self.__smu._set_range(self.__channel, SMU26xx.UNIT_VOLTAGE, value)
def set_current_range(self, value):
"""
Sets the range for the current.
Args:
value: set to the maximum expected current be sourced or measured
Examples:
to set the current range to 100 mA use:
>>> self.set_voltage_range(0.1)
you can also use scientific notation: i.e. set the current to 1 µA
>>> self.set_voltage_range(1e-6)
Note:
The range is applied to the source function as well as the measurement function.
"""
# store the requested current range; we check it when the limit is set
self.__current_range = value
self.__smu._set_range(self.__channel, SMU26xx.UNIT_CURRENT, value)
def set_voltage_limit(self, value):
"""
Limits the voltage output of the current source.
Args:
value: set to the maximum allowed voltage.
Examples:
to set the limit to 20 V
>>> self.set_voltage_limit(20)
Note:
If you are in voltage source mode the voltage limit has no effect.
Raises:
ValueError: If `value` is bigger then the selected voltage range.
"""
# check if the limit is within the range
if value <= self.__voltage_range:
self.__smu._set_limit(self.__channel, SMU26xx.UNIT_VOLTAGE, value)
else:
raise ValueError("The limit is not within the range. Please set the range first")
def set_current_limit(self, value):
"""
Limits the current output of the voltage source.
Args:
value: set to the maximum allowed current.
Examples:
to set the limit to 1 mA (both of the lines below do the same)
>>> self.set_current_limit(0.001)
>>> self.set_current_limit(1e-3)
Note:
If you are in current source mode the current limit has no effect.
Raises:
ValueError: If `value` is bigger then the selected current range.
"""
# check if the limit is within the range
if value <= self.__current_range:
self.__smu._set_limit(self.__channel, SMU26xx.UNIT_CURRENT, value)
else:
raise ValueError("The limit is not within the range. Please set the range first")
def set_power_limit(self, value):
"""
Limits the output power.
Args:
value: set to the maximum allowed power.
if you set the `value` to 0 the limit will be disabled
Examples:
to set the limit to 1 mW (both of the lines below do the same)
>>> self.set_power_limit(0.001)
>>> self.set_power_limit(1e-3)
to disable the output power limit
>>> self.set_power_limit(0)
"""
self.__smu._set_limit(self.__channel, SMU26xx.UNIT_POWER, value)
def set_voltage(self, value):
"""
Sets the output level of the voltage source.
Args:
value: source voltage level.
Examples:
to set the output level to 500 mV
>>> self.set_voltage(0.5)
Note:
If the source is configured as a voltage source and the output is on,
the new setting is sourced immediately.
The sign of `level` dictates the polarity of the source.
Positive values generate positive voltage from the high terminal of the source relative to the low terminal.
Negative values generate negative voltage from the high terminal of the source relative to the low terminal.
"""
self.__smu._set_level(self.__channel, SMU26xx.UNIT_VOLTAGE, value)
def set_current(self, value):
"""
Sets the output level of the current source.
Args:
value: source current level.
Examples:
to set the output level to 10 µA
>>> self.set_current(10e-6)
Note:
If the source is configured as a current source and the output is on, the new setting is sourced immediately.
The sign of `level` dictates the polarity of the source.
Positive values generate positive current from the high terminal of the source relative to the low terminal.
Negative values generate negative current from the high terminal of the source relative to the low terminal.
"""
self.__smu._set_level(self.__channel, SMU26xx.UNIT_CURRENT, value)
def enable_output(self):
"""
Sets the source output state to on.
Examples:
to enable the output
>>> self.enable_output()
Note:
When the output is switched on, the SMU sources either voltage or current, as set by
set_mode_voltage_source() or set_mode_current_source()
"""
self.__smu._set_output_state(self.__channel, SMU26xx.STATE_ON)
def disable_output(self):
"""
Sets the source output state to off.
Examples:
to disable the output
>>> self.disable_output()
Note:
When the output is switched off, the SMU goes in to low Z mode (meaning: the output is shorted).
Be careful when using the SMU for measurement of high power devices. The disabling of the output could lead
high current flow.
"""
self.__smu._set_output_state(self.__channel, SMU26xx.STATE_OFF)
"""
#####################################################################################
commands for setting what measurement will be shown at the display of the SMU channel
#####################################################################################
"""
def display_voltage(self):
"""
The voltage measurement will be displayed on the SMU.
"""
self.__smu._set_display(self.__channel, SMU26xx.DISPLAY_VOLTAGE)
def display_current(self):
"""
The current measurement will be displayed on the SMU.
"""
self.__smu._set_display(self.__channel, SMU26xx.DISPLAY_CURRENT)
def display_resistance(self):
"""
The calculated resistance will be displayed on the SMU.
"""
self.__smu._set_display(self.__channel, SMU26xx.DISPLAY_RESISTANCE)
def display_power(self):
"""
The calculated power will be displayed on the SMU.
"""
self.__smu._set_display(self.__channel, SMU26xx.DISPLAY_POWER)
"""
#####################################################################################
commands for setting the sense mode (2-wire or 4-wire)
#####################################################################################
"""
def set_sense_2wire(self):
"""
Setting the the sense mode to local (2-wire)
Notes:
Corresponding LUA command (SMU 2600B reference manual page 2-77)
smuX.sense = smuX.SENSE_LOCAL
"""
self.__smu._set_sense_mode(self.__channel, SMU26xx.SENSE_MODE_2_WIRE)
def set_sense_4wire(self):
"""
Setting the the sense mode to local (4-wire)
Notes:
Corresponding LUA command (SMU 2600B reference manual page 2-77)
smuX.sense = smuX.SENSE_REMOTE
"""
self.__smu._set_sense_mode(self.__channel, SMU26xx.SENSE_MODE_4_WIRE)
"""
#####################################################################################
commands for setting the measurement speed / accuracy
#####################################################################################
"""
def set_measurement_speed_fast(self):
"""
This attribute controls the integration aperture for the analog-to-digital converter (ADC).
fast corresponds to 0.01 PLC (Power Line Cycles) -> approx. 5000 measurements per second
Results in: fast performance, but accuracy is reduced
"""
self.__smu._set_measurement_speed(self.__channel, SMU26xx.SPEED_FAST)
def set_measurement_speed_med(self):
"""
This attribute controls the integration aperture for the analog-to-digital converter (ADC).
fast corresponds to 0.1 PLC (Power Line Cycles) -> approx. 500 measurements per second
Results in: speed and accuracy are balanced
"""
self.__smu._set_measurement_speed(self.__channel, SMU26xx.SPEED_MED)
def set_measurement_speed_normal(self):
"""
This attribute controls the integration aperture for the analog-to-digital converter (ADC).
fast corresponds to 1 PLC (Power Line Cycles) -> approx. 50 measurements per second
Results in: speed and accuracy are balanced
"""
self.__smu._set_measurement_speed(self.__channel, SMU26xx.SPEED_NORMAL)
def set_measurement_speed_hi_accuracy(self):
"""
This attribute controls the integration aperture for the analog-to-digital converter (ADC).
fast corresponds to 10 PLC (Power Line Cycles) -> approx. 5 measurements per second
Results in: high accuracy, but speed is reduced
"""
self.__smu._set_measurement_speed(self.__channel, SMU26xx.SPEED_HI_ACCURACY)
"""
#####################################################################################
commands for reading values
#####################################################################################
"""
def measure_voltage(self):
"""
Causes the SMU to trigger a voltage measurement and return a single reading.
Returns:
float: the value of the reading in volt
"""
return self.__smu._measure(self.__channel, SMU26xx.UNIT_VOLTAGE)
def measure_current(self):
"""
Causes the SMU to trigger a current measurement and return a single reading.
Returns:
float: the value of the reading in ampere
"""
return self.__smu._measure(self.__channel, SMU26xx.UNIT_CURRENT)
def measure_resistance(self):
"""
Causes the SMU to trigger a resistance measurement and return a single reading.
Returns:
float: the value of the reading in ohm
"""
return self.__smu._measure(self.__channel, SMU26xx.UNIT_RESISTANCE)
def measure_power(self):
"""
Causes the SMU to trigger a power measurement and return a single reading.
Returns:
float: the value of the reading in watt
"""
return self.__smu._measure(self.__channel, SMU26xx.UNIT_POWER)
def measure_current_and_voltage(self):
"""
Causes the SMU to trigger a voltage and current measurement simultaneously.
Use this function if you need exact time correlation between voltage and current.
Examples:
measure current and voltage simultaneously
>>> [current, voltage] = self.measure_current_and_voltage()
Returns:
list: a list of the two measured values.
current as the first list element
voltage as the second list element
"""
return self.__smu._measure(self.__channel, SMU26xx.UNIT_CURRENT_VOLTAGE)
def measure_voltage_sweep(self, start_value, stop_value, settling_time, points):
"""
Causes the SMU to make a voltage sweep based on a staircase profile.
Args:
start_value: the voltage level from which the sweep will start.
stop_value: the voltage level at which the sweep will stop.
settling_time: the time the unit will wait after a voltage step is reached before a measurement
is triggered. If set to 0 the measurement will be done as fast as possible.
points: the number of steps.
Note:
If you want to measure really fast be sure that you have set the measurement speed accordingly
Examples:
perform a voltage sweep from 0 V to 5 V with 500 steps (so 10 mV step size) as fast as possible
>>> self.set_measurement_speed_fast()
>>> [current_list, voltage_list] = self.measure_voltage_sweep(0, 5, 0, 500)
Returns:
list: the returning list contains itself two lists
first element is a list of the measured current values
second element is a list of the voltage source values (not the actual measured voltage)
"""
return self.__smu._measure_linear_sweep(self.__channel, SMU26xx.UNIT_VOLTAGE,
start_value, stop_value, settling_time, points)
def measure_current_sweep(self, start_value, stop_value, settling_time, points):
"""
Causes the SMU to make a current sweep based on a staircase profile.
Args:
start_value: the current level from which the sweep will start.
stop_value: the current level at which the sweep will stop.
settling_time: the time the unit will wait after a current step is reached before a measurement
is triggered. If set to 0 the measurement will be done as fast as possible.
points: the number of steps.
Note:
If you want to measure really fast be sure that you have set the measurement speed accordingly
Examples:
perform a current sweep from 1 mA to 100 mA with 1000 steps (so 0.1 mA step size)
and let the device under test 1 second time to settle before taking a measurement
>>> self.set_measurement_speed_normal()
>>> [current_list, voltage_list] = self.measure_voltage_sweep(1e-3, 0.1, 1, 1000)
Returns:
list: the returning list contains itself two lists
first element is a list of the current source values (not the actual measured current)
second element is a list of the measured voltage
"""
return self.__smu._measure_linear_sweep(self.__channel, SMU26xx.UNIT_CURRENT,
start_value, stop_value, settling_time, points)
class SMU26xx:
# define strings that are used in the LUA commands
CHANNEL_A = "a"
CHANNEL_B = "b"
# defines an arbitrary word; when used the program tries to access all available channels
CHANNEL_ALL = "all"
CURRENT_MODE = "DCAMPS"
VOLTAGE_MODE = "DCVOLTS"
DISPLAY_VOLTAGE = 'DCVOLTS'
DISPLAY_CURRENT = 'DCAMPS'
DISPLAY_RESISTANCE = 'OHMS'
DISPLAY_POWER = 'WATTS'
SENSE_MODE_2_WIRE = 'SENSE_LOCAL'
SENSE_MODE_4_WIRE = 'SENSE_REMOTE'
UNIT_VOLTAGE = "v"
UNIT_CURRENT = "i"
UNIT_CURRENT_VOLTAGE = "iv"
UNIT_POWER = "p"
UNIT_RESISTANCE = "r"
STATE_ON = "ON"
STATE_OFF = "OFF"
SPEED_FAST = 0.01
SPEED_MED = 0.1
SPEED_NORMAL = 1
SPEED_HI_ACCURACY = 10
# maximum amount of values that can be read from the Keithley buffer without an error from the
# pyvisa interface. We set it to 1000 values.
__PYVISA_MAX_BUFFER_REQUEST = 1000
def __init__(self, visa_resource_name, timeout=1000):
"""
Implements the global (channel independent) functionality for the Keithley SMU 2600 series.
The communication is made through NI-VISA (you need to have this installed)
Args:
visa_resource_name: use exactly the VISA-resource-name you see in your NI-MAX
Returns:
pyvisa.ResourceManager.open_resource: Object to control the SMU
"""
# Variables to store the capabilities of the instrument
self.__voltage_ranges = None
self.__current_ranges = None
self.__channel_b_present = None
# variable to store if the debug output was enabled
self.__debug = False
# open the resource manager
__rm = pyvisa.ResourceManager()
# Connect to the device
self.__instrument = __rm.open_resource(visa_resource_name)
self.__connected = True
# set the timeout
self.__instrument.timeout = timeout
# clear the error queue
self.__clear_error_queue()
# clear everything that may is in the buffer
self.__instrument.clear()
# find out the ranges of the device and set the limits
model = self.identify_model()
self.set_model_limits(model)
def disconnect(self):
"""
Disconnect the instrument. After this no further communication is possible.
"""
if self.__connected:
self.__instrument.close()
self.__connected = False
def get_channel(self, channel):
"""
Gives you an object with which you can control the individual parameters of a channel.
Args:
channel: the channel you want to connect to.
Use the keywords SMU26xx.CHANNEL_A or SMU26xx.CHANNEL_B
Returns:
_SMUChannel: an "channel" object that has methods to control the channel
Raises:
ValueError: If the channel is not available.
"""
# check if the channel b is available. We don't have to check channel a because every smu has one
if channel is SMU26xx.CHANNEL_B and not self.__channel_b_present:
raise ValueError("No channel B on this model")
return _SMUChannel(self, channel)
def enable_debug_output(self):
"""
Enables the debug output of all communication to the SMU.
The messages will be printed on the console.
"""
self.__debug = True
def disable_debug_output(self):
"""
Disables the debug output. Nothing will be printed to the console that you haven't specified yourself.
"""
self.__debug = False
"""
#####################################################################################
commands for communicating with the instrument via the pyvisa interface
#####################################################################################
"""
def __clear_error_queue(self):
"""
internal function to clear the error queue of the SMU
"""
self.write_lua("errorqueue.clear()")
def __check_error_queue(self):
"""
requests the error queue from the SMU. If there is an error this function will raise an
value error containing the message from the SMU.
Raises:
ValueError: If there is an error stored at the SMU
"""
# check if there was an error
cmd = "errorcode, message = errorqueue.next()\nprint(errorcode, message)"
response = self.__instrument.query(str(cmd))
if self.__debug:
print('Error msg: ' + str(response))
try:
[code, message] = response.split('\t', 1)
if float(code) != 0:
# if we have an error code something happened and we should raise an error
raise ValueError('The SMU said: "' + str(message) + '" / Keithley-Error-Code: ' + str(code))
except:
raise ValueError('The SMU said: "' + str(response))
def write_lua(self, cmd, check_for_errors=True):
"""
Writes a command to the pyvisa connection. It expects no return message from the SMU
Args:
cmd: the TSP command for the SMU
check_for_errors: by default the error queue of the SMU is checked after every command that is send to the
SMU. In some cases the SMU will not respond to this check and a pyvisa timeout would occur. In such
a case you can disable this check.
"""
if self.__debug:
print('Write cmd: ' + str(cmd))
self.__instrument.write(str(cmd))
# check if the command executed without any errors
if check_for_errors:
self.__check_error_queue()
def query_lua(self, cmd, check_for_errors=True):
"""
Queries something from the SMU with TSP syntax.
basically we just write a TSP command and expect some kind of response from the SMU
Args:
cmd: the TSP command for the SMU
check_for_errors: by default the error queue of the SMU is checked after every command that is send to the
SMU. In some cases the SMU will not respond to this check and a pyvisa timeout would occur. In such
a case you can disable this check.
"""
if self.__debug:
print('Query cmd: ' + str(cmd))
# send the request to the device
reading = self.__instrument.query(str(cmd)).rstrip('\r\n')
if self.__debug:
print('Query answer: ' + str(reading))
# check if the command executed without any errors
if check_for_errors:
self.__check_error_queue()
return reading
"""
#####################################################################################
commands that gather information of the device and set parameter
#####################################################################################
"""
def identify_model(self):
"""
Returns the model number of the SMU. Based on this string the model limits are set.
Returns:
str: the model number of the SMU
"""
return self.query_lua('print(localnode.model)')
def set_model_limits(self, model_number):
"""
This function is used to set the model specific differences. This method is called at the initialisation
process. There is usually no need for you to call this method.
Args:
model_number (str): the model number of the SMU.
"""
if self.__debug:
print("Model " + str(model_number) + " detected. Setting ranges ...")
if "2601B" in model_number:
self.__voltage_ranges = [0.1, 1, 6, 40]
self.__current_ranges = [1E-7, 1E-6, 1E-5, 1E-4, 1E-3, 1E-2, 1E-1, 1, 3]
self.__channel_b_present = False
elif "2612A" in model_number:
self.__voltage_ranges = [0.2, 2, 20, 200]
self.__current_ranges = [1E-7, 1E-6, 1E-5, 1E-4, 1E-3, 1E-2, 1E-1, 1, 1.5]
self.__channel_b_present = True
elif "2614B" in model_number:
self.__voltage_ranges = [0.2, 2, 20, 200]
self.__current_ranges = [1E-7, 1E-6, 1E-5, 1E-4, 1E-3, 1E-2, 1E-1, 1, 1.5]
self.__channel_b_present = True
elif "2636A" in model_number:
self.__voltage_ranges = [0.2, 2, 20, 200]
self.__current_ranges = [1E-9, 1E-8, 1E-7, 1E-6, 1E-5, 1E-4, 1E-3, 1E-2, 1E-1, 1, 1.5]
self.__channel_b_present = True
else:
raise ValueError("unknown model number")
def get_available_voltage_ranges(self):
"""
Returns a list containing the available voltage ranges based on the model limits.
Returns:
list: containing the available voltage ranges
"""
return self.__voltage_ranges
def get_available_current_ranges(self):
"""
Returns a list containing the available current ranges based on the model limits.
Returns:
list: containing the available current ranges
"""
return self.__current_ranges
"""
#####################################################################################
commands for measuring values from the two channels simultaneously
#####################################################################################
"""
def measure_voltage(self):
"""
Causes the SMU to trigger a voltage measurement and return a single reading for both channels (if available).
Use this function if you need exact time correlation between the voltage of the two channels.
Examples:
measure voltage simultaneously on both channels
>>> [v_chan_a, v_chan_b] = self.measure_voltage()
Returns:
list: a list of floats containing the two measured values.
voltage measurement of channel a as the first list element
voltage measurement of channel b as the second list element
Raises:
ValueError: If the SMU has just one channel
"""
return self._measure(SMU26xx.CHANNEL_ALL, SMU26xx.UNIT_VOLTAGE)
def measure_current(self):
"""
Causes the SMU to trigger a current measurement and return a single reading for both channels (if available).
Use this function if you need exact time correlation between the current of the two channels.
Examples:
measure current simultaneously on both channels
>>> [i_chan_a, i_chan_b] = self.measure_current()
Returns:
list: a list of floats containing the two measured values.
current measurement of channel a as the first list element
current measurement of channel b as the second list element
Raises:
ValueError: If the SMU has just one channel
"""
return self._measure(SMU26xx.CHANNEL_ALL, SMU26xx.UNIT_CURRENT)
def measure_resistance(self):
"""
Causes the SMU to trigger a resistance measurement and return a single reading for both channels (if available).
Use this function if you need exact time correlation between the resistance of the two channels.
Examples:
measure resistance simultaneously on both channels
>>> [r_chan_a, r_chan_b] = self.measure_resistance()
Returns:
list: a list of floats containing the two measured values.
resistance measurement of channel a as the first list element
resistance measurement of channel b as the second list element
Raises:
ValueError: If the SMU has just one channel
"""
return self._measure(SMU26xx.CHANNEL_ALL, SMU26xx.UNIT_RESISTANCE)
def measure_power(self):
"""
Causes the SMU to trigger a power measurement and return a single reading for both channels (if available).
Use this function if you need exact time correlation between the power of the two channels.
Examples:
measure power simultaneously on both channels
>>> [p_chan_a, p_chan_b] = self.measure_power()
Returns:
list: a list of floats containing the two measured values.
power of channel a as the first list element
power of channel b as the second list element
Raises:
ValueError: If the SMU has just one channel
"""
return self._measure(SMU26xx.CHANNEL_ALL, SMU26xx.UNIT_POWER)
def measure_current_and_voltage(self):
"""
Causes the SMU to trigger a voltage and current measurement simultaneously for both channels (if available).
Use this function if you need exact time correlation between voltage and current of the two channels.
Examples:
measure current and voltage simultaneously on both channels
>>> [i_chan_a, v_chan_a, i_chan_b, v_chan_b] = self.measure_current_and_voltage()
Returns:
list: a list of floats containing the four measured values.
current of channel a as the first list element
voltage of channel a as the second list element
current of channel b as the third list element
voltage of channel b as the fourth list element
Raises:
ValueError: If the SMU has just one channel
"""
return self._measure(SMU26xx.CHANNEL_ALL, SMU26xx.UNIT_CURRENT_VOLTAGE)
"""
#####################################################################################
commands for setting the parameters of channels
those should not be accessed directly but through the channel class
#####################################################################################
"""
def _reset(self, channel):
"""restore the default settings"""
cmd = 'smu' + str(channel) + '.reset()'
self.write_lua(cmd)
def _set_display(self, channel, function):
"""defines what measurement will be shown on the display"""
cmd = 'display.smu' + str(channel) + '.measure.func = display.MEASURE_' + str(function)
self.write_lua(cmd)
def _set_measurement_speed(self, channel, speed):
"""defines how many PLC (Power Line Cycles) a measurement takes"""
cmd = 'smu' + str(channel) + '.measure.nplc = ' + str(speed)
self.write_lua(cmd)
def _set_mode(self, channel, mode):
cmd = 'smu' + str(channel) + '.source.func = ' + 'smu' + str(channel) + '.OUTPUT_' + str(mode)
self.write_lua(cmd)
def _set_sense_mode(self, channel, mode):
"""
set 2-wire or 4-wire sense mode
Manual page 2-77
Notes:
LUA commands look like this
smua.sense = smua.SENSE_REMOTE
smua.sense = smua.SENSE_LOCAL
"""
cmd = 'smu' + str(channel) + '.sense = ' + 'smu' + str(channel) + '.' + str(mode)
self.write_lua(cmd)
def _set_autorange(self, channel, unit, state):
"""enables or disables the autorange feature"""
# set the source range
cmd = 'smu' + str(channel) + '.source.autorange' + str(unit) \
+ ' = smu' + str(channel) + '.AUTORANGE_' + str(state)
self.write_lua(cmd)
# set the measurement range
cmd = 'smu' + str(channel) + '.measure.autorange' + str(unit) \
+ ' = smu' + str(channel) + '.AUTORANGE_' + str(state)
self.write_lua(cmd)
def _set_range(self, channel, unit, range_value):
"""Set the range to the given value (or to the next suitable range)"""
range_found = 0
# select the range you want to compare to based on the given type
if unit is self.UNIT_CURRENT:
range_to_check = self.__current_ranges
elif unit is self.UNIT_VOLTAGE:
range_to_check = self.__voltage_ranges
else:
raise ValueError('Type "' + str(unit) + '" is valid in range setting')
# find the range that fits the desired value best
if range_value in range_to_check:
range_found = range_value
else:
# if there is no exact match use the range that is best suitable
for v in sorted(range_to_check):
if v > range_value:
range_found = v
break
# if none of the ranges above work ... raise an error
if not range_found:
raise ValueError("no suitable range found")
# set the source range
cmd = 'smu' + str(channel) + '.source.range' + str(unit) + ' = ' + str(range_found)
self.write_lua(cmd)
# set the measurement range
cmd = 'smu' + str(channel) + '.measure.range' + str(unit) + ' = ' + str(range_found)
self.write_lua(cmd)
def _set_limit(self, channel, unit, value):
"""command used to set the limits for voltage, current or power"""
# send the command to the SourceMeter
cmd = 'smu' + str(channel) + '.source.limit' + str(unit) + ' = ' + str(value)
self.write_lua(cmd)
def _set_level(self, channel, unit, value):
# send the command to the SourceMeter
cmd = 'smu' + str(channel) + '.source.level' + str(unit) + ' = ' + str(value)
self.write_lua(cmd)
def _set_output_state(self, channel, state):
cmd = 'smu' + str(channel) + '.source.output = smu' + str(channel) + '.OUTPUT_' + str(state)
self.write_lua(cmd)
"""
#####################################################################################
commands for reading values from the channels
those should not be accessed directly but through the channel class
#####################################################################################
"""
def _measure(self, channel, unit):
"""function for getting a single reading of the specified value"""
# if CHANNEL_ALL is specified this has only an effect on two channel units
if channel == SMU26xx.CHANNEL_ALL:
# if channel b is present then modify the LUA command
if self.__channel_b_present:
# In case we want to measure voltage and current we get four return parameters
# so the LUA command has to be different.
if unit == SMU26xx.UNIT_CURRENT_VOLTAGE:
cmd = 'iChA, vChA = smua.measure.' + str(unit) + '()\n' \
+ 'iChB, vChB = smub.measure.' + str(unit) + '()\n' \
+ 'print(iChA, vChA, iChB, vChB)'
else:
cmd = 'ChA = smua.measure.' + str(unit) + '()\n' \
+ 'ChB = smub.measure.' + str(unit) + '()\n' \
+ 'print(ChA, ChB)'
else:
raise ValueError("This device has only ONE channel. "
"Use the measurement function of the channel instead.")
else:
cmd = 'print(smu' + str(channel) + '.measure.' + str(unit) + '())'
reading = self.query_lua(cmd)
reading = reading.replace("'", "")
# if we get more than one value out then put it in a list
out = []
parts = reading.split("\t")
if len(parts) > 1:
for value in parts:
out.append(float(value))
return out
else:
return float(reading)
def _measure_linear_sweep(self, channel, unit, start_value, stop_value, settling_time, points):
"""function to sweep voltage or current and measure current resp. voltage"""
sweep_unit = measure_unit = ''
if unit is self.UNIT_VOLTAGE:
sweep_unit = 'V'
measure_unit = 'I'
elif unit is self.UNIT_CURRENT:
sweep_unit = 'I'
measure_unit = 'V'
else:
ValueError('Only possible to sweep Voltage or Current')
# prepare the buffer