基于SCPI可编程仪器通讯-新能源/测试(万用表/内阻表/电压表通讯)(六)
背景:
SCPI是一套用于控制可编程测试测量仪器的标准语法和命令,广泛用于测量仪器比如万用表,内阻表中,工控领域如新能源行业上位机开发,或其他测试测量环节经常用到,下面开始记录C#对接模块SCPI通信开发。
- SCPI指令有很多,以下为本次电压、内阻测量使用的命令:
#region BT3562A public const string 版本查询 = "*IDN?"; public const string 初始化 = "∗RST"; //量程 public const string 设置电阻量程 = ":RESistance:RANGe";//0 ~ 3100 电阻量程的设定 public const string 电阻量程查询 = ":RESistance:RANGe?";//3.000E-3 ~ 3.0000E+3 电阻量程的查询 public const string 设置电压量程 = ":VOLTage:RANGe";//:VOLTage:RANGe -300 ~ 300 电压量程的设定 public const string 电压量程查询 = ":VOLTage:RANGe?";//:VOLTage:RANGe? 6.00000E+0 ~300.000E+0 电压量程的查询 //测量值的读出 public const string 测量值的读出L1 = ":FETCh?"; public const string 测量值的读出L2 = ":READ?"; //:FETCh? < 电阻测量值 >, < 电压测量值 > ΩV最新测量值的读出功能 //:READ? < 电阻测量值 >, < 电压测量值 > ΩV //功能设置查询 public const string 设置功能 = ":FUNCtion"; public const string 功能查询 = ":FUNCtion?"; //自动量程 public const string 自动量程查询 = ":AUT?"; //:AUTorange 1/ 0/ ON/ OFF 自动量程的设定 public const string 自动量程设置 = ":AUT";//:AUTorange? ON/ OFF 自动量程设定的查询 #endregion #region DMM7510 public const string DMM7510_设置电压测量功能 = ":SENS:FUNC 'VOLT:DC'"; public const string DMM7510_设置电压量程 = ":SENS:VOLT:RANG {0}"; #endregion #region BT3562A public const string BT3562A_电阻功能 = "RESISTANCE"; public const string BT3562A_电压功能 = "VOLTAGE"; #endregion
其他指令:
//调零 //:ADJust:CLEAr 调零的解除 //:ADJust? 0/ 1 调零的执行与结果查询 //采样速度 //:SAMPle:RATE EXFast/ FAST/ MEDium/ SLOW 采样速度的设定 //:SAMPle:RATE? EXFast/ FAST/ MEDium/ SLOW 采样速度的查询 //平均值功能 //:CALCulate:AVERage:STATe 1/ 0/ ON/ OFF 平均值功能的设定 //:CALCulate:AVERage:STATe? ON/ OFF 平均值功能执行的查询 133 //:CALCulate:AVERage 2 ~ 16 平均次数的设定 //:CALCulate:AVERage? 2 ~ 16 平均次数的查询 133 //比较器 //:CALCulate:LIMit:STATe 1/ 0/ ON/ OFF 比较器的设定 133 //:CALCulate:LIMit:STATe? ON/OFF 比较器的查询 133 //:CALCulate:LIMit:BEEPer OFF/ HL/ IN/ BOTH1/ BOTH2比较器判定蜂鸣器的设定 134 //:CALCulate:LIMit:BEEPer? OFF/ HL/ IN/ BOTH1/ BOTH2比较器判定蜂鸣器的查询 134 //:CALCulate:LIMit:RESistance:MODE HL/ REF 电阻比较器模式的设定 134 //:CALCulate:LIMit:RESistance:MODE? HL/ REF 电阻比较器模式的查询 134 //:CALCulate:LIMit:VOLTage:MODE HL/ REF 电压比较器模式的设定 134 //:CALCulate:LIMit:VOLTage:MODE? HL/ REF 电压比较器模式的查询 134 //:CALCulate:LIMit:RESistance:UPPer<上限值> 电阻上限值的设定 135 //:CALCulate:LIMit:RESistance:UPPer? < 上限值 > 电阻上限值的查询 135 //:CALCulate:LIMit:VOLTage:UPPer<上限值> 电压上限值的设定 135 //:CALCulate:LIMit:VOLTage:UPPer? < 上限值 > 电压上限值的查询 135 //:CALCulate:LIMit:RESistance:LOWer<下限值> 电阻下限值的设定 136 //:CALCulate:LIMit:RESistance:LOWer? < 下限值 > 电阻下限值的查询 136 //:CALCulate:LIMit:VOLTage:LOWer<下限值> 电压下限值的设定 136 //:CALCulate:LIMit:VOLTage:LOWer? < 下限值 > 电压下限值的查询 136 //:CALCulate:LIMit:RESistance:REFerence<基准电阻> 基准电阻值的设定 137 //:CALCulate:LIMit:RESistance:REFerence? < 基准电阻 > 电阻基准值的查询 137 //:CALCulate:LIMit:VOLTage:REFerence<基准电阻> 电压基准值的设定 137 //:CALCulate:LIMit:VOLTage:REFerence? < 基准电阻 > 电压基准值的查询 137 //:CALCulate:LIMit:RESistance:PERCent<范围 (%) > 电阻范围的设定 138 //:CALCulate:LIMit:RESistance:PERCent? < 范围 (%) > 电阻范围的查询 138 //:CALCulate:LIMit:VOLTage:PERCent<范围 (%) > 电压范围的设定 138 //:CALCulate:LIMit:VOLTage:PERCent? < 范围 (%) > 电压范围的查询 138 //:CALCulate:LIMit:RESistance:RESult? HI/ IN/ LO/ OFF/ ERR 电阻判定结果的查询 138 //:CALCulate:LIMit:VOLTage:RESult? HI/ IN/ LO/ OFF/ ERR 电压判定结果的查询 138 //:CALCulate:LIMit:ABS 1/ 0/ ON/ OFF 比较器绝对值判定功能的设定 139 //:CALCulate:LIMit:ABS? ON/ OFF 比较器绝对值判定功能的查询 139 统计功能 //:CALCulate:STATistics:STATe 1/ 0/ ON/ OFF 统计运算功能执行的设定 139 //:CALCulate:STATistics:STATe? ON/OFF 统计运算功能执行的查询 139 //:CALCulate:STATistics:CLEAr 统计运算结果的清除 139 //:CALCulate:STATistics:RESistance:NUMBer? < 总数据数 >, < 有效数据数 >电阻测量值数据数的查询 140 //:CALCulate:STATistics:VOLTage:NUMBer? < 总数据数 >, < 有效数据数 >电压测量值数据数的查询 140 //:CALCulate:STATistics:RESistance:MEAN? < 平均值 > 电阻测量值平均值的查询 140 //:CALCulate:STATistics:VOLTage:MEAN? < 平均值 > 电压测量值平均值的查询 140 //:CALCulate:STATistics:RESistance:MAXimum? < 最大值 >, < 数据编号 >电阻测量值最大值的查询 141 //:CALCulate:STATistics:VOLTage:MAXimum? < 最大值 >, < 数据编号 >电压测量值最大值的查询 141 //:CALCulate:STATistics:RESistance:MINimum? < 最大值 >, < 数据编号 >电阻测量值最小值的查询 141 //:CALCulate:STATistics:VOLTage:MINimum? < 最小值 >, < 数据编号 >电压测量值最小值的查询 141 //:CALCulate:STATistics:RESistance:LIMit? <Hi 数 >, <IN 数 >,<Lo 数 >, < 测试异常数 >电阻测量值比较器判定结果的查询142 //:CALCulate:STATistics:VOLTage:LIMit? <Hi 数 >, <IN 数 >,<Lo 数 >, < 测试异常数 >电压测量值比较器判定结果的查询142 //:CALCulate:STATistics:RESistance:DEViation? <σ n>, <σ n-1> 电阻测量值标准偏差的查询 142 //:CALCulate:STATistics:VOLTage:DEViation? <σ n>, <σ n-1> 电压测量值标准偏差的查询 142 //:CALCulate:STATistics:RESistance:CP? <Cp>, <Cpk> 电阻测量值工序能力指数的查询 143 //:CALCulate:STATistics:VOLTage:CP? <Cp>, <Cpk> 电压测量值工序能力指数的查询 143 //存储功能 //:MEMory:STATe 1/ 0/ ON/ OFF 存储功能设定 143 //:MEMory:STATe? ON/ OFF 存储功能查询 143 //:MEMory:CLEAr 存储数据清除 143 //:MEMory:COUNt? 0 ~ 400 存储数据数查询 144 //:MEMory:DATA?[STEP] 存储数据查询 144 //自校正 //:SYSTem:CALibration 自校正的执行 145 //:SYSTem:CALibration:AUTO 1/ 0/ ON/ OFF 自动自校正的设定 145 //:SYSTem:CALibration:AUTO? ON/ OFF 自动自校正的查询 145 //触发输入测量值输出 //:SYSTem:DATAout 1/ 0/ ON/ OFF 触发输入时的测量值输出设定 145 //:SYSTem:DATAout? ON/ OFF 触发输入时的测量值输出查询 145 //按键操作音 //:SYSTem:BEEPer:STATe 1/ 0/ ON/ OFF 按键操作音设定 146 //:SYSTem:BEEPer:STATe? ON/ OFF 按键操作音的查询 146 //电源频率 //:SYSTem:LFRequency AUTO/50/ 60 电源频率的设定 146 //:SYSTem:LFRequency? AUTO/50/ 60 电源频率的查询 146 //按键锁定 //:SYSTem:KLOCk 1/ 0/ ON/ OFF 按键锁定状态的设定 146 //:SYSTem:KLOCk? ON/ OFF 按键锁定状态的查询 146 //EXT I/O 输出 //:SYSTem:ELOCk 1/ 0/ ON/ OFF 外部输入端子锁定的设定 147 //:SYSTem:ELOCk? ON/ OFF 外部输入端子锁定的 ON/OFF 查询 147 //本地 //:SYSTem:LOCal 本地状态的设定 147 //测量条件的保存和读入 //:SYSTem:SAVE<Table No.> 测量条件的保存 147 //:SYSTem:LOAD<Table No.> 测量条件的读出 147 //:SYSTem:BACKup 测量条件的备份 147 //信息头有无 //:SYSTem:HEADer 1/ 0/ ON/ OFF 信息头有无的设定 148 //:SYSTem:HEADer? ON/ OFF 信息头有无的查询 148 //ERR 输出 //:SYSTem:ERRor SYNChronous/ ASYNchronous错误输出时序的设定 148 //:SYSTem:ERRor? SYNCHRONOUS/ ASYNCHRONOUS //错误输出时序的查询 148 //EOM 输出 //:SYSTem:EOM:MODE<HOLD/PULSe> EOM 输出模式的设定 149 //:SYSTem:EOM:MODE? (<HOLD/PULSE>) EOM 输出模式的查询 149 //:SYSTem:EOM:PULSe<脉冲宽度> EOM 脉冲宽度的设定 149 //:SYSTem:EOM:PULSe? (0.001 ~ 0.099) EOM 脉冲宽度的查询 149 //测量电流脉冲输出功能的设定 //:SYSTem:CURRent CONTinuous/PULSe 测量电流脉冲输出功能的设定 149 //:SYSTem:CURRent? CONTINUOUS/PULSE 测量电流脉冲输出功能的查询 149 //终止符 //:SYSTem:TERMinator 0/ 1 终止符的设定 148 //:SYSTem:TERMinator? 0/ 1 终止符的查询 148 //系统复位 //:SYSTem:RESet 执行包括测量条件保存数据在内的复位 149 //EXT I/O //:IO:IN? 0 ~ 31 EXT I/O 输入 150 //触发 //:INITiate:CONTinuous 1/ 0/ ON/ OFF 连续测量的设定 152 //:INITiate:CONTinuous? ON/ OFF 连续测量的查询 152 //:INITiate[:IMMediate] 等待触发的设定 152 //触发源的设定 //:TRIGger:SOURce IMMediate/ EXTernal 触发源的设定 153 //:TRIGger:SOURce? IMMEDIATE/ EXTERNAL 触发源的查询 153 //:TRIGger:DELay:STATe 1/ 0/ ON/ OFF 触发延迟执行的设定 153 //:TRIGger:DELay:STATe? ON/ OFF 触发延迟的查询 153 //:TRIGger:DELay<延迟时间> 触发延迟时间的设定 153 //:TRIGger:DELay? 0 ~ 9.999 触发延迟时间的查询 153
- 通信基类-SOCKET实现(TcpClientSocketHelper)
SCPI也是通过socket进行数据发送与接收,所以在测试时,需要设置好IP与端口号。
socket需实现连接、关闭连接、接收数据、发送数据
连接:
1 public bool Open(out string msg) 2 { 3 lock(Lock) 4 { 5 msg = string.Empty; 6 try 7 { 8 if (!PingCheck(Ip, ConnectTimeout)) 9 { 10 msg = $"网络故障:Ip|{Ip},ConnectTimeout|{ConnectTimeout}!"; 11 return false; 12 } 13 System.Diagnostics.Stopwatch sp = new System.Diagnostics.Stopwatch(); 14 sp.Start(); 15 this.tcpClient = new TcpClient(); 16 this.tcpClient.ReceiveTimeout = ReceiveTimeout; 17 this.tcpClient.SendTimeout = SendTimeout; 18 this.tcpClient.Connect(Ip, Port); 19 Thread.Sleep(10); 20 if (!tcpClient.Connected) 21 { 22 throw new ApplicationException($"未连接到{Ip}"); 23 } 24 if (!IsConnected) SocketConnectedEvent?.BeginInvoke(this, EventArgs.Empty, null, null); 25 IsConnected = true; 26 msg = $"连接[{Ip}]成功,耗时{sp.Elapsed.TotalMilliseconds.ToString()}ms"; 27 28 return true; 29 30 } 31 catch (Exception ex) 32 { 33 Close(out string _msg); 34 msg = $"连接失败:{ex.Message},{_msg}"; 35 return false; 36 } 37 } 38 }
关闭:
1 public bool Close(out string msg) 2 { 3 lock(Lock) 4 { 5 msg = string.Empty; 6 try 7 { 8 if (IsConnected) SocketClosedEvent?.BeginInvoke(this, EventArgs.Empty,null,null); 9 this.tcpClient?.Close(); 10 this.tcpClient = null; 11 return true; 12 } 13 catch (Exception ex) 14 { 15 msg = $"关闭失败:{ex.Message}"; 16 tcpClient = null; 17 return false; 18 } 19 finally 20 { 21 IsConnected = false; 22 } 23 } 24 }
发送接收数据
1 public bool SendAndRecive(out string msg, byte[] sd,byte[] rd, int receiveTimeout = 3000) 2 { 3 msg = string.Empty; ReceiveTimeout = receiveTimeout; 4 try 5 { 6 #region 连接状态 7 if (tcpClient == null || !tcpClient.Connected) 8 { 9 if (!Open(out msg)) 10 { 11 Thread.Sleep(40); 12 if (!Open(out msg)) return false; 13 } 14 } 15 #endregion 16 tcpClient.GetStream().Write(sd, 0, sd.Length); 17 18 Thread.Sleep(100); 19 // 20 int index = 0; 21 System.Diagnostics.Stopwatch sp = new System.Diagnostics.Stopwatch(); 22 sp.Start(); 23 do 24 { 25 int len = tcpClient.GetStream().Read(rd, index, rd.Length - index); 26 if (len == 0) 27 return false;//这里控制读取不到数据时就跳出,网络异常断开,数据读取不完整。 28 else 29 index += len; 30 31 if (sp.ElapsedMilliseconds > ReceiveTimeout) return false;//超时截止 32 33 } while (index < rd.Length); 34 35 return true; 36 } 37 catch (Exception ex) 38 { 39 msg = ex.Message; 40 Close(out msg); 41 return false; 42 } 43 }
- SCPI通信基类实现(SCPIClientBase)
有三个主要功能消息接收、发送命令、查询指令:
1 public bool ReceiveMsg(out string msg, int receiveTimeout = 3000) 2 { 3 msg = string.Empty; 4 byte[] ary = new byte[65536]; 5 StringBuilder buf = new StringBuilder(); 6 Stopwatch sw = new Stopwatch(); 7 try 8 { 9 #region 连接状态 10 if (tcpClient == null || !tcpClient.Connected) 11 { 12 if (!Open(out msg)) 13 { 14 Thread.Sleep(40); 15 if (!Open(out msg)) return false; 16 } 17 } 18 #endregion 19 sw.Start(); 20 string rcv; 21 for (; ; ) 22 { 23 if (tcpClient.GetStream().DataAvailable) 24 { 25 int len = tcpClient.GetStream().Read(ary, 0, ary.Length); 26 rcv = Encoding.Default.GetString(ary).Substring(0, len); 27 rcv = rcv.Replace("\r", ""); 28 if (rcv.IndexOf("\n") >= 0) 29 { 30 break; 31 } 32 buf.Append(rcv); 33 } 34 if (sw.ElapsedMilliseconds > receiveTimeout) 35 { 36 goto Block_5; 37 } 38 } 39 rcv = rcv.Substring(0, rcv.IndexOf("\n")); 40 buf.Append(rcv); 41 msg = buf.ToString(); 42 sw.Stop(); 43 return true; 44 Block_5: 45 msg = $"接收超时:[{receiveTimeout}]"; 46 return false; 47 } 48 catch (Exception ex) 49 { 50 msg = $"接收失败:{ex.Message}"; 51 return false; 52 } 53 }
1 public bool SendMsg(string cmd, out string msg) 2 { 3 msg = string.Empty; 4 try 5 { 6 cmd += "\r\n"; 7 var SendBuffer = Encoding.Default.GetBytes(cmd); 8 return SendData(out msg, SendBuffer); 9 } 10 catch (Exception ex) 11 { 12 msg = $"指令[{cmd}]发送失败:{ex.Message}"; 13 return false; 14 } 15 }
1 public bool SendQueryMsg(string cmd, int timeout_ms, out string msg) 2 { 3 msg = string.Empty; 4 try 5 { 6 if (!this.SendMsg(cmd, out msg)) return false; 7 8 if (!cmd.Contains("?")) 9 { 10 msg = "非查询指令"; 11 return false; 12 } 13 14 return ReceiveMsg(out msg, timeout_ms); 15 } 16 catch (Exception ex) 17 { 18 msg = $"查询失败:{ex.Message}"; 19 return false; 20 } 21 }
- 电表通信类(以BT3562表为例)
1 public override bool 版本获取(out string msg) 2 { 3 msg = string.Empty; 4 try 5 { 6 var ar = SendQueryMsg($"{SCPI指令集.版本查询}", 2000, out msg); 7 8 if(!ar) return false; 9 //版本校验 10 if (!msg.StartsWith(BT3562常量定义.厂商)) 11 { 12 //型号不正确 13 msg = $"非{BT3562常量定义.厂商}厂商"; 14 return false; 15 } 16 string p = msg; 17 if (!BT3562常量定义.型号.Any(f=> p.Contains(f))) 18 { 19 msg = $"型号不正确:{p}"; 20 return false; 21 } 22 23 VersionID = msg; 24 return true; 25 } 26 catch (Exception ex) 27 { 28 msg = $"版本获取失败:{ex.Message}"; 29 return false; 30 } 31 }
1 public override bool 设置功能(out string msg, MultimeterFunc funcstring= MultimeterFunc.InternalResistance) 2 { 3 msg = string.Empty; 4 try 5 { 6 string cmd; 7 switch(funcstring) 8 { 9 case MultimeterFunc.Voltage: 10 case MultimeterFunc.ShellPressure: 11 cmd = SCPI指令集.设置功能 + " " + SCPI指令集.BT3562A_电压功能; 12 break; 13 case MultimeterFunc.InternalResistance: 14 default: 15 cmd = SCPI指令集.设置功能 + " " + SCPI指令集.BT3562A_电阻功能; 16 break; 17 } 18 19 return SendMsg(cmd, out msg); 20 } 21 catch (Exception ex) 22 { 23 msg = $"设置功能失败:{ex.Message}"; 24 return false; 25 } 26 }
1 public override bool 设置量程(out string msg,string cmd="3E-3") 2 { 3 msg = string.Empty; 4 try 5 { 6 string rand = $"{SCPI指令集.设置电阻量程} {cmd}"; 7 if(rand.Contains("AUTO")) 8 { 9 //自动量程 10 rand = ":AUT ON"; 11 } 12 return SendMsg(rand, out msg); 13 } 14 catch (Exception ex) 15 { 16 msg = $"设置量程失败:{ex.Message}"; 17 return false; 18 } 19 }
1 public override bool 测量(out string msg,out double v) 2 { 3 msg = string.Empty; v = 0; 4 try 5 { 6 if(!SendQueryMsg($"{SCPI指令集.测量值的读出L1}", 1000, out msg)) 7 { 8 return false; 9 } 10 if(!double.TryParse(msg,out v)) 11 { 12 msg = $"测量值不正确:{msg}"; 13 return false; 14 } 15 //转毫欧 16 v *= 1000; 17 return true; 18 } 19 catch (Exception ex) 20 { 21 msg = $"测量失败:{ex.Message}"; 22 return false; 23 } 24 }
- 测试结果
读取到电压值
本文来自博客园,作者:豆腐柠檬,转载请注明原文链接:https://www.cnblogs.com/ToufuLemon/p/16416491.html