当前位置: 首页 > news >正文

基于CAPL语法实现对程控电源的控制(通用DLL版,支持TCP协议,RS232协议,SCPI指令)

  • 🍅 我是蚂蚁小兵,专注于车载诊断领域,尤其擅长于对CANoe工具的使用
  • 🍅 寻找组织 ,答疑解惑,摸鱼聊天,博客源码,点击加入👉【相亲相爱一家人】
  • 🍅 玩转CANoe,博客目录大全,点击跳转👉

目录

  • 📙 SCPI协议
  • 📙 RS232协议发送SCPI指令的场景
  • 📙 RS232协议发送Hex数组指令的场景
  • 📙 TCP协议发送SCPI指令的场景
  • 📙 源码获取

🍅 背景:CAPL语言是支持远程控制硬件设备的,但是语法结构在我看来不够简洁,特别是获取数据需要在 RS232OnReceive回调函数中获取十分不方便,而且台架复杂的情况下,可能需要多台程控设备, CAPL脚本设计起来就显得非常杂乱。基于这种背景下,我决定基于 C++ 封装一个通用的脚本实现对设备的控制


📙 SCPI协议

  • SCPI(Standard Commands for Programmable Instruments)即可编程仪器标准命令,是一种为了控制测试和测量仪器而设计的标准化命令语言。以下是SCPI的一些基础知识:
  • 如果您要控制的仪器设备支持SCPI协议,那么您可以在设备的技术文档中或者官网中找到该设备的编程技术文档。

1. 起源与目的

SCPI起源于20世纪80年代,由惠普、泰克等公司共同开发。其目的是为了在不同厂商生产的仪器之间提供一种统一的、标准化的通信接口,方便用户对仪器进行编程控制。

2. 语法结构

SCPI命令通常由多个部分组成,遵循一定的层次结构,一般格式为:

<header>[<subheader>][:<subheader>...] <parameter>
  • 头(Header):表示仪器的主要功能模块,如 SYST 代表系统功能,MEAS 代表测量功能。
  • 子头(Subheader):进一步细化功能,例如 SYST:REM 表示进入远程控制模式。
  • 参数(Parameter):为命令提供具体的值,如 VOLT 10.0 中的 10.0 就是设置电压的值

3. 命令类型

查询命令:以问号 ? 结尾,用于从仪器获取信息。例如 *IDN? 用于查询仪器的标识信息。
设置命令:用于向仪器发送指令并设置参数。例如 OUTP ON 用于打开仪器输出。

4. 通用命令

SCPI定义了一些通用命令,以星号 * 开头,适用于大多数仪器。常见的通用命令有:

*IDN?:查询仪器的标识信息,返回制造商、型号、序列号和固件版本等。
*RST:将仪器恢复到默认设置。
*CLS:清除仪器的状态寄存器。
*OPC?:查询操作是否完成,完成后返回 1。

5. 通信协议

SCPI本身并不指定通信协议,它可以通过多种通信接口实现,如GPIB(General Purpose Interface Bus)、RS - 232、USB、LAN(以太网)等。在实际应用中,需要根据仪器支持的接口选择合适的通信方式。

📙 RS232协议发送SCPI指令的场景

seial_scpi.dll文件中有4个函数,实现对支持串口协议的设备的远程控制:

  • serial_connect:连接串口设备
  • serial_disconnect:断开连接串口设备
  • serial_command:发送SCPI指令,无需返回值
  • serial_query:发送SCPI指令,并获取返回值

在这里插入图片描述


完整CAPL代码如下,不仅没有冗余复杂的CAPL结构代码, 基本上一条指令就实现了一个功能,最重要的是支持多台设备同时连接。

/*@!Encoding:936*/
includes
{
    //注意这里是X86格式的DLL
     #pragma library("serial_scpi.dll")
}

