|
1 | | -#------------------------------------------------------------------------------- |
2 | | -# Name: Keithley 6517B electrometer |
3 | | -# Purpose: Current Measurement Backend |
4 | | -# Author: Prathamesh K Deshmukh |
5 | | -# Created: 03-03-2024 |
6 | | -# Updates: V1.3 (Fixed DataFrame saving bug) |
7 | | -#------------------------------------------------------------------------------- |
| 1 | +''' |
| 2 | +=============================================================================== |
| 3 | + PROGRAM: Keithley 6517B I-V Sweep Measurement |
| 4 | +
|
| 5 | + PURPOSE: Perform a voltage sweep and measure current to generate an I-V |
| 6 | + curve using a Keithley 6517B. |
| 7 | +
|
| 8 | + DESCRIPTION: This script automates an I-V sweep measurement using a Keithley |
| 9 | + 6517B electrometer. It prompts the user for sweep parameters |
| 10 | + (start/stop voltage, steps, delay), performs a zero-check |
| 11 | + correction, executes the voltage sweep while measuring current, |
| 12 | + saves the data to a CSV file, and displays a plot of the |
| 13 | + resulting I-V curve. |
| 14 | +
|
| 15 | + AUTHOR: Prathamesh Deshmukh |
| 16 | + GUIDED BY: Dr. Sudip Mukherjee |
| 17 | + INSTITUTE: UGC-DAE Consortium for Scientific Research, Mumbai Centre |
| 18 | +
|
| 19 | + VERSION: 5.1 |
| 20 | + LAST EDITED: 04/10/2025 |
| 21 | +=============================================================================== |
| 22 | +''' |
8 | 23 |
|
9 | 24 | import time |
| 25 | +import csv |
10 | 26 | import numpy as np |
11 | | -import pandas as pd |
12 | | -import pyvisa |
13 | | -from pymeasure.instruments.keithley import Keithley6517B |
| 27 | +import matplotlib.pyplot as plt |
| 28 | +from datetime import datetime |
| 29 | + |
| 30 | +try: |
| 31 | + from pymeasure.instruments.keithley import Keithley6517B |
| 32 | + from pyvisa.errors import VisaIOError |
| 33 | +except ImportError: |
| 34 | + print("Error: Required packages not found.") |
| 35 | + print("Please install them by running: pip install numpy matplotlib pymeasure") |
| 36 | + exit() |
| 37 | + |
| 38 | +# --- 1. USER CONFIGURATION --- |
| 39 | +# The VISA address is fixed as it was in V5. |
| 40 | +VISA_ADDRESS = "GPIB1::27::INSTR" |
| 41 | + |
| 42 | +def get_sweep_parameters(): |
| 43 | + """Gets I-V sweep parameters from the user.""" |
| 44 | + print("--- I-V Sweep Configuration ---") |
| 45 | + start_v = float(input("Enter Start Voltage (V): ")) |
| 46 | + stop_v = float(input("Enter Stop Voltage (V): ")) |
| 47 | + steps = int(input("Enter Number of Steps: ")) |
| 48 | + delay = float(input("Enter Settling Delay between points (s): ")) |
| 49 | + filename = input("Enter the filename to save data (e.g., SampleA_IV.csv): ") |
| 50 | + |
| 51 | + if not filename.lower().endswith('.csv'): |
| 52 | + filename += '.csv' |
| 53 | + return start_v, stop_v, steps, delay, filename |
| 54 | + |
| 55 | +def plot_results(data): |
| 56 | + """Plots the I-V curve from the collected data.""" |
| 57 | + if not data: |
| 58 | + print("\nNo data to plot.") |
| 59 | + return |
| 60 | + |
| 61 | + voltages = [float(row[1]) for row in data] |
| 62 | + currents = [float(row[2]) for row in data] |
| 63 | + |
| 64 | + plt.figure(figsize=(8, 6)) |
| 65 | + plt.plot(voltages, currents, 'o-', label='I-V Data', color='#003f5c') |
| 66 | + plt.title('I-V Measurement Curve', fontsize=16) |
| 67 | + plt.xlabel('Applied Voltage (V)', fontsize=12) |
| 68 | + plt.ylabel('Measured Current (A)', fontsize=12) |
| 69 | + plt.grid(True, which="both", ls="--", alpha=0.6) |
| 70 | + plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) |
| 71 | + plt.legend() |
| 72 | + plt.tight_layout() |
| 73 | + print("\nDisplaying I-V plot...") |
| 74 | + plt.show() |
14 | 75 |
|
15 | | -I = [] |
16 | | -t = [] |
| 76 | +# --- Main Execution --- |
| 77 | +keithley = None |
| 78 | +results = [] |
17 | 79 |
|
18 | 80 | try: |
19 | | - # Initialize Instrument |
20 | | - keithley = Keithley6517B("GPIB0::27::INSTR") |
21 | | - time.sleep(0.5) |
22 | | - |
23 | | - # Setup Measurement |
24 | | - keithley.measure_current() |
25 | | - time.sleep(0.5) |
26 | | - |
27 | | - print("Starting measurement... Press Ctrl+C to stop.") |
28 | | - start_time = time.time() |
29 | | - |
30 | | - while True: |
31 | | - elapsed_time = time.time() - start_time |
32 | | - current = keithley.current # Read current in Amps |
33 | | - |
34 | | - # Store data in lists |
35 | | - t.append(elapsed_time) |
36 | | - I.append(current) |
37 | | - |
38 | | - print("Time: " + str(elapsed_time) + "\t\t\t|\t\t\t Current: " + str(current) + " A") |
39 | | - time.sleep(2) |
40 | | - |
41 | | -except KeyboardInterrupt: |
42 | | - # Graceful Exit on Ctrl+C |
43 | | - print("\nMeasurement stopped by User.") |
44 | | - |
45 | | - # --- FIX IS HERE: Define data_df before using it --- |
46 | | - data_df = pd.DataFrame({"Timestamp": t, "Current (A)": I}) |
47 | | - data_df.to_csv("demo_data.dat", index=False) |
48 | | - print(f"Data saved to file: demo_data.dat") |
49 | | - |
50 | | - # Shutdown Sequence |
51 | | - time.sleep(0.5) |
52 | | - keithley.shutdown() # Ramps current to 0 and disables output |
53 | | - print("Keithley closed.") |
| 81 | + # Get sweep parameters from the user |
| 82 | + start_v, stop_v, steps, delay, filename = get_sweep_parameters() |
| 83 | + voltage_sweep = np.linspace(start_v, stop_v, steps) |
54 | 84 |
|
| 85 | + # --- 2. CONNECT TO INSTRUMENT (V5 Logic) --- |
| 86 | + print(f"\nAttempting to connect to instrument at: {VISA_ADDRESS}") |
| 87 | + keithley = Keithley6517B(VISA_ADDRESS) |
| 88 | + print(f"Successfully connected to: {keithley.id}") |
| 89 | + |
| 90 | + # --- 3. CONFIGURE MEASUREMENT (V5 Logic) --- |
| 91 | + print("\nConfiguring instrument for measurement...") |
| 92 | + keithley.reset() |
| 93 | + # Set the function to resistance to ensure the ammeter is configured for zero correction. |
| 94 | + keithley.measure_resistance() |
| 95 | + |
| 96 | + # --- 4. PERFORM ZERO CHECK & CORRECTION (Exact V5 Logic) --- |
| 97 | + print("\nStarting zero correction procedure...") |
| 98 | + time.sleep(5) |
| 99 | + print("Step 1: Enabling Zero Check mode...") |
| 100 | + keithley.write(':SYSTem:ZCHeck ON') |
| 101 | + time.sleep(5) |
| 102 | + print("Step 2: Acquiring zero correction value...") |
| 103 | + #keithley.write(':SYSTem:ZCORrect:ACQuire') |
| 104 | + time.sleep(5) |
| 105 | + print("Step 3: Disabling Zero Check mode...") |
| 106 | + keithley.write(':SYSTem:ZCHeck OFF') |
| 107 | + time.sleep(5) |
| 108 | + print("Step 4: Enabling Zero Correction...") |
| 109 | + keithley.write(':SYSTem:ZCORrect ON') |
| 110 | + time.sleep(5) |
| 111 | + print("Zero Correction Complete.") |
| 112 | + |
| 113 | + # --- 5. SETUP AND PERFORM I-V SWEEP --- |
| 114 | + print(f"\nStarting I-V sweep from {start_v}V to {stop_v}V...") |
| 115 | + keithley.current_nplc = 1 # Set integration rate for noise reduction |
| 116 | + |
| 117 | + keithley.enable_source() |
| 118 | + |
| 119 | + with open(filename, 'w', newline='') as f: |
| 120 | + writer = csv.writer(f) |
| 121 | + writer.writerow([f"# Measurement Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"]) |
| 122 | + writer.writerow([f"# Sweep Parameters: Start={start_v}V, Stop={stop_v}V, Steps={steps}, Delay={delay}s"]) |
| 123 | + writer.writerow(["Timestamp (s)", "Applied Voltage (V)", "Measured Current (A)", "Resistance (Ohm)"]) |
| 124 | + |
| 125 | + start_time = time.time() |
| 126 | + for i, voltage in enumerate(voltage_sweep): |
| 127 | + keithley.source_voltage = voltage |
| 128 | + time.sleep(delay) |
| 129 | + resistance = keithley.resistance |
| 130 | + timestamp = time.time() - start_time |
| 131 | + #resistance = keithley.resistance |
| 132 | + current = voltage/resistance if resistance != 0 else float('inf') |
| 133 | + |
| 134 | + print(f"Step {i+1}/{steps}: V={voltage:.3f} V, I={current:.4e} A, R={resistance:.4e} Ω") |
| 135 | + |
| 136 | + data_point = [f"{timestamp:.3f}", f"{voltage:.4e}", f"{current:.4e}", f"{resistance:.4e}"] |
| 137 | + results.append(data_point) |
| 138 | + writer.writerow(data_point) |
| 139 | + |
| 140 | + print("\n--- I-V Sweep Complete ---") |
| 141 | + print(f"Data saved successfully to '{filename}'") |
| 142 | + |
| 143 | +except VisaIOError: |
| 144 | + print(f"\n[VISA Connection Error]") |
| 145 | + print(f"Could not connect to the instrument at '{VISA_ADDRESS}'.") |
| 146 | + print("Please check the address, cable connections, and if the instrument is on.") |
| 147 | +except ValueError: |
| 148 | + print("\n[Input Error] Please enter valid numbers for the sweep parameters.") |
55 | 149 | except Exception as e: |
56 | | - print(f"Error with Keithley: {e}") |
| 150 | + print(f"\n[An Unexpected Error Occurred] Details: {e}") |
| 151 | + |
| 152 | +finally: |
| 153 | + # --- 7. SAFELY SHUT DOWN (V5 Logic) --- |
| 154 | + if keithley: |
| 155 | + print("\nShutting down instrument...") |
| 156 | + keithley.shutdown() |
| 157 | + print("Voltage source OFF and instrument is safe.") |
| 158 | + |
| 159 | +# --- 8. PLOT RESULTS --- |
| 160 | +plot_results(results) |
0 commit comments