IGH_DC同步程序设计方案及接口函数介绍

设计方案

从站作为参考时钟

  1. 激活从站的DC功能。

    使用ecrt_slave_config_dc(ec_slave_config_t *sc, uint16_t assign_activate, uint32_t sync0_cycle, int32_t sync0_shift, uint32_t sync1_cycle, int32_t sync1_shift)

  2. 指定一个从站作为参考时钟,一般选用接入主站拓扑中的第一个带有DC功能的从站

    使用ecrt_master_select_reference_clock(ec_master_t *master, ec_slave_config_t *sc)

  3. 在每个通信周期里,将主站时钟,以及其他从站时钟都同步到参考时钟

    1. 每个周期,主站首先更新本地时钟

      使用ecrt_master_application_time(ec_master_t *master, uint64_t app_time)

    2. 然后主站获取参考时钟并保存在time中(注意主站时钟同步到参考时钟的程序需用户自行完成,IGH没有提供相应接口)

      使用ecrt_master_reference_clock_time(ec_master_t *master, uint32_t *time)

    3. 最后将参考时钟发送给其他从站,使其他从站时钟同步到参考时钟

      使用ecrt_master_sync_slave_clocks(ec_master_t *master)

主站作为参考时钟

  1. 激活从站DC功能

    使用ecrt_slave_config_dc(ec_slave_config_t *sc, uint16_t assign_activate, uint32_t sync0_cycle, int32_t sync0_shift, uint32_t sync1_cycle, int32_t sync1_shift)

  2. 将第一个带DC功能的从站设置为参考时钟

    使用ecrt_master_select_reference_clock(ec_master_t *master, ec_slave_config_t *sc)

  3. 每个周期里,将参考时钟的值改为主站时钟的值,并将改后的参考时钟发送给其他从站

    1. 主站首先更新本地时钟

      使用ecrt_master_application_time(ec_master_t *master, uint64_t app_time)

    2. 修改参考时钟,改为主站时钟的值

      使用ecrt_master_sync_reference_clock_to(ec_master_t *master, uint64_t sync_time)

      ecrt_master_sync_reference_clock(ec_master_t *master)

    3. 将参考时钟发送给其他从站,使其他从站时钟同步到参考时钟

      使用ecrt_master_sync_slave_clocks(ec_master_t *master)

接口函数

ecrt_slave_config_dc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/** Configure distributed clocks.
* 配置分布式时钟(DC)
*
* Sets the AssignActivate word and the cycle and shift times for the sync
* signals.
* 设置激活字以及同步信号的周期和偏移时间
*
* The AssignActivate word is vendor-specific and can be taken from the XML
* device description file (Device -> Dc -> AssignActivate). Set this to zero,
* if the slave shall be operated without distributed clocks (default).
* 激活字是厂商指定的,可以在ESI(Device -> Dc -> AssignActivate)中查询。
* 激活字用于设置从站的0x0980和0x0981两个寄存器的值,这两个寄存器正是同步信号控制器相关的寄存器。
* 大多数厂商的激活字都设置为0x0300,根据两个寄存器中每一位的置位功能分析出,设置0x0300功能为激活SYNC0信号。
* (0x0500为激活SYNC1信号,0x0700为激活SYNC0和SYNC1信号)
* 后四个参数用于设置SYNC0和SYNC1信号的周期和偏移时间,单位纳秒。
*
* This method has to be called in non-realtime context before
* ecrt_master_activate().
*
* \attention The \a sync1_shift time is ignored.
*/
void ecrt_slave_config_dc(
ec_slave_config_t *sc, /**< Slave configuration. */
uint16_t assign_activate, /**< AssignActivate word. */
uint32_t sync0_cycle, /**< SYNC0 cycle time [ns]. */
int32_t sync0_shift, /**< SYNC0 shift time [ns]. */
uint32_t sync1_cycle, /**< SYNC1 cycle time [ns]. */
int32_t sync1_shift /**< SYNC1 shift time [ns]. */
)
{
EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, assign_activate = 0x%04X,"
" sync0_cycle = %u, sync0_shift = %i,"
" sync1_cycle = %u, sync1_shift = %i\n",
__func__, sc, assign_activate, sync0_cycle_time, sync0_shift_time,
sync1_cycle_time, sync1_shift_time);

