[etherlab-users] DC questions
Graeme Foot
Graeme.Foot at touchcut.com
Tue Jun 21 01:48:09 CEST 2016
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.tommaso at gmail.com]
Sent: Monday, 20 June 2016 8:21 p.m.
To: Graeme Foot; gavinl at compacsort.com; etherlab-users at 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.etherlab.org/pipermail/etherlab-users/attachments/20160620/30012947/attachment-0004.htm>
More information about the Etherlab-users
mailing list