ethercatdc.c
Go to the documentation of this file.
1 /*
2  * Licensed under the GNU General Public License version 2 with exceptions. See
3  * LICENSE file in the project root for full license information
4  */
5 
11 #include "oshw.h"
12 #include "osal.h"
13 #include "ethercattype.h"
14 #include "ethercatbase.h"
15 #include "ethercatmain.h"
16 #include "ethercatdc.h"
17 
18 #define PORTM0 0x01
19 #define PORTM1 0x02
20 #define PORTM2 0x04
21 #define PORTM3 0x08
22 
24 #define SyncDelay ((int32)100000000)
25 
35 void ecx_dcsync0(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift)
36 {
37  uint8 h, RA;
38  uint16 slaveh;
39  int64 t, t1;
40  int32 tc;
41 
42  slaveh = context->slavelist[slave].configadr;
43  RA = 0;
44 
45  /* stop cyclic operation, ready for next trigger */
46  (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET);
47  if (act)
48  {
49  RA = 1 + 2; /* act cyclic operation and sync0, sync1 deactivated */
50  }
51  h = 0;
52  (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCUC, sizeof(h), &h, EC_TIMEOUTRET); /* write access to ethercat */
53  t1 = 0;
54  (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSYSTIME, sizeof(t1), &t1, EC_TIMEOUTRET); /* read local time of slave */
55  t1 = etohll(t1);
56 
57  /* Calculate first trigger time, always a whole multiple of CyclTime rounded up
58  plus the shifttime (can be negative)
59  This insures best synchronization between slaves, slaves with the same CyclTime
60  will sync at the same moment (you can use CyclShift to shift the sync) */
61  if (CyclTime > 0)
62  {
63  t = ((t1 + SyncDelay) / CyclTime) * CyclTime + CyclTime + CyclShift;
64  }
65  else
66  {
67  t = t1 + SyncDelay + CyclShift;
68  /* first trigger at T1 + CyclTime + SyncDelay + CyclShift in ns */
69  }
70  t = htoell(t);
71  (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSTART0, sizeof(t), &t, EC_TIMEOUTRET); /* SYNC0 start time */
72  tc = htoel(CyclTime);
73  (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE0, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC0 cycle time */
74  (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); /* activate cyclic operation */
75 
76  // update ec_slave state
77  context->slavelist[slave].DCactive = (uint8)act;
78  context->slavelist[slave].DCshift = CyclShift;
79  context->slavelist[slave].DCcycle = CyclTime;
80 }
81 
94 void ecx_dcsync01(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift)
95 {
96  uint8 h, RA;
97  uint16 slaveh;
98  int64 t, t1;
99  int32 tc;
100  uint32 TrueCyclTime;
101 
102  /* Sync1 can be used as a multiple of Sync0, use true cycle time */
103  TrueCyclTime = ((CyclTime1 / CyclTime0) + 1) * CyclTime0;
104 
105  slaveh = context->slavelist[slave].configadr;
106  RA = 0;
107 
108  /* stop cyclic operation, ready for next trigger */
109  (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET);
110  if (act)
111  {
112  RA = 1 + 2 + 4; /* act cyclic operation and sync0 + sync1 */
113  }
114  h = 0;
115  (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCUC, sizeof(h), &h, EC_TIMEOUTRET); /* write access to ethercat */
116  t1 = 0;
117  (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSYSTIME, sizeof(t1), &t1, EC_TIMEOUTRET); /* read local time of slave */
118  t1 = etohll(t1);
119 
120  /* Calculate first trigger time, always a whole multiple of TrueCyclTime rounded up
121  plus the shifttime (can be negative)
122  This insures best synchronization between slaves, slaves with the same CyclTime
123  will sync at the same moment (you can use CyclShift to shift the sync) */
124  if (CyclTime0 > 0)
125  {
126  t = ((t1 + SyncDelay) / TrueCyclTime) * TrueCyclTime + TrueCyclTime + CyclShift;
127  }
128  else
129  {
130  t = t1 + SyncDelay + CyclShift;
131  /* first trigger at T1 + CyclTime + SyncDelay + CyclShift in ns */
132  }
133  t = htoell(t);
134  (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSTART0, sizeof(t), &t, EC_TIMEOUTRET); /* SYNC0 start time */
135  tc = htoel(CyclTime0);
136  (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE0, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC0 cycle time */
137  tc = htoel(CyclTime1);
138  (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE1, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC1 cycle time */
139  (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); /* activate cyclic operation */
140 
141  // update ec_slave state
142  context->slavelist[slave].DCactive = (uint8)act;
143  context->slavelist[slave].DCshift = CyclShift;
144  context->slavelist[slave].DCcycle = CyclTime0;
145 }
146 
147 /* latched port time of slave */
149 {
150  int32 ts;
151  switch (port)
152  {
153  case 0:
154  ts = context->slavelist[slave].DCrtA;
155  break;
156  case 1:
157  ts = context->slavelist[slave].DCrtB;
158  break;
159  case 2:
160  ts = context->slavelist[slave].DCrtC;
161  break;
162  case 3:
163  ts = context->slavelist[slave].DCrtD;
164  break;
165  default:
166  ts = 0;
167  break;
168  }
169  return ts;
170 }
171 
172 /* calculate previous active port of a slave */
174 {
175  uint8 pport = port;
176  uint8 aport = context->slavelist[slave].activeports;
177  switch(port)
178  {
179  case 0:
180  if(aport & PORTM2)
181  pport = 2;
182  else if (aport & PORTM1)
183  pport = 1;
184  else if (aport & PORTM3)
185  pport = 3;
186  break;
187  case 1:
188  if(aport & PORTM3)
189  pport = 3;
190  else if (aport & PORTM0)
191  pport = 0;
192  else if (aport & PORTM2)
193  pport = 2;
194  break;
195  case 2:
196  if(aport & PORTM1)
197  pport = 1;
198  else if (aport & PORTM3)
199  pport = 3;
200  else if (aport & PORTM0)
201  pport = 0;
202  break;
203  case 3:
204  if(aport & PORTM0)
205  pport = 0;
206  else if (aport & PORTM2)
207  pport = 2;
208  else if (aport & PORTM1)
209  pport = 1;
210  break;
211  }
212  return pport;
213 }
214 
215 /* search unconsumed ports in parent, consume and return first open port */
216 static uint8 ecx_parentport(ecx_contextt *context, uint16 parent)
217 {
218  uint8 parentport = 0;
219  uint8 b;
220  /* search order is important, here 3 - 1 - 2 - 0 */
221  b = context->slavelist[parent].consumedports;
222  if (b & PORTM3)
223  {
224  parentport = 3;
225  b &= (uint8)~PORTM3;
226  }
227  else if (b & PORTM1)
228  {
229  parentport = 1;
230  b &= (uint8)~PORTM1;
231  }
232  else if (b & PORTM2)
233  {
234  parentport = 2;
235  b &= (uint8)~PORTM2;
236  }
237  else if (b & PORTM0)
238  {
239  parentport = 0;
240  b &= (uint8)~PORTM0;
241  }
242  context->slavelist[parent].consumedports = b;
243  return parentport;
244 }
245 
252 boolean ecx_configdc(ecx_contextt *context)
253 {
254  uint16 i, slaveh, parent, child;
255  uint16 parenthold = 0;
256  uint16 prevDCslave = 0;
257  int32 ht, dt1, dt2, dt3;
258  int64 hrt;
259  uint8 entryport;
260  int8 nlist;
261  int8 plist[4];
262  int32 tlist[4];
263  ec_timet mastertime;
264  uint64 mastertime64;
265 
266  context->slavelist[0].hasdc = FALSE;
267  context->grouplist[0].hasdc = FALSE;
268  ht = 0;
269 
270  ecx_BWR(context->port, 0, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET); /* latch DCrecvTimeA of all slaves */
271  mastertime = osal_current_time();
272  mastertime.sec -= 946684800UL; /* EtherCAT uses 2000-01-01 as epoch start instead of 1970-01-01 */
273  mastertime64 = (((uint64)mastertime.sec * 1000000) + (uint64)mastertime.usec) * 1000;
274  for (i = 1; i <= *(context->slavecount); i++)
275  {
276  context->slavelist[i].consumedports = context->slavelist[i].activeports;
277  if (context->slavelist[i].hasdc)
278  {
279  if (!context->slavelist[0].hasdc)
280  {
281  context->slavelist[0].hasdc = TRUE;
282  context->slavelist[0].DCnext = i;
283  context->slavelist[i].DCprevious = 0;
284  context->grouplist[context->slavelist[i].group].hasdc = TRUE;
285  context->grouplist[context->slavelist[i].group].DCnext = i;
286  }
287  else
288  {
289  context->slavelist[prevDCslave].DCnext = i;
290  context->slavelist[i].DCprevious = prevDCslave;
291  }
292  /* this branch has DC slave so remove parenthold */
293  parenthold = 0;
294  prevDCslave = i;
295  slaveh = context->slavelist[i].configadr;
296  (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET);
297  context->slavelist[i].DCrtA = etohl(ht);
298  /* 64bit latched DCrecvTimeA of each specific slave */
299  (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSOF, sizeof(hrt), &hrt, EC_TIMEOUTRET);
300  /* use it as offset in order to set local time around 0 + mastertime */
301  hrt = htoell(-etohll(hrt) + mastertime64);
302  /* save it in the offset register */
303  (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSOFFSET, sizeof(hrt), &hrt, EC_TIMEOUTRET);
304  (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME1, sizeof(ht), &ht, EC_TIMEOUTRET);
305  context->slavelist[i].DCrtB = etohl(ht);
306  (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME2, sizeof(ht), &ht, EC_TIMEOUTRET);
307  context->slavelist[i].DCrtC = etohl(ht);
308  (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME3, sizeof(ht), &ht, EC_TIMEOUTRET);
309  context->slavelist[i].DCrtD = etohl(ht);
310 
311  /* make list of active ports and their time stamps */
312  nlist = 0;
313  if (context->slavelist[i].activeports & PORTM0)
314  {
315  plist[nlist] = 0;
316  tlist[nlist] = context->slavelist[i].DCrtA;
317  nlist++;
318  }
319  if (context->slavelist[i].activeports & PORTM3)
320  {
321  plist[nlist] = 3;
322  tlist[nlist] = context->slavelist[i].DCrtD;
323  nlist++;
324  }
325  if (context->slavelist[i].activeports & PORTM1)
326  {
327  plist[nlist] = 1;
328  tlist[nlist] = context->slavelist[i].DCrtB;
329  nlist++;
330  }
331  if (context->slavelist[i].activeports & PORTM2)
332  {
333  plist[nlist] = 2;
334  tlist[nlist] = context->slavelist[i].DCrtC;
335  nlist++;
336  }
337  /* entryport is port with the lowest timestamp */
338  entryport = 0;
339  if((nlist > 1) && (tlist[1] < tlist[entryport]))
340  {
341  entryport = 1;
342  }
343  if((nlist > 2) && (tlist[2] < tlist[entryport]))
344  {
345  entryport = 2;
346  }
347  if((nlist > 3) && (tlist[3] < tlist[entryport]))
348  {
349  entryport = 3;
350  }
351  entryport = plist[entryport];
352  context->slavelist[i].entryport = entryport;
353  /* consume entryport from activeports */
354  context->slavelist[i].consumedports &= (uint8)~(1 << entryport);
355 
356  /* finding DC parent of current */
357  parent = i;
358  do
359  {
360  child = parent;
361  parent = context->slavelist[parent].parent;
362  }
363  while (!((parent == 0) || (context->slavelist[parent].hasdc)));
364  /* only calculate propagation delay if slave is not the first */
365  if (parent > 0)
366  {
367  /* find port on parent this slave is connected to */
368  context->slavelist[i].parentport = ecx_parentport(context, parent);
369  if (context->slavelist[parent].topology == 1)
370  {
371  context->slavelist[i].parentport = context->slavelist[parent].entryport;
372  }
373 
374  dt1 = 0;
375  dt2 = 0;
376  /* delta time of (parentport - 1) - parentport */
377  /* note: order of ports is 0 - 3 - 1 -2 */
378  /* non active ports are skipped */
379  dt3 = ecx_porttime(context, parent, context->slavelist[i].parentport) -
380  ecx_porttime(context, parent,
381  ecx_prevport(context, parent, context->slavelist[i].parentport));
382  /* current slave has children */
383  /* those children's delays need to be subtracted */
384  if (context->slavelist[i].topology > 1)
385  {
386  dt1 = ecx_porttime(context, i,
387  ecx_prevport(context, i, context->slavelist[i].entryport)) -
388  ecx_porttime(context, i, context->slavelist[i].entryport);
389  }
390  /* we are only interested in positive difference */
391  if (dt1 > dt3) dt1 = -dt1;
392  /* current slave is not the first child of parent */
393  /* previous child's delays need to be added */
394  if ((child - parent) > 1)
395  {
396  dt2 = ecx_porttime(context, parent,
397  ecx_prevport(context, parent, context->slavelist[i].parentport)) -
398  ecx_porttime(context, parent, context->slavelist[parent].entryport);
399  }
400  if (dt2 < 0) dt2 = -dt2;
401 
402  /* calculate current slave delay from delta times */
403  /* assumption : forward delay equals return delay */
404  context->slavelist[i].pdelay = ((dt3 - dt1) / 2) + dt2 +
405  context->slavelist[parent].pdelay;
406  ht = htoel(context->slavelist[i].pdelay);
407  /* write propagation delay*/
408  (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSDELAY, sizeof(ht), &ht, EC_TIMEOUTRET);
409  }
410  }
411  else
412  {
413  context->slavelist[i].DCrtA = 0;
414  context->slavelist[i].DCrtB = 0;
415  context->slavelist[i].DCrtC = 0;
416  context->slavelist[i].DCrtD = 0;
417  parent = context->slavelist[i].parent;
418  /* if non DC slave found on first position on branch hold root parent */
419  if ( (parent > 0) && (context->slavelist[parent].topology > 2))
420  parenthold = parent;
421  /* if branch has no DC slaves consume port on root parent */
422  if ( parenthold && (context->slavelist[i].topology == 1))
423  {
424  ecx_parentport(context, parenthold);
425  parenthold = 0;
426  }
427  }
428  }
429 
430  return context->slavelist[0].hasdc;
431 }
432 
433 #ifdef EC_VER1
434 void ec_dcsync0(uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift)
435 {
436  ecx_dcsync0(&ecx_context, slave, act, CyclTime, CyclShift);
437 }
438 
439 void ec_dcsync01(uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift)
440 {
441  ecx_dcsync01(&ecx_context, slave, act, CyclTime0, CyclTime1, CyclShift);
442 }
443 
444 boolean ec_configdc(void)
445 {
446  return ecx_configdc(&ecx_context);
447 }
448 #endif
ec_configdc
boolean ec_configdc(void)
Definition: ethercatdc.c:444
slave
int slave
Definition: aliastool.c:44
ecx_porttime
static int32 ecx_porttime(ecx_contextt *context, uint16 slave, uint8 port)
Definition: ethercatdc.c:148
ec_slave::consumedports
uint8 consumedports
Definition: ethercatmain.h:177
ethercatmain.h
Headerfile for ethercatmain.c.
ec_slave::topology
uint8 topology
Definition: ethercatmain.h:173
ECT_REG_DCSYSDELAY
@ ECT_REG_DCSYSDELAY
Definition: ethercattype.h:438
ECT_REG_DCSYSOFFSET
@ ECT_REG_DCSYSOFFSET
Definition: ethercattype.h:437
ec_group::DCnext
uint16 DCnext
Definition: ethercatmain.h:254
ECT_REG_DCSYSTIME
@ ECT_REG_DCSYSTIME
Definition: ethercattype.h:435
osal_current_time
ec_timet osal_current_time(void)
Definition: erika/osal.c:37
uint32
uint32_t uint32
Definition: osal.h:30
ec_slave::DCrtA
int32 DCrtA
Definition: ethercatmain.h:185
ECT_REG_DCCYCLE0
@ ECT_REG_DCCYCLE0
Definition: ethercattype.h:445
ecx_context::port
ecx_portt * port
Definition: ethercatmain.h:387
htoell
#define htoell(A)
Definition: ethercattype.h:535
SyncDelay
#define SyncDelay
Definition: ethercatdc.c:24
int64
int64_t int64
Definition: osal.h:31
ec_slave::group
uint8 group
Definition: ethercatmain.h:225
ec_slave::parentport
uint8 parentport
Definition: ethercatmain.h:181
ec_slave::DCrtC
int32 DCrtC
Definition: ethercatmain.h:189
ec_slave::DCshift
int32 DCshift
Definition: ethercatmain.h:201
int32
int32_t int32
Definition: osal.h:27
uint8
uint8_t uint8
Definition: osal.h:28
uint64
uint64_t uint64
Definition: osal.h:32
ethercatbase.h
Headerfile for ethercatbase.c.
ec_slave::DCcycle
int32 DCcycle
Definition: ethercatmain.h:199
ec_slave::parent
uint16 parent
Definition: ethercatmain.h:179
ec_dcsync01
void ec_dcsync01(uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift)
Definition: ethercatdc.c:439
EC_TIMEOUTRET
#define EC_TIMEOUTRET
Definition: ethercattype.h:64
ecx_parentport
static uint8 ecx_parentport(ecx_contextt *context, uint16 parent)
Definition: ethercatdc.c:216
osal.h
ecx_dcsync0
void ecx_dcsync0(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift)
Definition: ethercatdc.c:35
ecx_configdc
boolean ecx_configdc(ecx_contextt *context)
Definition: ethercatdc.c:252
PORTM3
#define PORTM3
Definition: ethercatdc.c:21
ecx_BWR
int ecx_BWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
Definition: ethercatbase.c:158
ec_slave::configadr
uint16 configadr
Definition: ethercatmain.h:113
etohll
#define etohll(A)
Definition: ethercattype.h:538
ECT_REG_DCCYCLE1
@ ECT_REG_DCCYCLE1
Definition: ethercattype.h:446
ethercatdc.h
Headerfile for ethercatdc.c.
uint16
uint16_t uint16
Definition: osal.h:29
PORTM1
#define PORTM1
Definition: ethercatdc.c:19
ecx_prevport
static uint8 ecx_prevport(ecx_contextt *context, uint16 slave, uint8 port)
Definition: ethercatdc.c:173
ecx_context::grouplist
ec_groupt * grouplist
Definition: ethercatmain.h:395
ec_slave::DCrtB
int32 DCrtB
Definition: ethercatmain.h:187
TRUE
#define TRUE
Definition: osal.h:19
ecx_context::slavelist
ec_slavet * slavelist
Definition: ethercatmain.h:389
FALSE
#define FALSE
Definition: osal.h:22
ec_slave::hasdc
boolean hasdc
Definition: ethercatmain.h:169
ecx_context
Definition: ethercatmain.h:384
ecx_FPRD
int ecx_FPRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
Definition: ethercatbase.c:318
ec_slave::entryport
uint8 entryport
Definition: ethercatmain.h:183
ec_slave::DCprevious
uint16 DCprevious
Definition: ethercatmain.h:197
ECT_REG_DCSTART0
@ ECT_REG_DCSTART0
Definition: ethercattype.h:444
ECT_REG_DCCUC
@ ECT_REG_DCCUC
Definition: ethercattype.h:442
htoel
#define htoel(A)
Definition: ethercattype.h:534
ECT_REG_DCSOF
@ ECT_REG_DCSOF
Definition: ethercattype.h:436
ecx_dcsync01
void ecx_dcsync01(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift)
Definition: ethercatdc.c:94
PORTM2
#define PORTM2
Definition: ethercatdc.c:20
ecx_context::slavecount
int * slavecount
Definition: ethercatmain.h:391
ECT_REG_DCTIME0
@ ECT_REG_DCTIME0
Definition: ethercattype.h:431
ECT_REG_DCTIME2
@ ECT_REG_DCTIME2
Definition: ethercattype.h:433
ECT_REG_DCTIME3
@ ECT_REG_DCTIME3
Definition: ethercattype.h:434
ec_timet::usec
uint32 usec
Definition: osal.h:39
ECT_REG_DCSYNCACT
@ ECT_REG_DCSYNCACT
Definition: ethercattype.h:443
PORTM0
#define PORTM0
Definition: ethercatdc.c:18
ec_slave::DCnext
uint16 DCnext
Definition: ethercatmain.h:195
ec_slave::DCactive
uint8 DCactive
Definition: ethercatmain.h:203
ECT_REG_DCTIME1
@ ECT_REG_DCTIME1
Definition: ethercattype.h:432
ethercattype.h
General typedefs and defines for EtherCAT.
etohl
#define etohl(A)
Definition: ethercattype.h:537
ec_slave::pdelay
int32 pdelay
Definition: ethercatmain.h:193
ec_slave::activeports
uint8 activeports
Definition: ethercatmain.h:175
ec_slave::DCrtD
int32 DCrtD
Definition: ethercatmain.h:191
ec_timet
Definition: osal.h:36
int8
int8_t int8
Definition: osal.h:25
ec_group::hasdc
boolean hasdc
Definition: ethercatmain.h:252
ecx_FPWR
int ecx_FPWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
Definition: ethercatbase.c:399
ec_dcsync0
void ec_dcsync0(uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift)
Definition: ethercatdc.c:434
ec_timet::sec
uint32 sec
Definition: osal.h:38


soem
Author(s): Arthur Ketels and M.J.G. van den Molengraft
autogenerated on Wed Mar 2 2022 01:01:48