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声明是在函数中,需要改变全局变量的值的时候使用的。
使用线程以后,软件在设备工作时,主进程就不会阻塞,这个时候还可以操作其他部分。

浙公网安备 33010602011771号