sc->dc_assign_activate = assign_activate;
sc->dc_sync[0].cycle_time = sync0_cycle_time;
sc->dc_sync[0].shift_time = sync0_shift_time;
sc->dc_sync[1].cycle_time = sync1_cycle_time;
sc->dc_sync[1].shift_time = sync1_shift_time;
}

ecrt_master_select_reference_clock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/** Selects the reference clock for distributed clocks.
* 给分布式时钟系统选择参考时钟
*
* If this method is not called for a certain master, or if the slave
* configuration pointer is NULL, then the first slave with DC functionality
* will provide the reference clock.
* 如果此方法没有被主站调用,或从站指针为NULL,则默认选择第一个具有DC功能的从站提供参考时钟
*
* \return 0 on success, otherwise negative error code.
* 返回0为成功,失败则返回负数错误代码
*/
int ecrt_master_select_reference_clock(
ec_master_t *master, /**< EtherCAT master. */
ec_slave_config_t *sc /**< Slave config of the slave to use as the
* reference slave (or NULL). */
)
{
master->dc_ref_config = sc;

if (master->dc_ref_config) {
EC_MASTER_INFO(master, "Application selected DC reference clock"
" config (%u-%u) set by application.\n",
master->dc_ref_config->alias,
master->dc_ref_config->position);
}
else {
EC_MASTER_INFO(master, "Application selected DC reference clock"
" config cleared by application.\n");
}

// update dc datagrams
ec_master_find_dc_ref_clock(master);

return 0;
}

ecrt_master_application_time

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/** Sets the application time.设置应用时间
*
* The master has to know the application's time when operating slaves with
* distributed clocks. The time is not incremented by the master itself, so
* this method has to be called cyclically.
*
*
* \attention The time passed to this method is used to calculate the phase of
* the slaves' SYNC0/1 interrupts. It should be called constantly at the same
* point of the realtime cycle. So it is recommended to call it at the start
* of the calculations to avoid deviancies due to changing execution times.
*
* The time is used when setting the slaves' <tt>System Time Offset</tt> and
* <tt>Cyclic Operation Start Time</tt> registers and when synchronizing the
* DC reference clock to the application time via
* ecrt_master_sync_reference_clock().
*
* The time is defined as nanoseconds from 2000-01-01 00:00. Converting an
* epoch time can be done with the EC_TIMEVAL2NANO() macro, but is not
* necessary, since the absolute value is not of any interest.
*/
void ecrt_master_application_time(
ec_master_t *master, /**< EtherCAT master. */
uint64_t app_time /**< Application time. */
)
{
master->app_time = app_time;

if (unlikely(!master->dc_ref_time)) {
master->dc_ref_time = app_time;
}
}

ecrt_master_reference_clock_time

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/** Get the lower 32 bit of the reference clock system time.
* 获取参考时钟的低32位
*
* This method can be used to synchronize the master to the reference clock.
* 可以用于将主站时钟同步到参考时钟
*
* The reference clock system time is queried via the
* ecrt_master_sync_slave_clocks() method, that reads the system time of the
* reference clock and writes it to the slave clocks (so be sure to call it
* cyclically to get valid data).
*
* \attention The returned time is the system time of the reference clock
* minus the transmission delay of the reference clock.
*
* \retval 0 success, system time was written into \a time.
* \retval -ENXIO No reference clock found.
* \retval -EIO Slave synchronization datagram was not received.
*/
int ecrt_master_reference_clock_time(
ec_master_t *master, /**< EtherCAT master. */
uint32_t *time /**< Pointer to store the queried system time. */
)
{
if (!master->dc_ref_clock) {
return -ENXIO;
}

if (master->sync_datagram.state != EC_DATAGRAM_RECEIVED) {
return -EIO;
}

if (!master->dc_offset_valid) {
return -EAGAIN;
}

// Get returned datagram time, transmission delay removed.
*time = EC_READ_U32(master->sync_datagram.data) -
master->dc_ref_clock->transmission_delay;

return 0;
}

ecrt_master_sync_slave_clocks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/** Queues the DC clock drift compensation datagram for sending.
* 将DC漂移补偿数据报排队发送
*
* All slave clocks synchronized to the reference clock.
* 使所有从站时钟同步到参考时钟
*/
void ecrt_master_sync_slave_clocks(
ec_master_t *master /**< EtherCAT master. */
)
{
if (master->dc_ref_clock && master->dc_offset_valid) {
ec_datagram_zero(&master->sync_datagram);
ec_master_queue_datagram(master, &master->sync_datagram);
}
}

