跳到主要内容

DC 同步

从站级 DC(分布式时钟)配置。通过 Slave 类的 DC 相关方法访问。

推荐使用主站级方法

大多数场景建议使用主站级 master.ConfigureDC() 一次性配置所有 DC 从站。 从站级方法适用于需要对个别从站设置不同参数的场景。

DC 能力检测

启用 DC 前应先确认从站 既支持 DC 硬件、又在 ESI 中声明使用 DC 同步。两者满足才能安全调用 ConfigureDC()

HasDC()

bool HasDC() const;

ESC 硬件是否支持 DC(读取 ESC 寄存器 0x0008)。仅反映硬件能力,不代表应用层一定使用 DC。部分从站 ESC 报 DC capable 但 ESI 未声明 DC OpMode,强行启用 DC 会让从站拒绝进入 OP(典型代表:GCAN-8200)。

HasEsiDcSync()

bool HasEsiDcSync() const;   // 默认: 与 HasDC() 一致 (没有 ESI 路径时退化)

ESI 是否声明此从站使用 DC 同步(读取 ESI 中至少一个 OpMode AssignActivate != 0)。若调用方已通过 EsiManager::BindToSlave() 绑定 ESI,则真正解析 XML;否则保守退化为 HasDC(),避免误关 DC。

示例 (启用 DC 前的稳妥写法):

auto& s = master.GetSlave(1);
if (s.HasDC() && s.HasEsiDcSync()) {
s.ConfigureDC(1'000'000);
} else {
// 走 FreeRun / SM-Sync, 不强行启用 DC
}
HasDC vs HasEsiDcSync
  • HasDC() — ESC 寄存器 0x0008 bit, 硬件能力
  • HasEsiDcSync() — ESI XML 至少一个 <DcOpMode> 的 AssignActivate != 0, 应用层声明

只信 HasDC() 会导致 GCAN-8200 这类 "硬件 capable / ESI 没声明" 的从站启用 DC 后拒绝进入 OP。两者同时为真才安全。

DcSyncMode 枚举

enum class DcSyncMode : uint8_t {
FreeRun = 0x00, // 自由运行模式(无同步)
Sync0 = 0x01, // SYNC0 同步
Sync01 = 0x03, // SYNC0 + SYNC1 同步
Sync1 = 0x02, // 仅 SYNC1 同步(罕见)
SmSync = 0x22 // SyncManager 事件同步(SM-Sync)
};

CurrentDcSyncMode()

DcSyncMode CurrentDcSyncMode() const;

获取从站当前的 DC 同步模式。

示例:

auto mode = slave.CurrentDcSyncMode();
if (mode == DcSyncMode::Sync0) {
printf("从站使用 SYNC0 同步\n");
}

DC 参数设置

ConfigureDC()

void ConfigureDC(uint32_t sync0CycleNs, uint32_t sync1CycleNs = 0, int32_t shiftNs = 0) const;

设置从站 DC 同步参数。所有参数单位:纳秒(ns)

参数:

  • sync0CycleNs — SYNC0 周期(纳秒),0 表示禁用
  • sync1CycleNs — SYNC1 增量时间(纳秒),0 表示仅 SYNC0
  • shiftNs — 相位偏移(纳秒)

示例:

auto& slave = master.GetSlave(1);

// SYNC0 = 1ms
slave.ConfigureDC(1'000'000);

// SYNC0 = 1ms, SYNC1 = 2ms
slave.ConfigureDC(1'000'000, 2'000'000);

// SYNC0 = 1ms, 相位偏移 200us
slave.ConfigureDC(1'000'000, 0, 200'000);

ConfigureDC(DcSyncMode)

void ConfigureDC(DcSyncMode mode,
uint32_t sync0CycleNs = 0,
uint32_t sync1CycleNs = 0,
int32_t shiftNs = 0) const;

按同步模式配置从站 DC(ETG.1020)。SDK 自动写入对应 SM 同步类型寄存器 / SYNC0 / SYNC1,免去手动算 AssignActivate 位掩码的繁琐。

参数:

  • mode (DcSyncMode) — 同步模式
  • sync0CycleNs / sync1CycleNs — 仅在 Sync0 / Sync01 / Sync1 模式下有效
  • shiftNs — 仅在 DC 模式下有效

示例:

auto& s = master.GetSlave(1);

// DC SYNC0 = 1ms (常规伺服)
s.ConfigureDC(DcSyncMode::Sync0, 1'000'000);

// SM 事件同步 (大多数 IO 模块默认)
s.ConfigureDC(DcSyncMode::SmSync);

// 自由运行 (不使用 DC 同步)
s.ConfigureDC(DcSyncMode::FreeRun);

DisableDC()

void DisableDC() const;

禁用从站的 DC。

只读属性

auto& slave = master.GetSlave(1);

// 传播延迟(纳秒)
int delay = slave.PropagationDelay();
printf("传播延迟: %d ns\n", delay);

// DC 相关寄存器值
uint16_t dcActive = slave.DCActive();
int dcCycle0 = slave.DCCycle0();
int dcCycle1 = slave.DCCycle1();
int dcShift = slave.DCShift();

同步窗口诊断

GetSyncWindowStatus()

std::optional<SyncWindowStatus> GetSyncWindowStatus() const;

获取从站 DC 同步窗口的详细状态。不支持 DC 或读取失败时返回 std::nullopt

相关结构:

struct SyncWindowStatus {
int DiffNs; // 当前与参考时钟的时间差(纳秒)
int MaxDiffNs; // 最大时间差(纳秒)
int MinDiffNs; // 最小时间差(纳秒)
bool InSync; // 是否在同步窗口内
uint32_t OutOfSyncCount; // 超出同步窗口次数
};

示例:

auto status = slave.GetSyncWindowStatus();
if (status) {
printf("同步差=%dns, 最大=%dns, 同步=%s\n",
status->DiffNs, status->MaxDiffNs,
status->InSync ? "是" : "否");
printf("超出同步次数: %u\n", status->OutOfSyncCount);
}

ResetSyncWindowStats()

通过 SlaveDC 命名空间重置同步窗口统计:

darra::SlaveDC::ResetSyncWindowStats(slave.Dll(), master.MasterNumber(), 1);

DC 同步丢失事件

通过主站事件注册 DC 同步丢失回调:

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

完整示例

#include "ethercat.hpp"
using namespace darra;

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

auto& slave = master.GetSlave(1);

// 配置 SYNC0 = 1ms
slave.ConfigureDC(1'000'000);

printf("传播延迟: %d ns\n", slave.PropagationDelay());

// 进入 OP
master.SetState(EcState::OP);
master.Start();

// 监控同步状态
auto status = slave.GetSyncWindowStatus();
if (status) {
printf("同步差=%dns, 同步=%s, 超出次数=%u\n",
status->DiffNs,
status->InSync ? "是" : "否",
status->OutOfSyncCount);
}

getchar();
return 0;
}