<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="Generator" content="Microsoft Word 12 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
        {font-family:Tahoma;
        panose-1:2 11 6 4 3 5 4 4 2 4;}
@font-face
        {font-family:Consolas;
        panose-1:2 11 6 9 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0cm;
        margin-bottom:.0001pt;
        font-size:12.0pt;
        font-family:"Times New Roman","serif";
        color:black;}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:purple;
        text-decoration:underline;}
pre
        {mso-style-priority:99;
        mso-style-link:"HTML Preformatted Char";
        margin:0cm;
        margin-bottom:.0001pt;
        font-size:10.0pt;
        font-family:"Courier New";
        color:black;}
span.HTMLPreformattedChar
        {mso-style-name:"HTML Preformatted Char";
        mso-style-priority:99;
        mso-style-link:"HTML Preformatted";
        font-family:Consolas;
        color:black;}
span.EmailStyle19
        {mso-style-type:personal-reply;
        font-family:"Calibri","sans-serif";
        color:#1F497D;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-size:10.0pt;}
@page WordSection1
        {size:612.0pt 792.0pt;
        margin:72.0pt 72.0pt 72.0pt 72.0pt;}
div.WordSection1
        {page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body bgcolor="white" lang="EN-NZ" link="blue" vlink="purple">
<div class="WordSection1">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D">Hi,<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D">EK1100 modules are fine to be used as the reference clock slave, it is just that it is not capable of being an active DC slave.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D">Graeme.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p> </o:p></span></p>
<div>
<div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0cm 0cm 0cm">
<p class="MsoNormal"><b><span lang="EN-US" style="font-size:10.0pt;font-family:"Tahoma","sans-serif";color:windowtext">From:</span></b><span lang="EN-US" style="font-size:10.0pt;font-family:"Tahoma","sans-serif";color:windowtext"> etherlab-users [mailto:etherlab-users-bounces@etherlab.org]
<b>On Behalf Of </b>Bilko AS, Oguz Dilmac<br>
<b>Sent:</b> Tuesday, 23 February 2016 2:23 a.m.<br>
<b>To:</b> Richard Hacker; Thomas Bitsky Jr<br>
<b>Cc:</b> etherlab-users@etherlab.org<br>
<b>Subject:</b> Re: [etherlab-users] Distributed Clocks<o:p></o:p></span></p>
</div>
</div>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal" style="margin-bottom:12.0pt">Hi, <br>
<br>
I didn't check the codes. But is it OK to have the first device in the bus a EK1100?
<br>
I can't find it now, but a message in this list was telling me EK1100 does not support DC fully.<br>
The fist node on the bus should be a drive or some other device which can support DC fully.<br>
<br>
Best regards,<br>
Oguz.<o:p></o:p></p>
<div>
<p class="MsoNormal">22.2.2016 11:52 tarihinde Richard Hacker yazdı:<o:p></o:p></p>
</div>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p class="MsoNormal">I do not have the patience to look into your code in detail to find out what you're doing wrong, but here is an excerpt of my code I use in etherlab Simulink Coder (ecrt_support.c):
<br>
<br>
void <br>
ecs_send() <br>
{ <br>
    //... <br>
<br>
    list_for_each(master, &ecat_data.master_list, list) { <br>
        list_for_each(domain, &master->domain_list, list) { <br>
            // ... <br>
<br>
            ecrt_domain_queue(domain->handle); <br>
        } <br>
<br>
        if (/* send frames */) { <br>
            struct timespec tp; <br>
<br>
            clock_gettime(CLOCK_MONOTONIC, &tp); <br>
            ecrt_master_application_time(master->handle, <br>
                    EC_TIMESPEC2NANO(tp)); <br>
<br>
            if (/* sync_clocks */)) { <br>
                ecrt_master_sync_reference_clock(master->handle); <br>
                //... <br>
            } <br>
<br>
            ecrt_master_sync_slave_clocks(master->handle); <br>
            ecrt_master_send(master->handle); <br>
        } <br>
    } <br>
} <br>
<br>
void <br>
ecs_receive() <br>
{ <br>
    //... <br>
<br>
    list_for_each(master, &ecat_data.master_list, list) { <br>
<br>
        if (/* receive frames */) { <br>
            ecrt_master_receive(master->handle); <br>
        } <br>
<br>
        list_for_each(domain, &master->domain_list, list) { <br>
<br>
            ecrt_domain_process(domain->handle); <br>
            ecrt_domain_state(domain->handle, &domain->state); <br>
<br>
        } <br>
    } <br>
} <br>
<br>
void <br>
calculate() <br>
{ <br>
    while (forever) { <br>
        ecs_receive(); <br>
        // do calculations <br>
        ecs_send(); <br>
        sleep(); <br>
    } <br>
} <br>
<br>
Some of this is pseudo code, but I trust in your programming capabilities to be able to adapt this to your code ;)
<br>
<br>
It is very important to call clock_gettime() and ecrt_master_application_time() as close to ecrt_master_send() as possible.
<br>
<br>
As you can see, it is not necessary to call ecrt_master_sync_reference_clock() with every ecrt_master_send(). In fact, ~100Hz of syncing should be enough, provided that your call to clock_gettime() is accurate. On the other hand, if clock_gettime() is inaccurate,
 you're in trouble when using DC anyway! <br>
