跳到主要内容

DC 同步 (从站级)

从站级 DC(分布式时钟)属性和配置。主站级 DC 配置详见 DC 配置。

推荐使用主站级方法

大多数场景建议使用 ConfigureDCAll() 一次性配置所有 DC 从站。 从站级方法适用于需要对个别从站设置不同参数的场景。两者都默认自动计算偏移。

只读属性​

类别函数返回类型读写说明
DC 状态GetSlaveHasDC(mi, si)BOOL只读ESC 硬件是否支持 DC
DC 状态slave_has_esi_dc_sync(mi, si)BOOL只读ESI 是否声明此从站使用 DC 同步 (slave/dc.h 高层封装)
DC 状态GetSlaveDCActive(mi, si)uint16_t只读DC 激活寄存器。0=未启用 DC;0x0300=SYNC0 已激活(才是 DC 同步);其它非零值不是 SYNC0,不能当成已同步
DC 时间GetSlaveDCCycle0(mi, si)int32_t只读当前 SYNC0 周期 (纳秒)
DC 时间GetSlaveDCCycle1(mi, si)int32_t只读当前 SYNC1 周期 (纳秒)
DC 时间GetSlaveDCShift(mi, si)int32_t只读当前相位偏移 (纳秒)
DC 时间GetSlavePropagationDelay(mi, si)int32_t只读帧从主站到达此从站的传播延迟 (纳秒)
DC 拓扑GetSlaveDCNext(mi, si) / GetSlaveDCPrevious(mi, si)uint16_t只读DC 链表下一个 / 上一个从站索引
DC 拓扑GetSlaveDCParentPort(mi, si)int32_t只读DC 父端口号
DC 接收时间GetSlaveDCReceiveTimeA/B/C/D(mi, si)int32_t只读各端口接收时间 (纳秒)
HasDC vs HasEsiDcSync

GetSlaveHasDC 仅反映 ESC 硬件 bit, 不代表应用层一定使用 DC。部分从站 ESC 报 DC capable 但 ESI 未声明 DC OpMode, 此时应跑 FreeRun / SM-Sync, 强行启用 DC 会让从站拒绝进入 OP。

slave_has_esi_dc_sync() 走真实 ESI DC OpMode 查询(EcEsi_GetDeviceDcSyncMode,需先通过 esi_dll_load_directory() 导入 ESI 或进 PreOp 自动绑定):ESI 声明 DC-Sync0/01 时返回真,FreeRun/SM-Sync 或 ESI 无 <Dc> 节点时返回假;ESI 未绑定时退化为 GetSlaveHasDC 硬件位保守判定。

启用 DC 前的稳妥写法 (slave_has_esi_dc_sync 是 slave/dc.h 高层封装,直接调用):

#include "slave/dc.h"

if (GetSlaveHasDC(master, 1) && slave_has_esi_dc_sync(master, 1)) {
SetSyncBySlaveIndex(master, 1, 1000000, 0, 0);
}
FreeRun 不是 DC 同步

DC_FREE_RUN 可查询,但是从站本地时钟,不是 DC 已启用、也不是已同步。DC 同步 = SYNC0(0x1C32/0x1C33 = 2,且 GetSlaveDCActive == 0x0300)。in_sync 在 FreeRun 下不得解读为已同步。

配置方法​

SetSyncBySlaveIndex()​

void SetSyncBySlaveIndex(uint16_t master_index, uint16_t slave_index,
uint32_t sync0_ns, uint32_t sync1_ns, int32_t shift_ns);

配置单个从站的 DC 同步参数。所有参数单位纳秒。sync0_ns=0 禁用 DC,sync1_ns=0 仅 SYNC0,shift_ns=0 不设置偏移。

示例:

/* SYNC0 = 1ms, 无 SYNC1, 无偏移 */
SetSyncBySlaveIndex(master, 1, 1000000, 0, 0);

/* SYNC0 + SYNC1, 偏移 500us */
SetSyncBySlaveIndex(master, 1, 1000000, 2000000, 500000);

/* 禁用 DC */
SetSyncBySlaveIndex(master, 1, 0, 0, 0);

SetDcSyncMode()​

typedef enum {
DC_FREE_RUN = 0, /* 自由运行: 不使用 DC */
DC_SM_SYNCHRON = 1, /* SM 事件同步 */
DC_SYNCHRON = 2, /* DC SYNC0 同步 */
DC_SYNCHRON_01 = 3 /* DC SYNC0 + SYNC1 */
} dc_sync_mode_t;

