[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