<br>
- Richard <br>
<br>
Am 2016-02-22 um 04:23 schrieb Thomas Bitsky Jr: <br>
<br>
<o:p></o:p></p>
<p class="MsoNormal" style="margin-bottom:12.0pt">Ok, after following all your advice, plus the clock adjustment from
<br>
Mattieu, I’m pretty close. I enable the DC on the drive, get everything <br>
into op, the domains all come up and it would appear that the clocks are <br>
syncing. Except for one thing: <br>
<br>
[10248.633619] EtherCAT 0: Domain 0: Working counter changed to 1/17. <br>
[10249.535776] EtherCAT WARNING 0: 3989 datagrams UNMATCHED! <br>
[10249.539861] EtherCAT WARNING: Datagram f3bf2d0c (domain0-0-main) was <br>
SKIPPED 1 time. <br>
[10249.539887] EtherCAT WARNING: Datagram f3bf2a8c (domain2-120-main) <br>
was SKIPPED 1 time. <br>
[10249.539918] EtherCAT WARNING: Datagram f3bf2e8c (domain1-114-main) <br>
was SKIPPED 1 time. <br>
[10249.635934] EtherCAT 0: Domain 0: 9 working counter changes - now 17/17. <br>
[10249.919163] EtherCAT 0: Domain 2: Working counter changed to 1/1. <br>
[10250.018245] EtherCAT 0: Domain 1: Working counter changed to 1/1. <br>
[10250.040259] EtherCAT 0: Slave states on main device: OP. <br>
[10250.536654] EtherCAT WARNING 0: 4001 datagrams UNMATCHED! <br>
[10251.537469] EtherCAT WARNING 0: 4000 datagrams UNMATCHED! <br>
[10252.538282] EtherCAT WARNING 0: 4000 datagrams UNMATCHED! <br>
[10252.538296] EtherCAT WARNING 0: 4000 datagrams UNMATCHED! <br>
[10253.539097] EtherCAT WARNING 0: 4000 datagrams UNMATCHED! <br>
[10254.539909] EtherCAT WARNING 0: 4000 datagrams UNMATCHED! <br>
[10255.540723] EtherCAT WARNING 0: 4000 datagrams UNMATCHED! <br>
[10256.541537] EtherCAT WARNING 0: 4000 datagrams UNMATCHED!<— This <br>
continues indefinitely <br>
<br>
<br>
Everything else seems okay: <br>
<br>
user@core:~/dist$ ethercat slaves <br>
0  0:0  OP  +  EK1100 EtherCAT-Koppler (2A E-Bus) <br>
1  0:1  OP  +  EL1104 4K. Dig. Eingang 24V, 3ms, Sensorversorgung <br>
2  0:2  OP  +  EL1104 4K. Dig. Eingang 24V, 3ms, Sensorversorgung <br>
3  0:3  OP  +  EL5101 1K. Inc. Encoder 5V <br>
4  0:4  OP  +  EL7041 1K. Schrittmotor-Endstufe (50V, 5A) <br>
5  0:5  OP  +  EL7041 1K. Schrittmotor-Endstufe (50V, 5A) <br>
6  0:6  OP  +  EL2004 4K. Dig. Ausgang 24V, 0.5A <br>
7  0:7  OP  +  EL2004 4K. Dig. Ausgang 24V, 0.5A <br>
8  0:8  OP  +  EL4001 1K. Ana. Ausgang 0-10V, 12bit <br>
9  1:0  OP  +  0x00000539:0x02200001 <br>
user@core:~/dist$ ethercat master <br>
Master0 <br>
   Phase: Operation <br>
   Active: yes <br>
   Slaves: 10 <br>
   Ethernet devices: <br>
     Main: 00:30:18:c8:d4:92 (attached) <br>
       Link: UP <br>
       Tx frames:   5204542 <br>
       Tx bytes:    733318966 <br>
       Rx frames:   5204539 <br>
       Rx bytes:    733318636 <br>
       Tx errors:   0 <br>
       Tx frame rate [1/s]:   4000   3944   2090 <br>
       Tx rate [KByte/s]:    823.7  811.9  412.9 <br>
       Rx frame rate [1/s]:   4000   3944   2090 <br>
       Rx rate [KByte/s]:    823.7  811.9  412.9 <br>
     Common: <br>
       Tx frames:   5204542 <br>
       Tx bytes:    733318966 <br>
       Rx frames:   5204539 <br>
       Rx bytes:    733318636 <br>
       Lost frames: 3 <br>
       Tx frame rate [1/s]:   4000   3944   2090 <br>
       Tx rate [KByte/s]:    823.7  811.9  412.9 <br>
       Rx frame rate [1/s]:   4000   3944   2090 <br>
       Rx rate [KByte/s]:    823.7  811.9  412.9 <br>
       Loss rate [1/s]:          0      0      0 <br>
       Frame loss [%]:         0.0    0.0    0.0 <br>
   Distributed clocks: <br>
     Reference clock: Slave 0 <br>
     Application time: 509424243322706771 <br>
                       2016-02-22 02:44:03.322706771 <br>
<br>
<br>
<br>
<br>
<br>
<br>
I must have something out of sync. Is there something more I’m supposed <br>
to do to adjust the clock? I can make the number of dropped packets <br>
worse by simplifying the time adjustment, but I can’t seem to get it any <br>
better. I think I’m not understanding something about the clock <br>
adjustment. Can you see what I’m doing wrong? <br>
<br>
Thanks! <br>
<br>
My cyclic task: <br>
<br>
<br>
static void <br>
sync_distributed_clocks(void) <br>
{ <br>
     uint32_t ref_time = 0; <br>
     uint64_t prev_app_time = dc_time_ns; <br>
<br>
     dc_time_ns = system_time_ns(); <br>
<br>
     // set master time in nano-seconds <br>
     ecrt_master_application_time(master_, dc_time_ns); <br>
<br>
     // get reference clock time to synchronize master cycle <br>
     ecrt_master_reference_clock_time(master_, &ref_time); <br>
     dc_diff_ns = (uint32_t) prev_app_time - ref_time; <br>
<br>
<br>
     // call to sync slaves to ref slave <br>
     ecrt_master_sync_slave_clocks(master_); <br>
} <br>
<br>
static unsigned int cycle_ns = 1000000;  // 1 millisecond <br>
<br>
void update_master_clock(void) <br>
{ <br>
     // calc drift (via un-normalised time diff) <br>
     int32_t delta = dc_diff_ns - prev_dc_diff_ns; <br>
     prev_dc_diff_ns = dc_diff_ns; <br>
<br>
     // normalise the time diff <br>
     dc_diff_ns = <br>
         ((dc_diff_ns + (cycle_ns / 2)) % cycle_ns) - (cycle_ns / 2); <br>
<br>
     // only update if primary master <br>
     if (dc_started) <br>
{ <br>
<br>
         // add to totals <br>
         dc_diff_total_ns += dc_diff_ns; <br>
         dc_delta_total_ns += delta; <br>
         dc_filter_idx++; <br>
<br>
         if (dc_filter_idx >= DC_FILTER_CNT) <br>
{ <br>
             // add rounded delta average <br>
             dc_adjust_ns += <br>
                 ((dc_delta_total_ns + (DC_FILTER_CNT / 2)) / <br>
DC_FILTER_CNT); <br>
<br>
             // and add adjustment for general diff (to pull in drift) <br>
             dc_adjust_ns += sign(dc_diff_total_ns / DC_FILTER_CNT); <br>
<br>
             // limit crazy numbers (0.1% of std cycle time) <br>
             if (dc_adjust_ns < -1000) { <br>
                 dc_adjust_ns = -1000; <br>
             } <br>
             if (dc_adjust_ns > 1000) { <br>
                 dc_adjust_ns =  1000; <br>
             } <br>
<br>
             // reset <br>
             dc_diff_total_ns = 0LL; <br>
             dc_delta_total_ns = 0LL; <br>
             dc_filter_idx = 0; <br>
         } <br>
<br>
         // add cycles adjustment to time base (including a spot adjustment) <br>
         system_time_base += dc_adjust_ns + sign(dc_diff_ns); <br>
     } <br>
     else <br>
{ <br>
         dc_started = (dc_diff_ns != 0); <br>
<br>
         if (dc_started) <br>
{ <br>
             // output first diff <br>
             PRINT("First master diff: %d.\n", dc_diff_ns); <br>
<br>
             // record the time of this initial cycle <br>
             dc_start_time_ns = dc_time_ns; <br>
         } <br>
     } <br>
<br>
} <br>
<br>
<br>
int <br>
<br>
ecatMain_process(void* lp) <br>
<br>
{ <br>
<br>
<br>
sync_distributed_clocks(); <br>
<br>
update_master_clock(); <br>
<br>
<br>
ecrt_master_receive(master_); <br>
<br>
ecrt_domain_process(lrwDomainMgr_.domain); <br>
<br>
ecrt_domain_process(noLrwWriteDomainMgr_.domain); <br>
<br>
ecrt_domain_process(noLrwReadDomainMgr_.domain); <br>
<br>
… // handle my business <br>
<br>
<br>
// send process data <br>
<br>
ecrt_domain_queue(lrwDomainMgr_.domain); <br>
<br>
ecrt_domain_queue(noLrwWriteDomainMgr_.domain); <br>
<br>
ecrt_domain_queue(noLrwReadDomainMgr_.domain); <br>
<br>
// send EtherCAT data <br>
<br>
ecrt_master_send(master_); <br>
<br>
return 1; <br>
<br>
} <br>
<br>
<br>
<br>
<br>
I made my sync0 time half of the can rate, as you prescribed: <br>
<br>
void <br>
ecatModule_registerDcClock( Slave* slave, uint16_t addr ) <br>
{ <br>
struct timespec cur_time; <br>
clock_gettime(CLOCK_REALTIME, &cur_time); <br>
uint16_t sync0time = (uint16_t)((globalScanRate_us * 1000) / 2); <br>
ecrt_slave_config_dc( <br>
slave->sc, <br>
addr, <br>
(globalScanRate_us * 1000), <br>
sync0time, <br>
0, <br>
0); <br>
} <br>
<br>
<br>
<br>
<br>
<br>
From: Graeme Foot <<a href="mailto:Graeme.Foot@touchcut.com">Graeme.Foot@touchcut.com</a>
<br>
<a href="mailto:Graeme.Foot@touchcut.com"><mailto:Graeme.Foot@touchcut.com></a>> <br>
Date: Sunday, February 21, 2016 at 4:07 PM <br>
To: Thomas Bitsky <<a href="mailto:tbj@automateddesign.com">tbj@automateddesign.com</a>
<a href="mailto:tbj@automateddesign.com"><mailto:tbj@automateddesign.com></a>> <br>
Cc: "<a href="mailto:etherlab-users@etherlab.org">etherlab-users@etherlab.org</a>
<a href="mailto:etherlab-users@etherlab.org"><mailto:etherlab-users@etherlab.org></a>"
<br>
<<a href="mailto:etherlab-users@etherlab.org">etherlab-users@etherlab.org</a> <a href="mailto:etherlab-users@etherlab.org">
<mailto:etherlab-users@etherlab.org></a>> <br>
Subject: RE: Distributed Clocks <br>
<br>
Hi, <br>
<br>
I build and patch against revision 2526, so don’t know what patches / <br>
fixes have made it through to the latest release.  However for my <br>
revision I need fixes for reference clock selections and dc <br>
synchronization issues. <br>
<br>
I’ve attached the dc related patches I use, but these are applied after <br>
some other patches so you may get some conflicts or offsetting. <br>
<br>
*01 - Distributed Clock fixes and helpers.patch* <br>
<br>
This sorts out some ref slave issues, allowing a user selected ref <br>
slave.  It also adds some helper functions: <br>
<br>
- ecrt_master_setup_domain_memory() : this allows me to set up the <br>
domain memory and do stuff with it before calling ecrt_master_activate() <br>
(for user space apps) <br>
<br>
- ecrt_master_deactivate_slaves() : this lets me deactivate the slaves <br>
while still in realtime to avoid the slaves getting some shutdown sync <br>
errors <br>
<br>
*02 - Distributed Clock fixes from Jun Yuan - dc sync issues.patch* <br>
<br>
This sorts out some timing issues to do with slave dc syncing.  Without <br>
it they can start syncing from one cycle out causing a large syncing <br>
time overhead, one slave at a time. <br>
<br>
Regards, <br>
<br>
Graeme. <br>
<br>
*From:*Thomas Bitsky Jr [<a href="mailto:tbj@automateddesign.com">mailto:tbj@automateddesign.com</a>]
<br>
*Sent:* Sunday, 21 February 2016 10:27 a.m. <br>
*To:* Graeme Foot <br>
*Cc:* <a href="mailto:etherlab-users@etherlab.org">etherlab-users@etherlab.org</a>
<a href="mailto:etherlab-users@etherlab.org"><mailto:etherlab-users@etherlab.org></a>
<br>
*Subject:* Re: Distributed Clocks <br>
<br>
Graeme, <br>
<br>
Thank you so much for the detailed response. I'm away from my computer <br>
right now, so I can't try out your advice, but I noticed you asked about <br>
patches. I am not running any patches; which should I get? <br>
<br>
Thanks! <br>
Thomas Bitsky Jr <br>
<br>
On Feb 20, 2016, at 3:04 PM, Graeme Foot <<a href="mailto:Graeme.Foot@touchcut.com">Graeme.Foot@touchcut.com</a>
<br>
<a href="mailto:Graeme.Foot@touchcut.com"><mailto:Graeme.Foot@touchcut.com></a>> wrote:
<br>
<br>
    Hi, <br>