ecrt_master_sync_reference_clock_to

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/** Queues the DC reference clock drift compensation datagram for sending.
* 将DC参考时钟漂移补偿数据报排队发送
*
* The reference clock will by synchronized to the time passed in the
* sync_time parameter.
* 参考时钟会被同步到第二个参数的值
*/
void ecrt_master_sync_reference_clock_to(
ec_master_t *master, /**< EtherCAT master. */
uint64_t sync_time /**< Sync reference clock to this time. */
)
{
if (master->dc_ref_clock) {
EC_WRITE_U32(master->ref_sync_datagram.data, sync_time);
ec_master_queue_datagram(master, &master->ref_sync_datagram);
}
}

ecrt_master_sync_reference_clock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/** Queues the DC reference clock drift compensation datagram for sending.
*
* The reference clock will by synchronized to the application time provided
* by the last call off ecrt_master_application_time().
* 与上一个函数功能类似,区别是不需要指定时间值,自动选择ecrt_master_application_time()获取的值。
*/
void ecrt_master_sync_reference_clock(
ec_master_t *master /**< EtherCAT master. */
)
{
if (master->dc_ref_clock && master->dc_offset_valid) {
EC_WRITE_U32(master->ref_sync_datagram.data, master->app_time);
ec_master_queue_datagram(master, &master->ref_sync_datagram);
}
}

程序示例

  • 从站:两个LAN9252 EtherCAT开发板
  • 功能:流水灯
  • 同步精度:从站作为参考时钟,同步精度达到20ns左右,收敛时间约为3分钟
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <sys/mman.h>
#include <malloc.h>

#include "ecrt.h"

/*****************************************************************************/
// ethercat
static ec_master_t *master = NULL;
static ec_domain_t *domain1 = NULL;
static ec_slave_config_t *slave0 = NULL;
static ec_slave_config_t *slave1 = NULL;

/*****************************************************************************/
// application parameters
//#define CLOCK_TO_USE CLOCK_MONOTONIC
#define CLOCK_TO_USE CLOCK_REALTIME

#define cycle_t 10000000 /*本次设置周期PERIOD_NS为10ms*/

#define NSEC_PER_SEC (1000000000L)
#define TIMESPEC2NS(T) ((uint64_t) (T).tv_sec * NSEC_PER_SEC + (T).tv_nsec)

#define SLAVE_REF 1 // 选择从站作为参考时钟:1 选择主站作为参考时钟:0

const struct timespec cycletime = {0, cycle_t};
struct timespec wakeupTime; // 唤醒时间
struct timespec sync_time; // 用于将主站时间写入从站

int cycle_times = 0; // 循环次数
int LED[8] = {1,1,1,1,1,1,1,1};
int led = 0;

uint32_t diff_time_ns; // 最大时钟偏移值,单位ns

struct timeval start,end; // 用于记录程序运行时间

FILE *fp = NULL; // 输出数据到csv文件

/*****************************************************************************/
// process data
#define PANASONIC_0 0,0 /*EtherCAT address on the bus*/
#define PANASONIC_1 0,1 /*EtherCAT address on the bus*/
#define VID_PID 0x00000009, 0x00009252 /*Vendor ID, product code*/

