PyVISA使用——用python控制TEK示波器源码实现

项目要求

项目上提出自动化测试需求,要求能够对板级信号使用示波器等仪器做自动化测量

实现方案

使用PyVISA 方案实现。
VISA (Virtual Instrument Software Architecture) 是一个用于访问测量仪器和控制计算机之间的接口标准,由美国国家仪器公司(National Instruments)推出,用于实现仪器之间的通信和控制。VISA提供了一组标准API函数,可以实现多种通信协议和连接方式的仪器控制。VISA相比于VXI11更加灵活,支持多种接口(底层协议),比如GPIB,RS232,USB, VXIBus等,对于仪器控制有更强大的功能。
Pyvisa是一个基于Python语言开发的访问VISA资源的工具包。通过调用VISA标准API函数来实现与仪器的通信和控制。Pyvisa提供了一组Python的API函数,可以方便地访问VISA库中的函数和方法,从而实现与仪器的数据读取、写入、控制等操作。Pyvisa相比于LabVIEW,Pyvisa是一种跨平台的软件,可以在Linux,Windows和Mac OS上运行,且其拥有更灵活的用户界面,可以自行定制。

如果该脚本运行在服务器上,建议使用Pycharm Professional 或者VSCode + Remote-SSH 插件进行开发,即本地开发,远程部署或调试。

本例使用SCPI命令访问示波器的VISA库,SCPI命令的格式见示波器软件编程手册

脚本实现功能

(1)连接示波器
注意:电脑并没有使用NI-VISA后台,而是使用纯python代码实现的Pyvisa-py, 因此需要提前安装下 pip install -U pyvisa-py
(2)简单设置示波器参数,包括channal 选择,横轴窗口设置,纵轴电压窗口设置,Trigger触发方式选择
(3)波形数据以csv 和图片格式保存到本地
(4)保存示波器的截图到本地

脚本与注释

import time
import pyvisa as visa
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from datetime import datetime
from pyvisa.errors import VisaIOError
from PIL import Image

# rm = visa.ResourceManager('@py')
# rm.list_resources()
# Tek = rm.open_resource('TCPIPO::10.163.xxx.xxx::INSTR')
# Tek.timeout=10000
# print(Tek.query('*IDN?'))
# self.inst.write('*RST')
# self.inst.write('AUTOSET EXECUTE')


