跳到主要内容

DC 配置

什么是 DC?

DC(分布式时钟)是 EtherCAT 的硬件级时间同步机制。它让网络中所有从站共享同一个时间基准,使各从站能在完全相同的时刻执行动作。

DC 对设备的好处
  • 多轴同步运动 — 所有伺服驱动器在同一时刻更新位置指令,消除轴间抖动,轨迹插补更平滑
  • IO 同步采样 — 所有输入输出在同一时刻,主站读取时数据一致,适合高速控制和测量
  • 精确 I/O 时间戳 — 输入数据带有硬件时间戳,主站可精确知道采样发生的物理时刻
  • 确定性输出 — 输出在固定时刻生效,不受 EtherCAT 帧到达时间的抖动影响,降低系统延迟和不确定性
相关页面
  • 单个从站的 DC 配置请参见从站 ConfigureDC() 方法
  • 周期配置请参考 配置
  • DC 同步监控请参考 主站诊断

主站级 DC 配置

ConfigureDC()

void ConfigureDC(uint32_t sync0CycleNs, uint32_t sync1CycleNs = 0);

为所有 DC 从站配置 DC 同步。所有参数单位:纳秒(ns)。默认自动完成:

  1. 传播延迟测量 — 读取各端口接收时间并计算延迟
  2. 偏移计算 — 基于传播延迟自动计算每个从站的相位偏移
  3. DC 应用 — 写入 SYNC0/SYNC1 周期和起始时间到各从站

参数:

  • sync0CycleNs (uint32_t) — SYNC0 周期(纳秒),0 表示禁用 DC
  • sync1CycleNs (uint32_t) — SYNC1 增量时间(纳秒),0 表示仅 SYNC0

示例:

// SYNC0 = 1ms,仅 SYNC0
master.ConfigureDC(1000000);

// SYNC0 = 1ms, SYNC1 = 500us
master.ConfigureDC(1000000, 500000);

// SYNC0 = 125us(WDK 驱动推荐)
master.ConfigureDC(125000);
推荐配置

125us 高性能配置(WDK 驱动推荐):

master.LoopCycle(125000);
master.ConfigureDC(125000);

1ms 常规配置(通用场景):

master.LoopCycle(1000000);
master.ConfigureDC(1000000);
  • SYNC0 与 LoopCycle — 应保持一致或为其整数倍,数据未到达的同步没有意义
  • 自动偏移(推荐) — 主站自动测量传播延迟并计算
  • 过采样例外 — Oversampling 设备中 SYNC0 < LoopCycle,需设备硬件和 PDO 映射专门支持
关于 SYNC1

SYNC1 是相对于 SYNC0 的增量时间。例如 SYNC0=1,000,000ns, SYNC1=500,000ns 表示 SYNC1 在每个 SYNC0 后 500us 触发。

从站级 DC 配置

从站级 ConfigureDC() 支持 DcSyncMode 参数(通过 SlaveDC 命名空间实现),可对单个从站设置同步模式。

DcSyncMode 枚举:

enum class DcSyncMode : int {
FreeRun = 0, // 自由运行(无同步)
SmSynchron = 1, // SM 同步模式
DcSynchron = 2, // DC 同步(仅 SYNC0)
DcSynchron01 = 3 // DC 同步(SYNC0 + SYNC1)
};
using namespace darra;
auto& s = master.GetSlave(1);

// 通过 SlaveDC 命名空间按同步模式配置
SlaveDC::ConfigureDC(s.Dll(), master.MasterNumber(), 1,
DcSyncMode::DcSynchron, 1000000); // DC 同步 SYNC0 = 1ms
SlaveDC::ConfigureDC(s.Dll(), master.MasterNumber(), 1,
DcSyncMode::SmSynchron); // SM 同步模式
SlaveDC::ConfigureDC(s.Dll(), master.MasterNumber(), 1,
DcSyncMode::FreeRun); // 禁用 DC 同步
与主站级配置的区别
  • master.ConfigureDC() — 一次性配置所有 DC 从站
  • SlaveDC::ConfigureDC() — 配置单个从站

仅在需要对个别从站设置不同参数时使用从站级方法。

从站级快捷方法

auto& s = master.GetSlave(1);

// 设置单个从站的 DC 同步参数
s.ConfigureDC(1000000); // SYNC0 = 1ms
s.ConfigureDC(1000000, 500000); // SYNC0 = 1ms, SYNC1 = 500us
s.ConfigureDC(125000, 0, 100000); // SYNC0 = 125us, 手动偏移 100us

// 禁用 DC
s.DisableDC();

持续传播延迟测量

EnableContinuousMeasurement()

// 通过 SlaveDC 命名空间调用
SlaveDC::EnableContinuousMeasurement(dll, masterIndex, enable, intervalSec);

启用或禁用持续传播延迟测量(ETG.1500 5.13.2)。启用后主站定期重新测量传播延迟,适用于温度漂移等导致延迟变化的场景。

