4. SCPI命令体系精讲
在Tektronix多仪器自动测试平台中,无论你使用Python的PyVISA、NI-MAX还是Tek的TekVISA,最终发到仪器上的都是SCPI(Standard Commands for Programmable Instruments)字符串。理解SCPI的语法结构,是写出稳定、可维护测试脚本的前提。SCPI不是某一台仪器的私有协议,而是一套分层、可组合的命令语言:它规定了命令如何拼写、参数如何书写、查询如何返回,以及不同厂商如何在标准框架内扩展自己的子系统。
一、SCPI命令的语法结构
一条SCPI命令由“命令头 + 参数”构成,命令头用冒号分隔层级,形成树状路径。以Tektronix MSO54示波器为例:
:CHANnel1:SCALe 0.5
:TRIGger:A:EDGE:SOURce CH1
:MEASUrement:MEAS1:SOURce CH1
这里CHANnel1是根节点下的子系统,SCALe是它的子命令。SCPI规定:命令头中大写字母是必需部分,小写字母可省略。因此:CHANnel1:SCALe可简写为:CHAN1:SCAL,但:CHAN1:SCA不合法,因为SCAL是缩写下限。实际编程中建议使用完整拼写,可读性更好;在需要压缩命令长度时再使用缩写。
命令头还分“公共命令”和“子系统命令”。公共命令以星号*开头,如*IDN?、*RST、*OPC?,它们不隶属于任何子系统,所有SCPI仪器都必须支持。子系统命令以冒号开头,如:CHANnel1、:TRIGger,由厂商按仪器功能定义。
二、参数类型与书写规则
SCPI参数有明确的类型约定,写错类型仪器会返回错误或忽略命令。常见类型包括:
- 数值型:整数、小数、科学计数法,如
0.5、1e-3、100。可带单位后缀,如500mV、1kHz,仪器会自动换算。 - 布尔型:
ON|OFF、1|0,查询时通常返回0或1。 - 枚举型:从固定集合中取值,如
:TRIGger:A:EDGE:SLOPe可取RISe|FALL|EITHer。 - 字符串型:用单引号或双引号包裹,如
:MEASUrement:MEAS1:LABel "Vpp"。 - 块数据:用于波形传输,如
:CURVe?返回的#开头数据块。
参数之间用逗号分隔,命令与参数之间用空格分隔。例如设置信号源AFG31000的双通道输出:
:SOURce1:FUNCtion:SHAPe SINusoid
:SOURce1:FREQuency 1e6
:SOURce1:VOLTage:AMPLitude 2.0
:SOURce1:OUTPut:STATe ON
三、查询与设置命令
SCPI用“问号”区分设置与查询:不带问号是设置命令,带问号是查询命令。查询命令返回当前值,返回格式由仪器定义,通常与设置参数类型一致。例如:
:CHANnel1:SCALe 0.5 // 设置
:CHANnel1:SCALe? // 查询,返回 5.000E-01
查询返回的数值常带科学计数法,解析时需用float()转换。对于枚举型查询,返回的是缩写形式,如RIS而非RISe,比较时应做大小写或前缀匹配。公共命令*IDN?返回厂商、型号、序列号、固件版本四段信息,是自动测试平台识别仪器的第一步:
*IDN? → TEKTRONIX,MSO54,C012345,1.2.3
另一个关键公共命令是*OPC?(Operation Complete Query)。它用于同步:发送*OPC?后,仪器在当前所有操作完成后才返回1。在自动测试中,设置完触发或采集后,用*OPC?等待,比固定延时更可靠。
*RST复位,再发送*CLS清除状态,最后用*IDN?确认连接,形成稳定的会话起点。四、Tektronix实例:示波器与信号源联动
下面是一个典型的多仪器联动片段:AFG31000输出1MHz正弦波,MSO54采集并测量峰峰值。命令序列如下:
# 信号源设置
AFG.write(':SOURce1:FUNCtion:SHAPe SINusoid')
AFG.write(':SOURce1:FREQuency 1e6')
AFG.write(':SOURce1:VOLTage:AMPLitude 2.0')
AFG.write(':SOURce1:OUTPut:STATe ON')
# 示波器设置
SCOPE.write(':CHANnel1:SCALe 0.5')
SCOPE.write(':CHANnel1:COUPling DC')
SCOPE.write(':TRIGger:A:EDGE:SOURce CH1')
SCOPE.write(':TRIGger:A:EDGE:SLOPe RISe')
SCOPE.write(':TRIGger:A:LEVel 0.5')
SCOPE.write(':MEASUrement:MEAS1:SOURce CH1')
SCOPE.write(':MEASUrement:MEAS1:TYPe PK2pk')
# 同步与读取
SCOPE.query('*OPC?')
vpp = SCOPE.query(':MEASUrement:MEAS1:VALue?')
print(float(vpp))
这段代码中,:MEASUrement:MEAS1:TYPe PK2pk设置测量类型,:MEASUrement:MEAS1:VALue?读取结果。注意查询返回的可能是2.01E+00这样的字符串,需转换后使用。若返回9.9E+37,通常表示测量无效或超时,应检查触发和通道设置。
五、常见错误与排查
SCPI调试中最常见的三类问题:一是命令拼写错误,仪器返回-113 Undefined header;二是参数类型或范围错误,返回-222 Data out of range;三是查询未加问号,导致设置命令被当作查询处理。建议在脚本中加入错误查询:
err = SCOPE.query('SYSTem:ERRor?')
print(err) # 0,"No error" 表示正常
每次关键设置后读取一次:SYSTem:ERRor?,可以快速定位是哪条命令出了问题。对于多仪器平台,建议封装一个write_check(cmd)函数,写入后自动查询错误队列,把错误与命令一起记录到日志,便于回溯。
:MEASUrement下的子命令不完全一致。编写通用脚本前,应以*IDN?返回的型号为键,维护一份命令映射表,避免硬编码。掌握SCPI的语法结构、公共命令、子系统命令、参数类型以及查询与设置的区别后,你就能把示波器、信号源、电源等仪器的操作统一到同一套字符串接口上。下一阶段的多仪器协同、波形采集与结果判定,都建立在这一层命令体系之上。