上节课程我们介绍了全国产EtherCAT运动控制边缘控制器ZMC432H的硬件接口与功能,本节课程我们主要讲解一下正运动API函数封装原理以及自定义API封装例程。
全国产EtherCAT运动控制边缘控制器ZMC432H是正运动的一款软硬件全国产自主可控,运动控制接口兼容EtherCAT总线和脉冲型的独立式运动控制器,最多支持32轴运动控制,同时支持正运动远程HMI功能,能提供网络组态显示,可实时监控和调整参数配置。
ZMC432H具备丰富的硬件接口和控制功能模块,能实现高效稳定的运动控制和实时数据采集,以满足工业控制协同工业互联网的应用需求。
ZMC432H内置了Linux系统,可以使用本地的LOCAL接口进行连接,可以做到更快速的指令交互,单条指令与多条指令一次性交互时间为40us左右。
ZMC432H视频介绍:
全国产EtherCAT运动控制边缘控制器ZMC432H
所有的控制器和控制卡均使用同一套API函数,均支持C、C++、C#、LabVIEW、Python、Delphi等开发语言,支持VC6.0、VB6.0、Qt、.Net等平台,支持Windows、Linux、WinCE、iMac等操作系统。
各个开发语言都有各自所对应的函数库,所调用的API均一致,这大大提高了可移植性。各个开发语言库的调用方式可参考“ZMotion PC函数库编程手册 V2.1.1”。
文档参考路径:光盘资料\04PC函数\Zmotion PC函数库编程手册及例程源码。
以下为各个功能部分API指令一览表;
更多API接口详情可以参考“ZMotion PC函数库编程手册 V2.1.1”。
ZAux_Execute或ZAux_DirectCommand可对Basic指令进行封装。如果使用到没有封装的命令或者想封装自己的函数,可以通过ZAux_Execute发送或ZAux_DirectCommand,或是参照已有代码修改增加相应的函数。
发送字符串命令有两种方式,缓冲方式和直接方式 。具体如图所示:
直接方式:直接执行单个变量/数组/参数相关命令,此时所有传递的参数必须是具体的数值,不能是表达式;
缓冲方式:可以执行所有命令,并支持表达式作为参数,但是速度慢一些;
以zmcaux.cpp中对已封装的设置运动速度的函数ZAux_Direct_SetSpeed()与获取当前编码器反馈位置的函数ZAux_Direct_GetMpos为例。
程序如下:
#include "zmotion.h"
#include "zauxdll2.h"
int ZAux_Direct_SetSpeed(ZMC_HANDLE handle, int iaxis, float fValue)
{
char cmdbuff[2048];
char cmdbuffAck[2048];
if (iaxis> MAX_AXIS_AUX) //MAX_AXIS_AUX为zuaxdll2.h中定义的宏,zuaxdll2.h为正运动库头文件
{
return ERR_AUX_PARAERR;
}
sprintf(cmdbuff,"SPEED(%d)=%f",iaxis,fValue);//生成对应命令的字符串
ZAux_DirectCommand(handle,cmdbuff,cmdbuffAck,2048);
}
int ZAux_Direct_GetMpos(ZMC_HANDLE handle, int iaxis, float fValue)
{
char cmdbuff[2048];
char cmdbuffAck[2048];
if (iaxis> MAX_AXIS_AUX)
{
return ERR_AUX_PARAERR;
}
sprintf(cmdbuff,"MPOS(%d)=%f",iaxis,fValue);//生成对应命令的字符串
ZAux_DirectCommand(handle,cmdbuff,cmdbuffAck,2048);
}
1、自定义API封装
自定义封装API的原理实际上是利用了在线命令的机制,上位机生成由各种ZBASIC指令来达到自己想要的功能。
ZAux库便是直接利用ZBASIC命令通过ZAux_Execute方式或ZAux_DirectCommand方式发送到控制器上,相应函数可以参考ZBASIC手册对应的命令介绍。
ZAux库是完全开源库,源代码皆可从官网下载,可以在源代码中添加用户自定义的函数,用户也可以新增库进行封装。
PC函数库辅助库的封装
2、实用封装例程
(1)直接获取多种类型数据
用户若想要获取多种数据,如轴的命令位置,轴的反馈位置,板卡上的 IO点等等,往往都是通过多种单独独立的函数获取不同的数据,这样堆积,会导致读写次数的上位,导致程序的卡顿。
为了提升一个上位程序读取控制器数据的速度,往往可以通过自定义一个函数,快速的把数据传输到上位程序上面来,而不是通过多次循环来获取不同类型的数据。
例:假设有一个简易的三轴平台,需要读取轴0,轴1,轴2的命令位置,反馈位置,以及控制器板卡上的输入口0,输入口32,输出口0,输出口33,以及三个轴的状态。
获取数据程序如下:
// test1.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include #include "zmotion.h"
#include "zauxdll2.h"
void commandCheckHandler(const char *command, int ret)
{
if (ret)//非0则失败
{
printf("%s fail!return code is %d\n", command, ret);
}
}
/*************************************************************
Description: //我的自定义直接获取数据函数
Input: //handle 卡链接
iaxisNum 轴的总数量
iaxislist 轴号列表
fDposlist 输出的命令位置值
fMposlist 输出的反馈位置值
iAxisstatuslist 输出的轴状态位置值,按位对应
startIn 要获取起始的IN编号
endIn 要获取结束的IN编号
iIn 输出的IN状态,按位对应
startOut 要获取起始的OUT编号
endOut 要获取结束的OUT编号
iOut 输出的OUT状态,按位对应
Output: //
Return: //错误码
*************************************************************/
int Demo_Direct_MyGetData(ZMC_HANDLE handle,int iaxisNum, int* iaxislist, float* fDposlist,float* fMposlist,int32* iAxisstatuslist,int startIn , int endIn,int *iIn,int startOut , int endOut,int *iOut)
{
char cmdbuff[2048];
char tempbuff[2048];
char cmdbuffAck[20480];
//若传进来的地址为空,则退出
if(NULL == iaxislist || NULL == fDposlist || NULL == fMposlist || NULL == iAxisstatuslist || NULL == iIn || NULL == iOut)
{
return ERR_AUX_PARAERR;
}
//若传进来的结束编号小于起始编码,则退出
if ((endIn<startIn) || (endOut<startOut))
{
return ERR_AUX_PARAERR;
}
int ret=0;
int i;
//生成命令
sprintf(cmdbuff, "?");
//拼接DPOS
for (i=0;i1000)
{
return ERR_AUX_PARAERR; //参数错误,字符串拼接过长
}
}
//拼接MPOS
for (i=0;i1000)
{
return ERR_AUX_PARAERR; //参数错误,字符串拼接过长
}
}
//拼接AXISSTATUS
for (i=0;i1000)
{
return ERR_AUX_PARAERR; //参数错误,字符串拼接过长
}
}
int32 ostart,istart,iend,oend; //一次最多32个
bool addflag;
addflag=false;
int32 temp; //一次最多32个
int32 temp2; //一次最多32个
temp=endIn-startIn+1;
if (temp%32 == 0)
{
temp=temp/32;
}
else
{
temp=temp/32+1;
}
//拼接IN
for (i=0;iendIn)
{
iend=endIn;
}
//生成命令
sprintf(tempbuff, "IN(%d,%d),", istart,iend);
strcat(cmdbuff, tempbuff);//字符串拼接
if (strlen(cmdbuff)>1000)
{
return ERR_AUX_PARAERR ; //参数错误,字符串拼接过长
}
}
temp2=endOut-startOut+1;
if (temp2%32 == 0)
{
temp2=temp2/32;
}
else
{
temp2=temp2/32+1;
}
//拼接OUT
for (i=0;iendOut)
{
oend=endOut;
}
//生成命令
sprintf(tempbuff, "OUT(%d,%d)", ostart,oend);
strcat(cmdbuff, tempbuff);//字符串拼接
if (i1000)
{
return ERR_AUX_PARAERR; //参数错误,字符串拼接过长
}
}
printf("拼接的字符串:\n",cmdbuff);
printf("%s\n",cmdbuff);
ret=ZAux_DirectCommand(handle,cmdbuff,cmdbuffAck,2048);
if(ERR_OK != ret)
{
return ret;
}
//printf("%s\n",cmdbuffAck);
//printf("%d\n",strlen(cmdbuffAck));
//
if(0 == strlen(cmdbuffAck))
{
return ERR_NOACK;
}
float ftempbuff[200];
int itempbuff[200];
ZAux_TransStringtoFloat(cmdbuffAck,iaxisNum*2,ftempbuff);//字符串转换为浮点数
//DPOS输出
for(i=0;i<iaxisNum;i++)
{
//printf("%f\n",ftempbuff[i]);
fDposlist[i]=ftempbuff[i];
}
//MPOS输出
for(i=0;i<iaxisNum;i++)
{
//printf("%f\n",ftempbuff[i+iaxisNum]);
fMposlist[i]=ftempbuff[i+iaxisNum];
}
ZAux_TransStringtoInt(cmdbuffAck,iaxisNum*3+temp2+temp,itempbuff);//字符串转换为整形
//AXISSTATUS输出
for(i=0;i<iaxisNum;i++)
{
//printf("%d\n",itempbuff[i+iaxisNum*2]);
iAxisstatuslist[i]=itempbuff[i+iaxisNum*2];
}
//IN输出
for(i=0;i<temp;i++)
{
//printf("%d\n",itempbuff[i+iaxisNum*3]);
iIn[i]=itempbuff[i+iaxisNum*3];
}
//OUT输出
for(i=0;i<temp2;i++)
{
//printf("%d\n",itempbuff[i+iaxisNum*3+temp]);
iOut[i]=itempbuff[i+iaxisNum*3+temp];
}
return ERR_OK;
}
int _tmain(int argc, _TCHAR* argv[])
{
char *ip_addr = (char *)"127.0.0.1"; //控制器IP地址
ZMC_HANDLE handle = NULL; //连接句柄
int ret = ZAux_OpenEth(ip_addr, &handle); //连接控制器
if (ERR_SUCCESS != ret)
{
printf("控制器连接失败!\n");
handle = NULL;
Sleep(2000);
return -1;
}
printf("控制器连接成功!\n");
int axis[4]={0,1,2,4};
float d_dpos[4];
float d_mpos[4];
int32 d_axis_status[4];
int d_in[10];
int d_out[10];
ret=Demo_Direct_MyGetData(handle,3,axis,d_dpos,d_mpos,d_axis_status,0,32,d_in,0,33,d_out);
int i;
printf("获取到的轴命令位置:\n");
for (i=0;i<3;i++)
{
printf("\t轴%d :%f",i,d_dpos[i]);
}
printf("\n");
printf("获取到的轴反馈位置:\n");
for (i=0;i<3;i++)
{
printf("\t轴%d :%f",i,d_mpos[i]);
}
printf("\n");
printf("获取到的轴状态(按位对应):\n");
for (i=0;i<3;i++)
{
printf("\t轴%d :%d",i,d_axis_status[i]);
}
printf("\n");
printf("获取到的输入口状态:\n");
int j=0;
int tempval;
for (i=0;i0) )
{
j++;
}
//转换成位
tempval=d_in[j]>>(i-32*j);
printf(" IN(%d):%d",i,tempval &(0x01));
if (((i%8)==0)&&(i>0) )
{
printf("\n");
}
}
printf("\n");
printf("获取到的输出口状态:\n");
j=0;
for (i=0;i0) )
{
j++;
}
//转换成位
tempval=d_out[j]>>(i-32*j);
printf(" OUT(%d):%d",i,tempval &(0x01));
if (((i%8)==0)&&(i>0) )
{
printf("\n");
}
}
printf("\n");
Sleep(20000);
ret = ZAux_Close(handle); //关闭连接
commandCheckHandler("ZAux_Close", ret) ;//判断指令是否执行成功
printf("connection closed!\n");
handle = NULL;
return 0;
}
(2)一行命令执行多条不同类型缓冲指令
一般点胶行业、木工行业用的较多的是连续轨迹,连续轨迹之间有插入缓冲输出,如果把运动和连续轨迹分开发送的话,难免会有局限性,可以通过自己单独封装运动函数,来达到一行命令执行多个函数的效果。
例: 假设控制一个XY两轴平台,从坐标点(0,0),(100,0)(输出口0输出50ms) → (100,100)(输出口0输出50ms) → (0,100)(输出口0输出50ms) → (0,0)(输出口0输出50ms)的轨迹,则可以通过自己封装,用一条函数,快速发送下去。
一行命令执行多个函数程序如下:
// test1.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include #include "zmotion.h"
#include "zauxdll2.h"
void commandCheckHandler(const char *command, int ret)
{
if (ret)//非0则失败
{
printf("%s fail!return code is %d\n", command, ret);
}
}
/*************************************************************
Description: //我的自定义运动函数
Input: //handle 卡链接
iMoveLen 填写的运动长度
iaxisNum 参与运动总轴数
iaxislist 轴号列表
fPoslist 距离列表
iout 缓冲输出口
outlist 缓冲输出列表(每条运动,决定是否输出,0为不输出,1为在运动后输出)
outtime 缓冲输出时间
Output: //
Return: //错误码
*************************************************************/
int Demo_Direct_MyMoveABS(ZMC_HANDLE handle,int iMoveLen,int iaxisNum, int* iaxislist, float* fPoslist,int iout,int *outlist,int outtime)
{
char cmdbuff[2048];
char tempbuff[2048];
char cmdbuffAck[20480];
//若传进来的地址为空,则退出
int ret=0;
int i;
//先读取剩余直线缓冲
int iBuffLen = 0;
ret = ZAux_Direct_GetRemain_LineBuffer(handle,iaxislist[0],&iBuffLen);
if(iBuffLen <= iMoveLen*2)
{
return 1002; //运动缓冲不够
}
//生成命令
sprintf(cmdbuff, "BASE(");
//拼接运动轴列表
for (i=0;i1000)
{
return ERR_AUX_PARAERR; //参数错误,字符串拼接过长
}
}
sprintf(tempbuff,"%d)\n",iaxislist[i]);//生成对应命令的字符串
strcat(cmdbuff,tempbuff);
//拼接运动
for (i=0;i1000)
{
return ERR_AUX_PARAERR; //参数错误,字符串拼接过长
}
ret=ZAux_DirectCommand(handle,cmdbuff,cmdbuffAck,2048);
return ret;
}
int _tmain(int argc, _TCHAR* argv[])
{
char *ip_addr = (char *)"127.0.0.1"; //控制器IP地址
ZMC_HANDLE handle = NULL; //连接句柄
int ret = ZAux_OpenEth(ip_addr, &handle); //连接控制器
if (ERR_SUCCESS != ret)
{
printf("控制器连接失败!\n");
handle = NULL;
Sleep(2000);
return -1;
}
printf("控制器连接成功!\n");
ret =ZAux_Direct_SetAtype(handle,0,1);//设置轴0轴类型为1
commandCheckHandler("ZAux_Direct_SetAtype", ret) ;//判断指令是否执行成功
ret =ZAux_Direct_SetAtype(handle,1,1);//设置轴1轴类型为1
commandCheckHandler("ZAux_Direct_SetAtype", ret) ;//判断指令是否执行成功
ret =ZAux_Direct_SetUnits(handle,0,100);//设置轴0脉冲当量为100
commandCheckHandler("ZAux_Direct_SetUnits", ret) ;//判断指令是否执行成功
ret =ZAux_Direct_SetUnits(handle,1,100);//设置轴1脉冲当量为100
commandCheckHandler("ZAux_Direct_SetUnits", ret) ;//判断指令是否执行成功
ret =ZAux_Direct_SetAccel(handle,0,500);//设置轴0加速度
commandCheckHandler("ZAux_Direct_SetAccel", ret) ;//判断指令是否执行成功
ret =ZAux_Direct_SetAccel(handle,1,500);//设置轴1加速度
commandCheckHandler("ZAux_Direct_SetAccel", ret) ;//判断指令是否执行成功
ret =ZAux_Direct_SetDecel(handle,0,500);//设置轴0减速度
commandCheckHandler("ZAux_Direct_SetDecel", ret) ;//判断指令是否执行成功
ret =ZAux_Direct_SetDecel(handle,1,500);//设置轴1减速度
commandCheckHandler("ZAux_Direct_SetDecel", ret) ;//判断指令是否执行成功
ret =ZAux_Direct_SetDpos(handle,0,0);//设置轴0 DPOS清0
commandCheckHandler("ZAux_Direct_SetDpos", ret) ;//判断指令是否执行成功
ret =ZAux_Direct_SetDpos(handle,1,0);//设置轴1 DPOS清0
commandCheckHandler("ZAux_Direct_SetDpos", ret) ;//判断指令是否执行成功
ret =ZAux_Direct_SetSpeed(handle,0,100);//设置轴0速度
commandCheckHandler("ZAux_Direct_SetDecel", ret) ;//判断指令是否执行成功
ret =ZAux_Direct_SetSpeed(handle,1,100);//设置轴1速度
commandCheckHandler("ZAux_Direct_SetDecel", ret) ;//判断指令是否执行成功
ret =ZAux_Direct_SetMerge(handle,0,1);//设置开启连续插补(开启主轴的即可,如轴0,轴1插补,轴0为主轴,主轴号取决于连续插补运动指令轴列表的第一个轴号)
int axis[2]={0,1};
float POS[12]={0,0,0,100,100,100,100,0,0,0};
int otlist[5]={0,1,1,1,1};
ZAux_Trigger(handle);//触发示波器
ret = Demo_Direct_MyMoveABS(handle,5,2,axis,POS,0,otlist,50);//
commandCheckHandler("Demo_Direct_MyMoveABS", ret) ;//判断指令是否执行成功
Sleep(20000);
ret = ZAux_Close(handle); //关闭连接
commandCheckHandler("ZAux_Close", ret) ;//判断指令是否执行成功
printf("connection closed!\n");
handle = NULL;
return 0;
}
3、例程讲解
自定义API封装例程
本次,正运动技术全国产EtherCAT运动控制边缘控制器(二):统一的上位机API接口,就分享到这里 。