参数:

  • enable (bool) — true 启用,false 禁用
  • intervalSec (uint32_t) — 测量间隔(秒),0 表示使用默认间隔

示例:

// 每 60 秒重新测量
SlaveDC::EnableContinuousMeasurement(master.Dll(), master.MasterNumber(), true, 60);

// 禁用
SlaveDC::EnableContinuousMeasurement(master.Dll(), master.MasterNumber(), false);
备注

默认情况下 DC 偏移在 ConfigureDC() 时一次性计算。仅在长时间运行且环境温度变化较大时启用此功能。

传播延迟

从站传播延迟

auto& s = master.GetSlave(1);
int delay = s.PropagationDelay();
printf("从站 1 传播延迟: %d ns\n", delay);

同步窗口监控

主站每秒自动检查各 DC 从站的时间偏差。当从站偏差超出阈值时触发 DCSyncLost 事件。

// 阈值配置
darra::set_sync_window_threshold(master, 500); // 设为 500ns

// 事件:从站从"同步"变为"失同步"时触发一次(不重复触发)
master.Events().DCSyncLost = [](uint16_t mi, uint16_t si, int diff) {
printf("从站 %d DC 同步丢失: 偏差 %d ns\n", si, diff);
};

GetMaxSyncDifference()

// 通过 SlaveDC 命名空间调用
int SlaveDC::GetMaxSyncDifference(dll_t& dll, uint16_t mi);

获取所有 DC 从站中的最大时间偏差(纳秒)。

返回值:

  • int — 最大时间偏差(纳秒),失败返回 -1

示例:

int maxDiff = SlaveDC::GetMaxSyncDifference(master.Dll(), master.MasterNumber());
printf("最大同步偏差: %d ns\n", maxDiff);

IsAllSlavesInSync()

// 通过 SlaveDC 命名空间调用
bool SlaveDC::IsAllSlavesInSync(dll_t& dll, uint16_t mi, int thresholdNs = 1000);

检查所有 DC 从站是否都在同步窗口内。

返回值:

  • bool — 所有从站同步返回 true

示例:

if (!SlaveDC::IsAllSlavesInSync(master.Dll(), master.MasterNumber()))
printf("存在从站同步偏差过大\n");

ResetAllSyncWindowStats()

// 通过 SlaveDC 命名空间调用
void SlaveDC::ResetAllSyncWindowStats(dll_t& dll, uint16_t mi);

重置所有从站的同步窗口统计(最大/最小时间差、超出同步次数等)。

示例:

SlaveDC::ResetAllSyncWindowStats(master.Dll(), master.MasterNumber());

从站同步窗口状态

auto& s = master.GetSlave(1);

auto status = s.GetSyncWindowStatus();
if (status) {
printf("从站 1 同步状态:\n");
printf(" 当前偏差: %d ns\n", status->DiffNs);
printf(" 最大偏差: %d ns\n", status->MaxDiffNs);
printf(" 最小偏差: %d ns\n", status->MinDiffNs);
printf(" 同步状态: %s\n", status->InSync ? "正常" : "异常");
printf(" 失步次数: %u\n", status->OutOfSyncCount);
}
单个从站

单个从站的同步窗口详细状态使用 slave.GetSyncWindowStatus(),包含 DiffNs、MaxDiffNs、MinDiffNs、InSync、OutOfSyncCount。

DC 同步丢失回调

master.Events().DCSyncLost = [](uint16_t mi, uint16_t si, int diff) {
printf("从站 %d DC 同步丢失: 偏差 %d ns\n", si, diff);
};

DC 漂移补偿

EnableDriftCompensation()

// 通过 MasterConfig 配置
auto& config = master.GetConfig();
config.DriftCompensation(bool enable, int32_t thresholdNs, int32_t gain);

启用或禁用 DC 漂移补偿。漂移补偿在主站侧持续修正系统时钟与 DC 参考时钟之间的偏差,防止长时间运行后同步精度下降。

参数:

  • enable (bool) — true 启用,false 禁用
  • thresholdNs (int32_t) — 触发补偿的偏差阈值(纳秒),偏差低于此值时不做修正,默认 1000ns
  • gain (int32_t) — 补偿增益,控制修正速率(值越大修正越快但可能引入振荡),默认 512

示例:

auto& config = master.GetConfig();

// 使用默认参数启用
config.DriftCompensation(true);

// 自定义参数:500ns 阈值,较低增益(更平滑的修正)
config.DriftCompensation(true, 500, 256);

// 禁用
config.DriftCompensation(false);

也可通过 SlaveDC 命名空间直接调用:

SlaveDC::EnableDriftCompensation(master.Dll(), master.MasterNumber(), true, 1000, 512);
推荐

对于长时间运行的系统(>24小时),建议启用漂移补偿。默认参数适合大多数场景,仅在需要更精细控制时调整阈值和增益。

