基于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
View Code
  • 通信基类-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         }
View Code

关闭:

 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         }
View Code

发送接收数据

 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         }
View Code
  • 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         }
测量
  • 测试结果

 

 读取到电压值

posted @ 2022-09-16 09:44  豆腐柠檬  阅读(796)  评论(0编辑  收藏  举报