<br>
    The slave clocks get synced via the distributed clock system using <br>
    the master methods: ecrt_master_reference_clock_time(), <br>
    ecrt_master_sync_slave_clocks(), ecrt_master_application_time(), <br>
    ecrt_master_sync_reference_clock(). <br>
<br>
    However each individual slave can choose (if it is capable of it) <br>
    whether to synchronize its reading and writing of data to a <br>
    particular point in time within the dc cycle.  If that slave does <br>
    not choose to do so then the reading and writing of the data occurs <br>
    at the time the EtherCAT frame goes past, resulting in a progressive <br>
    update and application of data as the frame progresses through all <br>
    of the slaves. <br>
<br>
    If a slave is registered to use the dc clock then it caches the <br>
    frame data until the sync0 interrupt so in theory all dc slaves <br>
    apply the data at the same time.  It also means you don’t have to <br>
    worry about jitter as to the time the frame is sent over the wire. <br>
    The only thing is to choose a good sync0 time to ensure your frames <br>
    are always sent out and have reached all of the slaves before the <br>
    sync0 time occurs, and that the next frame is not sent out before <br>
    the previous sync0 time occurs. <br>
<br>
    In my application my cycle time is 1000us.  I choose a sync0 time of <br>
    500us.  I also send my frame as close as possible to the beginning <br>
    of the cycle. This gives the frame up to half the cycle time to <br>
    reach all of the slaves and then the other half of the cycle for the <br>
    frame to return in time for the master receive call.  I could choose <br>
    a sync0 time later than 500us but I want it processed as soon as <br>
    possible after the frame is received while still allowing for a bus <br>
    with a large number of terminals. <br>
<br>
    So below where you are calculating loop_shift based on the current <br>
    time is wrong.  Just choose a time within the dc cycle and use that <br>
    value for all slaves.  Note: the beginning of the dc cycle is in <br>
    phase with the first call to ecrt_master_application_time(), so all <br>
    of your realtime looping should also be in phase with that initial time. <br>
<br>
    Note, when selecting a slave to be the DC reference slave you should <br>
    generally choose the first slave on your bus, regardless of whether <br>
    it will be (or can be) registered to use the dc synchronization.  At <br>
    the very least it must be before, or be the, first slave that will <br>
    be registered as a dc slave. <br>
<br>
    Also note that some slaves clocks are not very stable and shouldn’t <br>
    be used as the DC reference slave.  My original testing was on a <br>
    Beckhoff CX1020 with a CX1100-0004, this could not be used as a <br>
    reference slave as its clock was not stable. <br>
<br>
    I see you are configuring the slaves via ecrt_slave_config_*()then <br>
    calling ecrt_slave_config_pdos()at the end.  If you call <br>
    ecrt_slave_config_pdos() at the beginning you don’t need all the <br>
    other config calls. However I hear AKD drives and some other slaves <br>
    prefer explicit slave config calls but most slaves are happy with <br>
    just the ecrt_slave_config_reg_pdo_entry()methods. <br>
<br>
    This also leads to another issue.  One of the configured PDO items <br>
    is the “mode of operation” value (0x6060 0x00).  You are also <br>
    configuring this with: ecrt_slave_config_sdo8( sc, 0x6060, 0, 8 ). <br>
    This value should be instead be set via the PDO value.  Use <br>
    ecrt_slave_config_reg_pdo_entry()to get the offset to the value and <br>
    set the value there. <br>
