In the previous lesson, we introduced the hardware interface and functions of the domestically produced EtherCAT motion control edge controller ZMC432H. In this lesson, we will mainly explain the encapsulation principle of positive motion API functions and custom API encapsulation examples.
01
Function Introduction
The ZMC432H, a fully domestically produced EtherCAT motion control edge controller, is a positive motion device with domestically produced and independently controllable hardware and software. Its motion control interface is compatible with EtherCAT bus and pulse-type standalone motion controllers, supporting up to 32-axis motion control. It also supports positive motion remote HMI functionality, providing network configuration display and real-time monitoring and adjustment of parameter settings.
The ZMC432H features a rich set of hardware interfaces and control function modules, enabling efficient and stable motion control and real-time data acquisition to meet the application needs of industrial control and the Industrial Internet. The ZMC432H has a built-in Linux system and can connect via a local LOCAL interface, allowing for faster command interaction; the interaction time for a single command or multiple commands is approximately 40µs.
ZMC432H Video Introduction:
02
Unified API Interface
All controllers and control cards use the same set of API functions, supporting development languages such as C, C++, C#, LabVIEW, Python, and Delphi, and platforms including VC6.0, VB6.0, Qt, and .Net, as well as operating systems such as Windows, Linux, WinCE, and iMac. Each development language has its own corresponding function library, and the APIs called are consistent, greatly improving portability. For instructions on calling the libraries of different development languages, please refer to "ZMotion PC Function Library Programming Manual V2.1.1". Documentation reference path: CD-ROM materials\04PC functions\Zmotion PC Function Library Programming Manual and Example Source Code.
The following is a list of API commands for each functional section;
1. Controller connection
2. Controller Information Acquisition
3. Basic axis parameter settings
4. Basic motion control
5. VR Register
6. Table Register
7. Modbus register
8. Flash/File Read/Write
For more details on the API interfaces, please refer to "ZMotion PC Function Library Programming Manual V2.1.1".
03
Mechanism of online commands
ZAux_Execute or ZAux_DirectCommand can wrap Basic commands. If you need to use commands that are not wrapped or want to wrap your own functions, you can send them via ZAux_Execute or ZAux_DirectCommand, or modify existing code to add the corresponding functions.
There are two ways to send string commands: buffered and direct. See the diagram for details.
Direct method: Directly executes commands related to single variables/arrays/parameters. All passed parameters must be specific numerical values, not expressions. Buffered method: Can execute all commands and supports expressions as parameters, but is slower. For example, consider the encapsulated functions ZAux_Direct_SetSpeed() for setting motion speed and ZAux_Direct_GetMpos for obtaining the current encoder feedback position in zmcaux.cpp. The program is as follows:
·
#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 is a macro defined in zuaxdll2.h, which is the header file of the positive motion library { return ERR_AUX_PARAERR; } sprintf(cmdbuff,"SPEED(%d)=%f",iaxis,fValue);//Generate the string corresponding to the command 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);//Generate the string corresponding to the command ZAux_DirectCommand(handle,cmdbuff,cmdbuffAck,2048); }
04
Introduction and Examples of Custom API Encapsulation
1. Custom API encapsulation
The principle behind custom API encapsulation is actually to utilize the online command mechanism, where the host computer generates various ZBASIC instructions to achieve the desired functions.
The ZAux library directly uses ZBASIC commands to send to the controller via ZAux_Execute or ZAux_DirectCommand. For the corresponding functions, please refer to the command descriptions in the ZBASIC manual.
The ZAux library is a completely open-source library, and all source code can be downloaded from the official website. Users can add custom functions to the source code, and users can also create new libraries for encapsulation.
2. Practical encapsulation routines
(1) Directly acquire multiple types of data
If users want to obtain various types of data, such as axis command positions, axis feedback positions, and I/O points on the control board, they often use multiple separate functions to retrieve different data. This accumulation leads to a high number of read/write operations, causing program lag. To improve the speed at which a host program reads controller data, a custom function can be defined to quickly transfer data to the host program, instead of using multiple loops to retrieve different types of data. For example, suppose there is a simple three-axis platform that needs to read the command and feedback positions of axes 0, 1, and 2, as well as the input ports 0 and 32, output ports 0 and 33 on the control board, and the status of the three axes.
The data acquisition procedure is as follows:
·
// test1.cpp : Defines the entry point for the console application. #include "stdafx.h" #include #include "zmotion.h" #include "zauxdll2.h" void commandCheckHandler(const char *command, int ret){ if (ret)//If non-zero, it fails{ printf("%s fail!return code is %d\n", command, ret); }}/*****************************************************************Description: //My custom function to directly retrieve data Input: //handle card link iaxisNum Total number of axes iaxislist Axis number list fDposlist Output command position value fMposlist Output feedback position value iAxisstatuslist Output axis status position value, bitwise corresponding startIn Get the starting IN number endIn Get the ending IN number iIn Output IN status, bitwise corresponding startOut Get the starting OUT number endOut Get the ending OUT number iOut Output OUT status, bitwise corresponding Output: //Return: //Error code*************************************************************/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 the passed address is empty, exit if(NULL == iaxislist || NULL == fDposlist || NULL == fMposlist || NULL == iAxisstatuslist || NULL == iIn || NULL == iOut) { return ERR_AUX_PARAERR; } //If the passed end number is less than the start code, exit if ((endIn<startin) (strlen(cmdbuff)="" if="" string concatenation="" tempbuff);="" strcat(cmdbuff,="" generate the string corresponding to the command="" sprintf(tempbuff,?dpos(%d),?,iaxislist[i]);="" {="" (i="0;i<iaxisNum;i++)" for="" concatenate dpos="" ???);="" sprintf(cmdbuff,="" generate the command="" i;="" int="" ret="0;" }="" err_aux_paraerr;="" return="" (endout 1000) { return ERR_AUX_PARAERR; // Parameter error, string concatenation too long } } // Concatenate MPOS for (i=0;i 1000) { return ERR_AUX_PARAERR; // Parameter error, string concatenation too long } } // Concatenate AXISSTATUS for (i=0;i 1000) { return ERR_AUX_PARAERR; // Parameter error, string concatenation too long } } int32 ostart,istart,iend,oend; // Maximum 32 at a time bool addflag; addflag=false; int32 temp; // Maximum 32 at a time int32 temp2; // Maximum 32 at a time temp=endIn-startIn+1; if (temp%32 == 0) { temp=temp/32; } else { temp=temp/32+1; } // Concatenate IN for (i=0;i endIn) { iend=endIn; } // Generate command sprintf(tempbuff, "IN(%d,%d),", istart,iend); strcat(cmdbuff, tempbuff);// String concatenation if (strlen(cmdbuff)>1000) { return ERR_AUX_PARAERR ; // Parameter error, string concatenation too long } } temp2=endOut-startOut+1; if (temp2%32 == 0) { temp2=temp2/32; } else { temp2=temp2/32+1; } // Concatenate OUT for (i=0;i endOut) { oend=endOut; } // Generate command sprintf(tempbuff, "OUT(%d,%d)", ostart,oend); strcat(cmdbuff, tempbuff);// String concatenation if (i 1000) { return ERR_AUX_PARAERR; // Parameter error, string concatenation too long } } printf("Concatenated string:\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);//String to floating-point number//DPOS output for(i=0;i 0) ) { j++; } // Convert to bitwise 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("Output port status obtained:\n"); j=0; for (i=0;i<=33;i++) { if (((i%32)==0)&&(i>0) ) { j++; } // Convert to bitwise 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); // Close the connection commandCheckHandler("ZAux_Close", ret); // Check if the command was executed successfully printf("connection closed!\n"); handle = NULL; return 0;}
(2) A single command executes multiple buffered instructions of different types.
In industries like dispensing and woodworking, continuous trajectories are commonly used. These trajectories have buffered outputs between each other. Sending the motion and continuous trajectory separately has limitations. A better approach is to encapsulate a separate motion function, allowing a single command to execute multiple functions. For example, suppose you control an XY-axis platform, tracking the trajectory from coordinates (0, 0), (100, 0) (output port 0 outputs 50ms) → (100, 100) (output port 0 outputs 50ms) → (0, 0) (output port 0 outputs 50ms). This can be achieved by encapsulating a single function and sending it quickly. The program for executing multiple functions with a single command is as follows:
·
// test1.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include #include "zmotion.h" #include "zauxdll2.h" void commandCheckHandler(const char *command, int ret){ if (ret)//If non-zero, it fails{ printf("%s fail!return code is %d\n", command, ret); }}/*****************************************************************Description: //My custom motion function Input: //handle Card link iMoveLen The length of motion filled in iaxisNum The total number of axes involved in the motion iaxislist Axis number list fPoslist Distance list iout Buffered output port outlist Buffered output list (for each motion, it determines whether to output, 0 for no output, 1 for output after the motion) outtime Buffered output time Output: //Return: //Error code*************************************************************/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]; // If the address passed in is empty, then exit int ret=0; int i; // First read the remaining line buffer int iBuffLen = 0; ret = ZAux_Direct_GetRemain_LineBuffer(handle,iaxislist[0],&iBuffLen); if(iBuffLen <= iMoveLen*2) { return 1002; // Motion buffer is insufficient} // Generate command sprintf(cmdbuff, "BASE("); // Concatenate motion axis list for (i=0;i 1000) { return ERR_AUX_PARAERR; // Parameter error, string concatenation too long } } sprintf(tempbuff,"%d)\n",iaxislist[i]);// Generate the string corresponding to the command strcat(cmdbuff,tempbuff); // Concatenation movement for (i=0;i 1000) { return ERR_AUX_PARAERR; // Parameter error, string concatenation too long } ret=ZAux_DirectCommand(handle,cmdbuff,cmdbuffAck,2048); return ret;}int _tmain(int argc, _TCHAR* argv[]){ char *ip_addr = (char *)"127.0.0.1"; // Controller IP address ZMC_HANDLE handle = NULL; // Connection handle int ret = ZAux_OpenEth(ip_addr, &handle); // Connect to controller if (ERR_SUCCESS != ret) { printf("Controller connection failed!\n"); handle = NULL; Sleep(2000); return -1; } printf("Controller connection successful!\n"); ret =ZAux_Direct_SetAtype(handle,0,1);// Set axis 0 axis type to 1 commandCheckHandler("ZAux_Direct_SetAtype", ret); // Check if the command was executed successfully ret = ZAux_Direct_SetAtype(handle, 1, 1); // Set axis 1's axis type to 1 commandCheckHandler("ZAux_Direct_SetAtype", ret); // Check if the command was executed successfully ret = ZAux_Direct_SetUnits(handle, 0, 100); // Set axis 0's pulse equivalent to 100 commandCheckHandler("ZAux_Direct_SetUnits", ret); // Check if the command was executed successfully ret = ZAux_Direct_SetUnits(handle, 1, 100); // Set axis 1's pulse equivalent to 100 commandCheckHandler("ZAux_Direct_SetUnits", ret); // Check if the command was executed successfully ret =ZAux_Direct_SetAccel(handle,0,500); // Sets the acceleration of axis 0 commandCheckHandler("ZAux_Direct_SetAccel", ret); // Checks if the command was executed successfully ret =ZAux_Direct_SetAccel(handle,1,500); // Sets the acceleration of axis 1 commandCheckHandler("ZAux_Direct_SetAccel", ret); // Checks if the command was executed successfully ret =ZAux_Direct_SetDecel(handle,0,500); // Sets the deceleration of axis 0 commandCheckHandler("ZAux_Direct_SetDecel", ret); // Checks if the command was executed successfully ret =ZAux_Direct_SetDecel(handle,1,500); // Sets the deceleration of axis 1 commandCheckHandler("ZAux_Direct_SetDecel", ret); // Checks if the command was executed successfully ret =ZAux_Direct_SetDpos(handle,0,0); // Set axis 0 DPOS to 0 commandCheckHandler("ZAux_Direct_SetDpos", ret); // Check if the command was executed successfully ret =ZAux_Direct_SetDpos(handle,1,0); // Set axis 1 DPOS to 0 commandCheckHandler("ZAux_Direct_SetDpos", ret); // Check if the command was executed successfully ret =ZAux_Direct_SetSpeed(handle,0,100); // Set axis 0 speed commandCheckHandler("ZAux_Direct_SetDecel", ret); // Check if the command was executed successfully ret =ZAux_Direct_SetSpeed(handle,1,100); // Set axis 1 speed commandCheckHandler("ZAux_Direct_SetDecel", ret); // Check if the command was executed successfully ret =ZAux_Direct_SetMerge(handle,0,1);//Set continuous interpolation to enable (enable the main axis, such as axis 0, axis 1 interpolation, axis 0 is the main axis, the main axis number depends on the first axis number in the continuous interpolation motion command axis list) 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);//Trigger the oscilloscope ret = Demo_Direct_MyMoveABS(handle,5,2,axis,POS,0,otlist,50);// commandCheckHandler("Demo_Direct_MyMoveABS", ret);//Check if the command was executed successfully Sleep(20000); ret = ZAux_Close(handle); // Close the connection commandCheckHandler("ZAux_Close", ret); // Check if the command was executed successfully printf("connection closed!\n"); handle = NULL; return 0;}
The oscilloscope sampling waveform is shown in the figure:
Full code download address
▼
This concludes our presentation on the domestically produced EtherCAT motion control edge controller (Part 2): a unified host computer API interface.
For more exciting content, please follow the "Zheng Motion Assistant" WeChat official account. For related development environment and example code, please contact Zheng Motion's technical sales engineer: 400-089-8936.
This article is original content from Zheng Motion Technology. We welcome everyone to reprint it for mutual learning and to jointly improve China's intelligent manufacturing level. Copyright belongs to Zheng Motion Technology. Please indicate the source if you reprint this article.