ethercatdc.c
Go to the documentation of this file.
1 /*
2  * Simple Open EtherCAT Master Library
3  *
4  * File : ethercatdc.c
5  * Version : 1.3.0
6  * Date : 24-02-2013
7  * Copyright (C) 2005-2013 Speciaal Machinefabriek Ketels v.o.f.
8  * Copyright (C) 2005-2013 Arthur Ketels
9  * Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven
10  *
11  * SOEM is free software; you can redistribute it and/or modify it under
12  * the terms of the GNU General Public License version 2 as published by the Free
13  * Software Foundation.
14  *
15  * SOEM is distributed in the hope that it will be useful, but WITHOUT ANY
16  * WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18  * for more details.
19  *
20  * As a special exception, if other files instantiate templates or use macros
21  * or inline functions from this file, or you compile this file and link it
22  * with other works to produce a work based on this file, this file does not
23  * by itself cause the resulting work to be covered by the GNU General Public
24  * License. However the source code for this file must still be made available
25  * in accordance with section (3) of the GNU General Public License.
26  *
27  * This exception does not invalidate any other reasons why a work based on
28  * this file might be covered by the GNU General Public License.
29  *
30  * The EtherCAT Technology, the trade name and logo “EtherCAT” are the intellectual
31  * property of, and protected by Beckhoff Automation GmbH. You can use SOEM for
32  * the sole purpose of creating, using and/or selling or otherwise distributing
33  * an EtherCAT network master provided that an EtherCAT Master License is obtained
34  * from Beckhoff Automation GmbH.
35  *
36  * In case you did not receive a copy of the EtherCAT Master License along with
37  * SOEM write to Beckhoff Automation GmbH, Eiserstraße 5, D-33415 Verl, Germany
38  * (www.beckhoff.com).
39  */
40 
46 #include "oshw.h"
47 #include "osal.h"
48 #include "ethercattype.h"
49 #include "ethercatbase.h"
50 #include "ethercatmain.h"
51 #include "ethercatdc.h"
52 
53 #define PORTM0 0x01
54 #define PORTM1 0x02
55 #define PORTM2 0x04
56 #define PORTM3 0x08
57 
59 #define SyncDelay ((int32)100000000)
60 
70 void ecx_dcsync0(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime, uint32 CyclShift)
71 {
72  uint8 h, RA;
73  uint16 wc, slaveh;
74  int64 t, t1;
75  int32 tc;
76 
77  slaveh = context->slavelist[slave].configadr;
78  RA = 0;
79 
80  /* stop cyclic operation, ready for next trigger */
81  wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET);
82  if (act)
83  {
84  RA = 1 + 2; /* act cyclic operation and sync0, sync1 deactivated */
85  }
86  h = 0;
87  wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCCUC, sizeof(h), &h, EC_TIMEOUTRET); /* write access to ethercat */
88  wc = ecx_FPRD(context->port, slaveh, ECT_REG_DCSYSTIME, sizeof(t1), &t1, EC_TIMEOUTRET); /* read local time of slave */
89  t1 = etohll(t1);
90 
91  /* Calculate first trigger time, always a whole multiple of CyclTime rounded up
92  plus the shifttime (can be negative)
93  This insures best sychronisation between slaves, slaves with the same CyclTime
94  will sync at the same moment (you can use CyclShift to shift the sync) */
95  if (CyclTime > 0)
96  {
97  t = ((t1 + SyncDelay) / CyclTime) * CyclTime + CyclTime + CyclShift;
98  }
99  else
100  {
101  t = t1 + SyncDelay + CyclShift;
102  /* first trigger at T1 + CyclTime + SyncDelay + CyclShift in ns */
103  }
104  t = htoell(t);
105  wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCSTART0, sizeof(t), &t, EC_TIMEOUTRET); /* SYNC0 start time */
106  tc = htoel(CyclTime);
107  wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE0, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC0 cycle time */
108  wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); /* activate cyclic operation */
109 }
110 
123 void ecx_dcsync01(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, uint32 CyclShift)
124 {
125  uint8 h, RA;
126  uint16 wc, slaveh;
127  int64 t, t1;
128  int32 tc;
129 
130  slaveh = context->slavelist[slave].configadr;
131  RA = 0;
132 
133  /* stop cyclic operation, ready for next trigger */
134  wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET);
135  if (act)
136  {
137  RA = 1 + 2 + 4; /* act cyclic operation and sync0 + sync1 */
138  }
139  h = 0;
140  wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCCUC, sizeof(h), &h, EC_TIMEOUTRET); /* write access to ethercat */
141  wc = ecx_FPRD(context->port, slaveh, ECT_REG_DCSYSTIME, sizeof(t1), &t1, EC_TIMEOUTRET); /* read local time of slave */
142  t1 = etohll(t1);
143 
144  /* Calculate first trigger time, always a whole multiple of CyclTime rounded up
145  plus the shifttime (can be negative)
146  This insures best sychronisation between slaves, slaves with the same CyclTime
147  will sync at the same moment (you can use CyclShift to shift the sync) */
148  if (CyclTime0 > 0)
149  {
150  t = ((t1 + SyncDelay) / CyclTime0) * CyclTime0 + CyclTime0 + CyclShift;
151  }
152  else
153  {
154  t = t1 + SyncDelay + CyclShift;
155  /* first trigger at T1 + CyclTime + SyncDelay + CyclShift in ns */
156  }
157  t = htoell(t);
158  wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCSTART0, sizeof(t), &t, EC_TIMEOUTRET); /* SYNC0 start time */
159  tc = htoel(CyclTime0);
160  wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE0, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC0 cycle time */
161  tc = htoel(CyclTime1);
162  wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE1, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC1 cycle time */
163  wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); /* activate cyclic operation */
164 }
165 
166 /* latched port time of slave */
167 static int32 ecx_porttime(ecx_contextt *context, uint16 slave, uint8 port)
168 {
169  int32 ts;
170  switch (port)
171  {
172  case 0:
173  ts = context->slavelist[slave].DCrtA;
174  break;
175  case 1:
176  ts = context->slavelist[slave].DCrtB;
177  break;
178  case 2:
179  ts = context->slavelist[slave].DCrtC;
180  break;
181  case 3:
182  ts = context->slavelist[slave].DCrtD;
183  break;
184  default:
185  ts = 0;
186  }
187  return ts;
188 }
189 
190 /* calculate previous active port of a slave */
191 static uint8 ecx_prevport(ecx_contextt *context, uint16 slave, uint8 port)
192 {
193  uint8 pport = port;
194  uint8 aport = context->slavelist[slave].activeports;
195  switch(port)
196  {
197  case 0:
198  if(aport & PORTM2)
199  pport = 2;
200  else if (aport & PORTM1)
201  pport = 1;
202  else if (aport & PORTM2)
203  pport = 3;
204  break;
205  case 1:
206  if(aport & PORTM3)
207  pport = 3;
208  else if (aport & PORTM0)
209  pport = 0;
210  else if (aport & PORTM2)
211  pport = 2;
212  break;
213  case 2:
214  if(aport & PORTM1)
215  pport = 1;
216  else if (aport & PORTM3)
217  pport = 3;
218  else if (aport & PORTM0)
219  pport = 0;
220  break;
221  case 3:
222  if(aport & PORTM0)
223  pport = 0;
224  else if (aport & PORTM2)
225  pport = 2;
226  else if (aport & PORTM1)
227  pport = 1;
228  break;
229  }
230  return pport;
231 }
232 
233 /* search unconsumed ports in parent, consume and return first open port */
234 static uint8 ecx_parentport(ecx_contextt *context, uint16 parent)
235 {
236  uint8 parentport = 0;
237  uint8 b;
238  /* search order is important, here 3 - 1 - 2 - 0 */
239  b = context->slavelist[parent].consumedports;
240  if (b & PORTM3)
241  {
242  parentport = 3;
243  b &= (uint8)~PORTM3;
244  }
245  else if (b & PORTM1)
246  {
247  parentport = 1;
248  b &= (uint8)~PORTM1;
249  }
250  else if (b & PORTM2)
251  {
252  parentport = 2;
253  b &= (uint8)~PORTM2;
254  }
255  else if (b & PORTM0)
256  {
257  parentport = 0;
258  b &= (uint8)~PORTM0;
259  }
260  context->slavelist[parent].consumedports = b;
261  return parentport;
262 }
263 
270 boolean ecx_configdc(ecx_contextt *context)
271 {
272  uint16 i, wc, slaveh, parent, child;
273  uint16 parenthold = 0;
274  uint16 prevDCslave = 0;
275  int32 ht, dt1, dt2, dt3;
276  int64 hrt;
277  uint8 entryport;
278  int8 nlist;
279  int8 plist[4];
280  int32 tlist[4];
281 
282  context->slavelist[0].hasdc = FALSE;
283  context->grouplist[0].hasdc = FALSE;
284  ht = 0;
285  ecx_BWR(context->port, 0, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET); /* latch DCrecvTimeA of all slaves */
286  for (i = 1; i <= *(context->slavecount); i++)
287  {
288  context->slavelist[i].consumedports = context->slavelist[i].activeports;
289  if (context->slavelist[i].hasdc)
290  {
291  if (!context->slavelist[0].hasdc)
292  {
293  context->slavelist[0].hasdc = TRUE;
294  context->slavelist[0].DCnext = i;
295  context->slavelist[i].DCprevious = 0;
296  context->grouplist[0].hasdc = TRUE;
297  context->grouplist[0].DCnext = i;
298  }
299  else
300  {
301  context->slavelist[prevDCslave].DCnext = i;
302  context->slavelist[i].DCprevious = prevDCslave;
303  }
304  /* this branch has DC slave so remove parenthold */
305  parenthold = 0;
306  prevDCslave = i;
307  slaveh = context->slavelist[i].configadr;
308  wc = ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET);
309  context->slavelist[i].DCrtA = etohl(ht);
310  /* 64bit latched DCrecvTimeA of each specific slave */
311  wc = ecx_FPRD(context->port, slaveh, ECT_REG_DCSOF, sizeof(hrt), &hrt, EC_TIMEOUTRET);
312  /* use it as offset in order to set local time around 0 */
313  hrt = htoell(-etohll(hrt));
314  /* save it in the offset register */
315  wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSOFFSET, sizeof(hrt), &hrt, EC_TIMEOUTRET);
316  wc = ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME1, sizeof(ht), &ht, EC_TIMEOUTRET);
317  context->slavelist[i].DCrtB = etohl(ht);
318  wc = ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME2, sizeof(ht), &ht, EC_TIMEOUTRET);
319  context->slavelist[i].DCrtC = etohl(ht);
320  wc = ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME3, sizeof(ht), &ht, EC_TIMEOUTRET);
321  context->slavelist[i].DCrtD = etohl(ht);
322 
323  /* make list of active ports and their time stamps */
324  nlist = 0;
325  if (context->slavelist[i].activeports & PORTM0)
326  {
327  plist[nlist] = 0;
328  tlist[nlist] = context->slavelist[i].DCrtA;
329  nlist++;
330  }
331  if (context->slavelist[i].activeports & PORTM3)
332  {
333  plist[nlist] = 3;
334  tlist[nlist] = context->slavelist[i].DCrtD;
335  nlist++;
336  }
337  if (context->slavelist[i].activeports & PORTM1)
338  {
339  plist[nlist] = 1;
340  tlist[nlist] = context->slavelist[i].DCrtB;
341  nlist++;
342  }
343  if (context->slavelist[i].activeports & PORTM2)
344  {
345  plist[nlist] = 2;
346  tlist[nlist] = context->slavelist[i].DCrtC;
347  nlist++;
348  }
349  /* entryport is port with the lowest timestamp */
350  entryport = 0;
351  if((nlist > 1) && (tlist[1] < tlist[entryport]))
352  {
353  entryport = 1;
354  }
355  if((nlist > 2) && (tlist[2] < tlist[entryport]))
356  {
357  entryport = 2;
358  }
359  if((nlist > 3) && (tlist[3] < tlist[entryport]))
360  {
361  entryport = 3;
362  }
363  entryport = plist[entryport];
364  context->slavelist[i].entryport = entryport;
365  /* consume entryport from activeports */
366  context->slavelist[i].consumedports &= (uint8)~(1 << entryport);
367 
368  /* finding DC parent of current */
369  parent = i;
370  do
371  {
372  child = parent;
373  parent = context->slavelist[parent].parent;
374  }
375  while (!((parent == 0) || (context->slavelist[parent].hasdc)));
376  /* only calculate propagation delay if slave is not the first */
377  if (parent > 0)
378  {
379  /* find port on parent this slave is connected to */
380  context->slavelist[i].parentport = ecx_parentport(context, parent);
381  if (context->slavelist[parent].topology == 1)
382  {
383  context->slavelist[i].parentport = context->slavelist[parent].entryport;
384  }
385 
386  dt1 = 0;
387  dt2 = 0;
388  /* delta time of (parentport - 1) - parentport */
389  /* note: order of ports is 0 - 3 - 1 -2 */
390  /* non active ports are skipped */
391  dt3 = ecx_porttime(context, parent, context->slavelist[i].parentport) -
392  ecx_porttime(context, parent,
393  ecx_prevport(context, parent, context->slavelist[i].parentport));
394  /* current slave has children */
395  /* those childrens delays need to be substacted */
396  if (context->slavelist[i].topology > 1)
397  {
398  dt1 = ecx_porttime(context, i,
399  ecx_prevport(context, i, context->slavelist[i].entryport)) -
400  ecx_porttime(context, i, context->slavelist[i].entryport);
401  }
402  /* we are only interrested in positive diference */
403  if (dt1 > dt3) dt1 = -dt1;
404  /* current slave is not the first child of parent */
405  /* previous childs delays need to be added */
406  if ((child - parent) > 1)
407  {
408  dt2 = ecx_porttime(context, parent,
409  ecx_prevport(context, parent, context->slavelist[i].parentport)) -
410  ecx_porttime(context, parent, context->slavelist[parent].entryport);
411  }
412  if (dt2 < 0) dt2 = -dt2;
413 
414  /* calculate current slave delay from delta times */
415  /* assumption : forward delay equals return delay */
416  context->slavelist[i].pdelay = ((dt3 - dt1) / 2) + dt2 +
417  context->slavelist[parent].pdelay;
418  ht = htoel(context->slavelist[i].pdelay);
419  /* write propagation delay*/
420  wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSDELAY, sizeof(ht), &ht, EC_TIMEOUTRET);
421  }
422  }
423  else
424  {
425  context->slavelist[i].DCrtA = 0;
426  context->slavelist[i].DCrtB = 0;
427  context->slavelist[i].DCrtC = 0;
428  context->slavelist[i].DCrtD = 0;
429  parent = context->slavelist[i].parent;
430  /* if non DC slave found on first position on branch hold root parent */
431  if ( (parent > 0) && (context->slavelist[parent].topology > 2))
432  parenthold = parent;
433  /* if branch has no DC slaves consume port on root parent */
434  if ( parenthold && (context->slavelist[i].topology == 1))
435  {
436  ecx_parentport(context, parenthold);
437  parenthold = 0;
438  }
439  }
440  }
441 
442  return context->slavelist[0].hasdc;
443 }
444 
445 #ifdef EC_VER1
446 void ec_dcsync0(uint16 slave, boolean act, uint32 CyclTime, uint32 CyclShift)
447 {
448  ecx_dcsync0(&ecx_context, slave, act, CyclTime, CyclShift);
449 }
450 
451 void ec_dcsync01(uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, uint32 CyclShift)
452 {
453  ecx_dcsync01(&ecx_context, slave, act, CyclTime0, CyclTime1, CyclShift);
454 }
455 
456 boolean ec_configdc(void)
457 {
458  return ecx_configdc(&ecx_context);
459 }
460 #endif
int ecx_FPWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
Definition: ethercatbase.c:409
static int32 ecx_porttime(ecx_contextt *context, uint16 slave, uint8 port)
Definition: ethercatdc.c:167
Headerfile for ethercatdc.c.
ec_slavet * slavelist
Definition: ethercatmain.h:418
#define htoel(A)
Definition: ethercattype.h:548
uint8 topology
Definition: ethercatmain.h:204
ec_groupt * grouplist
Definition: ethercatmain.h:424
void ecx_dcsync01(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, uint32 CyclShift)
Definition: ethercatdc.c:123
int32 pdelay
Definition: ethercatmain.h:224
#define PORTM3
Definition: ethercatdc.c:56
uint8_t uint8
Definition: osal.h:33
int ecx_BWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
Definition: ethercatbase.c:168
uint8 consumedports
Definition: ethercatmain.h:208
#define PORTM2
Definition: ethercatdc.c:55
uint16 DCprevious
Definition: ethercatmain.h:228
uint16_t uint16
Definition: osal.h:34
uint16 DCnext
Definition: ethercatmain.h:283
void ecx_dcsync0(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime, uint32 CyclShift)
Definition: ethercatdc.c:70
General typedefs and defines for EtherCAT.
#define TRUE
Definition: osal.h:28
uint16 parent
Definition: ethercatmain.h:210
int32 DCrtA
Definition: ethercatmain.h:216
boolean hasdc
Definition: ethercatmain.h:281
boolean hasdc
Definition: ethercatmain.h:200
int64_t int64
Definition: osal.h:36
#define FALSE
Definition: osal.h:29
ecx_portt * port
Definition: ethercatmain.h:416
uint8 entryport
Definition: ethercatmain.h:214
int32_t int32
Definition: osal.h:32
int32 DCrtD
Definition: ethercatmain.h:222
int32 DCrtC
Definition: ethercatmain.h:220
int8_t int8
Definition: osal.h:30
static uint8 ecx_parentport(ecx_contextt *context, uint16 parent)
Definition: ethercatdc.c:234
#define htoell(A)
Definition: ethercattype.h:549
#define PORTM1
Definition: ethercatdc.c:54
#define EC_TIMEOUTRET
Definition: ethercattype.h:90
static uint8 ecx_prevport(ecx_contextt *context, uint16 slave, uint8 port)
Definition: ethercatdc.c:191
boolean ecx_configdc(ecx_contextt *context)
Definition: ethercatdc.c:270
Headerfile for ethercatbase.c.
#define etohl(A)
Definition: ethercattype.h:551
#define etohll(A)
Definition: ethercattype.h:552
Headerfile for ethercatmain.c.
uint32_t uint32
Definition: osal.h:35
int32 DCrtB
Definition: ethercatmain.h:218
uint16 DCnext
Definition: ethercatmain.h:226
#define SyncDelay
Definition: ethercatdc.c:59
uint8 parentport
Definition: ethercatmain.h:212
uint8 activeports
Definition: ethercatmain.h:206
uint16 configadr
Definition: ethercatmain.h:144
int ecx_FPRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
Definition: ethercatbase.c:328
int * slavecount
Definition: ethercatmain.h:420
#define PORTM0
Definition: ethercatdc.c:53


youbot_driver
Author(s): Jan Paulus
autogenerated on Mon Jun 10 2019 15:46:24