Keysight 237控制软件之---利用线程和设备通信(python)

前情:实验室有一个Keysight237设备,主要是做半导体电学测试的,可以测电流电压等等,性能还不错。但是唯一的不足是操作界面,有很多按钮去设置,数据也只能显示在一小块液晶屏上,并不能保存。但是这个设备提供了GPIB通信接口,可以用电脑控制该设备。电脑给出设置好的指令,比如电压扫描,从0V到10V,测电流,量程是多少等等。然后可以直接读数据到电脑,这样就可以直接保存数据,十分方便。我使用Python写这个软件(当然可以用别的语言,labview也很不错)。之前版本存在的问题是,电脑给出扫描指令,设备扫描需要很长时间,这个时间主程序会被占用,软件会卡主。一直想用多线程来优化软件。今天就记录一下吧。(其实挺简单的!)

 

我用的是python3 所以使用threading库。库的安装就直接pip。当然可以直接使用threading.Thread(target=...)这样的方法。但是我改写了thread类来实现。

首先需要记录一点,网上有很多教程说判断一个线程是否结束使用isAlive()方法,但是我发现会报错,说没有这个方法。于是改写成is_alive(),可能是版本不同。

下面是一个小的测试代码,测试代码调试成功后,整合进软件代码就ok了。

import threading
import time

#定义执行IV扫描的线程类
class iv_sweep_thread(threading.Thread):

    def __init__(self, cmd='H0X'):
        # 重写threading.Thread的__init__方法时,确保在所有操作之前先调用threading.Thread.__init__方法
        threading.Thread.__init__(self)
        self.cmd = cmd
        #self.sleep = sleep

    def run(self):
        """线程内容"""
        write_cmd(self.cmd)
        time.sleep(2)
        global result_x,result_y
        result_x = read_data()
        result_y = read_data()
        print(result_x)
        print(result_y)
        
def read_data():
    return range(10)

def write_cmd(cmd):
    #time.sleep(1)
    print(cmd)


result_x = []
result_y = []

aa = iv_sweep_thread()
print('1',aa.is_alive())
aa.start()
print('2',aa.is_alive())
time.sleep(5)
print('3',aa.is_alive())

这里面我用一个range(10)代替了设备返回的数据。首先在类的定义里 init 部分需要继承threading类,然后运行代码放在run()部分。开始线程需要调用线程的start()方法,线程会在run()里面的代码运行完后自动销毁。

 

 可以看到线程先是向设备写入H0X的指令,然后等待几秒(如果是实际的设备则需要等待设备扫描完成),读取数据,然后线程就被摧毁了。

 

在向软件中植入这部分的时候需要改动一下,因为开始扫描是一个按键的触发函数,在触发函数中我们需要判断上一个扫描有没有结束,只有在上一个扫描结束以后才能开始新的扫描线程。我的思路是新建一个扫描的类,定义为全局变量,然后按键触发函数部分先判断,这个全局变量是不是还活着,如果是“否”,则改变这个全局变量的cmd参数(也就是需要向设备写入的指令),然后start这个线程。

代码部分放出来

#---------定义执行iv扫描的线程类---------
class iv_sweep_thread_class(threading.Thread):

    def __init__(self, cmd='H0X'):
    # 重写threading.Thread的__init__方法时,确保在所有操作之前先调用threading.Thread.__init__方法
        threading.Thread.__init__(self)
        self.cmd = cmd

    def run(self):  #线程内容

        write_cmdlist(self.cmd)    #写运行指令
        '''
        while status_check() == 0: #等待扫描结束
            time.sleep(1)
            print('wait')
            pass
        write_one_cmd('N0X')       #结束扫描
        '''
        global result_x,result_y
        result_x = read_data('source')  #读横坐标数据
        result_y = read_data('measure') #读纵坐标数据

        plt.yscale("log")
        plt.plot(result_x,result_y)
        canvas_data.draw()              #画图
        print("扫描结束")
#---------开始扫描-------------
def run_IV_sweep(): #需要检查是否为数字,该功能没做

    test_cmd = myfunction.generate_IV_cmdlist(Test_parameter) #生成测试指令

    global iv_thread
    if iv_thread.is_alive() == False:
        iv_thread = iv_sweep_thread_class(cmd=test_cmd)
        iv_thread.start()


#---存储测试参数和扫描线程---#
global iv_thread
iv_thread = iv_sweep_thread_class() #iv扫描线程

当然这部分代码并不能直接运行,只是展示了线程部分。一个是定义线程的类,一个是按键的触发函数(run_IV_sweep()),最后就是一个全局变量的定义。如果这个全局变量的定义放在主函数中,则可以不需要声明global iv_thread。global声明是在函数中,需要改变全局变量的值的时候使用的。

使用线程以后,软件在设备工作时,主进程就不会阻塞,这个时候还可以操作其他部分。

posted @ 2022-04-25 22:04  流心蛋黄酥  阅读(560)  评论(0)    收藏  举报