<br>
    Sorry if that was a bit long but DC’s is not an easy topic to get <br>
    your head around.  Here’s a bit of a summary: <br>
<br>
    - You can choose which slaves get registered with <br>
    ecrt_slave_config_dc(). But each slave you want synced must get its <br>
    own call to ecrt_slave_config_dc(). <br>
<br>
    - If your yaskawa drives get to OP without <br>
    ecrt_slave_config_dc()then they should get to OP with <br>
    ecrt_slave_config_dc(). <br>
<br>
    - The yaskawa drives require a very stable reference slave time, <br>
    which is why we sync the EtherCAT master to the reference slave <br>
    rather than the other way around. <br>
<br>
    And some other comments: <br>
<br>
    - Never use anEL9010 endcap module.  These break the distributed <br>
    clock calculations.  I don’t think they are available anymore though. <br>
<br>
    - There are some patches out there that fix various DC clock issues, <br>
    are you using any of these? <br>
<br>
    Regards, <br>
<br>
    Graeme. <br>
<br>
    *From:*Thomas Bitsky Jr [<a href="mailto:tbj@automateddesign.com">mailto:tbj@automateddesign.com</a>]
<br>
    *Sent:* Sunday, 21 February 2016 7:15 a.m. <br>
    *To:* Graeme Foot; <a href="mailto:etherlab-users@etherlab.org">etherlab-users@etherlab.org</a>
<br>
    <a href="mailto:etherlab-users@etherlab.org"><mailto:etherlab-users@etherlab.org></a>
<br>
    *Subject:* Re: Distributed Clocks <br>
<br>
    [snip] <br>
<br>
    I’ve never been able to get the EL7041 stepper modules to work in dc <br>
    mode. <br>
<br>
    [/snip] <br>
<br>
    Is it all or nothing? I need the servo drives, the LVDT and the <br>
    EL3356 tied to a distributed clock. The EL7041 is optional for me. <br>
<br>
    [snip] <br>
<br>
    I don’t see in your code calls to ecrt_slave_config_dc(). <br>
<br>
    For the yaskawa drive, during the config stage, I use the following <br>
    calls… <br>
<br>
    [/snip] <br>
<br>
    Forgot to put that part; my bad. This is what I had for the <br>
    Yaskawa/AKD, although I was only doing it to one of the drives. I <br>
    thought I was supposed to set up one distributed clock, and it <br>
    became the master and handled the rest. Am I supposed to do this for <br>
    all the cards, or do I select? <br>
<br>
    Yaskawa(AKD drive code is pretty much the same): <br>
<br>
       if (!(sc = ecrt_master_slave_config( <br>
<br>
                         master, <br>
<br>
    slavePosDomain, <br>
<br>
    slavePosIndex, <br>
<br>
    vendorId, productCode))) <br>
<br>
    { <br>
<br>
    return FALSE; <br>
<br>
         } <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1C12, 0, 0 ); /* clear sm pdo 0x1c12 */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1C13, 0, 0 ); /* clear sm pdo 0x1c12 */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1A00, 0, 0 ); /* clear TxPDO0 */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1A01, 0, 0 ); /* clear TxPDO1 */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1A02, 0, 0 ); /* clear TxPDO2 */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1A03, 0, 0 ); /* clear TxPDO3 */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1600, 0, 0 ); /* number of var in this <br>
    PDO */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1601, 0, 0 ); /* clear RxPdo 0x1601 */ <br>
<br>
         ecrt_slave_config_sdo8( sc, 0x1602, 0, 0 ); /* clear RxPdo <br>
    0x1602 */ <br>
<br>
         ecrt_slave_config_sdo8( sc, 0x1603, 0, 0 ); /* clear RxPdo <br>
    0x1603 */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1A00, 0, 0 ); /* clear TxPDO0 */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A00, 1, 0x60410010 ); // Status word <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A00, 2,0x60640020 );// Position <br>
    actual value, per encoder <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A00, 3,0x60770010 );// Torque, <br>
    actual value <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A00, 4,0x60F40020 );// Following <br>
    error, actual value <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A00, 5,0x60610008 );// Modes of <br>
    operation display <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A00, 6,0x00000008 );// GAP <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A00, 7,0x60B90010 );// Touch probe <br>
    status <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A00, 8, 0x60BA0020 ); // Touch probe <br>
    1 position <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1A00, 0, 8 ); /* pdo entries */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1A01, 0, 0 ); /* clear TxPDO1 */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A01,1,0x60410010 ); // Status word <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A01,2,0x60640020 );// Position <br>
    actual value, per encoder <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1A01, 0, 2 ); /* pdo entries */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1A02, 0, 0 ); /* clear TxPDO2 */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A02,1,0x60410010 ); // Status word <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A02,2,0x60640020 );// Position <br>
    actual value, per encoder <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1A02, 0, 2 ); /* pdo entries */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1A03, 0, 0 ); /* clear TxPDO2 */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A03,1,0x60410010 ); // Status word <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A03,2,0x60640020 );// Position <br>
    actual value, per encoder <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1A03,3,0x60770010 );// Torque, actual <br>
    value <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1A03, 0, 3 ); /* pdo entries */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1600, 0, 0 ); /* clear entries */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1600, 1, 0x60400010  ); /* control <br>
    word */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1600, 2, 0x607A0020  ); /* target <br>
    position */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1600, 3, 0x60FF0020  ); /* target <br>
    velocity */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1600, 4, 0x60710010  ); /* target <br>
    torque */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1600, 5, 0x60720010  ); /* max torque */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1600, 6, 0x60600008  ); /* modes of <br>
    operation */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1600, 7, 0x00000008  ); /* gap */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1600, 8, 0x60B80010  ); /* touch <br>
    probe function */ <br>
<br>
    ecrt_slave_config_sdo8(sc, 0x1600, 0, 8 ); /* pdo entries */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1601, 0, 0 ); /* clear entries */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1601, 1, 0x60400010  ); /* control <br>
    word */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1601, 2, 0x607A0020  ); /* target <br>
    position */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1601, 0, 2 ); /* pdo entries */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1602, 0, 0 ); /* clear entries */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1602, 1, 0x60400010  ); /* control <br>
    word */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1602, 2, 0x60FF0020  ); /* target <br>
    position */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1602, 0, 2 ); /* pdo entries */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1603, 0, 0 ); /* clear entries */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1603, 1, 0x60400010  ); /* control <br>
    word */ <br>
<br>
    ecrt_slave_config_sdo32( sc, 0x1603, 2, 0x60710020  ); /* target <br>
    position */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1603, 0, 2 ); /* pdo entries */ <br>
<br>
    ecrt_slave_config_sdo16( sc, 0x1C12, 1, 0x1601 ); /* download pdo <br>
    1C12 index */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1C12, 0, 1 ); /* set number of RxPDO */ <br>
<br>
    ecrt_slave_config_sdo16( sc, 0x1C13, 1, 0x1A01 ); /* download pdo <br>
    1C13 index */ <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x1C13, 0, 1 ); /* set number of TxPDO */ <br>
<br>
    // OPMODE <br>
<br>
    // Yaskawa recommends 8 <br>
<br>
    ecrt_slave_config_sdo8( sc, 0x6060, 0, 8 ); <br>
<br>
    unsigned char interpolationTime = 0xFF; <br>
<br>
    // 250 <br>
<br>
    unsigned char cycleExponent = 0xFA; <br>
