I'm unable to configure the AM3359 ICE v2.1 board to synchronize using Distributed Clocks using the Simple Open EtherCAT Master (SOEM) library.
When I try to configure Distributed Clocks and push the EtherCAT slave(s) into Operational Mode, the slave device(s) eventually transitions into Safe-Op+Error (state = 0x14) and reports AL StatusCode = 0x2D (No sync error), so it looks like the application is being configured to use the SYNC0 HW pin as a trigger, but the SYNC0 line never pulses.
Note that D.C. SYNC0 does seem to work correctly with TwinCAT v2.11, but looking through the Wireshark logs between the TwinCAT and SOEM configuration it appears as if most of the Synchronization configuration is the same... I'm told that SOEM can successfully configure other EtherCAT slave devices (non-AM3359) to use Distributed Clocks.
I'm using the following environment:
- SOEM v1.3.1 with modified "simple_test" application (see attached file)
- GNU v4.8.4 (Linaro)
- SYS/BIOS 6.42.3.35- XDCtools 3.31.2.38_core
- sysbios_ind_sdk_2.1.1.2
- TI EtherCAT sample application (from sysbios_ind_sdk_2.1.1.2\sdk\examples\ethercat_slave)
- Is there any special configuration required to get the SYNC0 pin on AM3359 to pulse?
- EtherCAT register 0x0141 (ESC Configuration) looks to always be read as 0x00, but according to the EtherCAT Register Description document it should be 0x04 to enable Distributed Clocks SYNC Out Unit... however, TwinCAT seems to trigger Distributed Clocks just fine despite this setting.
Does register 0x0141 have any affect with the AM3359? - EtherCAT register 0x0151 (Sync/Latch PDI Configuration) value reads 0x88 which according to the EtherCAT Register Description document means that the HW pin will be configured as "LATCH0 Input" (as opposed to "SYNC0 Output"), yet once again TwinCAT seems to trigger D.C. just fine.
Does register 0x0151 have any affect with the AM3359?
Are there any other possible steps I could be missing in configuring the EtherCAT Slave to use Distributed Clocks SYNC0?
/** \file * \brief Example code for Simple Open EtherCAT master * * Usage : simple_test [ifname1] * ifname is NIC interface, f.e. eth0 * * This is a minimal test. * * (c)Arthur Ketels 2010 - 2011 */ #include <stdio.h> #include <string.h> #include <sys/time.h> #include <unistd.h> #include <pthread.h> #include "ethercattype.h" #include "nicdrv.h" #include "ethercatbase.h" #include "ethercatmain.h" #include "ethercatdc.h" #include "ethercatcoe.h" #include "ethercatfoe.h" #include "ethercatconfig.h" #include "ethercatprint.h" #define EC_TIMEOUTMON 500 char IOmap[4096]; OSAL_THREAD_HANDLE thread1; int expectedWKC; boolean needlf; volatile int wkc; boolean inOP; uint8 currentgroup = 0; void simpletest(char *ifname) { int i, j, oloop, iloop, chk; needlf = FALSE; inOP = FALSE; printf("Starting simple test\n"); /* initialise SOEM, bind socket to ifname */ if (ec_init(ifname)) { printf("ec_init on %s succeeded.\n",ifname); /* find and auto-config slaves */ if ( ec_config_init(FALSE) > 0 ) { printf("%d slaves found and configured.\n",ec_slavecount); // TI defect SDOCM00105048, cannot use LRW commands ec_slave[1].blockLRW = 1; ecx_context.grouplist[0].blockLRW = 1; // Configure Distributed Clocks ec_configdc(); ec_dcsync0(1, TRUE, 1000000U, 0); // Configure IOMAP ec_config_map(&IOmap); printf("Slaves mapped, state to SAFE_OP.\n"); /* wait for all slaves to reach SAFE_OP state */ ec_statecheck(0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE * 4); oloop = ec_slave[0].Obytes; if ((oloop == 0) && (ec_slave[0].Obits > 0)) oloop = 1; if (oloop > 8) oloop = 8; iloop = ec_slave[0].Ibytes; if ((iloop == 0) && (ec_slave[0].Ibits > 0)) iloop = 1; if (iloop > 8) iloop = 8; printf("segments : %d : %d %d %d %d\n",ec_group[0].nsegments ,ec_group[0].IOsegment[0],ec_group[0].IOsegment[1],ec_group[0].IOsegment[2],ec_group[0].IOsegment[3]); printf("Request operational state for all slaves\n"); expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC; printf("Calculated workcounter %d\n", expectedWKC); ec_slave[0].state = EC_STATE_OPERATIONAL; /* send one valid process data to make outputs in slaves happy*/ ec_send_processdata(); ec_receive_processdata(EC_TIMEOUTRET); /* request OP state for all slaves */ ec_writestate(0); chk = 40; /* wait for all slaves to reach OP state */ do { ec_send_processdata(); ec_receive_processdata(EC_TIMEOUTRET); ec_statecheck(0, EC_STATE_OPERATIONAL, 1000000); } while (chk-- && (ec_slave[0].state != EC_STATE_OPERATIONAL)); if (ec_slave[0].state == EC_STATE_OPERATIONAL ) { printf("Operational state reached for all slaves.\n"); inOP = TRUE; /* cyclic loop */ for(i = 1; i <= 1000; i++) { ec_send_processdata(); wkc = ec_receive_processdata(EC_TIMEOUTRET); if(wkc >= expectedWKC) { printf("Processdata cycle %4d, WKC %d , O:", i, wkc); for(j = 0 ; j < oloop; j++) { printf(" %2.2x", *(ec_slave[0].outputs + j)); } printf(" I:"); for(j = 0 ; j < iloop; j++) { printf(" %2.2x", *(ec_slave[0].inputs + j)); } printf(" T:%lld\r",ec_DCtime); needlf = TRUE; } usleep(5000); } inOP = FALSE; } else { printf("Not all slaves reached operational state.\n"); ec_readstate(); for(i = 1; i<=ec_slavecount ; i++) { if(ec_slave[i].state != EC_STATE_OPERATIONAL) { printf("Slave %d State=0x%2.2x StatusCode=0x%4.4x : %s\n", i, ec_slave[i].state, ec_slave[i].ALstatuscode, ec_ALstatuscode2string(ec_slave[i].ALstatuscode)); } } } printf("\nRequest init state for all slaves\n"); ec_slave[0].state = EC_STATE_INIT; /* request INIT state for all slaves */ ec_writestate(0); } else { printf("No slaves found!\n"); } printf("End simple test, close socket\n"); /* stop SOEM, close socket */ ec_close(); } else { printf("No socket connection on %s\nExcecute as root\n",ifname); } } OSAL_THREAD_FUNC ecatcheck( void *ptr ) { int slave; while(1) { if( inOP && ((wkc < expectedWKC) || ec_group[currentgroup].docheckstate)) { if (needlf) { needlf = FALSE; printf("\n"); } /* one ore more slaves are not responding */ ec_group[currentgroup].docheckstate = FALSE; ec_readstate(); for (slave = 1; slave <= ec_slavecount; slave++) { if ((ec_slave[slave].group == currentgroup) && (ec_slave[slave].state != EC_STATE_OPERATIONAL)) { ec_group[currentgroup].docheckstate = TRUE; if (ec_slave[slave].state == (EC_STATE_SAFE_OP + EC_STATE_ERROR)) { printf("ERROR : slave %d is in SAFE_OP + ERROR, attempting ack.\n", slave); ec_slave[slave].state = (EC_STATE_SAFE_OP + EC_STATE_ACK); ec_writestate(slave); } else if(ec_slave[slave].state == EC_STATE_SAFE_OP) { printf("WARNING : slave %d is in SAFE_OP, change to OPERATIONAL.\n", slave); ec_slave[slave].state = EC_STATE_OPERATIONAL; ec_writestate(slave); } else if(ec_slave[slave].state > 0) { if (ec_reconfig_slave(slave, EC_TIMEOUTMON)) { ec_slave[slave].islost = FALSE; printf("MESSAGE : slave %d reconfigured\n",slave); } } else if(!ec_slave[slave].islost) { /* re-check state */ ec_statecheck(slave, EC_STATE_OPERATIONAL, EC_TIMEOUTRET); if (!ec_slave[slave].state) { ec_slave[slave].islost = TRUE; printf("ERROR : slave %d lost\n",slave); } } } if (ec_slave[slave].islost) { if(!ec_slave[slave].state) { if (ec_recover_slave(slave, EC_TIMEOUTMON)) { ec_slave[slave].islost = FALSE; printf("MESSAGE : slave %d recovered\n",slave); } } else { ec_slave[slave].islost = FALSE; printf("MESSAGE : slave %d found\n",slave); } } } if(!ec_group[currentgroup].docheckstate) printf("OK : all slaves resumed OPERATIONAL.\n"); } usleep(10000); } } int main(int argc, char *argv[]) { printf("SOEM (Simple Open EtherCAT Master)\nSimple test\n"); if (argc > 1) { /* create thread to handle slave error handling in OP */ // pthread_create( &thread1, NULL, (void *) &ecatcheck, (void*) &ctime); osal_thread_create(&thread1, 128000, &ecatcheck, (void*) &ctime); /* start cyclic part */ simpletest(argv[1]); } else { printf("Usage: simple_test ifname1\nifname = eth0 for example\n"); } printf("End program\n"); return (0); }