<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>