Today, Zheng Sports Assistant will share information about periodic reporting from motion control cards. By pre-setting frequently read parameters for proactive periodic reporting, the time spent on PC polling can be reduced. This introduction will use ECI2A18B as an example, mainly explaining how to use the C++ programming language to write and develop the periodic reporting function.
01. Hardware Introduction of ECI2A18B Control Card
The ECI2A18B economical multi-axis motion control card is a pulse-type, modular, network-based motion control card. The card itself supports up to 10 axes to achieve simple trajectory control requirements such as linear interpolation, arbitrary circular interpolation, spatial circular interpolation, helical interpolation, electronic cams, electronic gears, synchronous following, virtual axes, and robot commands. Utilizing an optimized network communication protocol, it can achieve real-time motion control.
Features of the ECI2A18B Control Card:
(1) It supports motion control of 6 differential pulse axes + 4 single-ended pulse axes, and can be expanded to a maximum of 12 axes of motion control.
(2) Pulse output mode: pulse/direction or double pulse.
(3) The AXIS interface supports encoder position measurement and can be configured as a handwheel input mode.
(4) Dedicated handwheel input interface.
(5) Maximum output pulse frequency per axis: 10MHz.
(6) It can be expanded to a maximum of 256 isolated input ports and 256 isolated output ports via CAN bus.
(7) The positive and negative limit signal ports/origin signal ports of the axis can be freely configured to any input port.
(8) The maximum output current of the general digital output port can reach 500mA, which can directly drive some solenoid valves.
(9) RS232 interface, Ethernet interface, CAN interface.
(10) Supports up to 12-axis linear interpolation, arbitrary circular interpolation, and spiral interpolation.
(11) Supports point-to-point motion, electronic cam, linear interpolation, circular interpolation, continuous interpolation motion, and robot commands.
(12) Supports Basic multi-file multi-task programming.
(13) Multiple program encryption methods to protect customers' intellectual property rights.
Interface definition:
The ECI2000 series of economical multi-axis motion control cards can be used in pulse applications with up to 12 axes, such as electronic semiconductor equipment (testing equipment, assembly equipment, latching equipment, soldering machines), dispensing equipment, and production lines.
The controller supports development on various operating systems including Windows, Linux, Mac, Android, and WinCE, and provides DLL libraries for various environments such as VC, C#, VB.NET, and LabVIEW, as shown in the figure below. For upper-computer software programming, refer to the "ZMotion PC Function Library Programming Manual".
02. Why is periodic reporting necessary, and what is its purpose?
1. If the PC actively polls too many times, the following problems may occur:
(1) Consumes system resources
Polling increases system resource consumption; both task polling and timer polling consume system resources. In multi-user or resource-constrained environments, this can easily lead to a decline in system performance.
(2) Wasting CPU resources
Polling always wastes CPU resources. This is because polling runs continuously within the system, regardless of whether the current state of the device changes. In reality, many device states do not change frequently, and polling idly only wastes CPU time.
(3) Affects power management
An increased frequency of peripheral device reports to the PC can increase power consumption, which may shorten battery life or increase energy consumption, thus affecting power management.
(4) Reduce response speed
If the polling frequency is too high, the system's response time to other tasks may slow down. This is because the CPU will continuously poll the current state, which slows down the processing of other computational or user interaction tasks.
(5) Increased network load
If polling involves network communication, too many polling requests may increase the network load, causing network congestion or increased latency.
(6) Increased server load
In a client-server architecture, frequent polling requests can put pressure on the server. This is especially true when server resources are insufficient, which may lead to decreased service quality or request timeouts.
2. Differences in program runtime percentage across various data acquisition methods:
When discussing the impact of single fetch, multiple fetch, and periodic fetch on program execution, we need to consider the characteristics of these operations and their potential impact on the overall program performance.
(1) Single retrieval
Single-item retrieval means that the program processes only one data item at a time. This method is simple and straightforward; however, it is inefficient when processing large amounts of data because each operation incurs the overhead of context switching and resource management. In this case, the program's runtime is mainly consumed by data processing.
(2) Multiple Acquisitions
Multiple fetches mean processing multiple data items simultaneously. In modern computer systems, this is often achieved using multithreading or concurrency techniques, which can significantly improve data processing throughput. However, while multithreading has its advantages, these can be offset by problems such as lock contention, memory contention, and context switching. Therefore, while multiple fetches may shorten the relative running time of processing a single data item, whether the overall running time is reduced depends on the effectiveness of multithreading optimizations.
(3) Periodic acquisition
Periodic data retrieval refers to repeatedly performing data retrieval operations at fixed time intervals. This is common in applications such as real-time monitoring systems and scheduled tasks that require periodic updates to data status. Its runtime percentage depends on the task's periodicity and the actual workload within each period. If the periodic task load is light, its impact on the overall program runtime is minimal.
Application Scenarios: In practical applications, the specific application scenario, data characteristics, and performance requirements determine the choice of data acquisition strategy. For example, if the program needs to respond quickly to a single event, single data acquisition may be more suitable; if the goal is to maximize data processing speed, multiple data acquisitions may be more advantageous; and for applications that need to maintain data freshness regularly, periodic data acquisition is indispensable.
03. Create a new MFC project and add function libraries
1. First, open Visual Studio 2022 and click Create New Project.
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. Go to the official website of Zhengdong and download the PC function library. The path is as follows (this article uses the 64-bit function library as an example).
(1) Go to the official website, select Support and Services, open the Download Center and select Library Files to find all PC function libraries.
(2) Click to download Windows C++ (64-bit), and save it to the desired path as needed.
(3) Save the function library 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.
(1) First, right-click the project file, then select: "Add" → "Existing Item".
(2) 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.
04. Review the PC function manual and familiarize yourself with the relevant function interfaces.
1. The PC function manual can also be found on the Zhengdong website under "Support and Service" → "Download Center" → "Programming Manual".
2. Connect to the controller and obtain the connection handle.
3. The controller automatically reports relevant instructions.
4. Read digital input/output related instructions.
5. Read Modbus register related instructions.
05. Implementing periodic reporting of axes using MFC
1. The example interface is as follows.
2. Select the connection method for the controller/control card using the drop-down control.
·
BOOL CTest_CycleUpDlg::OnInitDialog(){ CDialogEx::OnInitDialog(); // Adds the "About..." menu item to the system menu. // IDM_ABOUTBOX must be within the system command scope. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if(pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if(!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX,strAboutMenu); } } // Set the icon of this dialog box. When the application's main window is not a dialog box, the frame will automatically perform this action: `SetIcon(m_hIcon, TRUE); // Set large icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization code here GetDlgItem(IDC_COMBO2)->SetWindowTextA("Network port\n"); CComboBox *connetList; connetList = (CComboBox *)GetDlgItem(IDC_COMBO2); connetList->AddString(_T("Network port\n")); connetList->AddString(_T("LOCAL\n")); connetList->AddString(_T("PCI\n")); connetList->AddString(_T("Serial port\n")); return TRUE; // Returns TRUE unless focus is set on the control}`
3. Automatically search for IP addresses.
·
void CTest_CycleUpDlg::OnCbnDropdownCombo1(){ char Buffer[256]; CTest_CycleUpDlg* pDlg = (CTest_CycleUpDlg*)AfxGetMainWnd(); GetDlgItemText(IDC_COMBO2,Buffer,256); Buffer[255] = '\0'; if(0==strcmp(Buffer,"Serial port\n")) { Com_SCAN(pDlg); } else if(0==strcmp(Buffer,"Network port\n")) { IP_SCAN(pDlg); } else if(0==strcmp(Buffer,"PCI\n")) { PCI_SCAN(pDlg); } else if(0==strcmp(Buffer,"LOCAL\n")) { CComboBox *m_pEthList; m_pEthList = (CComboBox *)GetDlgItem(IDC_COMBO1); m_pEthList->ResetContent(); m_pEthList->AddString(_T("LOCAL1\n")); }else { CString str; MessageBox("Please select the correct link type!"); return; } return;}
4. Enable reporting.
·
// Enable/disable reporting void CTest_CycleUpDlg::OnBnClickedCheckStart(){ if(NULL == G_ZmcHandle) { MessageBox(_T("Controller not connected")); return; } CString tempstr; UpdateData(true); int iret = 0; if(m_If_StartUp) // Enable reporting{ GetCycleStr(); iret = ZAux_CycleUpEnable(G_ZmcHandle,m_CyclePort,m_CycleTime,Str_CycleCmd); if(ERR_SUCCESS != iret) { tempstr.Format("Periodic reporting failed to start! Error code: %d Command: %s\r\n",iret,Str_CycleCmd); AppendTextOut(tempstr); return; } tempstr.Format("Periodic reporting started! Command: %s\r\n",Str_CycleCmd); AppendTextOut(tempstr); ifirsttimeus = GetTickCount(); SetTimer(1,1,NULL); } else { m_CycleCount = ZAux_CycleUpGetRecvTimes(G_ZmcHandle,m_CyclePort); iret = ZAux_CycleUpDisable(G_ZmcHandle,m_CyclePort); if(ERR_SUCCESS != iret) { tempstr.Format("Periodic reporting closed! Error code: %d \r\n",iret); AppendTextOut(tempstr); return; } tempstr.Format("Periodic reporting closed - reporting time: %dms, number of reports: %d, average time: %.3fms\r\n",(GetTickCount() - ifirsttimeus),m_CycleCount,(float)(GetTickCount() - ifirsttimeus)/m_CycleCount); AppendTextOut(tempstr); KillTimer(1); }}
5. Select the parameter type, starting address, and quantity to obtain.
·
//Get the reported parameters void CTest_CycleUpDlg::GetCycleStr(){ memset(Str_CycleCmd,0,sizeof(Str_CycleCmd)); CString TempString = ""; int ilen = 0; if(m_CycleParaEnAble[0]) { switch(m_CyclePara[0]) { case 0: //AXISSTATUS TempString.Format("AXISSTATUS(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]); break; case 1: //DPOS TempString.Format("DPOS(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]); break; case 2: //IDLE TempString.Format("IDLE(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]); break; case 3: //IN TempString.Format("IN(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]); break; case 4: //MODBUS_REG TempString.Format("MODBUS_REG(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]); break; case 5: //MPOS TempString.Format("MPOS(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]); break; case 6: //OP TempString.Format("OP(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]); break; case 7: //TABLE TempString.Format("TABLE(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]); break; default: break; } } ilen += TempString.GetLength(); memcpy(Str_CycleCmd,TempString.GetBuffer(0),TempString.GetLength()*sizeof(TCHAR)); if(m_CycleParaEnAble[1]) { switch(m_CyclePara[1]) { case 0: //AXISSTATUS TempString.Format("AXISSTATUS(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]); break; case 1: //DPOS TempString.Format("DPOS(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]); break; case 2: //IDLE TempString.Format("IDLE(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]); break; case 3: //IN TempString.Format("IN(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]); break; case 4: //MODBUS_REG TempString.Format("MODBUS_REG(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]); break; case 5: //MPOS TempString.Format("MPOS(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]); break; case 6: //OP TempString.Format("OP(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]); break; case 7: //TABLE TempString.Format("TABLE(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]); break; default: break; } } if((ilen + TempString.GetLength()) < 1000) { memcpy(&Str_CycleCmd[ilen],TempString.GetBuffer(0),TempString.GetLength()*sizeof(TCHAR)); ilen += TempString.GetLength(); } if(m_CycleParaEnAble[2]) { switch(m_CyclePara[2]) { case 0: //AXISSTATUS TempString.Format("AXISSTATUS(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]); break; case 1: //DPOS TempString.Format("DPOS(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]); break; case 2: //IDLE TempString.Format("IDLE(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]); break; case 3: //IN TempString.Format("IN(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]); break; case 4: //MODBUS_REG TempString.Format("MODBUS_REG(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]); break; case 5: //MPOS TempString.Format("MPOS(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]); break; case 6: //OP TempString.Format("OP(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]); break; case 7: //TABLE TempString.Format("TABLE(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]); break; default: break; } } if((ilen + TempString.GetLength()) < 1000) { memcpy(&Str_CycleCmd[ilen],TempString.GetBuffer(0),TempString.GetLength()*sizeof(TCHAR)); ilen += TempString.GetLength(); }}
6. Obtain and output the reported results.
·
//Get the reported results void CTest_CycleUpDlg::GetCycleInfo(){ CString ParaString = ""; CString TempString = ""; CString ShowString = ""; int iret = 0; int ival = 0; for(int inum=0;inum<3;inum++) { ShowString =""; if(m_CycleParaEnAble[inum]) { switch(m_CyclePara[inum]) { case 0: //AXISSTATUS ParaString = "AXISSTATUS"; break; case 1: //DPOS ParaString = "DPOS"; break; case 2: //IDLE ParaString = "IDLE"; break; case 3: //IN ParaString = "IN"; break; case 4: //MODBUS_REG ParaString = "MODBUS_REG"; break; case 5: //MPOS ParaString = "MPOS"; break; case 6: //OP ParaString = "OP"; break; case 7: //TABLE ParaString = "TABLE"; break; default: break; } ShowString += ParaString; for(int i =0;i
7. Mandatory reporting once.
·
// Force one report void CTest_CycleUpDlg::OnBnClickedBtnCycleup(){ if(NULL == G_ZmcHandle) { MessageBox(_T("Controller not connected")); return; } UpdateData(true); int iret = ZAux_CycleUpForceOnce(G_ZmcHandle, m_CyclePort); if(ERR_SUCCESS != iret) { MessageBox(_T("Periodic report refresh failed!")); return; }}
8. The output window and axis parameter window of the positive motion RTSys software make it easy to directly observe the values reported in our cycle.
9. The host computer reads the value reported periodically and enters it into the text box.
10. You can click the drop-down box to select other parameters or change the starting address and quantity to read data from different areas.
11. Comparison between periodic reporting and single-time information acquisition.
Full code download address
▼
This concludes our sharing of the C++ section on the periodic reporting of real-time data IO status by the Zhengdong motion control card.
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.