完整示例

125us 高性能(WDK 驱动)

#include "ethercat.hpp"
using namespace darra;

int main() {
try {
EtherCATMaster master(dll);
master.SetNetwork("\\Device\\NPF_{...}");
master.Build();

// 1. 配置周期 125us
master.LoopCycle(125000);

// 2. 配置 DC (SYNC0 = 125us)
master.ConfigureDC(125000);

// 3. 漂移补偿
master.GetConfig().DriftCompensation(true, 1000, 512);

// 4. 监听 DC 同步丢失
master.Events().DCSyncLost = [](uint16_t mi, uint16_t si, int diff) {
printf("从站 %d DC 同步丢失: %d ns\n", si, diff);
};

// 5. 切换到 OP
master.SetState(EcState::OP);
master.Start();

printf("运行中... 按 Enter 停止\n");
getchar();

} catch (const ethercat::DarraException& e) {
printf("错误: %s\n", e.what());
return -1;
}
return 0;
}

持续测量与同步监控

// 长时间运行:启用持续传播延迟测量(温度漂移补偿)
SlaveDC::EnableContinuousMeasurement(master.Dll(), master.MasterNumber(), true, 60);

// 同步窗口监控
if (!SlaveDC::IsAllSlavesInSync(master.Dll(), master.MasterNumber())) {
int maxDiff = SlaveDC::GetMaxSyncDifference(master.Dll(), master.MasterNumber());
printf("同步偏差过大: %d ns\n", maxDiff);
}

// 重置统计
SlaveDC::ResetAllSyncWindowStats(master.Dll(), master.MasterNumber());

1ms 常规配置

EtherCATMaster master(dll);
master.SetNetwork("\\Device\\NPF_{...}");
master.Build();

// 配置 1ms 周期
master.LoopCycle(1000000);
master.ConfigureDC(1000000);

// 启用漂移补偿
master.GetConfig().DriftCompensation(true, 1000, 512);

// 切换到 OP
master.SetState(EcState::OP);
master.Start();

// 定期检查同步状态
for (int i = 1; i <= master.SlaveCount(); ++i) {
auto status = master.GetSlave(i).GetSyncWindowStatus();
if (status && !status->InSync) {
printf("从站 %d 同步偏差: %d ns\n", i, status->DiffNs);
}
}
CurrentDcSyncMode

CurrentDcSyncMode 通过 SlaveDC::GetCurrentDcSyncMode() 获取从站当前的 DC 同步模式。

主站 DC 时间查询

主站提供两个只读接口查询 DC 时间和参考时钟信息:获取当前主站侧 DC 时间戳(用于日志关联、事件时间轴对齐),以及查询哪个从站被选为参考时钟源。

GetMasterDCTime()

class EtherCATMaster {
public:
uint64_t GetMasterDCTime() const;
};

获取主站当前的 DC 时间戳(纳秒),基准为 2000-01-01 00:00:00 UTC(EtherCAT DC 协议定义)。内部通过主站 DC 补偿后的本地时钟读取,精度与同步窗口阈值相关。

返回值:

  • uint64_t — 从 2000-01-01 起的纳秒数;主站未初始化或未启用 DC 时返回 0

示例:

using namespace std::chrono;

uint64_t dcNs = master.GetMasterDCTime();

// 转换为 time_point (2000-01-01 + dcNs)
constexpr auto ec_epoch = sys_days{2000y / January / 1};
auto tp = ec_epoch + nanoseconds{dcNs};

auto t = system_clock::to_time_t(system_clock::time_point{tp.time_since_epoch()});
std::cout << "DC 时间: " << std::put_time(std::localtime(&t), "%F %T")
<< " (+" << (dcNs % 1'000'000'000) << " ns)\n";

GetReferenceClockSlaveIndex()

int GetReferenceClockSlaveIndex() const;

获取被选为 DC 参考时钟的从站索引。参考时钟通常是总线上第一个支持 DC 的从站,SDK 在 ConfigureDC() 时自动选择。

返回值:

  • int — 参考时钟从站索引(1 基);无 DC 从站或未配置时返回 -1

示例:

int refSlave = master.GetReferenceClockSlaveIndex();
if (refSlave > 0) {
auto& s = master.GetSlave(refSlave);
std::cout << "参考时钟: 从站 " << refSlave
<< " (" << s.Name() << ")\n";
}
应用场景
  • 日志时间对齐: 将主站日志时间戳替换为 DC 时间,便于与从站侧硬件时间戳(如 IO 采样时间)精确关联
  • 事件时间轴: 报警、诊断、PDO 异常事件统一使用 DC 时间戳,便于多站点数据汇总分析
  • 参考时钟监控: 若参考时钟从站掉线,需手动触发 DC 重新配置
参考

ETG.1000 Part 4 §6.8.3 分布式时钟参考时钟机制; ETG.1020 §6.4。