void MainTest ()
{
  int device1 = 1;
  int device2 = 2;
  char response[1024];//必须是1024
  long ret;
  double setVoltage;
  char command[1024];
  
  //连接串口设备
  ret = serial_connect(device1,"COM36",9600);
  write("连接串口设备 1, ret = %d",ret);
  
  ret = serial_connect(device2,"COM25",9600);
  write("连接串口设备 2, ret = %d",ret);
  
  //进入远程控制
  ret = serial_command(device1, "SYST:REM");
  write("设备 1 远程控制锁定,ret = %d",ret);
  
  ret = serial_command(device2, "SYST:REM");;
  write("设备 2 远程控制锁定, ret = %d",ret);
   
  //返回设备信息
  ret = serial_query(device1,"*IDN?",response);
  write("设备 1 信息读取, ret = %d,response = %s",ret,response);
  ret = serial_query(device2,"*IDN?",response);
  write("设备 2 信息读取, ret = %d,response = %s",ret,response);  
  testWaitForTimeout(1000);
  
  //开启输出
  ret = serial_command(device1,"OUTP ON");
  write( "设备 1 打开输出,ret = %d",ret);
  ret = serial_command(device2, "OUTP ON");
  write( "设备 2 打开输出ret = %d",ret);
  testWaitForTimeout(1000);
  
  //设置电压    
  setVoltage = random(10)+10;
  snprintf(command,elcount(command),"VOLT %.2f",setVoltage);
  ret = serial_command(device1,command);
  write( "设备 1 设置电压到%.2f V,ret = %d",setVoltage,ret);
  ret = serial_command(device2, command);
  write( "设备 2 设置电压到%.2f V,ret = %d",setVoltage,ret);
  testWaitForTimeout(1000);
  
  //读取电压
  ret = serial_query(device1, "MEAS:VOLT?", response);
  write( "设备 1 读取电压,ret = %d , response = %s",ret,response );
  ret = serial_query(device2, "MEAS:VOLT?", response);
  write( "设备 2 读取电压,ret = %d , response = %s",ret,response );


  //读取电流
  ret = serial_query(device1, "MEAS:CURR?", response);
  write( "设备 1 读取电流,ret = %d , response = %s",ret,response );
  ret = serial_query(device2, "MEAS:CURR?", response);
  write( "设备 2 读取电流,ret = %d , response = %s",ret,response );
  
  //关闭输出
  ret = serial_command(device1,"OUTP OFF");
  write( "设备 1 输出关闭,ret = %d",ret);
  ret = serial_command(device2, "OUTP OFF");
  write( "设备 2 输出关闭,ret = %d",ret);
  testWaitForTimeout(100);
  
  //退出远程控制
  ret = serial_command(device1,"SYST:LOC");
  write( "设备 1 退出远程控制,ret = %d",ret);
  ret = serial_command(device2, "SYST:LOC");
  write( "设备 2 退出远程控制,ret = %d",ret);
  testWaitForTimeout(100);
  
  //断开串口连接
  ret = serial_disconnect(device1);
  write( "设备 1 断开连接,ret = %d",ret);
  ret = serial_disconnect(device2);
  write( "设备 2 断开连接,ret = %d",ret);
  testWaitForTimeout(100);
}

下图是上面示例脚本的测试结果,函数返回值为1说明,指令执行成功,0说明指令执行失败。由于函数返回值都是字符串,在实际读取电压电流参数时还需要进一步将字符串转为数值类型等。

Program / Model	连接串口设备 1, ret = 1
Program / Model	连接串口设备 2, ret = 1
Program / Model	设备 1 远程控制锁定,ret = 1
Program / Model	设备 2 远程控制锁定, ret = 1
Program / Model	设备 1 信息读取, ret = 1,response = ITECH Electronics,IT6832A, 802035043787510120, 1.07-1.02
Program / Model	设备 2 信息读取, ret = 1,response = ITECH Electronics,IT6832A, 802035043797470019, 1.07-1.02
Program / Model	设备 1 打开输出,ret = 1
Program / Model	设备 2 打开输出ret = 1
Program / Model	设备 1 设置电压到14.00 V,ret = 1
Program / Model	设备 2 设置电压到14.00 V,ret = 1
Program / Model	设备 1 读取电压,ret = 1 , response = 13.998
Program / Model	设备 2 读取电压,ret = 1 , response = 13.998
Program / Model	设备 1 读取电流,ret = 1 , response = 0.0002
Program / Model	设备 2 读取电流,ret = 1 , response = 0
Program / Model	设备 1 输出关闭,ret = 1
Program / Model	设备 2 输出关闭,ret = 1
Program / Model	设备 1 退出远程控制,ret = 1
Program / Model	设备 2 退出远程控制,ret = 1
Program / Model	设备 1 断开连接,ret = 1
Program / Model	设备 2 断开连接,ret = 1

📙 RS232协议发送Hex数组指令的场景


因为有些设备不支持SCPI指令集,使用的是自定义的指令,所以我决定在**seial_scpi.dll** 文件中再增加如下3个函数,支持发送Hex数组指令:
  • serial_connect:连接串口设备
  • serial_disconnect:断开连接串口设备
  • serial_command:发送SCPI指令,无需返回值
  • serial_query:发送SCPI指令,并获取返回值

在这里插入图片描述