uint8_t *domain1_pd = NULL;
unsigned int slave0_6000_01;
unsigned int slave0_6000_01_bit;
unsigned int slave0_6000_02;
unsigned int slave0_6000_02_bit;
unsigned int slave0_6000_03;
unsigned int slave0_6000_03_bit;
unsigned int slave0_6000_04;
unsigned int slave0_6000_04_bit;
unsigned int slave0_6000_05;
unsigned int slave0_6000_05_bit;
unsigned int slave0_6000_06;
unsigned int slave0_6000_06_bit;
unsigned int slave0_6000_07;
unsigned int slave0_6000_07_bit;
unsigned int slave0_6000_08;
unsigned int slave0_6000_08_bit;
unsigned int slave0_7010_01;
unsigned int slave0_7010_01_bit;
unsigned int slave0_7010_02;
unsigned int slave0_7010_02_bit;
unsigned int slave0_7010_03;
unsigned int slave0_7010_03_bit;
unsigned int slave0_7010_04;
unsigned int slave0_7010_04_bit;
unsigned int slave0_7010_05;
unsigned int slave0_7010_05_bit;
unsigned int slave0_7010_06;
unsigned int slave0_7010_06_bit;
unsigned int slave0_7010_07;
unsigned int slave0_7010_07_bit;
unsigned int slave0_7010_08;
unsigned int slave0_7010_08_bit;
unsigned int slave1_6000_01;
unsigned int slave1_6000_01_bit;
unsigned int slave1_6000_02;
unsigned int slave1_6000_02_bit;
unsigned int slave1_6000_03;
unsigned int slave1_6000_03_bit;
unsigned int slave1_6000_04;
unsigned int slave1_6000_04_bit;
unsigned int slave1_6000_05;
unsigned int slave1_6000_05_bit;
unsigned int slave1_6000_06;
unsigned int slave1_6000_06_bit;
unsigned int slave1_6000_07;
unsigned int slave1_6000_07_bit;
unsigned int slave1_6000_08;
unsigned int slave1_6000_08_bit;
unsigned int slave1_7010_01;
unsigned int slave1_7010_01_bit;
unsigned int slave1_7010_02;
unsigned int slave1_7010_02_bit;
unsigned int slave1_7010_03;
unsigned int slave1_7010_03_bit;
unsigned int slave1_7010_04;
unsigned int slave1_7010_04_bit;
unsigned int slave1_7010_05;
unsigned int slave1_7010_05_bit;
unsigned int slave1_7010_06;
unsigned int slave1_7010_06_bit;
unsigned int slave1_7010_07;
unsigned int slave1_7010_07_bit;
unsigned int slave1_7010_08;
unsigned int slave1_7010_08_bit;

const static ec_pdo_entry_reg_t domain1_regs[] = {
{PANASONIC_0, VID_PID, 0x6000, 1, &slave0_6000_01, &slave0_6000_01_bit},
{PANASONIC_0, VID_PID, 0x6000, 2, &slave0_6000_02, &slave0_6000_02_bit},
{PANASONIC_0, VID_PID, 0x6000, 3, &slave0_6000_03, &slave0_6000_03_bit},
{PANASONIC_0, VID_PID, 0x6000, 4, &slave0_6000_04, &slave0_6000_04_bit},
{PANASONIC_0, VID_PID, 0x6000, 5, &slave0_6000_05, &slave0_6000_05_bit},
{PANASONIC_0, VID_PID, 0x6000, 6, &slave0_6000_06, &slave0_6000_06_bit},
{PANASONIC_0, VID_PID, 0x6000, 7, &slave0_6000_07, &slave0_6000_07_bit},
{PANASONIC_0, VID_PID, 0x6000, 8, &slave0_6000_08, &slave0_6000_08_bit},
{PANASONIC_0, VID_PID, 0x7010, 1, &slave0_7010_01, &slave0_7010_01_bit},
{PANASONIC_0, VID_PID, 0x7010, 2, &slave0_7010_02, &slave0_7010_02_bit},
{PANASONIC_0, VID_PID, 0x7010, 3, &slave0_7010_03, &slave0_7010_03_bit},
{PANASONIC_0, VID_PID, 0x7010, 4, &slave0_7010_04, &slave0_7010_04_bit},
{PANASONIC_0, VID_PID, 0x7010, 5, &slave0_7010_05, &slave0_7010_05_bit},
{PANASONIC_0, VID_PID, 0x7010, 6, &slave0_7010_06, &slave0_7010_06_bit},
{PANASONIC_0, VID_PID, 0x7010, 7, &slave0_7010_07, &slave0_7010_07_bit},
{PANASONIC_0, VID_PID, 0x7010, 8, &slave0_7010_08, &slave0_7010_08_bit},
{PANASONIC_1, VID_PID, 0x6000, 1, &slave1_6000_01, &slave1_6000_01_bit},
{PANASONIC_1, VID_PID, 0x6000, 2, &slave1_6000_02, &slave1_6000_02_bit},
{PANASONIC_1, VID_PID, 0x6000, 3, &slave1_6000_03, &slave1_6000_03_bit},
{PANASONIC_1, VID_PID, 0x6000, 4, &slave1_6000_04, &slave1_6000_04_bit},
{PANASONIC_1, VID_PID, 0x6000, 5, &slave1_6000_05, &slave1_6000_05_bit},
{PANASONIC_1, VID_PID, 0x6000, 6, &slave1_6000_06, &slave1_6000_06_bit},
{PANASONIC_1, VID_PID, 0x6000, 7, &slave1_6000_07, &slave1_6000_07_bit},
{PANASONIC_1, VID_PID, 0x6000, 8, &slave1_6000_08, &slave1_6000_08_bit},
{PANASONIC_1, VID_PID, 0x7010, 1, &slave1_7010_01, &slave1_7010_01_bit},
{PANASONIC_1, VID_PID, 0x7010, 2, &slave1_7010_02, &slave1_7010_02_bit},
{PANASONIC_1, VID_PID, 0x7010, 3, &slave1_7010_03, &slave1_7010_03_bit},
{PANASONIC_1, VID_PID, 0x7010, 4, &slave1_7010_04, &slave1_7010_04_bit},
{PANASONIC_1, VID_PID, 0x7010, 5, &slave1_7010_05, &slave1_7010_05_bit},
{PANASONIC_1, VID_PID, 0x7010, 6, &slave1_7010_06, &slave1_7010_06_bit},
{PANASONIC_1, VID_PID, 0x7010, 7, &slave1_7010_07, &slave1_7010_07_bit},
{PANASONIC_1, VID_PID, 0x7010, 8, &slave1_7010_08, &slave1_7010_08_bit},
{}
};