BOOL SetDcSyncMode(uint16_t master_index, uint16_t slave_index, uint8_t sync_type);
uint8_t GetDcSyncMode(uint16_t master_index, uint16_t slave_index);

/* 高层封装 (slave/dc.h) */
BOOL dc_configure_with_mode(uint16_t master_index, uint16_t slave_index,
dc_sync_mode_t mode,
uint32_t sync0_ns, uint32_t sync1_ns,
int32_t shift_ns);

SetDcSyncMode() 直接设置/读取从站 SyncManager 同步类型。dc_configure_with_mode() 是 slave/dc.h 提供的高层封装,按同步模式一次性完成从站 DC 配置(SYNC0/SYNC1 信号 + SyncManager 同步类型),DC 同步模式按 ETG.1020 §22 / ESI AssignActivate 分四档。

高层封装调用

dc_configure_with_mode() 等 slave/dc.h 高层封装直接调用(无句柄、无函数指针表),需 #include "slave/dc.h"。

示例:

#include "slave/dc.h"

/* 仅 SYNC0, 周期 1ms */
dc_configure_with_mode(master, 1, DC_SYNCHRON, 1000000, 0, 0);

/* SYNC0 + SYNC1, 偏移 500us */
dc_configure_with_mode(master, 1, DC_SYNCHRON_01, 1000000, 2000000, 500000);

禁用 DC​

/* 通过 SetSyncBySlaveIndex 把 sync0_ns 置 0 即可 */
SetSyncBySlaveIndex(master, 1, 0, 0, 0);

同步窗口诊断​

GetSlaveSyncWindowStatus()​

BOOL GetSlaveSyncWindowStatus(uint16_t master_index, uint16_t slave_index,
int* diff_ns, int* max_diff_ns, int* min_diff_ns,
BOOL* in_sync, uint32_t* out_of_sync_count);

获取从站 DC 同步窗口的详细状态。不支持 DC 或读取失败时返回 FALSE。输出参数: 当前/最大/最小时间差、是否在 DC SYNC0 同步窗口内、累计失同步次数。in_sync 在 FreeRun 下不得解读为已同步(DC 同步要求 DCActive == 0x0300 且 0x1C32/0x1C33 = 2)。

查询

如果只需要 in_sync 或当前差值,可通过 GetSlaveLinkQuality / IsAllSlavesInSync 等接口;GetSlaveSyncWindowStatus() 适合需要完整统计信息的场景。

ResetSlaveSyncWindowStats()​

void ResetSlaveSyncWindowStats(uint16_t master_index, uint16_t slave_index);
int GetMaxSyncDifference(uint16_t master_index);
BOOL IsAllSlavesInSync(uint16_t master_index);

ResetSlaveSyncWindowStats() 重置此从站的同步窗口统计。GetMaxSyncDifference() / IsAllSlavesInSync() 是全局查询。

完整示例​

#include "ethercat.h"
#include <stdio.h>

int main(void) {
uint16_t master = Initialize();
SetNetwork(master, "\\Device\\NPF_{GUID}", "");
SetStateSequence(master, EC_STATE_OPERATIONAL, 10000);
Start(master);

if (GetSlaveHasDC(master, 1)) {
/* 配置从站1: SYNC0 = 1ms */
SetSyncBySlaveIndex(master, 1, 1000000, 0, 0);

printf("DC 激活: %d\n", GetSlaveDCActive(master, 1));
printf("SYNC0 周期: %d ns\n", GetSlaveDCCycle0(master, 1));
printf("相位偏移: %d ns\n", GetSlaveDCShift(master, 1));
printf("传播延迟: %d ns\n", GetSlavePropagationDelay(master, 1));

/* 同步窗口诊断 */
int diff, max_d, min_d;
BOOL sync;
uint32_t oos;
if (GetSlaveSyncWindowStatus(master, 1, &diff, &max_d, &min_d, &sync, &oos)) {
printf("同步差: %d ns, 同步=%s, 失步=%u\n",
diff, sync ? "是" : "否", oos);
}

if (IsAllSlavesInSync(master))
printf("所有从站已同步\n");

/* 禁用 DC */
SetSyncBySlaveIndex(master, 1, 0, 0, 0);
}

Stop(master);
Dispose(master);
return 0;
}