完整CAPL代码如下,不仅没有冗余复杂的CAPL结构代码, 基本上一条指令就实现了一个功能,**最重要的是支持多台设备同时连接。**
/*@!Encoding:936*/
includes
{
    //注意这里是X86格式的DLL
    #pragma library("serial_scpi.dll")
}


void MainTest ()
{
  int device1 = 1;
  byte response[1024];
  long i,ret;
  double setVoltage;
 
  //连接串口设备
  ret = serial_connect(device1,"COM37",9600);
  write("连接串口设备 1, ret = %d",ret);
  testWaitForTimeout(1000);
 
  //开启输出
  ret = serial_binary_command(device1,OpenPower,elCount(OpenPower));
  write( "设备 1 打开输出,ret = %d",ret);
    testWaitForTimeout(1000);
  
  //设置电压  
  {
    word crcData;
    long setValue;
    setVoltage = random(10)+10;
    setValue = setVoltage*100;
    SetVlotage[4] = setValue>>8;
  	SetVlotage[5] = setValue&0xff;
  	crcData = crc16(SetVlotage,6);
    SetVlotage[6] = crcData&0xff;
  	SetVlotage[7] = crcData>>8;
  
    ret = serial_binary_command(device1,SetVlotage,elCount(SetVlotage));
    write( "设备 1 设置电压到%.2f V,ret = %d",setVoltage,ret);
    testWaitForTimeout(1000);
  }
   ret = serial_clear_receive_buffer(device1);
   write( "设备 1 清空接受缓存,ret = %d",ret);
  //读取电压
  ret = serial_binary_query(device1, ReadVlotage,elCount(ReadVlotage), response,elcount(response));

  for(i = 0;i<ret;i++)
    write("response[%d] = 0x%x",i,response[i]);

    write( "设备 1 读取电压 = %.2f,ret = %d ",(response[3]<<8 ||response[4])/100.0,ret);
  
  //关闭输出
  ret = serial_binary_command(device1,ClosePower,elCount(ClosePower));
  write( "设备 1 关闭输出,ret = %d",ret);
  testWaitForTimeout(1000);
  
  //断开串口连接
  ret = serial_disconnect(device1);
  write( "设备 1 断开连接,ret = %d",ret);
}

下图是上面示例脚本的测试结果,函数返回值为1说明,指令执行成功,0说明指令执行失败。由于函数返回值都是字符串,在实际读取电压电流参数时还需要进一步将字符串转为数值类型等。

Program / Model	连接串口设备 1, ret = 1
Program / Model	设备 1 打开输出,ret = 1
Program / Model	设备 1 设置电压到13.00 V,ret = 1
Program / Model	设备 1 清空接受缓存,ret = 1
Program / Model	response[0] = 0x1
Program / Model	response[1] = 0x3
Program / Model	response[2] = 0x2
Program / Model	response[3] = 0x5
Program / Model	response[4] = 0x14
Program / Model	response[5] = 0xbb
Program / Model	response[6] = 0x1b
Program / Model	设备 1 读取电压 = 13.00,ret = 7 
Program / Model	设备 1 关闭输出,ret = 1
Program / Model	设备 1 断开连接,ret = 1

📙 TCP协议发送SCPI指令的场景

下面是在CAPL中应用的Demo,tcp_scpi.dll文件中有4个函数:

  • tcp_connect:连接以太网控制协议的设备
  • tcp_disconnect:断开设备连接
  • tcp_command:发送SCPI指令,无需返回值
  • tcp_query:发送SCPI指令,并获取返回值

在这里插入图片描述


完整CAPL代码如下

  • TCP协议设备,需要设置好设备的IP地址和端口,同时要设置好电脑或者拓展坞的IP地址,并保证和仪器设备在同一网段中,

仪器设备的IP地址:

在这里插入图片描述
本机的IP地址如下,和仪器设备的IP地址同属于一个网段。

在这里插入图片描述

  • 完整的示例代码如下,tcp_scpi.dll同样支持多个设备同时连接(示例中仅仅连接单台设备)
/*@!Encoding:936*/
includes
{
    //注意这里是X86格式的DLL
    #pragma library("..\..\tcp_scpi_demo_vs2022\Debug\tcp_scpi.dll")
}