/*****************************************************************************/
/* Slave 0, "LAN9252-EVB-HBI"
* Vendor ID: 0x00000009
* Product code: 0x00009252
* Revision number: 0x00000001
*/

ec_pdo_entry_info_t slave_0_pdo_entries[] = {
{0x7010, 0x01, 1}, /* LED 1 */
{0x7010, 0x02, 1}, /* LED 2 */
{0x7010, 0x03, 1}, /* LED 3 */
{0x7010, 0x04, 1}, /* LED 4 */
{0x7010, 0x05, 1}, /* LED 5 */
{0x7010, 0x06, 1}, /* LED 6 */
{0x7010, 0x07, 1}, /* LED 7 */
{0x7010, 0x08, 1}, /* LED 8 */
{0x0000, 0x00, 8}, /* None */
{0x6000, 0x01, 1}, /* Switch 1 */
{0x6000, 0x02, 1}, /* Switch 2 */
{0x6000, 0x03, 1}, /* Switch 3 */
{0x6000, 0x04, 1}, /* Switch 4 */
{0x6000, 0x05, 1}, /* Switch 5 */
{0x6000, 0x06, 1}, /* Switch 6 */
{0x6000, 0x07, 1}, /* Switch 7 */
{0x6000, 0x08, 1}, /* Switch 8 */
{0x0000, 0x00, 8}, /* None */
{0x6020, 0x01, 1}, /* Underrange */
{0x6020, 0x02, 1}, /* Overrange */
{0x6020, 0x03, 2}, /* Limit 1 */
{0x6020, 0x05, 2}, /* Limit 2 */
{0x0000, 0x00, 8}, /* Gap */
{0x1802, 0x07, 1}, /* TxPDOState */
{0x1802, 0x09, 1}, /* TxPDO Toggle */
{0x6020, 0x11, 16}, /* Analog input */
};

ec_pdo_info_t slave_0_pdos[] = {
{0x1601, 9, slave_0_pdo_entries + 0}, /* DO Outputs */
{0x1a00, 9, slave_0_pdo_entries + 9}, /* DI Inputs */
{0x1a02, 8, slave_0_pdo_entries + 18}, /* AI TxPDO-Map */
};

