Introduction
Today, Zheng Sports Assistant will share with you the usage of the ECI0016PA series Ethernet I/O card, the development of the ECI data acquisition card in C# language, the testing of the interaction speed of multiple I/O read and write operations, and the acquisition of oscilloscope curves.
Hardware Introduction
The ECI0016PA/ECI00PAB and other ECI0 series motion control cards support Ethernet and RS232 communication interfaces for connection to a computer, receiving commands from the computer to run operations. They can also connect to various expansion modules via a CAN bus to expand the number of input/output points. The difference between ECI0016PAB and ECI0016PA is that ECI0016PAB supports offline Basic programming language.
ECI0016PA\ECI0016PAB Data Acquisition Card
The ECI0016PA/ECI0016PAB and other ECI00 series products adopt optimized network communication protocols, enabling real-time logic control and IO status monitoring.
Applications for ECI0 series IO cards such as ECI0016PA/ECI0016PAB 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 RTSys software can be connected to the controller simultaneously for convenient debugging and observation.
Features of ECI0016PA/ECI0016PAB
The ECI0016PA/ECI0016PAB basic resources include 8 NPN inputs, 8 NPN outputs, 12 analog inputs (16-bit resolution, -10V to +10V range), and 2 analog outputs (12-bit resolution, -10V to +10V range).
The ECI0016PA has a default floating ground value of approximately 2.5V when not connected. This value is not meaningful; the voltage obtained after connecting to the load should be used as the reference.
The ECI0016PA Ethernet communication rate is 7 seconds for 10,000 Ethernet network acquisitions of basic resources (ps: 12 AD, 2 DA, 8 IN, and 8 OUT on the board are basic hardware resources). That is, the average acquisition of resource information is less than 1ms (actual communication efficiency is related to the network card). If the acquisition of transmission resource information is reduced, the communication rate can be increased to a faster level.
The ECI0016PAB supports offline detection, software offline detection, and automatic board positioning output; if the board is offline, the software layer can obtain the offline status through timed heartbeat detection.
Wiring Reference
1. General purpose input port circuit diagram
2. Wiring reference diagram for general input port
3. General Output Port Circuit Diagram
4. Wiring reference diagram for general output ports
5. Analog Input/Output Specifications
6. Analog Input/Output Wiring Reference Diagram
7. Controller Basic Information
ZCAN extension method
1. When the number of I/Os provided by the ECI0016PA itself is insufficient, the ECI0016PA can be used as the master station via ZCAN, and the required number of I/Os can be increased by using the ZIO extension version.
The wiring diagram for ECI0016PA as follows:
The ECI0016PA control card and the ZIO expansion module both use dual power supplies. During use, the main power supply of the expansion module and the main power supply of the control card can share a single power source. When the ECI0016PA control card and the ZIO expansion module are powered by different power sources, the control card's power supply GND must be connected to the expansion module's power supply GND; otherwise, the CAN bus may be damaged.
When connecting multiple ZIO expansion modules to the CAN bus, connect a 120-ohm resistor to each end of the CAN bus. For expansion modules with 8-bit DIP switches, the terminating resistor can be implemented using the DIP switches.
2. When there is another master card that does not require the ECI0016PA as a master station, and you want to extend the resources of the ECI0016PA to the master card for use with the master card, you can extend the ECI0016PA as a slave station. The wiring diagram of the ECI0016PA as a slave station is as follows:
When connecting multiple ZIO expansion modules to the CAN bus, connect a 120-ohm resistor to each end of the CAN bus. For expansion modules with 8-bit DIP switches, the terminating resistors can be implemented using the DIP switches. To ensure communication quality, please use twisted-pair shielded cable with the shield grounded. The controller and expansion modules should use the same power supply.
Because the ECI0016PA does not have a DIP switch, when the ECI0016PA is set as a slave, you need to use RTSys software to connect to the ECI0016PA board and manually set the slave ID.
·
CANIO_ADDRESS = 1 'Sets CAN ID to 1, in this case, it is a slave, baud rate 500Kbps, used as a ZCAN slave, IO start address is 32 (16+16*1). CANIO_ADDRESS = 2 'Sets CAN ID to 2, in this case, it is a slave, baud rate 500Kbps, used as a ZCAN slave, IO start address is 48 (16+16*2). CANIO_ADDRESS = 3 'Sets CAN ID to 3, in this case, it is a slave, baud rate 500Kbps, used as a ZCAN slave, IO start address is 64 (16+16*3). CANIO_ADDRESS = 4 'Sets CAN ID to 4, in this case, it is a slave, baud rate 500Kbps, used as a ZCAN slave, IO start address is 80 (16+16*4). CANIO_ADDRESS = 2 +256 'Sets CAN 'CAN ID is 2, currently a slave, baud rate 250Kbps, used as a ZCAN slave, IO start address is 48(16+16*2).' CANIO_ADDRESS = 2 +512 'Sets CAN ID to 2, currently a slave, baud rate 125Kbps, used as a ZCAN slave, IO start address is 48(16+16*2).' CANIO_ADDRESS = 2+768 'Sets CAN ID to 2, currently a slave, baud rate 1Mbps, used as a ZCAN slave, IO start address is 48(16+16*2).'
Developing an ECI IO controller using C#
1. In VS2010, go to "File" → "New" → "Project" to start the Project Creation Wizard.
2. Select "Visual C#" as the development language and .NET Framework 4 as the Windows Forms application.
3. Locate the C# function library in the CD-ROM provided by the manufacturer. The path is as follows (taking the 32-bit library as an example).
1) Go to the CD-ROM provided by the manufacturer, find the "04PC Functions" folder, and click to enter.
2) Select the "Function Library 2.1" folder.
3) Select the "Windows Platform" folder.
4) Select the corresponding function library as needed; here, we choose the 32-bit library.
5) Unzip the C# compressed package, which contains the corresponding C# function library.
6) The specific path to the function library is as follows.
4. Copy the C# library files and related files provided by the vendor into the newly created project.
1) Copy the zmcaux.cs file into the newly created project.
2) Place the zaux.dll and zmotion.dll files into the bin\debug folder.
5. Open the newly created project file in Visual Studio. In the Solution Explorer on the right, click "Show all files," then right-click the zmcaux.cs file and click "Include in project."
6. Double-click Form1 in Form1.cs to open the code editing interface. At the beginning of the file, write "using cszmcaux" and declare the controller handle g_handle.
7. Register the OCX control. Place zmotion.dll, ZScope.lib, and ZScope.ocx in the C drive directory. Then, open CMD with administrator privileges and send the command `regsvr32 C:\ZScope.ocx` to register the control. A pop-up window will appear when the registration is successful.
8. Add a COM component to the toolbar. Tools → Toolbox Items → COM Components → Check the ZScope component → OK.
9. At this point, the project is complete and ready for C# project development.
PC function introduction
1. The PC function manual can be viewed on the CD-ROM. The specific path is as follows.
2. Connect to the controller and obtain the connection handle.
3. Quickly read the current status and interface description of multiple input ports.
4. Quickly read the current status and interface description of multiple output ports.
C# test routine for quickly reading multiple I/O states
1. Example Interface
2. Related code
(1) In the event handling function of the link button, the interface function ZAux_FastOpen() of the link controller is called to link with the controller. After the link is successfully established, timer 2 automatically monitors the controller's IO and analog quantity status.
·
//Link private void button3_Click(object sender, EventArgs e){ LinkType = comboBox1.Text; if (LinkType == "Network port") { byte[] tempbuff = new byte[257]; //Received returned character if (g_handle == (IntPtr)0) { Adrr = comboBox2.Text; if (System.Text.RegularExpressions.Regex.IsMatch(Adrr, "[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}")) //Network port string format judgment{ string[] ips = Adrr.Split('.'); if (ips.Length == 4 || ips.Length == 6) { if (System.Int32.Parse(ips[0]) < 256 && System.Int32.Parse(ips[1]) < 256 & System.Int32.Parse(ips[2]) < 256 & System.Int32.Parse(ips[3]) < 256) { //ret = zmcaux.ZAux_OpenEth(Adrr, out g_handle); ret = zmcaux.ZAux_FastOpen(2, Adrr, 1000, out g_handle); if (0 != ret) { MessageBox.Show("Controller not found"); return; } this.Text = "Easy-to-use Ethernet analog acquisition control card (connected, connection method: network port)"; axZScope1.SetControllerIP((int)IPToUInt64(Adrr)); //axZScope1.SetControllerIP(100772032); } else { MessageBox.Show("IP address input format error!"); return; } } else { MessageBox.Show("IP address input format error!"); return; } } else { MessageBox.Show("IP address input format error!"); return; } }}else{ byte[] tempbuff = new byte[1024]; // Received returned character if (g_handle == (IntPtr)0) { ret = zmcaux.ZAux_FastOpen(1, comboBox2.Text, 1000, out g_handle); f (0 != ret) { MessageBox.Show("Controller not found"); return; } this.Text = "Easy-to-use Ethernet analog signal acquisition and control card (connected, connection method: serial port)"; StringBuilder radBuff= new StringBuilder(2048); ret=zmcaux.ZAux_Execute(g_handle, "?IP_ADDRESS", radBuff, 2048); if (ret == 0) { int ival = Convert.ToInt32(radBuff.ToString()); axZScope1.SetControllerIP(ival); } }}//Load heartbeat configuration file, if ECI0016PAB (offline version supported) float pfValue=0; int times = 0; StringBuilder radBuff1= new StringBuilder(2048); StringBuilder radBuff2= new StringBuilder(2048); StringBuilder radBuff3= new StringBuilder(2048); zmcaux.ZAux_GetControllerInfo(g_handle, radBuff1, radBuff2, radBuff3); textBox12.Text = radBuff1.ToString(); ret = zmcaux.ZAux_Direct_GetUserVar(g_handle, "Heart_Time", ref pfValue);//Get board script (basic) variables, confirm whether the script is loaded if (ret!=0) { textBox1.Text="Not loaded"; textBox2.Text = "Not loaded"; } else { ret = zmcaux.ZAux_Direct_GetVariableInt(g_handle, "Heart_StarFlag", ref Sys_HeartStarFlag);//Get whether heartbeat is enabled ret += zmcaux.ZAux_Direct_GetVariableInt(g_handle, "Heart_Time", ref times);//Timeout ret += zmcaux.ZAux_Direct_GetVariableInt(g_handle, "OutList_Address", ref Sys_HeartModbusStar);//Emergency stop OUTmodbus_long starting address (Ps: For series 3 and below, custom variables are 24-bit floating-point numbers, 32-bit precision is lost, use modbus_long to replace) ret += zmcaux.ZAux_Modbus_Get4x_Long(g_handle, 0, 4, Sys_HeartIo); if (ret != 0) { MessageBox.Show("err: Heartbeat parameter retrieval error!"); } if (Sys_HeartStarFlag == 1) { textBox1.Text = "Enabled"; } else { textBox1.Text = "Disabled"; } textBox2.Text = times.ToString(); textBox4.Text = Sys_HeartModbusStar.ToString(); List_Draw(sender, e); }}
(2) Monitor multiple I/O states of the controller and analog quantity states using timers. In the following demonstration, the actual wiring connects OUT0 to IN0 and DA0 to AD0, meaning that the corresponding inputs will change when OUT0 and DA0 are operated.
·
public void draw_io(){ if (g_handle != (IntPtr)0) { Graphics g = this.groupBox4.CreateGraphics(); Pen redPen = new Pen(Color.FromArgb(245, 115, 103), 13);//Red Pen greenPen = new Pen(Color.FromArgb(132,239,109), 13);//Green Font drawFont = new Font("Arial", 9); SolidBrush drawBrush = new SolidBrush(Color.Black);//Brush color zmcaux.ZAux_GetModbusIn(g_handle,0,7,in_state); zmcaux.ZAux_GetModbusOut(g_handle, 0, 7, out_state); for (int i = 0; i < 8; i++) { if (((in_state[0] >> i) & 1) == 1) { g.DrawEllipse(greenPen, new Rectangle(60 + 50 * i, 38, 13, 13)); //Red, no signal g.DrawString("IN" + i.ToString(), drawFont, drawBrush, new PointF(55 + 50 * i, 38)); } else { g.DrawEllipse(redPen, new Rectangle(60 + 50 * i, 38, 13, 13)); //Red, no signal g.DrawString("IN" + i.ToString(), drawFont, drawBrush, new PointF(55 + 50 * i, 38)); } if (((out_state[0] >> i) & 1) == 1) { btn[i].BackColor = Color.FromArgb(132, 239, 109); btn[i].Checked = true; } else { btn[i].BackColor = Color.FromArgb(245, 115, 103); btn[i].Checked = false; } } }}//Timer 2private void timer2_Tick(object sender, EventArgs e){ if (g_handle != (IntPtr)0) { draw_io(); int iret = zmcaux.ZAux_Direct_GetAD(g_handle, Convert.ToInt32(comboBox3.Text), ref mAd_rVal); if (iret == 0) { textBox5.Text = mAd_rVal.ToString(); mAd_rVal1 = ((mAd_rVal - 32768) / 32768)*10; textBox8.Text = mAd_rVal1.ToString(); }else { textBox5.Text = "err"; textBox8.Text = "err"; } iret = zmcaux.ZAux_Direct_GetDA(g_handle, Convert.ToInt32(comboBox4.Text), ref mDa_rVal); if (iret == 0) { textBox6.Text = mDa_rVal.ToString(); mDa_rVal1 = ((mDa_rVal - 2047) / 2048) * 10; textBox7.Text = mDa_rVal1.ToString(); } else { textBox6.Text = "err"; textBox7.Text = "err"; } }}
(3) The heartbeat detection script loads. After the heartbeat is started, if the software disconnects, the board automatically opens the setting output port. If the board disconnects, a software detection pop-up window appears. (ECI0016PA is not supported)
①Configuration loading:
·
//Configure loading private void button9_Click(object sender, EventArgs e){ if (g_handle != (IntPtr) 0) { string currentPath = System.AppDomain.CurrentDomain.BaseDirectory; m_FilePath = currentPath + "config\\Heartbeat.bas"; // MessageBox.Show(m_FilePath);//Get the heartbeat bas file in the software directory ret = zmcaux.ZAux_BasDown(g_handle, m_FilePath, 1); //Download the heartbeat to ROM if (0 != ret) { MessageBox.Show("Failed to download heartbeat to ROM"); return; } else { Thread.Sleep(1000);//Delay for 1 second to ensure the program loads completely int times = 0; ret = zmcaux.ZAux_Direct_GetVariableInt(g_handle, "Heart_StarFlag", ref Sys_HeartStarFlag); // Get whether heartbeat is enabled if (ret != 0) { MessageBox.Show("err: Heartbeat parameter acquisition error!"); return; } ret = zmcaux.ZAux_Direct_GetVariableInt(g_handle, "Heart_Time", ref times); // Timeout if (ret != 0) { MessageBox.Show("err: Heartbeat parameter acquisition error!"); return; } ret = zmcaux.ZAux_Direct_GetVariableInt(g_handle, "OutList_Address", ref Sys_HeartModbusStar); // Emergency stop OUT modbus_long starting address (Ps: For series 3 and below, custom variables are 24-bit floating-point numbers, 32-bit precision is lost, use modbus_long to replace) if (ret != 0) { MessageBox.Show("err: Heartbeat parameter acquisition error!"); return; } ret = `zmcaux.ZAux_Modbus_Get4x_Long(g_handle, 0, 4, Sys_HeartIo); if (ret != 0) { MessageBox.Show("err: Heartbeat parameter retrieval error!"); return; } if (Sys_HeartStarFlag == 1) { textBox1.Text = "Enabled"; } else { textBox1.Text = "Disabled"; } textBox2.Text = times.ToString(); textBox4.Text = Sys_HeartModbusStar.ToString(); List_Draw( sender, e); MessageBox.Show("Heartbeat configuration file successfully downloaded to ROM!"); } } else { MessageBox.Show("Not linked!"); }}`
②Heart start-stop:
·
//Heartbeat start private void button1_Click(object sender, EventArgs e){ Sys_HeartIo[0] = 0; for (int i = 0; i < 8; i++) { if (Convert.ToInt32(dataGridView1.Rows[i].Cells["Column1"].Value) == 1) { Sys_HeartIo[0] = (Sys_HeartIo[0]) | (1 << i); } } ushort start = (ushort)Convert.ToInt32(textBox4.Text); ret = zmcaux.ZAux_Modbus_Set4x_Long(g_handle, start, 4, Sys_HeartIo); if (ret != 0) { MessageBox.Show("Startup failed!"); return; } int iret = zmcaux.ZAux_Direct_SetUserVar(g_handle, "Heart_StarFlag", 1); // Send heartbeat signal if (ret != 0) { MessageBox.Show("Startup failed!"); return; } textBox1.Text = "Enabled"; timer1.Interval = Convert.ToInt32(textBox3.Text); timer1.Enabled = true;// Enable timer1 }// Heartbeat off private void button2_Click(object sender, EventArgs e){ timer1.Stop(); textBox1.Text = "Not enabled";}
③ Software heartbeat scan:
·
//Timer1 private void timer1_Tick(object sender, EventArgs e){ if (g_handle != (IntPtr)0) { int iret = zmcaux.ZAux_Direct_SetUserVar(g_handle, "Heart_Status", 0); //Send heartbeat signal if (iret == 3402 || iret > 20000) //Communication timeout or connection lost { //PC communication error Sys_HeartAddNum++; if (Sys_HeartAddNum > 3) { g_handle = (IntPtr)0; this.Text = "Easy-to-use Ethernet analog acquisition and control card"; textBox1.Text = "Not enabled"; timer1.Enabled = false;//Timer1 MessageBox.Show("Connection lost!"); } } else { Sys_HeartAddNum = 0; } }}
④ Internal basic scan of the board:
·
'*************************************Heartbeat Related Variables Definition************************Global Heart_StarFlag 'Communication Identifier Variable Global Heart_Status 'Communication Identifier Variable Global CONST Heart_Time=200 'Heartbeat Timeout Global Heart_OutNum 'Number of Emergency Stop OPs Global OutList_Address 'Starting address of Emergency Stop OUT modbus_long (Note: For series 3 and below, custom variables are 24-bit floating-point numbers, 32-bit precision is lost, so modbus_long is used instead) GLOBAL CONST cVer=CONTROL Heart_StarFlag =-1 Heart_Status = 0 Heart_OutNum = 10 OutList_Address =0 dim i,j WHILE TRUE WAIT until Heart_StarFlag = 1 'Wait for PC software connection wa Heart_Time 'After PC connection, delay for a timeout period to avoid upper if cVer>0 and cVer<2619 THEN 'eci1408,2418,2618 does not support Modbus usage WHILE TRUE if Heart_Status = 0 then Heart_Status = 1 'Wait for the PC to modify the communication variable ticks = Heart_Time 'Timeout judgment and handling, wait for the PC to modify Heart_Flag = 0 WAIT until ticks < 0 or Heart_Status=0 if Heart_Status = 1 THEN RAPIDSTOP(2) WAIT IDLE for i = 0 to 3 for j=0 to 31 if ((MODBUS_LONG(OutList_Address+i*2)>>j)and 1) THEN op(i*32+j, ON) ELSE op(i*32+j, OFF) ENDIF NEXT next Heart_StarFlag=-1 Heart_Status = 0 EXIT WHILE Endif WEND ELSE WHILE TRUE if Heart_Status = 0 then Heart_Status = 1 'Wait for the PC to modify the communication variable ticks = Heart_Time 'Timeout judgment and handling, wait for the PC to modify Heart_Flag = 0 WAIT until ticks < 0 or Heart_Status=0 if Heart_Status = 1 THEN RAPIDSTOP(2) WAIT IDLE for i = 0 to 3 MODBUS_BIT(20000+i*32,20031+i*32)=MODBUS_LONG(OutList_Address+i*2) next Heart_StarFlag=-1 Heart_Status = 0 EXIT WHILE Endif WEND ENDIF WENDend
(4) DA waveform simulation test. In actual wiring, DA0 is connected to AD0, that is, when DA0 is operated, the corresponding AIN will also change. This operation simulates a sine wave with an amplitude of ±10V and a period of about 400ms by operating DA0.
·
//DA output waveform simulation private void button6_Click(object sender, EventArgs e){ if (g_handle != (IntPtr) 0) { // Define parameter int tmp= Convert.ToInt32(comboBox4.Text); // Create thread and use parameter in lambda expression Thread t = new Thread(() => { // Here you can use myParameter ret = zmcaux.ZAux_Direct_SetDA(g_handle, tmp, 0); if (ret != 0) { MessageBox.Show("Setting failed!"); return; } zmcaux.ZAux_Trigger(g_handle); for (int i = 0; i < 799; i++) { mDa_wVal = (float)(2047 * Math.Sin((Math.PI * i) / 100) + 2048); zmcaux.ZAux_Direct_SetDA(g_handle, tmp, mDa_wVal); Thread.Sleep(1); } MessageBox.Show("Simulation complete!"); }); t.Start(); }}
(5) Verification of network port communication speed.
·
// Communication speed test function private void button7_Click(object sender, EventArgs e){ StringBuilder radBuff = new StringBuilder(20480); String tmp="?IN(0,8)\n"; // Get all IN values on the card tmp = tmp + "?OP(0,8)\n"; // Get all OUT values on the card int i; for (i = 0; i < 12; i++) // Get all AIN values on the card{ tmp = tmp + "?AIN(" + i.ToString() + ")\n"; } for (i = 0; i < 2; i++) // Get all Aout values on the card{ tmp = tmp + "?AOUT(" + i.ToString() + ")\n"; } DateTime beforeDT = System.DateTime.Now; int k = 0; for (i = 0; i < 10000; i++) { ret = zmcaux.ZAux_DirectCommand(g_handle, tmp, radBuff, 20480);//One communication, get the status of all hardware resources on the card if (ret != 0) { k++; } } DateTime afterDT = System.DateTime.Now; TimeSpan ts = afterDT - beforeDT; //Total time elapsed in ms textBox10.Text = ts.TotalMilliseconds.ToString("0.00"); MessageBox.Show("Number of failures: " + k.ToString());}private void button8_Click(object sender, EventArgs e){ StringBuilder radBuff = new StringBuilder(20480); int i; String tmp = "?IN(0,32)\n"; //Get all IN values on the card int k; for (i = 1; i < 8; i++) //Get all AIN values on the card{ k = i * 32; tmp = tmp + "?IN(" + k.ToString() + ",32)\n"; } tmp = tmp + "?IN(256,16)\n"; for (i = 0; i < 8; i++) //Get all AIN values on the card { k = i * 32; tmp = tmp + "?OP(" + k.ToString() + ",32)\n"; } tmp = tmp + "?OP(256,16)\n"; int length = tmp.Length; for (i = 0; i < 64; i++) { tmp = tmp + "?AIN(" + i.ToString() + ")\n"; tmp = tmp + "?AOUT(" + i.ToString() + ")\n"; length = tmp.Length; if (length > 1000) // Online command, maximum 1000 bytes sent each time { break; } } DateTime beforeDT = System.DateTime.Now; k=0; for (i = 0; i < 10000; i++) { ret = zmcaux.ZAux_DirectCommand(g_handle, tmp, radBuff, 20480);// One communication, get the status of all hardware resources on the card if (ret!=0) { k ++; } } DateTime afterDT = System.DateTime.Now; TimeSpan ts = afterDT - beforeDT; // Total time in milliseconds textBox11.Text = ts.TotalMilliseconds.ToString("0.00"); MessageBox.Show("Number of failures: "+k.ToString());}
Interaction speed test results
1. Basic resource acquisition time/ms (for 10,000 functions acquiring the basic resources of ECI0016PA: 8 IN, 8 OUT, 12 AIN, and 2 DA).
2. Time taken to acquire extreme resources / ms (Ten thousand functions acquire the extreme resources of ECI0016PA, with a maximum of 272 INs and 272 OUTs, concatenated DA/AD, reaching the maximum length of 1000 bytes for a single communication string).
Full code download address
▼
This concludes our sharing of C# application development for the simple and easy-to-use Ethernet data acquisition card of ZhengMotion Technology.
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.