Hi, Try having a look at: https://www.mail-archive.com/etherlab-users@etherlab.org/msg02641.html
The post above shows the general flow of my program. I use RTAI. Below are the structures and functions I currently use to sync the master to the slave reference master. I think there's also some other versions out there in past forum posts. Some of the structures used: struct _etherCATModule_s { ... ec_master_t *master; /**< the ethercat master reference once attached */ ecDevice_s *m_dcRefSlave; /**< is there a configured ref slave? */ uint64_t m_dcTimeStart; /**< distributed clock first now time */ uint64_t m_dcTime; /**< distributed clock now time */ int32_t m_dcDiff; /**< distributed clock master to ref slave diff time */ bool_t m_getDCDiff; /**< we are ready to get the distributed clock diff time */ ... }; /** the global application data structure */ typedef struct _app_s { ... uint64_t scanTimeNS; /**< the amount of time per scan in nanoseconds */ uint64_t scanTimeMS; /**< the amount of time per scan in milliseconds */ double scanTimeSec; /**< the amount of time per scan in seconds */ int32_t rtTimeDiff; /**< master time source time diff (ns) */ int32_t rtTimeDelta; /**< master time source time diff delta (ns) */ uint64_t rtTimeStart; /**< master time source time base start (ns) */ uint64_t rtTimeCurr; /**< master time source time base current (ns) */ int64_t rtAdjBy; /**< master time source cycle adjustment value (ns) */ uint64_t nextWakeTime; /**< next cycle wake time time (ns) */ uint64_t actualWakeTime; /**< actual cycle wake time (ns) */ uint64_t overruns; /**< the number of overruns that have occured */ ... } app_s; When preparing to run: int ecRes; ec_slave_config_t *refSlaveConfig = NULL; // set up distributed clocks ecMod->m_dcTimeStart = app_getTimeNS(); ecMod->m_dcTime = ecMod->m_dcTimeStart; if (ecMod->m_dcRefSlave) { refSlaveConfig = ecMod->m_dcRefSlave->slaveConfig; } // set initial app time (time must be in phase with realtime cycle) ecrt_master_application_time(ecMod->master, ecMod->m_dcTimeStart); // select the slave device to be the reference clock ecRes = ecrt_master_select_reference_clock(ecMod->master, refSlaveConfig); if (ecRes < 0) return E_EC_ACTIVATE_ERROR; // activate the master ecRes = ecrt_master_activate(ecMod->master); if (ecRes < 0) return E_EC_ACTIVATE_ERROR; The "sync distributed clock" function is something like: int32_t ecMod_syncDistClock( void *this /**< pointer to module etherCATModule_s */ ) { etherCATModule_s *ecMod = this; uint32_t masterTime; // cache lower 32 bits of prev master time and get now masterTime = (uint32_t)ecMod->m_dcTime; ecMod->m_dcTime = app_getTimeNS(); // use the dc ref slave to adjust the masters time base // get lower 32 bit of clock time from reference slave (after first scan) if (ecMod->m_getDCDiff) { int res; uint32_t slaveTime; res = ecrt_master_reference_clock_time(ecMod->master, &slaveTime); switch (res) { case 0 : { // calc time diff ecMod->m_dcDiff = masterTime - slaveTime; } break; default : { // no ref clock found or datagram failure ecMod->m_dcDiff = 0; } } } else { ecMod->m_dcDiff = 0; ecMod->m_getDCDiff = true; } // call to sync slaves to ref slave // (which is used for ecrt_master_reference_clock_time) ecrt_master_sync_slave_clocks(ecMod->master); // update the master time for the next cycle (in nano-seconds) // (this is required for the master to figure out the modules initial // dc time) ecrt_master_application_time(ecMod->master, ecMod->m_dcTime + g_app.scanTimeNS); return 0; } The "calc slave to master time drift and adjust master clock and cycle period to match" function is: #define DC_FILTER_CNT 1024 static bool_t u_dcStarted = false; static int32_t u_dcLastDiff = 0; static int64_t u_dcDiffTot = 0LL; static int64_t u_dcDeltaTot = 0LL; static int u_dcIdx = 0; /** update the master time based on ref slaves time diff * * called after the ethercat frame is sent to avoid time jitter in * ecMod_syncDistClock() */ int32_t ecMod_updateMasterClock( void *this /**< pointer to module module_s */ ) { etherCATModule_s *ecMod = this; assert(ecMod->master); if (u_dcStarted) { // calc drift (via un-normalised time diff) g_app.rtTimeDelta = ecMod->m_dcDiff - u_dcLastDiff; u_dcLastDiff = ecMod->m_dcDiff; // normalise the time diff // the ethercat code can be one cycle out, but we just want to stay synced // to the nearest cycle ecMod->m_dcDiff = ((ecMod->m_dcDiff + (g_app.scanTimeNS/2)) % g_app.scanTimeNS) - (g_app.scanTimeNS/2); g_app.rtTimeDiff = ecMod->m_dcDiff; // update current time g_app.rtTimeCurr = ecMod->m_dcTime; // add to totals u_dcDiffTot += g_app.rtTimeDiff; u_dcDeltaTot += g_app.rtTimeDelta; u_dcIdx++; if (u_dcIdx >= DC_FILTER_CNT) { // add rounded delta average g_app.rtAdjBy += ((u_dcDeltaTot + (DC_FILTER_CNT/2)) / DC_FILTER_CNT); // and add adjustment for general diff g_app.rtAdjBy += sign(u_dcDiffTot / DC_FILTER_CNT); // limit crazy numbers (0.1% of std cycle time) g_app.rtAdjBy = ensureRange(g_app.rtAdjBy, -1000, 1000); // reset u_dcDiffTot = 0LL; u_dcDeltaTot = 0LL; u_dcIdx = 0; } // add cycles adjustment to time base (including a spot adjustment) app_addTimeBase(g_app.rtAdjBy + sign(ecMod->m_dcDiff)); } else { u_dcStarted = (ecMod->m_dcDiff != 0); if (u_dcStarted) { // output first diff logMsg(VERBOSITY_MINOR, "first master diff: %d.\n", ecMod->m_dcDiff); // cache first diff for delta calcs g_app.rtTimeDelta = 0LL; u_dcLastDiff = ecMod->m_dcDiff; // normalise the time diff ecMod->m_dcDiff = ((ecMod->m_dcDiff + (g_app.scanTimeNS/2)) % g_app.scanTimeNS) - (g_app.scanTimeNS/2); g_app.rtTimeDiff = ecMod->m_dcDiff; // record the time of this initial cycle g_app.rtTimeStart = ecMod->m_dcTime; } } return 0; } And the "wait for remainder of cycle" function: void waitPeriod() { bool_t badWake = false; while (true) { RTIME wakeTime = app2rtaiTime(g_app.nextWakeTime); RTIME currTime = rt_get_time(); if (wakeTime < currTime) { if (!g_app.emulationMode) { logMsg(VERBOSITY_MAJOR, "sysMain_waitPeriod unexpected wake time!!!\n"); logMsg(VERBOSITY_MAJOR, "(wakeTime < currTime, -%llu)\n", currTime - wakeTime); } badWake = true; } else if (wakeTime > currTime + 50*g_app.scanTimeNS) { if (!g_app.emulationMode) { logMsg(VERBOSITY_MAJOR, "sysMain_waitPeriod unexpected wake time!!!\n"); logMsg(VERBOSITY_MAJOR, "(wakeTime > currTime + 50 periods, %llu)\n", wakeTime - currTime); } } switch (rt_sleep_until(wakeTime)) { case RTE_UNBLKD : { if (!g_app.emulationMode) { logMsg(VERBOSITY_MAJOR, "sysMain_waitPeriod = RTE_UNBLKD\n"); } continue; } case RTE_TMROVRN : { if (!g_app.emulationMode) { logMsg(VERBOSITY_MAJOR, "sysMain_waitPeriod = RTE_TMROVRN\n"); } g_app.overruns++; if (g_app.overruns % 100 == 0) { // cycle time might be stuffed, so ensure other processes get some // time slice rt_sleep(10000); } } break; default : {} } // done if we got to here break; } // statistics and header data g_app.actualWakeTime = app_getTimeNS(); g_app.systemData.sampleNum++; g_app.systemData.systemTimeMS += g_app.scanTimeMS; // calc next wake time (in app time) g_app.nextWakeTime += g_app.scanTimeNS; } Misc time functions: static int64_t u_appTimeBase = 0LL; /** get the rt time in ns for the current cpu (adjusted for the app time base) * * \ret the time in ns */ uint64_t app_getTimeNS() { RTIME time = rt_get_time_ns(); if (u_appTimeBase > time) { logErr(VERBOSITY_CRITICAL, "app_getTimeNS error: TimeBase greater than system time (timeBase: %lld, sysTime: %llu\n", u_appTimeBase, time); return time; } else { return time - u_appTimeBase; } } /** convert app time to rtai time in counts (via the app time base) */ RTIME app2rtaiTime( uint64_t in_appTime ) { RTIME time; if ((u_appTimeBase < 0) && ((uint64_t)(-u_appTimeBase) > in_appTime)) { logErr(VERBOSITY_CRITICAL, "APP: app2rtaiTime error: TimeBase less than app time (timeBase: %lld, appTime: %llu\n", u_appTimeBase, in_appTime); time = in_appTime; } else { time = in_appTime + u_appTimeBase; } return nano2count(time); } /** get the app time base value * * \ret the time base in ns */ int64_t app_getTimeBase() { return u_appTimeBase; } /** set the app time base value * * if the app clock is slower (ie the period takes longer) then the time base * value should be increased each period */ void app_setTimeBase( int64_t in_timeBase ) { u_appTimeBase = in_timeBase; } /** add to the app time base value * * if the app clock is slower (ie the period takes longer) then the time base * value should be increased each period */ void app_addTimeBase( int64_t in_timeBase ) { u_appTimeBase += in_timeBase; } Regards, Graeme. From: Tommaso [mailto:furiosi.tomm...@gmail.com] Sent: Monday, 20 June 2016 8:21 p.m. To: Graeme Foot; gav...@compacsort.com; etherlab-users@etherlab.org Subject: R: [etherlab-users] DC questions Thank you both for your answers. I would like to get again your help in order to find a little example, focused only on the synchronization part, for the b method introduced by Graeme, because I have already tried something but the application continuosly sends the "Failed to get reference clock time: Input/Output error" error. Thank you again. Best regards, Tommaso
_______________________________________________ etherlab-users mailing list etherlab-users@etherlab.org http://lists.etherlab.org/mailman/listinfo/etherlab-users