class Tektronix_DPO:
        def __init__(self):
            self.rm = visa.ResourceManager('@py')
            self.rm.list_resources()
            self.inst = self.rm.open_resource('TCPIPO::10.163.xxx.xxx::INSTR')
            self.inst.timeout=10000
            print(self.inst.query('*IDN?'))
            self.inst.write('CLEAR')
            self.inst.write('ACQuire:MODe?')
            self.inst.write('ACQUIRE:STOPAFTER RUNSTOP')
            self.inst.write('ACQuire:STATE RUN')
            self.inst.write('autoset EXECUTE') # autoset
              
        def set_HORIZONTAL(self, POSITION, SCALE):         #horizontal settings
            self.inst.write('HORIZONTAL:POSITION %s'%POSITION)  #position means trigger point on the screen /%
            self.inst.write('HORIZONTAL:SCALE %se-6'%SCALE)     #scale means step /us
  
        def open_ch(self,ch):   # open channal
            self.inst.write('DISplay:GLObal:CH%s:STATE ON'%ch)
  
        def close_ch(self,ch):  # close channal
            self.inst.write('DISplay:GLObal:CH%s:STATE OFF'%ch)
  
        def vertical_ch(self,ch,scale,position):     #channal settings,
            self.inst.write('CH%s:BANDWIDTH FULl'%ch) # at its maximum bandwidth
            self.inst.write('CH%s:SCAle %sE-3'%(ch,scale))     #channal scale/mv
            self.inst.write('CH%s:POSition %s'%(ch,position))  #channal divisions above the center graticule
            self.inst.write('CH%s:COUPLING DC'%ch)     # DC coupling
            self.inst.write('CH%s:TERMINATION 10.0E+5'%ch) #channal input resistence is 1MΩ
  
        def trigger_set(self,ch,level):        #trigger settings
            self.inst.write('TRIGGER:A:EDGE:COUPLING DC')  # DC trigger coupling with input signal to the trigger circuitry
            self.inst.write('TRIGGER:A:EDGE:SOURCE CH%s'%ch)
            self.inst.write('TRIGGER:A:EDGE:SLOPE RISE')   #triggers on the rising edge of the signal
            self.inst.write('TRIGGER:A:LEVEL:CH%s %s'%(ch,level))  # level specifies channal trigger level for seach 1, /V
    
        def begin_trigger(self): #start once trigger action
            self.inst.write('acquire:state 0') # stop
            self.inst.write('acquire:stopafter SEQUENCE') # single
            self.inst.write('acquire:state 1') # run
            self.inst.query('*opc?') # sync
        
        def data_caul(self,ch): #calculate the channal value
            self.inst.write("HEADER 0")
            self.inst.write('DATA:SOURCE CH%s'%ch)
            self.inst.write("DAT:ENC SRI")   # Signed Binary Format, LSB order
            self.inst.write("DAT:WIDTH 1")
            self.inst.write("DAT:START 1")
            recordLength = int(self.inst.query('horizontal:recordlength?'))
            self.inst.write("DAT:STOP {0}".format(recordLength)) # Set data stop to match points available

            # Fetch horizontal scaling factors
            xinc = float(self.inst.query("WFMO:XINCR?"))
            xzero = float(self.inst.query("WFMO:XZERO?"))
            pt_off = int(self.inst.query("WFMO:PT_OFF?"))

            # Fetch vertical scaling factors
            ymult = float(self.inst.query("WFMO:YMULT?"))
            yzero = float(self.inst.query("WFMO:YZERO?"))
            yoff = float(self.inst.query("WFMO:YOFF?"))

            # Fetch waveform data and save as csv file
            rawData = self.inst.query_binary_values('curve?', datatype='b', container=np.array)       
            dataLen = len(rawData)
            t0 = (-pt_off * xinc) + xzero                
            with open('horizonal_vector.csv',mode='w') as file:
                for i in range(0,dataLen):
                    xvalues = t0 + xinc * i # Create timestamp for the data point
                    file.write(str(xvalues))
                    file.write(",") 
                    yvalues= float(rawData[i] - yoff) * ymult + yzero # Convert raw ADC value into a floating point value
                    file.write(str(yvalues))
                    file.write("\n")        
            # plotting the measurement figure
            data = pd.read_csv("horizonal_vector.csv")
            data_x = data.iloc[:,0]
            data_y = data.iloc[:,1]
            plt.plot(data_x, data_y)
            plt.title('channel 1') # plot label
            plt.xlabel('time (seconds)') # x label
            plt.ylabel('voltage (volts)') # y label
            print("look for plot window...")
            plt.savefig('test.png')     #save test picture
            plt.show()
               
        def close(self):
            self.inst.close()
            self.rm.close()
    
        def get_screen(self):
            self.inst.write("HARDCopy:PORT FILE;")
            self.inst.write("EXPort:FORMat PNG")
            self.inst.write("HARDCopy:FILEName \"C:\\est_Temp\\Temp.png\"")
            self.inst.write("HARDCopy STARt")
            self.inst.write("FILESystem:READFile \"C:\\Low_Speed_Test_Temp\\Temp.png\"")
            imgData = self.inst.read_raw()
            dt = datetime.now()
            fileName = dt.strftime("DPO70000_%Y%m%d_%H%M%S.png") # Generate a filename with date and time
            file = open(fileName, "wb")
            file.write(imgData)
            file.close()
            self.inst.write('FILESystem:DELEte \"C:\\Test_Temp\\Temp.png\"') # Delete image file from instrument's hard disk.
            

if __name__ == "__main__":
    
    my=Tektronix_DPO()
    my.set_HORIZONTAL(0,40E-3)
    my.open_ch(1)
    my.vertical_ch(1,200,0)
    my.trigger_set(1,0)
    my.begin_trigger()
    my.data_caul(1)
    my.get_screen()
    my.close()

参考

【1】TEK DPO系列示波器编程手册
【2】PyVISA官方文档
【3】TEK 技术交流论坛

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2023年9月8日
下一篇 2023年9月8日

相关推荐