<br>
    // microseconds <br>
<br>
    // globalSCanRate_us equals either 250 or 125. <br>
<br>
    unsigned int us = globalScanRate_us; <br>
<br>
    size_t i; <br>
<br>
    for ( i=0;i<6, us > 0xFF;++i ) <br>
<br>
    { <br>
<br>
    us /= 10; <br>
<br>
    cycleExponent += 1; <br>
<br>
    } <br>
<br>
    interpolationTime = us; <br>
<br>
    ecrt_slave_config_sdo8( akd->sc_akd, 0x60C2, 1, interpolationTime ); <br>
    /* Interpolation time */ <br>
<br>
    ecrt_slave_config_sdo8( akd->sc_akd, 0x60C2, 2, cycleExponent ); /* <br>
    Cycle exponent */ <br>
<br>
         PRINT("Configuring PDOs...\n"); <br>
<br>
         if (ecrt_slave_config_pdos(sc, EC_END, slave_syncs)) <br>
<br>
    { <br>
<br>
    PRINT("Failed to configure Yaskawa Sigma PDOs.\n"); <br>
<br>
             return FALSE; <br>
<br>
         } <br>
<br>
    *struct timespec cur_time;* <br>
<br>
    *clock_gettime(CLOCK_REALTIME, &cur_time);* <br>
<br>
    *size_t loop_period = globalScanRate_us * 1000;* <br>
<br>
    *if ( loop_period == 0 ) loop_period = 1;* <br>
<br>
    *size_t loop_shift * <br>
<br>
    *= loop_period - (cur_time.tv_nsec % loop_period);* <br>
<br>
    *ecrt_slave_config_dc(* <br>
<br>
    *sc, * <br>
<br>
    *0x0300, * <br>
<br>
    *loop_period, * <br>
<br>
    *loop_shift, * <br>
<br>
    *0, * <br>
<br>
    *0);* <br>
<br>
    For the EL3356, would I then? <br>
<br>
    KL3356StrainGauge* sg = (KL3356StrainGauge*)slave->instance; <br>
<br>
    printf( "Begin kl3356_ecConfigure...\n"); <br>
<br>
    // <br>
<br>
    // Create the slave configuration <br>
<br>
    // <br>
<br>
    if (!(sg->sc = ecrt_master_slave_config( <br>
<br>
    master, <br>
<br>
    slavePosDomain, slavePosIndex, // Bus position <br>
<br>
    vendorId, productCode <br>
<br>
    // Slave type <br>
<br>
    ))) <br>
<br>
    { <br>
<br>
    printf( <br>
<br>
    "kl3356_ecConfigure -- Failed to get slave configuration.\n"); <br>
<br>
        return FALSE; <br>
<br>
    } <br>
<br>
    // <br>
<br>
    // Register startup configuration for the hardware <br>
<br>
    // <br>
<br>
    ecrt_slave_config_sdo8( sg->sc, 0x1C12, 0, 0 ); /* clear sm pdo <br>
    0x1c12 */ <br>
<br>
    ecrt_slave_config_sdo8( sg->sc, 0x1C13, 0, 0 ); /* clear sm pdo <br>
    0x1c12 */ <br>
<br>
    ecrt_slave_config_sdo16( sg->sc, 0x1C12, 1, 0x1600 ); /* download <br>
    pdo 1C12 index */ <br>
<br>
    ecrt_slave_config_sdo8( sg->sc, 0x1C12, 0, 1 ); /* set number of <br>
    RxPDO */ <br>
<br>
    ecrt_slave_config_sdo16( sg->sc, 0x1C13, 1, 0x1A00 ); /* download <br>
    pdo 1C13 index */ <br>
<br>
    ecrt_slave_config_sdo16( sg->sc, 0x1C13, 2, 0x1A02 ); /* download <br>
    pdo 1C13 index */ <br>
<br>
    ecrt_slave_config_sdo8( sg->sc, 0x1C13, 0, 2 ); /* set number of <br>
    TxPDO */ <br>
<br>
    // <br>
<br>
    // Configure the hardware's PDOs <br>
<br>
    // <br>
<br>
    if (ecrt_slave_config_pdos(sg->sc, EC_END, kl3356_syncs)) <br>
<br>
    { <br>
<br>
        printf( <br>
<br>
    "kl3356_ecConfigure -- Failed to configure PDOs.\n"); <br>
<br>
        return FALSE; <br>
<br>
         } <br>
<br>
    *struct timespec cur_time;* <br>
<br>
    *clock_gettime(CLOCK_REALTIME, &cur_time);* <br>
<br>
    *size_t loop_period = globalScanRate_us * 1000;* <br>
<br>
    *if ( loop_period == 0 ) loop_period = 1;* <br>
<br>
    *size_t loop_shift * <br>
<br>
    *= loop_period - (cur_time.tv_nsec % loop_period);* <br>
<br>
    *ecrt_slave_config_dc(* <br>
<br>
    *s**g->sc, * <br>
<br>
    *0x0300, * <br>
<br>
    *loop_period, * <br>
<br>
    *loop_shift, * <br>
<br>
    *0, * <br>
<br>
    *0);* <br>
<br>
    Thanks! <br>
<br>
    Thomas C. Bitsky Jr. | Lead Developer <br>
<br>
    ADC | automateddesign.com <a href="http://automateddesign.com/"><http://automateddesign.com/></a>
<br>
<br>
    P: 630-783-1150 F: 630-783-1159 M: 630-632-6679 <br>
<br>
    Follow ADC news and media: <br>
<br>
    Facebook <a href="https://facebook.com/automateddesigncorp"><https://facebook.com/automateddesigncorp></a> | Twitter
<br>
    <a href="https://twitter.com/ADCSportsLogic"><https://twitter.com/ADCSportsLogic></a> | YouTube
<br>
    <a href="https://www.youtube.com/user/ADCSportsLogic"><https://www.youtube.com/user/ADCSportsLogic></a>
<br>
<br>
    *From: *Graeme Foot <<a href="mailto:Graeme.Foot@touchcut.com">Graeme.Foot@touchcut.com</a>
<br>
    <a href="mailto:Graeme.Foot@touchcut.com"><mailto:Graeme.Foot@touchcut.com></a>>
<br>
    *Date: *Friday, February 19, 2016 at 7:24 PM <br>
    *To: *Thomas Bitsky <<a href="mailto:tbj@automateddesign.com">tbj@automateddesign.com</a>
<br>
    <a href="mailto:tbj@automateddesign.com"><mailto:tbj@automateddesign.com></a>>, "<a href="mailto:etherlab-users@etherlab.org">etherlab-users@etherlab.org</a>
<br>
    <a href="mailto:etherlab-users@etherlab.org"><mailto:etherlab-users@etherlab.org></a>" <<a href="mailto:etherlab-users@etherlab.org">etherlab-users@etherlab.org</a>
<br>
    <a href="mailto:etherlab-users@etherlab.org"><mailto:etherlab-users@etherlab.org></a>>
<br>
    *Subject: *RE: Distributed Clocks <br>
<br>
    Hi, <br>
<br>
    I don’t see in your code calls to ecrt_slave_config_dc(). <br>
<br>
    For the yaskawa drive, during the config stage, I use the following <br>
    calls: <br>
<br>
         // set interpolation time period (free run mode) <br>
<br>
         // where 0x60C2 is time in seconds = (0x60C2, 0x01) x <br>
    10^(0x60C2, 0x02) <br>
<br>
         // eg period of 1ms: <br>
