Today, Zheng Sports Assistant will share with you how to use C++ to write a controller for the ECI3808 motion control card to set up the safety processing of the motion control system.
ECI3808 Hardware Introduction
1. Function Introduction
The ECI3808 series control cards support up to 12 axes of linear interpolation, arbitrary circular interpolation, spatial circular interpolation, helical interpolation, electronic cams, electronic gears, synchronous following, virtual axes, and robot commands; they also employ optimized network communication protocols to achieve real-time motion control.
The ECI3808 series motion control card supports Ethernet and RS232 communication interfaces to connect to a computer, receive commands from the computer, and can connect to various expansion modules via CAN bus to expand the number of input/output points or motion axes.
Applications for the ECI3808 series motion control card can be developed using software such as VC, VB, VS, C++, and C#. The program requires the dynamic library zmotion.dll to run. During debugging, the ZDevelop software can be connected to the controller simultaneously for convenient debugging and observation.
2. Hardware Interface
General purpose input circuit
General purpose output port circuit
AD/DA Interface Description
Local Pulse Axis Description
Auxiliary encoder CN15
3. Controller Basic Information
II. Motion Control Development using C++
1. Create a new MFC project and add function libraries.
(1) In the VS2015 menu, go to "File" → "New" → "Project" to start the Project Creation Wizard.
(2) Select “Visual C++” as the development language and “MFC Application” as the program type.
(3) Click Next.
(4) Select "Dialog-based" as the type, then click Next or Finish.
(5) Locate the CD-ROM data provided by the manufacturer. The path is as follows (64-bit library as an example).
A. Locate the "8.PC Functions" folder in the CD-ROM provided by the manufacturer and click to enter.
B. Select the "Function Library 2.1" folder.
C. Select the "Windows Platform" folder.
D. Select the appropriate function library as needed; here, we choose the 64-bit library.
E. Unzip the C++ compressed file, which contains the corresponding C++ function library.
F. The specific path to the function library is as follows.
(6) Copy the C++ library files and related header files provided by the vendor into the newly created project.
(7) Add static libraries and related header files to the project.
A. First, right-click the project file, then select: "Add" → "Existing Item".
B. In the pop-up window, add the static library and related header files in sequence.
(8) Declare the header files used and define the controller connection handle.
The project is now complete and ready for MFC project development.
2. Review the PC function manual and familiarize yourself with the relevant function interfaces. (1) The PC function manual is also included in the CD-ROM materials. The specific path is as follows: "CD-ROM materials\8.PC functions\Function library 2.1\ZMotion function library programming manual V2.1.pdf"
(2) Link controller, obtain link handle.
ZAux_OpenEth() Interface Description:
(3) Configure the function interface corresponding to the IO signal point as follows.
→For detailed information on the interface, please refer to the PC function manual.
The following is the interface for setting the positive and negative software limit size. When the limit is reached, it will be forcibly stopped and will not continue to run in order to achieve the purpose of protection. The acceleration and deceleration at the stop are based on the set FASTDEC rapid deceleration speed (when FASTDEC is set to 0, the set DECEL is used).
3. Security Settings for MFC Development Controllers
(1) The routine interface is as follows.
(2) In the event handling function of the link button, the interface function ZAux_OpenEth() of the link controller is called to link with the controller. After the link is successfully linked, timer 1 is started to monitor the status of the controller.
·
/Network port connection controller void CSingle_move_Dlg::OnOpen(){ char buffer[256]; int32 iresult; //If already connected, disconnect first if(NULL != g_handle) { ZAux_Close(g_handle); g_handle = NULL; } //Select and get the IP address from the IP drop-down box GetDlgItemText(IDC_IPLIST,buffer,255); buffer[255] = '\0'; //Start the connection controller iresult = ZAux_OpenEth(buffer, &g_handle); if(ERR_SUCCESS != iresult) { g_handle = NULL; MessageBox(_T("Connection failed")); SetWindowText("Not connected"); return; } //Start timer 1 if connection is successful SetWindowText("Connected"); SetTimer( 1, 100, NULL ); }
(3) Monitor the controller status through a timer.
·
void CSingle_homeDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: Add your message handler code here and/or call default if(NULL == g_handle) { MessageBox(_T("Connection disconnected")); return ; } if(1 == nIDEvent) { CString string; uint test; float position[4] = {0}; int status[4] = {0}; int nAxisStatus[4] = {0}; int nMaxSpeed[4] = { 0 }; for (int i = 0;i< 4; i++) { ZAux_Direct_GetDpos( g_handle,i,&position[i]); //Get the current axis position ZAux_Direct_GetIfIdle(g_handle,i,&status[i]); //Determine the current axis status ZAux_Direct_GetAxisStatus(g_handle, i, &nAxisStatus[i]); ZAux_Direct_GetMaxSpeed(g_handle,i, &nMaxSpeed[i]); }
if (status[0] == -1) { if (nAxisStatus[0] == 0 || (nAxisStatus[0] & 0x000040) == 64) { string.Format("X Stop%.2f Axis Status Normal", position[0]); GetDlgItem(IDC_STATE_X)->SetWindowText(string); } if ((nAxisStatus[0] & 0x000010) == 16) { string.Format("X Stop%.2f Axis Positive Hard Limit Alarm", position[0]); GetDlgItem(IDC_STATE_X)->SetWindowText(string); } if ((nAxisStatus[0] & 0x000020) == 32) { string.Format("X Stop%.2f Axis Reverse Hard Limit Alarm", position[0]); GetDlgItem(IDC_STATE_X)->SetWindowText(string); } if ((nAxisStatus[0] & 0x000200) == 512) { string.Format("X Stop%.2f Axis Positive Soft Limit Alarm", position[0]); GetDlgItem(IDC_STATE_X)->SetWindowText(string); } if ((nAxisStatus[0] & 0x000400) == 1024) { string.Format("X Stop%.2f Axis Reverse Soft Limit Alarm", position[0]); GetDlgItem(IDC_STATE_X)->SetWindowText(string); } if ((nAxisStatus[0] & 0x001000) == 4096) { string.Format("X Stop%.2f Pulse Frequency Exceeds max_speed:%d Limit", position[0],nMaxSpeed[0]); GetDlgItem(IDC_STATE_X)->SetWindowText(string); } } else { if (nAxisStatus[0] == 0 || (nAxisStatus[0] & 0x000040) == 64) { string.Format("X is running.2f axis status is normal", position[0]); GetDlgItem(IDC_STATE_X)->SetWindowText(string); } if ((nAxisStatus[0] & 0x000010) == 16) { string.Format("X is running.2f axis positive hard limit alarm", position[0]); GetDlgItem(IDC_STATE_X)->SetWindowText(string); } if ((nAxisStatus[0] & 0x000020) == 32) { string.Format("X is running.2f axis reverse hard limit alarm", position[0]); GetDlgItem(IDC_STATE_X)->SetWindowText(string); } if ((nAxisStatus[0] & 0x000200) == 512) { string.Format("X running %.2f axis positive soft limit alarm", position[0]); GetDlgItem(IDC_STATE_X)->SetWindowText(string); } if ((nAxisStatus[0] & 0x000400) == 1024) { string.Format("X running %.2f axis reverse soft limit alarm", position[0]); GetDlgItem(IDC_STATE_X)->SetWindowText(string); } if ((nAxisStatus[0] & 0x001000) == 4096) { string.Format("X running %.2f pulse frequency exceeds max_speed:%d limit", position[0], nMaxSpeed[0]); GetDlgItem(IDC_STATE_X)->SetWindowText(string); } } if (status[1] == -1) { if (nAxisStatus[1] == 0 || (nAxisStatus[1] & 0x000040) == 64) { string.Format("Y Stop%.2f Axis Status Normal", position[1]); GetDlgItem(IDC_STATE_Y)->SetWindowText(string); } if ((nAxisStatus[1] & 0x000010) == 16) { string.Format("Y Stop%.2f Axis Positive Hard Limit Alarm", position[1]); GetDlgItem(IDC_STATE_Y)->SetWindowText(string); } if ((nAxisStatus[1] & 0x000020) == 32) { string.Format("Y Stop %.2f axis reverse hard limit alarm", position[1]); GetDlgItem(IDC_STATE_Y)->SetWindowText(string); } if ((nAxisStatus[1] & 0x000200) == 512) { string.Format("Y Stop %.2f axis positive soft limit alarm", position[1]); GetDlgItem(IDC_STATE_Y)->SetWindowText(string); } if ((nAxisStatus[1] & 0x000400) == 1024) { string.Format("Y Stop %.2f axis reverse soft limit alarm", position[1]); GetDlgItem(IDC_STATE_Y)->SetWindowText(string); } if ((nAxisStatus[1] & 0x001000) == 4096) { string.Format("Y Stop %.2f pulse frequency exceeds max_speed:%d Limit", position[1], nMaxSpeed[1]); GetDlgItem(IDC_STATE_Y)->SetWindowText(string); } } else { if (nAxisStatus[1] == 0 || (nAxisStatus[1] & 0x000040) == 64) { string.Format("Y running.2f axis status normal", position[1]); GetDlgItem(IDC_STATE_Y)->SetWindowText(string); } if ((nAxisStatus[1] & 0x000010) == 16) { string.Format("Y running.2f axis positive hard limit alarm", position[1]); GetDlgItem(IDC_STATE_Y)->SetWindowText(string); } if ((nAxisStatus[1] & 0x000020) == 32) { string.Format("Y Running %.2f axis reverse hard limit alarm", position[1]); GetDlgItem(IDC_STATE_Y)->SetWindowText(string); } if ((nAxisStatus[1] & 0x000200) == 512) { string.Format("Y running %.2f axis positive soft limit alarm", position[1]); GetDlgItem(IDC_STATE_Y)->SetWindowText(string); } if ((nAxisStatus[1] & 0x000400) == 1024) { string.Format("Y running %.2f axis reverse soft limit alarm", position[1]); GetDlgItem(IDC_STATE_Y)->SetWindowText(string); } if ((nAxisStatus[1] & 0x001000) == 4096) { string.Format("Y running %.2f The pulse frequency exceeds the max_speed:%d limit", position[1], nMaxSpeed[1]); GetDlgItem(IDC_STATE_Y)->SetWindowText(string); } } if (status[2] == -1) { if (nAxisStatus[2] == 0 || (nAxisStatus[2] & 0x000040) == 64) { string.Format("Z Stop%.2f Axis Status Normal", position[2]); GetDlgItem(IDC_STATE_Z)->SetWindowText(string); } if ((nAxisStatus[2] & 0x000010) == 16) { string.Format("Z Stop%.2f Axis Positive Hard Limit Alarm", position[2]); GetDlgItem(IDC_STATE_Z)->SetWindowText(string); } if ((nAxisStatus[2] & 0x000020) == 32) { string.Format("Z Stop%.2f Axis Reverse Hard Limit Alarm", position[2]); GetDlgItem(IDC_STATE_Z)->SetWindowText(string); } if ((nAxisStatus[2] & 0x000200) == 512) { string.Format("Z Stop%.2f Axis Positive Soft Limit Alarm", position[2]); GetDlgItem(IDC_STATE_Z)->SetWindowText(string); } if ((nAxisStatus[2] & 0x000400) == 1024) { string.Format("Z Stop%.2f Axis Reverse Soft Limit Alarm", position[2]); GetDlgItem(IDC_STATE_Z)->SetWindowText(string); } if ((nAxisStatus[2] & 0x001000) == 4096) { string.Format("Z Stop%.2f Pulse frequency exceeds max_speed:%d limit", position[2], nMaxSpeed[2]); GetDlgItem(IDC_STATE_Z)->SetWindowText(string); } } else { if (nAxisStatus[2] == 0 || (nAxisStatus[2] & 0x000040) == 64) { string.Format("Z Running%.2f Axis status normal", position[2]); GetDlgItem(IDC_STATE_Z)->SetWindowText(string); } if ((nAxisStatus[2] & 0x000010) == 16) { string.Format("Z Running%.2f Axis positive hard limit alarm", position[2]); GetDlgItem(IDC_STATE_Z)->SetWindowText(string); } if ((nAxisStatus[2] & 0x000020) == 32) { string.Format("Z running %.2f axis reverse hard limit alarm", position[2]); GetDlgItem(IDC_STATE_Z)->SetWindowText(string); } if ((nAxisStatus[2] & 0x000200) == 512) { string.Format("Z running %.2f axis positive soft limit alarm", position[2]); GetDlgItem(IDC_STATE_Z)->SetWindowText(string); } if ((nAxisStatus[2] & 0x000400) == 1024) { string.Format("Z running %.2f axis reverse soft limit alarm", position[2]); GetDlgItem(IDC_STATE_Z)->SetWindowText(string); } if ((nAxisStatus[2] & 0x001000) == 4096) { string.Format("Z is running.2f pulse frequency exceeds max_speed:%d limit", position[2], nMaxSpeed[2]); GetDlgItem(IDC_STATE_Z)->SetWindowText(string); } } if (status[3] == -1) { if (nAxisStatus[3] == 0 || (nAxisStatus[3] & 0x000040) == 64) { string.Format("R is stopped.2f axis status is normal", position[3]); GetDlgItem(IDC_STATE_R)->SetWindowText(string); } if ((nAxisStatus[3] & 0x000010) == 16) { string.Format("R is stopped.2f axis positive hard limit alarm", position[3]); GetDlgItem(IDC_STATE_R)->SetWindowText(string); } if ((nAxisStatus[3] & 0x000020) == 32) { string.Format("R Stop %.2f axis reverse hard limit alarm", position[3]); GetDlgItem(IDC_STATE_R)->SetWindowText(string); } if ((nAxisStatus[3] & 0x000200) == 512) { string.Format("R Stop %.2f axis positive soft limit alarm", position[3]); GetDlgItem(IDC_STATE_R)->SetWindowText(string); } if ((nAxisStatus[3] & 0x000400) == 1024) { string.Format("R Stop %.2f axis reverse soft limit alarm", position[3]); GetDlgItem(IDC_STATE_R)->SetWindowText(string); } if ((nAxisStatus[3] & 0x001000) == 4096) { string.Format("R Stop %.2f Pulse frequency exceeds max_speed:%d limit", position[3], nMaxSpeed[3]); GetDlgItem(IDC_STATE_R)->SetWindowText(string); } } else { if (nAxisStatus[3] == 0 || (nAxisStatus[3] & 0x000040) == 64) { string.Format("R Running %.2f Axis status normal", position[3]); GetDlgItem(IDC_STATE_R)->SetWindowText(string); } if ((nAxisStatus[3] & 0x000010) == 16) { string.Format("R Running %.2f Axis positive hard limit alarm", position[3]); GetDlgItem(IDC_STATE_R)->SetWindowText(string); } if ((nAxisStatus[3] & 0x000020) == 32) { string.Format("R running %.2f axis reverse hard limit alarm", position[3]); GetDlgItem(IDC_STATE_R)->SetWindowText(string); } if ((nAxisStatus[3] & 0x000200) == 512) { string.Format("R running %.2f axis positive soft limit alarm", position[3]); GetDlgItem(IDC_STATE_R)->SetWindowText(string); } if ((nAxisStatus[3] & 0x000400) == 1024) { string.Format("R running %.2f axis reverse soft limit alarm", position[3]); GetDlgItem(IDC_STATE_R)->SetWindowText(string); } if ((nAxisStatus[3] & 0x000400) == 1024) { string.Format("R running %.2f axis reverse soft limit alarm", position[3]); GetDlgItem(IDC_STATE_R)->SetWindowText(string); } if ((nAxisStatus[3] & 0x001000) == 4096) { string.Format("R is running.2f The pulse frequency exceeds the max_speed:%d limit", position[3], nMaxSpeed[3]); GetDlgItem(IDC_STATE_R)->SetWindowText(string); } } } CDialog::OnTimer(nIDEvent);}
(4) Use the event handler function of the parameter setting button to initialize the parameters and set the corresponding safety limit settings.
·
void CSingle_homeDlg::OnHome() // Zeroing motion { // TODO: Add your control notification handler code here UpdateData(true);//Refresh parameters int status = 0; ZAux_Direct_GetIfIdle(g_handle, m_nAxis,&status); //Check the current axis status if (status == 0) //Already in motion return; //Set axis type 7 - Pulse axis type + encoder Z signal can be set to 1 if EZ zeroing is not required ZAux_Direct_SetAtype(g_handle, m_nAxis, 7); //Set pulse mode and logical direction (pulse + direction) ZAux_Direct_SetInvertStep(g_handle, m_nAxis, 0); //Set pulse equivalent 1 to indicate the number of pulses per 1MM, the unit of measurement is MM ZAux_Direct_SetUnits(g_handle, m_nAxis, m_units); //Set speed, acceleration and deceleration ZAux_Direct_SetLspeed(g_handle, m_nAxis, m_lspeed); ZAux_Direct_SetSpeed(g_handle, m_nAxis, m_speed); ZAux_Direct_SetAccel(g_handle, m_nAxis, m_acc); ZAux_Direct_SetDecel(g_handle, m_nAxis, m_dec); ZAux_Direct_SetCreep(g_handle, m_nAxis, m_creep); ZAux_Direct_SetFastDec(g_handle, m_nAxis, m_FastDec); ZAux_Direct_SetRsLimit(g_handle, m_nAxis, m_RsLimit); ZAux_Direct_SetFsLimit(g_handle, m_nAxis, m_FwdLimit); ZAux_Direct_SetMaxSpeed(g_handle, m_nAxis, m_nMaxSpeed); // Sets the origin input signal for the corresponding axis. ZAux_Direct_SetDatumIn(g_handle, m_nAxis, m_datumin); // ZMC series assumes the origin signal (normally closed) is encountered when OFF. For normally open sensors, the input port needs to be reversed; ECI series do not require reversal. ZAux_Direct_SetInvertIn(g_handle, m_datumin, 1); // Sets the positive limit input signal for the corresponding axis. ZAux_Direct_SetFwdIn(g_handle, m_nAxis, m_FwdIn); // ZMC series assumes the origin signal (normally closed) is encountered when OFF. For normally open sensors, the input port needs to be reversed; ECI series do not require reversal. ZAux_Direct_SetInvertIn(g_handle, m_FwdIn, 1); // Set the negative limit input signal for the corresponding axis ZAux_Direct_SetRevIn(g_handle, m_nAxis, m_RevIn); // ZMC series assumes that the origin signal (normally closed) has been encountered when OFF. If it is a normally open sensor, the input port needs to be reversed. ECI series does not need to be reversed. ZAux_Direct_SetInvertIn(g_handle, m_RevIn, 1); UpdateData(false); }
(5) Stop the current motion by using the event handler function of the stop motion button.
·
void CSingle_homeDlg::OnStop() //Stop movement{ // TODO: Add your control notification handler code here if(NULL == g_handle) { MessageBox(_T("Connection disconnected")); return ; } ZAux_Direct_Single_Cancel(g_handle,m_nAxis,2); //}
(6) The coordinates of the current axis are cleared by using the event handling function of the coordinate clearing button.
·
void CSingle_homeDlg::OnZero() // Clear coordinates to zero { if(NULL == g_handle) { MessageBox(_T("Connection disconnected")); return ; } // TODO: Add your control notification handler code here for (int i=0;i<4;i++) { ZAux_Direct_SetDpos(g_handle,i,0); // Set zero point } }
(7) Determine whether the current button state is lifted or pressed to trigger the continuous movement and stop state of the corresponding axis by the intercept button response time of the axis continuous running button.
·
BOOL CSingle_homeDlg::PreTranslateMessage(MSG * pMsg){ // Positive continuous motion if (pMsg->message == WM_LBUTTONDOWN) { if (pMsg->hwnd == GetDlgItem(IDC_BUTTON_FWD)->m_hWnd) { //TODO: ZAux_Direct_Single_Vmove(g_handle, m_nAxis, 1); } }// Release and stop motion else if (pMsg->message == WM_LBUTTONUP) { if (pMsg->hwnd == GetDlgItem(IDC_BUTTON_FWD)->m_hWnd) { //TODO: ZAux_Direct_Single_Cancel(g_handle,m_nAxis,2); } }// Negative continuous motion if (pMsg->message == WM_LBUTTONDOWN) { if (pMsg->hwnd == GetDlgItem(IDC_BUTTON_RWD)->m_hWnd) { // TODO: Add control notification handler code here ZAux_Direct_Single_Vmove(g_handle, m_nAxis, -1); //TODO: } }//Release to stop else if (pMsg->message == WM_LBUTTONUP) { if (pMsg->hwnd == GetDlgItem(IDC_BUTTON_RWD)->m_hWnd) { //TODO: ZAux_Direct_Single_Cancel(g_handle, m_nAxis, 2); } } return CDialog::PreTranslateMessage(pMsg);}
Full code download address
▼
III. Debugging and Monitoring
Compile and run the routines, and simultaneously monitor the controller status by connecting to the controller through the ZDevelop software.
1. Connect the ZDevelop software and click "View" → "Oscilloscope" to open the oscilloscope and monitor the axis movement.
2. During operation, if the pulse frequency obtained by multiplying the pulse equivalent by the speed is greater than the set maximum pulse frequency (maxspeed), an alarm will be triggered for 1000 hours and displayed on the interface.
→The system will also stop and issue an alarm when the software limit is exceeded.
3. ZDevelop software debugging video
This concludes our sharing of Zheng Motion Technology's easy-to-use motion control card (Part 12): Safety Settings for Motion Control Systems. For more exciting content, please follow the "Zheng Motion Assistant" WeChat official account. For related development environment and example code, please contact Zheng Motion Technology's 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.