Using Beckhoff EtherCat?(TM) I/O Bus clamps with EMC |
Using Beckhoff EtherCAT(TM) I/O Bus clamps with EMC |
EtherCat (EtherLab) Experience with Xubuntu 8.04 |
A german control systems firm called [Beckhoff Automation] sells a bus terminal system called EtherCAT. It is build of a Bus couppler and a number of input or output modules as terminals. You can order digital drive controllers speaking EtherCAT, too. Every module directly talks ethernet frames and optionally UDP. The modules insert and extract their data to/from the frame while transmitting it.
If you have a computer with a supported ethernet card, you can use this terminals and drive controllers with emc.
I will describe how to get this to work using the open source [etherlab library] here. A list of supported ethernet chipsets can be found at http://www.etherlab.org/en/ethercat/hardware.php
You can get an evaluation package including a bus coupler Type EK110, two 4 channel input modules EL1014 and two 4 channel output modules EL2004.
Here is a photo of such a setup:
From left to right:
I just wired the power supply and the button to one of the input terminals and the relais to one of the output terminals. The green cable is connected directly to the rtl8139 ethernet card in my pc.
They also offer modules for analog input, analog output, encoder input and even a 4-axis machine control module (combines encoders, DA and I/O). And you can plug in digital drive modules wich manage one to three motors including tachos and encoders/linear scales. Just look at their web site.
This is the software I installed.
I did that manually, because of the patches. If you choose to use the live cd you will at least need to install patched network drivers and etherlab.
Start from kernel 2.6.32.19, use the latest stable 2.6.32 availiable. Apply the rtai adeos patch from the rtai directory Configure your kernel. Be sure to check "processor type and features -> interrupt pipeline"
configure rtai. you can use menuconfig or configure to do this. Don't forget to check "base system -> other features -> mathfuns support in kernel" You need to tell rtai where your kernel sources are located if not at the default space.
etherlab master comes with its own patched versions of the default linux network drivers. I had to patch my linux driver for the current version of rtl8139. I sent that patch upstream so maybe it is included by now.
configure etherlab master using the configure script as described in the readme. Hint: use --prefix=/your/prefix/here --with-linux-dir=/your/linux/dir/here
After installing you have to edit /etc/ethercat to select the master device MAC and the kernel driver module to load. To test-start the etherlab master and do basic communication you will need following commands:
/etc/init.d/realtime start /etc/init.d/ethercat start
Immediately after you insert ethe etercat module, you will see blinking network led on the bus coupler. This is a preliminary mode. You can now query the bus topology and terminals and their design versions (very important for later use). To show the topology, issue:
ethercat slaves -v
The output looks like this:
<=== Master 0, Slave 0 === State: PREOP Flag: + Identity: Vendor Id: 0x00000002 Product code: 0x044c2c52 Revision number: 0x00110000 Serial number: 0x00000000 DL information: FMMU bit operation: no Distributed clocks: yes, 64 bit DC system time transmission delay: 0 ns Port Type Link Loop Signal NextSlave RxTime [ns] Diff [ns] NextDc [ns] 0 MII up open yes - 1634392210 0 0 1 EBUS up open yes 1 1634393360 1150 140 2 MII down closed no - - - - 3 N/A down closed no - - - - General: Group: SystemBk Image name: Order number: EK1100 Device name: EK1100 EtherCAT-Koppler (2A E-Bus) Flags: Enable SafeOp: no Enable notLRW: no Current consumption: -2000 mA === Master 0, Slave 1 === State: PREOP Flag: + Identity: Vendor Id: 0x00000002 Product code: 0x07d43052 Revision number: 0x00100000 Serial number: 0x00000000 DL information: FMMU bit operation: no Distributed clocks: yes, 64 bit DC system time transmission delay: 140 ns Port Type Link Loop Signal NextSlave RxTime [ns] Diff [ns] NextDc [ns] 0 EBUS up open yes 0 1634107860 0 140 1 EBUS up open yes 2 1634108730 870 140 2 N/A down closed no - - - - 3 N/C down closed no - - - - General: Group: DigOut Image name: Order number: EL2004 Device name: EL2004 4K. Dig. Ausgang 24V, 0.5A Flags: Enable SafeOp: no Enable notLRW: no Current consumption: 100 mA === Master 0, Slave 2 === State: PREOP Flag: + Identity: Vendor Id: 0x00000002 Product code: 0x07d43052 Revision number: 0x00100000 Serial number: 0x00000000 DL information: FMMU bit operation: no Distributed clocks: yes, 64 bit DC system time transmission delay: 280 ns Port Type Link Loop Signal NextSlave RxTime [ns] Diff [ns] NextDc [ns] 0 EBUS up open yes 1 1635119480 0 140 1 EBUS up open yes 3 1635120070 590 145 2 N/A down closed no - - - - 3 N/C down closed no - - - - General: Group: DigOut Image name: Order number: EL2004 Device name: EL2004 4K. Dig. Ausgang 24V, 0.5A Flags: Enable SafeOp: no Enable notLRW: no Current consumption: 100 mA === Master 0, Slave 3 === State: PREOP Flag: + Identity: Vendor Id: 0x00000002 Product code: 0x03f63052 Revision number: 0x00100000 Serial number: 0x00000000 DL information: FMMU bit operation: no Distributed clocks: yes, 64 bit DC system time transmission delay: 425 ns Port Type Link Loop Signal NextSlave RxTime [ns] Diff [ns] NextDc [ns] 0 EBUS up open yes 2 1634492040 0 145 1 EBUS up open yes 4 1634492340 300 150 2 N/A down closed no - - - - 3 N/C down closed no - - - - General: Group: DigIn Image name: TERM_DI Order number: EL1014 Device name: EL1014 4K. Dig. Eingang 24V, 10�s Flags: Enable SafeOp: no Enable notLRW: no Current consumption: 90 mA === Master 0, Slave 4 === State: PREOP Flag: + Identity: Vendor Id: 0x00000002 Product code: 0x03f63052 Revision number: 0x00100000 Serial number: 0x00000000 DL information: FMMU bit operation: no Distributed clocks: yes, 64 bit DC system time transmission delay: 575 ns Port Type Link Loop Signal NextSlave RxTime [ns] Diff [ns] NextDc [ns] 0 EBUS up open yes 3 1634388330 0 150 1 EBUS down closed no - - - - 2 N/A down closed no - - - - 3 N/C down closed no - - - - General: Group: DigIn Image name: TERM_DI Order number: EL1014 Device name: EL1014 4K. Dig. Eingang 24V, 10�s Flags: Enable SafeOp: no Enable notLRW: no Current consumption: 90 mA
The interesting thing is the bus topology and the Revision of each Slave. The latter caught me becaus I programmed for an older revision of the modules first and wondered why my module would not load until I figured out that the new revision supports single bit addressing instead of returning byte data.
The glue module is adapted, the original code is from Florian Pose, Ingenieurgemeinschaft IgH?. To compile the glue module you have to change directory to your emc source dir. There, you can do the compile by issuing:
sudo bin/comp --install ../your/path/here/ethercat.comp
Here is the comp module to make this setup working.
component ethercat"Ethercat."; pin out bit eingang_0; pin out bit eingang_1; pin out bit eingang_2; pin out bit eingang_3; pin out bit eingang_4; pin out bit eingang_5; pin out bit eingang_6; pin out bit eingang_7; pin in bit ausgang_0; pin in bit ausgang_1; pin in bit ausgang_2; pin in bit ausgang_3; pin in bit ausgang_4; pin in bit ausgang_5; pin in bit ausgang_6; pin in bit ausgang_7; option extra_setup; option extra_cleanup; //option constructable no; function update nofp; license "GPL"; ;; #include "/opt/ethercat/include/ecrt.h" /*****************************************************************************/ // EtherCAT static ec_master_t *master = NULL; static ec_domain_t *domain1 = NULL; spinlock_t master_lock = SPIN_LOCK_UNLOCKED; static ec_master_state_t master_status, old_status = {}; // data fields static uint8_t *domain1_pd; // process data memory // offsets to data fields static unsigned int o_dig_in[8], bo_dig_in[8]; static unsigned int o_dig_out[8], bo_dig_out[8]; // pdo definitions may be read from autogenerated file //#include "ethercat_cstruct.c" #define Beckhoff_EL2004 0x00000002, 0x07D43052 #define Beckhoff_EL1014 0x00000002, 0x03F63052 const static ec_pdo_entry_reg_t domain1_pdo_regs[] = { { 0, 1, Beckhoff_EL2004, 0x7000, 1, &o_dig_out[0], &bo_dig_in[0]}, { 0, 1, Beckhoff_EL2004, 0x7010, 1, &o_dig_out[1], &bo_dig_in[1]}, { 0, 1, Beckhoff_EL2004, 0x7020, 1, &o_dig_out[2], &bo_dig_in[2]}, { 0, 1, Beckhoff_EL2004, 0x7030, 1, &o_dig_out[3], &bo_dig_in[3]}, { 0, 2, Beckhoff_EL2004, 0x7000, 1, &o_dig_out[4], &bo_dig_in[4]}, { 0, 2, Beckhoff_EL2004, 0x7010, 1, &o_dig_out[5], &bo_dig_in[5]}, { 0, 2, Beckhoff_EL2004, 0x7020, 1, &o_dig_out[6], &bo_dig_in[6]}, { 0, 2, Beckhoff_EL2004, 0x7030, 1, &o_dig_out[7], &bo_dig_in[7]}, { 0, 3, Beckhoff_EL1014, 0x6000, 1, &o_dig_in[0] , &bo_dig_out[0]}, { 0, 3, Beckhoff_EL1014, 0x6010, 1, &o_dig_in[1] , &bo_dig_out[1]}, { 0, 3, Beckhoff_EL1014, 0x6020, 1, &o_dig_in[2] , &bo_dig_out[2]}, { 0, 3, Beckhoff_EL1014, 0x6030, 1, &o_dig_in[3] , &bo_dig_out[3]}, { 0, 4, Beckhoff_EL1014, 0x6000, 1, &o_dig_in[4] , &bo_dig_out[4]}, { 0, 4, Beckhoff_EL1014, 0x6010, 1, &o_dig_in[5] , &bo_dig_out[5]}, { 0, 4, Beckhoff_EL1014, 0x6020, 1, &o_dig_in[6] , &bo_dig_out[6]}, { 0, 4, Beckhoff_EL1014, 0x6030, 1, &o_dig_in[7] , &bo_dig_out[7]}, /* { 0, 1, Beckhoff_EL2004, 0x7000, 1, &o_dig_out[0], NULL}, */ /* { 0, 4, Beckhoff_EL1014, 0x6000, 1, &o_dig_in[0] , NULL}, */ {} }; /*********************************************************************** * LOCAL FUNCTION DECLARATIONS * ************************************************************************/ void request_lock(void *data) { spin_lock(&master_lock); } void release_lock(void *data) { spin_unlock(&master_lock); } /*****************************************************************************/ EXTRA_SETUP() { rtapi_print_msg(RTAPI_MSG_INFO,"Starting...\n"); //printk(KERN_INFO PFX "Starting...\n"); if (!(master = ecrt_request_master(0))) { rtapi_print_msg(RTAPI_MSG_INFO,"BRequesting master 0 failed!\n"); //printk(KERN_ERR PFX "Requesting master 0 failed!\n"); goto out_return; } ecrt_master_callbacks(master, request_lock, release_lock, NULL); rtapi_print_msg(RTAPI_MSG_INFO,"Registering domain...\n"); //printk(KERN_INFO PFX "Registering domain...\n"); if (!(domain1 = ecrt_master_create_domain(master))) { rtapi_print_msg(RTAPI_MSG_INFO,"Domain creation failed!\n"); //printk(KERN_ERR PFX "Domain creation failed!\n"); goto out_release_master; } rtapi_print_msg(RTAPI_MSG_INFO,"Registering PDOs...\n"); //rintk(KERN_INFO PFX "Registering PDOs...\n"); if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_pdo_regs)) { rtapi_print_msg(RTAPI_MSG_INFO,"PDO registration failed!\n"); //printk(KERN_ERR PFX "PDO registration failed!\n"); goto out_release_master; } rtapi_print_msg(RTAPI_MSG_INFO,"Activating master...\n"); //printk(KERN_INFO PFX "Activating master...\n"); if (ecrt_master_activate(master)) { rtapi_print_msg(RTAPI_MSG_INFO,"Failed to activate master!\n"); //printk(KERN_ERR PFX "Failed to activate master!\n"); goto out_release_master; } domain1_pd = ecrt_domain_data(domain1); rtapi_print_msg(RTAPI_MSG_INFO,"Started.\n"); //printk(KERN_INFO PFX "Started.\n"); return 0; out_release_master: rtapi_print_msg(RTAPI_MSG_INFO,"Releasing master...\n"); //printk(KERN_ERR PFX "Releasing master...\n"); ecrt_release_master(master); master= NULL; out_return: rtapi_print_msg(RTAPI_MSG_INFO,"Failed to load. Aborting.\n"); //printk(KERN_ERR PFX "Failed to load. Aborting.\n"); return -1; } EXTRA_CLEANUP() { rtapi_print_msg(RTAPI_MSG_INFO,"Stopping...\n"); //printk(KERN_INFO PFX "Stopping...\n"); if (master) { rtapi_print_msg(RTAPI_MSG_INFO,"Releasing master...\n"); //printk(KERN_INFO PFX "Releasing master...\n"); ecrt_release_master(master); } rtapi_print_msg(RTAPI_MSG_INFO,"Unloading.\n"); //printk(KERN_INFO PFX "Unloading.\n"); } /*****************************************************/ /*****************************************************/ FUNCTION(update) { // receive spin_lock(&master_lock); ecrt_master_receive(master); ecrt_domain_process(domain1); spin_unlock(&master_lock); eingang_0 = EC_READ_BIT( domain1_pd+o_dig_in[0], bo_dig_in[0]); eingang_1 = EC_READ_BIT( domain1_pd+o_dig_in[1], bo_dig_in[1]); eingang_2 = EC_READ_BIT( domain1_pd+o_dig_in[2], bo_dig_in[2]); eingang_3 = EC_READ_BIT( domain1_pd+o_dig_in[3], bo_dig_in[3]); eingang_4 = EC_READ_BIT( domain1_pd+o_dig_in[4], bo_dig_in[4]); eingang_5 = EC_READ_BIT( domain1_pd+o_dig_in[5], bo_dig_in[5]); eingang_6 = EC_READ_BIT( domain1_pd+o_dig_in[6], bo_dig_in[6]); eingang_7 = EC_READ_BIT( domain1_pd+o_dig_in[7], bo_dig_in[7]); EC_WRITE_BIT(domain1_pd+o_dig_out[0], bo_dig_out[0], ausgang_0); EC_WRITE_BIT(domain1_pd+o_dig_out[1], bo_dig_out[1], ausgang_1); EC_WRITE_BIT(domain1_pd+o_dig_out[2], bo_dig_out[2], ausgang_2); EC_WRITE_BIT(domain1_pd+o_dig_out[3], bo_dig_out[3], ausgang_3); EC_WRITE_BIT(domain1_pd+o_dig_out[4], bo_dig_out[4], ausgang_4); EC_WRITE_BIT(domain1_pd+o_dig_out[5], bo_dig_out[5], ausgang_5); EC_WRITE_BIT(domain1_pd+o_dig_out[6], bo_dig_out[6], ausgang_6); EC_WRITE_BIT(domain1_pd+o_dig_out[7], bo_dig_out[7], ausgang_7); /* eingang_0= EC_READ_BIT( domain1_pd+o_dig_in[0],0); */ /* EC_WRITE_BIT(domain1_pd+o_dig_out[0],0,ausgang_0); */ spin_lock(&master_lock); ecrt_master_state(master, &master_status); spin_unlock(&master_lock); if (master_status.al_states != old_status.al_states) { rtapi_print_msg(RTAPI_MSG_INFO,"bus status changed to %i.\n", master_status.al_states); } if (master_status.slaves_responding != old_status.slaves_responding) { rtapi_print_msg(RTAPI_MSG_INFO,"slaves_responding changed to %u.\n", master_status.slaves_responding); } old_status = master_status; // send spin_lock(&master_lock); ecrt_domain_queue(domain1); spin_unlock(&master_lock); spin_lock(&master_lock); ecrt_master_send(master); spin_unlock(&master_lock); }
Note: This is not the best design. I have to figure out how access bit data without wasting two 32bit intergers as data fields and offsets for every single bit.
If this system is getting adapted by emc, it would be good to provide an automated build script for creating this module from a given ethercat topology.
I just toss in the commands I issued here and will document them later.
Getting hal up and running:
insmod /opt/realtime/modules/rtai_math.ko insmod /opt/realtime/modules/emc2/ethercat.ko halcmd loadrt trivkins halcmd loadrt motmod servo_period_nsec=1000000 num_joints=3 halcmd addf ethercat.0.update servo-thread halcmd startThis loads all modules, ties the ehtercat update task to the servo thread and starts the realtime thread. If you use it for production, you will certainly start emc and wire this inside a hal file.
For testing you can use this commands:
halcmd show pin ethercat halcmd setp ethercat.0.ausgang-0 true halcmd setp ethercat.0.ausgang-0 falseThis shows the status of the input ports and switches the output port 0 (my relais will click!)
You can check the configuration of your system while it is running by using ethercat config -v
The output will look like this:
Alias: 0 Position: 1 Vendor Id: 0x00000002 Product code: 0x07d43052 Attached slave: 1 (OP) Watchdog divider: (Default) Watchdog intervals: (Default) SM0, Dir: Output, Watchdog: Default PDO 0x1600 PDO entry 0x7000:01, 1 bit PDO 0x1601 PDO entry 0x7010:01, 1 bit PDO 0x1602 PDO entry 0x7020:01, 1 bit PDO 0x1603 PDO entry 0x7030:01, 1 bit SDO configuration: None. Alias: 0 Position: 2 Vendor Id: 0x00000002 Product code: 0x07d43052 Attached slave: 2 (OP) Watchdog divider: (Default) Watchdog intervals: (Default) SM0, Dir: Output, Watchdog: Default PDO 0x1600 PDO entry 0x7000:01, 1 bit PDO 0x1601 PDO entry 0x7010:01, 1 bit PDO 0x1602 PDO entry 0x7020:01, 1 bit PDO 0x1603 PDO entry 0x7030:01, 1 bit SDO configuration: None. Alias: 0 Position: 3 Vendor Id: 0x00000002 Product code: 0x03f63052 Attached slave: 3 (OP) Watchdog divider: (Default) Watchdog intervals: (Default) SM0, Dir: Input, Watchdog: Default PDO 0x1a00 PDO entry 0x6000:01, 1 bit PDO 0x1a01 PDO entry 0x6010:01, 1 bit PDO 0x1a02 PDO entry 0x6020:01, 1 bit PDO 0x1a03 PDO entry 0x6030:01, 1 bit SDO configuration: None. Alias: 0 Position: 4 Vendor Id: 0x00000002 Product code: 0x03f63052 Attached slave: 4 (OP) Watchdog divider: (Default) Watchdog intervals: (Default) SM0, Dir: Input, Watchdog: Default PDO 0x1a00 PDO entry 0x6000:01, 1 bit PDO 0x1a01 PDO entry 0x6010:01, 1 bit PDO 0x1a02 PDO entry 0x6020:01, 1 bit PDO 0x1a03 PDO entry 0x6030:01, 1 bit SDO configuration: None.