<br>
         //   (0x60C2, 0x01) = 1 <br>
<br>
         //   (0x60C2, 0x02) = -3 <br>
<br>
         // => 1 x 10^(-3) = 0.001s <br>
<br>
         ecrt_slave_config_sdo8(dev->slaveConfig, 0x60C2, 0x01, <br>
    (uint8_t)g_app.scanTimeMS); <br>
<br>
         ecrt_slave_config_sdo8(dev->slaveConfig, 0x60C2, 0x02, <br>
    (int8_t)(-3)); <br>
<br>
         // set up the distributed clock <br>
<br>
         // 0x0000 = free run, 0x0300 = dc <br>
<br>
         // (Supported DC cycle: 125us to 4ms (every 125us cycle)) <br>
<br>
         ecrt_slave_config_dc(dev->slaveConfig, 0x0300, <br>
    g_app.scanTimeNS, 500000, 0, 0); <br>
<br>
    0x60C2 shouldn’t be necessary for dc mode, but I used it before I <br>
    had dc mode working and have never tried it without and it doesn’t <br>
    harm anything having it in. <br>
<br>
    The second value that is being passed to the <br>
    ecrt_slave_config_dcmethod is a value that is written to the ESC <br>
    register 0x980. The Yaskawa SGDV doco says this value should be <br>
    0x0000 for free run mode and 0x0300 for dc mode.  Other ESC’s may <br>
    required different values. <br>
<br>
    I’ve never been able to get the EL7041 stepper modules to work in dc <br>
    mode. <br>
<br>
    Graeme. <br>
<br>
    *From:*etherlab-users [<a href="mailto:etherlab-users-bounces@etherlab.org">mailto:etherlab-users-bounces@etherlab.org</a>]
<br>
    *On Behalf Of *Thomas Bitsky Jr <br>
    *Sent:* Saturday, 20 February 2016 1:09 p.m. <br>
    *To:* <a href="mailto:etherlab-users@etherlab.org">etherlab-users@etherlab.org</a>
<a href="mailto:etherlab-users@etherlab.org"><mailto:etherlab-users@etherlab.org></a>
<br>
    *Subject:* [etherlab-users] Distributed Clocks <br>
<br>
    Hello. <br>
<br>
    I’ve been using the EtherCAT master for years to great success, but <br>
    I’m stuck on a problem I can’t figure out that I think several <br>
    people here are doing successfully. I can’t implement distributed <br>
    clocks in my application. <br>
<br>
    I am having the same problem on two systems I have up and running: <br>
<br>
    SYSTEM ONE: <br>
<br>
    EtherLAB Master 1.52, E1000E Driver, Scan Rate 4Khz, Ubuntu Server <br>
    14.04LTS, RT-PREEMPT 3.12.50-rt68 <br>
<br>
    alias=0, position=0, device=EK1100 <br>
<br>
    alias=0, position=1, device=EL1104 <br>
<br>
    alias=0, position=2, device=EL2004 <br>
<br>
    alias=0, position=3, device=EL9510 <br>
<br>
    alias=0, position=4, device=EL3356 <br>
<br>
    alias=0, position=5, device=Kollmorgen AKD <br>
<br>
    alias=0, position=6, device=MTS LVDT <br>
<br>
    SYSTEM TWO: <br>
<br>
    EtherLAB Master 1.52, E1000E Driver, Scan Rate 8Khz, Ubuntu Server <br>
    14.04LTS, RT-PREEMPT 3.12.50-rt68 <br>
<br>
    alias=0, position=0, device=EK1100 <br>
<br>
    alias=0, position=1, device=EL3001 <br>
<br>
    alias=0, position=2, device=EL1104 <br>
<br>
    alias=0, position=3, device=EL1104 <br>
<br>
    alias=0, position=4, device=EL1104 <br>
<br>
    alias=0, position=5, device=EL2004 <br>
<br>
    alias=0, position=6, device=EL2004 <br>
<br>
    alias=0, position=7, device=EL9505 <br>
<br>
    alias=0, position=8, device=EL7041 <br>
<br>
    alias=0, position=9, device=EL7041 <br>
<br>
    alias=0, position=10, device=EL7041 <br>
<br>
    alias=0, position=11, device=EL7041 <br>
<br>
    alias=0, position=12, device=EL7041 <br>
<br>
    alias=0, position=13, device=EL7041 <br>
<br>
    alias=0, position=14, device=EK1110 <br>
<br>
    alias=1, position=0, device=SIGMA5-05 <br>
<br>
    alias=2, position=0, device=Yaskawa SIGMA5-05 <br>
<br>
    alias=3, position=0, device=Yaskawa SIGMA5-05 <br>
<br>
    Both of the system are fully operational. However, for various <br>
    reasons, I need to implement distributed clocks on these systems. <br>
    I’ve never been able to get this to work. <br>
<br>
    What follows is the code I used for both systems to try this. I read <br>
    through examples on the mailing list, plus the examples, but I’m not <br>
    seeing what I’m doing wrong. The result is always the same: all the <br>
    fieldbus cards go into operation, but the servo drives won’t because <br>
    of “bad configuration.” Take out the distributed clock code, and <br>
    they work fine. I’m getting away with it for now, but I do need <br>
    better clock resolution. <br>
<br>
    The systems have an LRW domain, then a separate read domain and <br>
    write domain for the servo drive(s) for a total of three domains <br>
    (LRW, read, write). The yaskawa drives necessitate this. The scan <br>
    rate is usually 4Khz, but I have tried it at both 1Khz and 8Khz and <br>
    gotten the same results. Everything about the implementation is <br>
    fairly straight-forward. There’s just something fundamental about <br>
    the DC configuration that I’m not understanding. <br>
<br>
    Almost all the code below is taken right from the examples or the <br>
    message boards (thanks, everybody!). If anyone could tell me what <br>
    I’m going wrong or offer any insights, it’s greatly appreciated. For <br>
    brevity, I tried to narrow it down to relevant parts, but please let <br>
    me know any additional information or code I can provide. <br>
<br>
    Thank you in advance, <br>
<br>
    Tom <br>
<br>
    ********************************************************** <br>
<br>
    // EtherCAT distributed clock variables <br>
<br>
    #define DC_FILTER_CNT          1024 <br>
<br>
    #define SYNC_MASTER_TO_REF        1 <br>
<br>
    static uint64_t dc_start_time_ns = 0LL; <br>
<br>
    static uint64_t dc_time_ns = 0; <br>
<br>
    static uint8_t  dc_started = 0; <br>
<br>
    static int32_t  dc_diff_ns = 0; <br>
<br>
    static int32_t  prev_dc_diff_ns = 0; <br>
<br>
    static int64_t  dc_diff_total_ns = 0LL; <br>
<br>
    static int64_t  dc_delta_total_ns = 0LL; <br>
<br>
    static int      dc_filter_idx = 0; <br>
<br>
    static int64_t  dc_adjust_ns; <br>
<br>
    static int64_t  system_time_base = 0LL; <br>
<br>
    static uint64_t wakeup_time = 0LL; <br>
<br>
    static uint64_t overruns = 0LL; <br>
<br>
    /** Get the time in ns for the current cpu, adjusted by <br>
    system_time_base. <br>
<br>
      * <br>
<br>
      * \attention Rather than calling rt_get_time_ns() directly, all <br>
    application <br>
<br>
      * time calls should use this method instead. <br>
<br>
      * <br>
<br>
      * \ret The time in ns. <br>
<br>
      */ <br>
