No, this was my TIMESPEC2NS:

#define TIMESPEC2NS(T) ((uint64_t) (T).tv_sec * NANOS_PER_SEC + (T).tv_nsec)


So, I’ll update it to:
#define TIMESPEC2NSEPOCH2000(T) ((uint64_t) (((T).tv_sec - 946684800ULL) * 
1000000000ULL) + (T).tv_nsec)


I hadn’t realized I needed to adjust for EtherCAT time EPOCH at all. Thanks!

Thanks!
Thomas C. Bitsky Jr. | Lead Developer

ADC | automateddesign.com <http://automateddesign.com/>
P: 630-783-1150 F: 630-783-1159 M: 630-632-6679

Follow ADC news and media:
Facebook <https://facebook.com/automateddesigncorp> | Twitter 
<https://twitter.com/ADCSportsLogic> | YouTube 
<https://www.youtube.com/user/ADCSportsLogic>








On 2/19/16, 7:25 PM, "Matthieu Bec" <m...@gmto.org> wrote:

>Hello Thomas,
>
>Just a wild guess, are you adjusting for EtherCAT time EPOCH? I have something 
>like:
>
>static inline uint64_t TIMESPEC2NSEPOCH2000(struct timespec ts)
>{
>  return (ts.tv_sec - 946684800ULL) * 1000000000ULL + ts.tv_nsec;
>}
>
>Regards,
>Matthieu
>
>
>
>
>
>
>
>
>On 2/19/16, 4:08 PM, "etherlab-users on behalf of Thomas Bitsky Jr" 
><etherlab-users-boun...@etherlab.org on behalf of t...@automateddesign.com> 
>wrote:
>
>Hello.
>
>
>I’ve been using the EtherCAT master for years to great success, but I’m stuck 
>on a problem I can’t figure out that I think several people here are doing 
>successfully. I can’t implement distributed clocks in my application.
>
>
>I am having the same problem on two systems I have up and running:
>
>
>
>
>
>
>SYSTEM ONE:
>
>
>EtherLAB Master 1.52, E1000E Driver, Scan Rate 4Khz, Ubuntu Server 14.04LTS, 
>RT-PREEMPT 3.12.50-rt68
>
>
>alias=0, position=0, device=EK1100 
>alias=0, position=1, device=EL1104 
>alias=0, position=2, device=EL2004
>alias=0, position=3, device=EL9510
>alias=0, position=4, device=EL3356
>alias=0, position=5, device=Kollmorgen AKD
>alias=0, position=6, device=MTS LVDT
>
>
>
>
>
>SYSTEM TWO:
>
>
>EtherLAB Master 1.52, E1000E Driver, Scan Rate 8Khz, Ubuntu Server 14.04LTS, 
>RT-PREEMPT 3.12.50-rt68
>
>
>alias=0, position=0, device=EK1100
>alias=0, position=1, device=EL3001
>alias=0, position=2, device=EL1104
>alias=0, position=3, device=EL1104
>alias=0, position=4, device=EL1104
>alias=0, position=5, device=EL2004
>alias=0, position=6, device=EL2004
>alias=0, position=7, device=EL9505
>alias=0, position=8, device=EL7041
>alias=0, position=9, device=EL7041
>alias=0, position=10, device=EL7041
>alias=0, position=11, device=EL7041
>alias=0, position=12, device=EL7041
>alias=0, position=13, device=EL7041
>alias=0, position=14, device=EK1110
>alias=1, position=0, device=SIGMA5-05
>alias=2, position=0, device=Yaskawa SIGMA5-05
>alias=3, position=0, device=Yaskawa SIGMA5-05
>
>
>
>
>
>Both of the system are fully operational. However, for various reasons, I need 
>to implement distributed clocks on these systems. I’ve never been able to get 
>this to work.
>
>
>
>
>What follows is the code I used for both systems to try this. I read through 
>examples on the mailing list, plus the examples, but I’m not seeing what I’m 
>doing wrong. The result is always the same: all the fieldbus cards go into 
>operation, but the servo
> drives won’t because of “bad configuration.” Take out the distributed clock 
> code, and they work fine. I’m getting away with it for now, but I do need 
> better clock resolution.
>
>
>The systems have an LRW domain, then a separate read domain and write domain 
>for the servo drive(s) for a total of three domains (LRW, read, write). The 
>yaskawa drives necessitate this. The scan rate is usually 4Khz, but I have 
>tried it at both 1Khz and
> 8Khz and gotten the same results. Everything about the implementation is 
> fairly straight-forward. There’s just something fundamental about the DC 
> configuration that I’m not understanding.
>
>
>Almost all the code below is taken right from the examples or the message 
>boards (thanks, everybody!). If anyone could tell me what I’m going wrong or 
>offer any insights, it’s greatly appreciated. For brevity, I tried to narrow 
>it down to relevant parts,
> but please let me know any additional information or code I can provide.
>
>
>Thank you in advance,
>Tom
>
>
>
>
>**********************************************************
>
>
>// EtherCAT distributed clock variables
>
>
>#define DC_FILTER_CNT          1024
>#define SYNC_MASTER_TO_REF        1
>
>
>static uint64_t dc_start_time_ns = 0LL;
>static uint64_t dc_time_ns = 0;
>static uint8_t  dc_started = 0;
>static int32_t  dc_diff_ns = 0;
>static int32_t  prev_dc_diff_ns = 0;
>static int64_t  dc_diff_total_ns = 0LL;
>static int64_t  dc_delta_total_ns = 0LL;
>static int      dc_filter_idx = 0;
>static int64_t  dc_adjust_ns;
>static int64_t  system_time_base = 0LL;
>static uint64_t wakeup_time = 0LL;
>static uint64_t overruns = 0LL;
>
>
>
>
>
>
>/** Get the time in ns for the current cpu, adjusted by system_time_base.
> *
> * \attention Rather than calling rt_get_time_ns() directly, all application
> * time calls should use this method instead.
> *
> * \ret The time in ns.
> */
>uint64_t system_time_ns(void)
>{
>struct timespec ts;
>clock_gettime(GLOBAL_CLOCK_TO_USE, &ts);
>return TIMESPEC2NS(ts);
>}
>
>
>
>
>
>
>static
>void sync_distributed_clocks(void)
>{
>    uint32_t ref_time = 0;
>    uint64_t prev_app_time = dc_time_ns;
>
>
>    dc_time_ns = system_time_ns();
>
>
>    // set master time in nano-seconds
>    ecrt_master_application_time(master_, dc_time_ns);
>
>
>    // get reference clock time to synchronize master cycle
>    ecrt_master_reference_clock_time(master_, &ref_time);
>    dc_diff_ns = (uint32_t) prev_app_time - ref_time;
>
>
>    // call to sync slaves to ref slave
>    ecrt_master_sync_slave_clocks(master_);
>}
>
>
>
>
>/** Return the sign of a number
> *
> * ie -1 for -ve value, 0 for 0, +1 for +ve value
> *
> * \retval the sign of the value
> */
>#define sign(val) \
>    ({ typeof (val) _val = (val); \
>    ((_val > 0) - (_val < 0)); })
>
>
>
>
>/*****************************************************************************/
>
>
>/** Update the master time based on ref slaves time diff
> *
> * called after the ethercat frame is sent to avoid time jitter in
> * sync_distributed_clocks()
> */
> 
>static unsigned int cycle_ns = 1000000;  // 1 millisecond 
>
>
>void update_master_clock(void)
>{
>
>
>    // calc drift (via un-normalised time diff)
>    int32_t delta = dc_diff_ns - prev_dc_diff_ns;
>    prev_dc_diff_ns = dc_diff_ns;
>
>
>    // normalise the time diff
>    dc_diff_ns =
>        ((dc_diff_ns + (cycle_ns / 2)) % cycle_ns) - (cycle_ns / 2);
>
>
>    // only update if primary master
>    if (dc_started) {
>
>
>        // add to totals
>        dc_diff_total_ns += dc_diff_ns;
>        dc_delta_total_ns += delta;
>        dc_filter_idx++;
>
>
>        if (dc_filter_idx >= DC_FILTER_CNT) {
>            // add rounded delta average
>            dc_adjust_ns +=
>                ((dc_delta_total_ns + (DC_FILTER_CNT / 2)) / DC_FILTER_CNT);
>
>
>            // and add adjustment for general diff (to pull in drift)
>            dc_adjust_ns += sign(dc_diff_total_ns / DC_FILTER_CNT);
>
>
>            // limit crazy numbers (0.1% of std cycle time)
>            if (dc_adjust_ns < -1000) {
>                dc_adjust_ns = -1000;
>            }
>            if (dc_adjust_ns > 1000) {
>                dc_adjust_ns =  1000;
>            }
>
>
>            // reset
>            dc_diff_total_ns = 0LL;
>            dc_delta_total_ns = 0LL;
>            dc_filter_idx = 0;
>        }
>
>
>        // add cycles adjustment to time base (including a spot adjustment)
>        system_time_base += dc_adjust_ns + sign(dc_diff_ns);
>    }
>    else {
>        dc_started = (dc_diff_ns != 0);
>
>
>        if (dc_started) 
>{
>            // record the time of this initial cycle
>            dc_start_time_ns = dc_time_ns;
>        }
>    }
>
>
>}
>
>
>
>
>
>
>struct timespec dcTime_;
>
>
>
>
>
>int
>ecatMain_process(void* lp)
>{
>
>ecrt_master_receive(master_);
>
>clock_gettime(CLOCK_REALTIME, &dcTime_);
>ecrt_master_application_time(master_, TIMESPEC2NS(dcTime_));
>
>
>ecrt_master_sync_reference_clock(master_);
>ecrt_master_sync_slave_clocks(master_);
>
>
>
>
>ecrt_domain_process(lrwDomainMgr_.domain);
>ecrt_domain_process(noLrwWriteDomainMgr_.domain);
>ecrt_domain_process(noLrwReadDomainMgr_.domain);
>
>
>… // handle my business
>
>
>// write application time to master
>clock_gettime(CLOCK_REALTIME, &dcTime_);
>ecrt_master_application_time(master_, TIMESPEC2NS(dcTime_));
>
>
>
>
>if (sync_ref_counter_) 
>{
>sync_ref_counter_--;
>} 
>else 
>{
>sync_ref_counter_ = 1; // sync every cycle
>ecrt_master_sync_reference_clock(master_);
>}
>
>
>// send process data
>ecrt_domain_queue(lrwDomainMgr_.domain);
>
>ecrt_domain_queue(noLrwWriteDomainMgr_.domain);
>ecrt_domain_queue(noLrwReadDomainMgr_.domain);
>
>
> 
>// sync distributed clock just before master_send to set
>// most accurate master clock time
>sync_distributed_clocks();
>
>
>// send EtherCAT data
>ecrt_master_send(master_);
>
>
>// update the master clock
>// Note: called after ecrt_master_send() to reduce time
>// jitter in the sync_distributed_clocks() call
>update_master_clock();
>
>return 1;
>}
>
>
>
>
>
>
>
>int
>ecatMain_start(void* lp)
>{
>
>//
>// domain regs must end in a null entry
>//
>
>lrwDomainMgr_.domainRegs = realloc(
>lrwDomainMgr_.domainRegs, 
>sizeof(ec_pdo_entry_reg_t) * (lrwDomainMgr_.size + 1)  );
>
>memset(
>&(lrwDomainMgr_.domainRegs[lrwDomainMgr_.size]),
>0, 
>sizeof(ec_pdo_entry_reg_t) );
>
>
>noLrwReadDomainMgr_.domainRegs = realloc(
>noLrwReadDomainMgr_.domainRegs, 
>sizeof(ec_pdo_entry_reg_t) * (noLrwReadDomainMgr_.size + 1)  );
>
>memset(
>&(noLrwReadDomainMgr_.domainRegs[noLrwReadDomainMgr_.size]),
>0, 
>sizeof(ec_pdo_entry_reg_t) );
>
>
>
>
>noLrwWriteDomainMgr_.domainRegs = realloc(
>noLrwWriteDomainMgr_.domainRegs, 
>sizeof(ec_pdo_entry_reg_t) * (noLrwWriteDomainMgr_.size + 1)  );
>
>memset(
>&(noLrwWriteDomainMgr_.domainRegs[noLrwWriteDomainMgr_.size]),
>0, 
>sizeof(ec_pdo_entry_reg_t) );
>
>
>
>
>
>
>
>
>//
>// NOTE: the Output Domain must be registered with
>// ecrt_domain_reg_pdo_entry_list before the Input Domain otherwise you 
>// will not have any data exchanged even though the drive goes into OP
>// mode.
>//
>
>
>PRINT("\nAttempting to register PDOs on WRITE ONLY domain...\n");
>if (ecrt_domain_reg_pdo_entry_list(
>noLrwWriteDomainMgr_.domain, noLrwWriteDomainMgr_.domainRegs)) 
>{
>PRINT("WRITE ONLY PDO entry registration failed!\n");
>return FALSE;
>    }
>
>PRINT("\nAttempting to register PDOs on READ ONLY domain...\n");
>if (ecrt_domain_reg_pdo_entry_list(
>noLrwReadDomainMgr_.domain, noLrwReadDomainMgr_.domainRegs)) 
>{
>PRINT("READ ONLY PDO entry registration failed!\n");
>return FALSE;
>  }
>
>
>
>
>//
>// And now we register the bi-directional domain.
>//
>PRINT("\nAttempting to register PDOs on LRW domain...\n");
>if (ecrt_domain_reg_pdo_entry_list(
>lrwDomainMgr_.domain, lrwDomainMgr_.domainRegs)) 
>{
>PRINT("LRW PDO entry registration failed!\n");
>return FALSE;
>    }
>
>
>
>/*
>* Finishes the configuration phase and prepares for cyclic operation.
>* This function tells the master that the configuration phase 
>* is finished and the realtime operation will begin.
>* The function allocates internal memory for the domains and calculates 
>* the logical FMMU addresses for domain members. 
>* It tells the master state machine that the bus configuration is 
>* now to be applied
>*/
>PRINT("\nAttempting to activate ECAT master...\n"); 
>if (ecrt_master_activate(master_))
>{
>PRINT(
>"%s Failed to activate master!\n",
>__FUNCTION__ );
>return FALSE;
>}
>
>/*
>* Returns the domain's process data. 
>*/
>PRINT( "%s getting LRW process data from master.\n", __FUNCTION__ ); 
>if (!(lrwDomainMgr_.processData 
>= ecrt_domain_data(lrwDomainMgr_.domain))) 
>{
>PRINT(
>"%s set ecProcessData -- domain data is NULL!\n",
>__FUNCTION__ );
>
>return FALSE;
>  }
>
>
>if (!(noLrwReadDomainMgr_.processData 
>= ecrt_domain_data(noLrwReadDomainMgr_.domain))) 
>{
>PRINT(
>"%s set read ProcessData -- domain data is NULL!\n",
>__FUNCTION__ );
>
>return FALSE;
>    } 
>
>if (!(noLrwWriteDomainMgr_.processData 
>= ecrt_domain_data(noLrwWriteDomainMgr_.domain))) 
>{
>PRINT(
>"%s set write ProcessData -- domain data is NULL!\n",
>__FUNCTION__ );
>
>return FALSE;
>    }
>
>
>
>… // blah blah blah
>
>
>
>doScan_ = TRUE;
>
>
>
>PRINT( "%s completed successfully.\n", __FUNCTION__ );
>return TRUE;
>}
>
>
>
>
>
>
_______________________________________________
etherlab-users mailing list
etherlab-users@etherlab.org
http://lists.etherlab.org/mailman/listinfo/etherlab-users

Reply via email to