ec_sync_info_t slave_0_syncs[] = {
{0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
{1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
{2, EC_DIR_OUTPUT, 1, slave_0_pdos + 0, EC_WD_ENABLE},
{3, EC_DIR_INPUT, 2, slave_0_pdos + 1, EC_WD_DISABLE},
{0xff}
};

/* Slave 1, "LAN9252-EVB-HBI"
* Vendor ID: 0x00000009
* Product code: 0x00009252
* Revision number: 0x00000001
*/
ec_pdo_entry_info_t slave_1_pdo_entries[] = {
{0x7010, 0x01, 1}, /* LED 1 */
{0x7010, 0x02, 1}, /* LED 2 */
{0x7010, 0x03, 1}, /* LED 3 */
{0x7010, 0x04, 1}, /* LED 4 */
{0x7010, 0x05, 1}, /* LED 5 */
{0x7010, 0x06, 1}, /* LED 6 */
{0x7010, 0x07, 1}, /* LED 7 */
{0x7010, 0x08, 1}, /* LED 8 */
{0x0000, 0x00, 8}, /* None */
{0x6000, 0x01, 1}, /* Switch 1 */
{0x6000, 0x02, 1}, /* Switch 2 */
{0x6000, 0x03, 1}, /* Switch 3 */
{0x6000, 0x04, 1}, /* Switch 4 */
{0x6000, 0x05, 1}, /* Switch 5 */
{0x6000, 0x06, 1}, /* Switch 6 */
{0x6000, 0x07, 1}, /* Switch 7 */
{0x6000, 0x08, 1}, /* Switch 8 */
{0x0000, 0x00, 8}, /* None */
{0x6020, 0x01, 1}, /* Underrange */
{0x6020, 0x02, 1}, /* Overrange */
{0x6020, 0x03, 2}, /* Limit 1 */
{0x6020, 0x05, 2}, /* Limit 2 */
{0x0000, 0x00, 8}, /* Gap */
{0x1802, 0x07, 1}, /* TxPDOState */
{0x1802, 0x09, 1}, /* TxPDO Toggle */
{0x6020, 0x11, 16}, /* Analog input */
};

ec_pdo_info_t slave_1_pdos[] = {
{0x1601, 9, slave_1_pdo_entries + 0}, /* DO Outputs */
{0x1a00, 9, slave_1_pdo_entries + 9}, /* DI Inputs */
{0x1a02, 8, slave_0_pdo_entries + 18}, /* AI TxPDO-Map */
};

ec_sync_info_t slave_1_syncs[] = {
{0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
{1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
{2, EC_DIR_OUTPUT, 1, slave_0_pdos + 0, EC_WD_ENABLE},
{3, EC_DIR_INPUT, 2, slave_0_pdos + 1, EC_WD_DISABLE},
{0xff}
};
/*****************************************************************************/
// Synchronise the distributed clocks
void sync_distributed_clocks(void)
{
#if SLAVE_REF
uint32_t ref_time = 0;
ecrt_master_reference_clock_time(master, &ref_time);//获取参考时钟的低32位
#else
clock_gettime(CLOCK_TO_USE, &sync_time);
ecrt_master_sync_reference_clock_to(master, TIMESPEC2NS(sync_time));
#endif
ecrt_master_sync_slave_clocks(master);// call to sync slaves to ref slave
}

/*****************************************************************************/
// timespec相加
struct timespec timespec_add(struct timespec time1, struct timespec time2)
{
struct timespec result;

if ((time1.tv_nsec + time2.tv_nsec) >= NSEC_PER_SEC) {
result.tv_sec = time1.tv_sec + time2.tv_sec + 1;
result.tv_nsec = time1.tv_nsec + time2.tv_nsec - NSEC_PER_SEC;
} else {
result.tv_sec = time1.tv_sec + time2.tv_sec;
result.tv_nsec = time1.tv_nsec + time2.tv_nsec;
}

return result;
}

/*****************************************************************************/
// Wait for the next period
void wait_period(void)
{
clock_nanosleep(CLOCK_TO_USE, TIMER_ABSTIME, &wakeupTime, NULL);
ecrt_master_application_time(master, TIMESPEC2NS(wakeupTime));
wakeupTime = timespec_add(wakeupTime, cycletime);
}

/*****************************************************************************/
// 循环任务
void cyclic_task()
{
// wait for next period (using adjustable system time)
wait_period();
// receive EtherCAT
ecrt_master_receive(master);
ecrt_domain_process(domain1);

// 最大时钟偏移
diff_time_ns = ecrt_master_sync_monitor_process(master); // 返回所有从站时钟偏移中的最大值
fprintf(fp,"%ld,",diff_time_ns);
printf("最大时钟偏移 = %ld 纳秒\n",diff_time_ns);

// 程序运行时间
gettimeofday(&end, NULL);
uint32_t timeuse = 1000000*(end.tv_sec - start.tv_sec) + end.tv_usec-start.tv_usec;
fprintf(fp,"%ld\n",timeuse);
printf("程序运行时间 = %ld 微秒\n",timeuse);

// 流水灯逻辑
if(cycle_times < 50){ // 每500ms切换一次
cycle_times++;
}else{
cycle_times = 0;
if(0<led && led<7){
LED[led-1] = 1;
LED[led] = 0;
led++;
}else if(led==7){
LED[led-1] = 1;
LED[led] = 0;
led=0;
}else if(led==0){
LED[7]=1;
LED[0]=0;
led=1;
}
}

// write slave0
EC_WRITE_BIT(domain1_pd + slave0_7010_01, slave0_7010_01_bit, LED[0]);
EC_WRITE_BIT(domain1_pd + slave0_7010_02, slave0_7010_02_bit, LED[1]);
EC_WRITE_BIT(domain1_pd + slave0_7010_03, slave0_7010_03_bit, LED[2]);
EC_WRITE_BIT(domain1_pd + slave0_7010_04, slave0_7010_04_bit, LED[3]);
EC_WRITE_BIT(domain1_pd + slave0_7010_05, slave0_7010_05_bit, LED[4]);
EC_WRITE_BIT(domain1_pd + slave0_7010_06, slave0_7010_06_bit, LED[5]);
EC_WRITE_BIT(domain1_pd + slave0_7010_07, slave0_7010_07_bit, LED[6]);
EC_WRITE_BIT(domain1_pd + slave0_7010_08, slave0_7010_08_bit, LED[7]);
// write slave1
EC_WRITE_BIT(domain1_pd + slave1_7010_01, slave1_7010_01_bit, LED[0]);
EC_WRITE_BIT(domain1_pd + slave1_7010_02, slave1_7010_02_bit, LED[1]);
EC_WRITE_BIT(domain1_pd + slave1_7010_03, slave1_7010_03_bit, LED[2]);
EC_WRITE_BIT(domain1_pd + slave1_7010_04, slave1_7010_04_bit, LED[3]);
EC_WRITE_BIT(domain1_pd + slave1_7010_05, slave1_7010_05_bit, LED[4]);
EC_WRITE_BIT(domain1_pd + slave1_7010_06, slave1_7010_06_bit, LED[5]);
EC_WRITE_BIT(domain1_pd + slave1_7010_07, slave1_7010_07_bit, LED[6]);
EC_WRITE_BIT(domain1_pd + slave1_7010_08, slave1_7010_08_bit, LED[7]);

ecrt_domain_queue(domain1);

sync_distributed_clocks();

// 读取所有从站0x092C寄存器(存储时钟偏移值)
ecrt_master_sync_monitor_queue(master);

ecrt_master_send(master);
}

int main(int argc, char **argv)
{
fp = fopen("/root/lan9252_test/diff_time_statistic.csv","w");

master = ecrt_request_master(0);
if (!master)
return -1;

domain1 = ecrt_master_create_domain(master);
if (!domain1)
return -1;

// Create configuration for bus coupler
slave0 = ecrt_master_slave_config(master, PANASONIC_0, VID_PID);
if (!slave0)
return -1;
printf("Configuring PDOs...\n");
if (ecrt_slave_config_pdos(slave0, EC_END, slave_0_syncs))
{
fprintf(stderr, "Failed to configure slave0 PDOs!\n");
exit(EXIT_FAILURE);
}
else
{
printf("*Success to configuring slave0 PDOs*\n");
}

slave1 = ecrt_master_slave_config(master, PANASONIC_1, VID_PID);
if (!slave1)
return -1;
if (ecrt_slave_config_pdos(slave1, EC_END, slave_1_syncs))
{
fprintf(stderr, "Failed to configure slave1 PDOs!\n");
exit(EXIT_FAILURE);
}
else
{
printf("*Success to configuring slave1 PDOs*\n");
}

if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs))
{
fprintf(stderr, "PDO entry registration failed!\n");
exit(EXIT_FAILURE);
}
else{
printf("PDO entries sucess..\n");
}

// configure SYNC signals for this slave
ecrt_slave_config_dc(slave0, 0x0300, cycle_t, 0, 0, 0);
ecrt_slave_config_dc(slave1, 0x0300, cycle_t, 0, 0, 0);
ecrt_master_select_reference_clock(master, slave0);

printf("Activating master...\n");
if (ecrt_master_activate(master))
return -1;

if (!(domain1_pd = ecrt_domain_data(domain1))) {
return -1;
}

clock_gettime(CLOCK_TO_USE, &wakeupTime);

printf("Start running...\n");
gettimeofday(&start, NULL);

while(1){
cyclic_task();
}
}