<br>
    uint64_t system_time_ns(void) <br>
<br>
    { <br>
<br>
    struct timespec ts; <br>
<br>
    clock_gettime(GLOBAL_CLOCK_TO_USE, &ts); <br>
<br>
    return TIMESPEC2NS(ts); <br>
<br>
    } <br>
<br>
    static <br>
<br>
    void sync_distributed_clocks(void) <br>
<br>
    { <br>
<br>
         uint32_t ref_time = 0; <br>
<br>
         uint64_t prev_app_time = dc_time_ns; <br>
<br>
         dc_time_ns = system_time_ns(); <br>
<br>
         // set master time in nano-seconds <br>
<br>
         ecrt_master_application_time(master_, dc_time_ns); <br>
<br>
         // get reference clock time to synchronize master cycle <br>
<br>
         ecrt_master_reference_clock_time(master_, &ref_time); <br>
<br>
         dc_diff_ns = (uint32_t) prev_app_time - ref_time; <br>
<br>
         // call to sync slaves to ref slave <br>
<br>
         ecrt_master_sync_slave_clocks(master_); <br>
<br>
    } <br>
<br>
    /** Return the sign of a number <br>
<br>
      * <br>
<br>
      * ie -1 for -ve value, 0 for 0, +1 for +ve value <br>
<br>
      * <br>
<br>
      * \retval the sign of the value <br>
<br>
      */ <br>
<br>
    #define sign(val) \ <br>
<br>
         ({ typeof (val) _val = (val); \ <br>
<br>
         ((_val > 0) - (_val < 0)); }) <br>
<br>
    /*****************************************************************************/<br>
<br>
    /** Update the master time based on ref slaves time diff <br>
<br>
      * <br>
<br>
      * called after the ethercat frame is sent to avoid time jitter in <br>
<br>
      * sync_distributed_clocks() <br>
<br>
      */ <br>
<br>
    static unsigned int cycle_ns = 1000000;  // 1 millisecond <br>
<br>
    void update_master_clock(void) <br>
<br>
    { <br>
<br>
         // calc drift (via un-normalised time diff) <br>
<br>
         int32_t delta = dc_diff_ns - prev_dc_diff_ns; <br>
<br>
         prev_dc_diff_ns = dc_diff_ns; <br>
<br>
         // normalise the time diff <br>
<br>
         dc_diff_ns = <br>
<br>
             ((dc_diff_ns + (cycle_ns / 2)) % cycle_ns) - (cycle_ns / 2); <br>
<br>
         // only update if primary master <br>
<br>
         if (dc_started) { <br>
<br>
             // add to totals <br>
<br>
             dc_diff_total_ns += dc_diff_ns; <br>
<br>
             dc_delta_total_ns += delta; <br>
<br>
             dc_filter_idx++; <br>
<br>
             if (dc_filter_idx >= DC_FILTER_CNT) { <br>
<br>
                 // add rounded delta average <br>
<br>
                 dc_adjust_ns += <br>
<br>
                     ((dc_delta_total_ns + (DC_FILTER_CNT / 2)) / <br>
    DC_FILTER_CNT); <br>
<br>
                 // and add adjustment for general diff (to pull in drift) <br>
<br>
                 dc_adjust_ns += sign(dc_diff_total_ns / DC_FILTER_CNT); <br>
<br>
                 // limit crazy numbers (0.1% of std cycle time) <br>
<br>
                 if (dc_adjust_ns < -1000) { <br>
<br>
                     dc_adjust_ns = -1000; <br>
<br>
                 } <br>
<br>
                 if (dc_adjust_ns > 1000) { <br>
<br>
                     dc_adjust_ns =  1000; <br>
<br>
                 } <br>
<br>
                 // reset <br>
<br>
                 dc_diff_total_ns = 0LL; <br>
<br>
                 dc_delta_total_ns = 0LL; <br>
<br>
                 dc_filter_idx = 0; <br>
<br>
             } <br>
<br>
             // add cycles adjustment to time base (including a spot <br>
    adjustment) <br>
<br>
             system_time_base += dc_adjust_ns + sign(dc_diff_ns); <br>
<br>
         } <br>
<br>
         else { <br>
<br>
             dc_started = (dc_diff_ns != 0); <br>
<br>
             if (dc_started) <br>
<br>
    { <br>
<br>
                 // record the time of this initial cycle <br>
<br>
                 dc_start_time_ns = dc_time_ns; <br>
<br>
             } <br>
<br>
         } <br>
<br>
    } <br>
<br>
    struct timespec dcTime_; <br>
<br>
    int <br>
<br>
    ecatMain_process(void* lp) <br>
<br>
    { <br>
<br>
    ecrt_master_receive(master_); <br>
<br>
    clock_gettime(CLOCK_REALTIME, &dcTime_); <br>
<br>
    ecrt_master_application_time(master_, TIMESPEC2NS(dcTime_)); <br>
<br>
    ecrt_master_sync_reference_clock(master_); <br>
<br>
    ecrt_master_sync_slave_clocks(master_); <br>
<br>
    ecrt_domain_process(lrwDomainMgr_.domain); <br>
<br>
    ecrt_domain_process(noLrwWriteDomainMgr_.domain); <br>
<br>
    ecrt_domain_process(noLrwReadDomainMgr_.domain); <br>
<br>
    … // handle my business <br>
<br>
    // write application time to master <br>
<br>
    clock_gettime(CLOCK_REALTIME, &dcTime_); <br>
<br>
    ecrt_master_application_time(master_, TIMESPEC2NS(dcTime_)); <br>
<br>
    if (sync_ref_counter_) <br>
<br>
    { <br>
<br>
    sync_ref_counter_--; <br>
<br>
    } <br>
<br>
    else <br>
<br>
    { <br>
<br>
    sync_ref_counter_ = 1; // sync every cycle <br>
<br>
    ecrt_master_sync_reference_clock(master_); <br>
<br>
    } <br>
<br>
    // send process data <br>
<br>
    ecrt_domain_queue(lrwDomainMgr_.domain); <br>
<br>
    ecrt_domain_queue(noLrwWriteDomainMgr_.domain); <br>
<br>
    ecrt_domain_queue(noLrwReadDomainMgr_.domain); <br>
<br>
    // sync distributed clock just before master_send to set <br>
<br>
    // most accurate master clock time <br>
<br>
    sync_distributed_clocks(); <br>
<br>
    // send EtherCAT data <br>
<br>
    ecrt_master_send(master_); <br>
<br>
    // update the master clock <br>
<br>
    // Note: called after ecrt_master_send() to reduce time <br>
<br>
    // jitter in the sync_distributed_clocks() call <br>
<br>
    update_master_clock(); <br>
<br>
    return 1; <br>
<br>
    } <br>
<br>
    int <br>
<br>
    ecatMain_start(void* lp) <br>