void MainTest ()
{
  char EA_IP[20] =  "172.20.1.101";
  long EA_Port =  5025;
  char response[1024];
  long ret,deviceID = 1;
  double setVoltage;
  char command[1024];
  
  ret= tcp_connect(EA_IP,EA_Port);
  if(ret>0)
  {
    write("TCP连接成功, ret= %d",ret);
  }
  else
  {
     write("TCP连接失败, ret= %d",ret);
    return;
  }
  
  ret = tcp_query(deviceID,"*IDN?",response);
  write("ret = %d,设备信息 = %s",ret,response);
  
  ret = tcp_command(deviceID,"SYSTem:LOCK ON");
  write("远程控制锁定 ret = %d",ret);
  testWaitForTimeout(1000);
  
  ret = tcp_command(deviceID,"OUTP ON");
  write("打开输出 ret = %d",ret);
  
  setVoltage = random(10)+10;
  snprintf(command,elcount(command),"VOLT %f",setVoltage);
  ret = tcp_command(deviceID,command);
  write("设置电压 = %f ,ret = %d",setVoltage,ret);
  testWaitForTimeout(1000);
  
  ret = tcp_query(deviceID,"MEAS:VOLT?",  response);
  write("读取电压 ret = %d, response = %s",ret,response);
  
  ret = tcp_query(deviceID,"MEAS:CURR?",  response);
  write("读取电流 ret = %d, response = %s",ret,response);
  
  ret = tcp_command(deviceID,"OUTP OFF");
  write("关闭输出 ret = %d",ret); 
  
  ret = tcp_command(deviceID,"SYSTem:LOCK OFF");
  write("退出远程锁定 ret = %d",ret); 
  
  ret = tcp_disconnect(deviceID );
  write("断开TCP连接 ,ret = %d",ret); 
}

下图是上面示例脚本的测试结果,函数返回值为1说明,指令执行成功,0说明指令执行失败。由于函数返回值都是字符串,在实际读取电压电流参数时还需要进一步将字符串转为数值类型等。

Program / Model	TCP连接成功, deviceID = 2
Program / Model	ret = 1,设备信息 = EA Elektro-Automatik GmbH & Co. KG, PS 9080-100, 2934691079, V2.05 04.03.2020 V3.07 06.08.2019 V1.0.22, 
Program / Model	远程控制锁定 ret = 1
Program / Model	打开输出 ret = 1
Program / Model	设置电压 = 11.00 ,ret = 1
Program / Model	读取电压 ret = 1, response = 11.07 V
Program / Model	读取电流 ret = 1, response = 0.0 A
Program / Model	关闭输出 ret = 1
Program / Model	退出远程锁定 ret = 1
Program / Model	断开TCP连接 ,ret = 1

📙 源码获取

博主肝了几个通宵才得以解决在CAPL脚本中仪器控制的棘手的问题,希望对您有帮助!

  • 基于CAPL语法规则生成的支持TCP协议控制仪器设备的DLL (源码和示例工程)
  • 基于CAPL语法规则生成的支持RS232协议控制仪器设备的DLL(源码和示例工程)
  • 基于CAPL语法规则生成的支持RS232和TCP协议控制仪器设备的DLL (源码和示例工程)
    23

7

  • 🚩要有最朴素的生活,最遥远的梦想,即使明天天寒地冻,路遥马亡!

  • 🚩如果这篇博客对你有帮助,请 “点赞” “评论”“收藏”一键三连 哦!码字不易,大家的支持就是我坚持下去的动力。
    18

相关文章:

  • 对解释器模式的理解
  • 互联网三高架构设计
  • JAVA对象创建过程和类加载过程
  • 整车CAN网络和CANoe
  • 如何使用Python自动化测试工具Selenium进行网页自动化?
  • Java类加载机制深度解析
  • 蓝桥杯基础算法-字符串与集合
  • 瑞萨RA4M2使用心得-KEIL5的第一次编译
  • pipwire cpu 100%使用率的问题
  • 大模型学习五:‌DeepSeek Janus-Pro-7B 多模态半精度本地部署指南:环境是腾讯cloudstudio高性能GPU 16G免费算力
  • 定制一款国密浏览器(3):修改浏览器应用程序安装路径
  • Java 基础-31-枚举-认识枚举
  • 每日一题(小白)回溯篇4
  • ngx_timezone_update
  • Python----计算机视觉处理(Opencv:道路检测之车道线拟合)
  • OpenSceneGraph 中的 osg::Transform详解
  • 图片尺寸修改软件下载
  • 车载ECU底软测试:方法与技术的深度解析
  • 利用NumPy核心知识点优化TensorFlow模型训练过程
  • 大厂机考——各算法与数据结构详解
  • 深圳设计网站建设公司/宁波优化推广选哪家
  • 建设网站的主要流程有哪些/app搜索优化
  • 网站建设内部风险分析/百度指数明星人气榜
  • 网站需要什么/seo优化一般包括哪些内容()
  • 赣州政府网站/除了91还有什么关键词
  • 厚街网站建设公司/百度网站大全