<br>
    { <br>
<br>
    // <br>
<br>
    // domain regs must end in a null entry <br>
<br>
    // <br>
<br>
    lrwDomainMgr_.domainRegs = realloc( <br>
<br>
    lrwDomainMgr_.domainRegs, <br>
<br>
    sizeof(ec_pdo_entry_reg_t) * (lrwDomainMgr_.size + 1)  ); <br>
<br>
    memset( <br>
<br>
    &(lrwDomainMgr_.domainRegs[lrwDomainMgr_.size]), <br>
<br>
    0, <br>
<br>
    sizeof(ec_pdo_entry_reg_t) ); <br>
<br>
    noLrwReadDomainMgr_.domainRegs = realloc( <br>
<br>
    noLrwReadDomainMgr_.domainRegs, <br>
<br>
    sizeof(ec_pdo_entry_reg_t) * (noLrwReadDomainMgr_.size + 1)  ); <br>
<br>
    memset( <br>
<br>
    &(noLrwReadDomainMgr_.domainRegs[noLrwReadDomainMgr_.size]), <br>
<br>
    0, <br>
<br>
    sizeof(ec_pdo_entry_reg_t) ); <br>
<br>
    noLrwWriteDomainMgr_.domainRegs = realloc( <br>
<br>
    noLrwWriteDomainMgr_.domainRegs, <br>
<br>
    sizeof(ec_pdo_entry_reg_t) * (noLrwWriteDomainMgr_.size + 1)  ); <br>
<br>
    memset( <br>
<br>
    &(noLrwWriteDomainMgr_.domainRegs[noLrwWriteDomainMgr_.size]), <br>
<br>
    0, <br>
<br>
    sizeof(ec_pdo_entry_reg_t) ); <br>
<br>
    // <br>
<br>
    // NOTE: the Output Domain must be registered with <br>
<br>
    // ecrt_domain_reg_pdo_entry_list before the Input Domain otherwise you <br>
<br>
    // will not have any data exchanged even though the drive goes into OP <br>
<br>
    // mode. <br>
<br>
    // <br>
<br>
    PRINT("\nAttempting to register PDOs on WRITE ONLY domain...\n"); <br>
<br>
    if (ecrt_domain_reg_pdo_entry_list( <br>
<br>
    noLrwWriteDomainMgr_.domain, noLrwWriteDomainMgr_.domainRegs)) <br>
<br>
    { <br>
<br>
    PRINT("WRITE ONLY PDO entry registration failed!\n"); <br>
<br>
    return FALSE; <br>
<br>
         } <br>
<br>
    PRINT("\nAttempting to register PDOs on READ ONLY domain...\n"); <br>
<br>
    if (ecrt_domain_reg_pdo_entry_list( <br>
<br>
    noLrwReadDomainMgr_.domain, noLrwReadDomainMgr_.domainRegs)) <br>
<br>
    { <br>
<br>
    PRINT("READ ONLY PDO entry registration failed!\n"); <br>
<br>
    return FALSE; <br>
<br>
      } <br>
<br>
    // <br>
<br>
    // And now we register the bi-directional domain. <br>
<br>
    // <br>
<br>
    PRINT("\nAttempting to register PDOs on LRW domain...\n"); <br>
<br>
    if (ecrt_domain_reg_pdo_entry_list( <br>
<br>
    lrwDomainMgr_.domain, lrwDomainMgr_.domainRegs)) <br>
<br>
    { <br>
<br>
    PRINT("LRW PDO entry registration failed!\n"); <br>
<br>
    return FALSE; <br>
<br>
         } <br>
<br>
    /* <br>
<br>
    * Finishes the configuration phase and prepares for cyclic operation. <br>
<br>
    * This function tells the master that the configuration phase <br>
<br>
    * is finished and the realtime operation will begin. <br>
<br>
    * The function allocates internal memory for the domains and calculates <br>
<br>
    * the logical FMMU addresses for domain members. <br>
<br>
    * It tells the master state machine that the bus configuration is <br>
<br>
    * now to be applied <br>
<br>
    */ <br>
<br>
    PRINT("\nAttempting to activate ECAT master...\n"); <br>
<br>
    if (ecrt_master_activate(master_)) <br>
<br>
    { <br>
<br>
    PRINT( <br>
<br>
    "%s Failed to activate master!\n", <br>
<br>
    __FUNCTION__ ); <br>
<br>
    return FALSE; <br>
<br>
    } <br>
<br>
    /* <br>
<br>
    * Returns the domain's process data. <br>
<br>
    */ <br>
<br>
    PRINT( "%s getting LRW process data from master.\n", __FUNCTION__ ); <br>
<br>
    if (!(lrwDomainMgr_.processData <br>
<br>
    = ecrt_domain_data(lrwDomainMgr_.domain))) <br>
<br>
    { <br>
<br>
    PRINT( <br>
<br>
    "%s set ecProcessData -- domain data is NULL!\n", <br>
<br>
    __FUNCTION__ ); <br>
<br>
    return FALSE; <br>
<br>
      } <br>
<br>
    if (!(noLrwReadDomainMgr_.processData <br>
<br>
    = ecrt_domain_data(noLrwReadDomainMgr_.domain))) <br>
<br>
    { <br>
<br>
    PRINT( <br>
<br>
    "%s set read ProcessData -- domain data is NULL!\n", <br>
<br>
    __FUNCTION__ ); <br>
<br>
    return FALSE; <br>
<br>
         } <br>
<br>
    if (!(noLrwWriteDomainMgr_.processData <br>
<br>
    = ecrt_domain_data(noLrwWriteDomainMgr_.domain))) <br>
<br>
    { <br>
<br>
    PRINT( <br>
<br>
    "%s set write ProcessData -- domain data is NULL!\n", <br>
<br>
    __FUNCTION__ ); <br>
<br>
    return FALSE; <br>
<br>
         } <br>
<br>
    … // blah blah blah <br>
<br>
    doScan_ = TRUE; <br>
<br>
    PRINT( "%s completed successfully.\n", __FUNCTION__ ); <br>
<br>
    return TRUE; <br>
<br>
    } <br>
<br>
<br>
<br>
_______________________________________________ <br>
etherlab-users mailing list <br>
<a href="mailto:etherlab-users@etherlab.org">etherlab-users@etherlab.org</a> <br>
<a href="http://lists.etherlab.org/mailman/listinfo/etherlab-users">http://lists.etherlab.org/mailman/listinfo/etherlab-users</a>
<o:p></o:p></p>
<p class="MsoNormal" style="margin-bottom:12.0pt">_______________________________________________
<br>
etherlab-users mailing list <br>
<a href="mailto:etherlab-users@etherlab.org">etherlab-users@etherlab.org</a> <br>
<a href="http://lists.etherlab.org/mailman/listinfo/etherlab-users">http://lists.etherlab.org/mailman/listinfo/etherlab-users</a>
<br>
<br>
<o:p></o:p></p>
</blockquote>
<p class="MsoNormal"><o:p> </o:p></p>
<div>
<p class="MsoNormal">-- <br>
<br>
<br>
<o:p></o:p></p>
<pre>Oguz Dilmac<o:p></o:p></pre>
<pre>ARGE Bolumu<o:p></o:p></pre>
<pre><o:p> </o:p></pre>
<pre>Bilko AS, R&D Department<o:p></o:p></pre>
<pre>====================================<o:p></o:p></pre>
<pre>Perpa Ticaret Merkezi B Blok Kat 13 Nr. 2568<o:p></o:p></pre>
<pre>TR-34384 Okmeydani Istanbul Turkey<o:p></o:p></pre>
<pre>Tel : +90 212 220 07 40  Fax :   +90 212 210 47 01<o:p></o:p></pre>
<pre>e-mail : <a href="mailto:odilmac@bilko-automation.com">odilmac@bilko-automation.com</a><o:p></o:p></pre>
<pre>web site : <a href="http://www.bilko-automation.com">http://www.bilko-automation.com</a><o:p></o:p></pre>
<pre>        <o:p></o:p></pre>
</div>
</div>
</body>
</html>