跳到主要内容

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 SlaveDC::HasEsiDcSync(dll_t& dll, uint16_t masterIndex, uint16_t slaveIndex);

ESI 是否声明此从站使用 DC 同步。经 ESI 绑定信息判定同步模式:DC-Sync0 / DC-Sync01(即 AssignActivate 非零)才算声明了 DC 同步;ESI 未声明则返回 false。若调用方尚未通过 EsiManager::BindToSlave() 绑定 ESI,保守退化为 HasDC(),避免误关 DC。

定义 ENABLE_ESI_XML 时另有严格版重载,直接解析给定 ESI XML 文件路径(任一 <Dc> / OpMode 的 AssignActivate != 0 即返回 true):

bool SlaveDC::HasEsiDcSync(dll_t& dll, uint16_t masterIndex, uint16_t slaveIndex,
const std::string& esiFilePath);

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

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

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

DcSyncMode 枚举​

enum class DcSyncMode : int {
FreeRun = 0, // 自由运行(本地时钟,不是 DC 同步)
SmSynchron = 1, // SyncManager 事件同步(SM-Sync)
DcSynchron = 2, // DC 同步(仅 SYNC0)
DcSynchron01 = 3 // DC 同步(SYNC0 + SYNC1)
};
FreeRun 不是 DC 同步

DcSyncMode::FreeRun 可查询,但是从站本地时钟,不是 DC 已启用、也不是已同步。DC 同步 = SYNC0(0x1C32/0x1C33 = 2,且 DCActive == 0x0300)。InSync 在 FreeRun 下恒为 false。

CurrentDcSyncMode()​

DcSyncMode CurrentDcSyncMode() const;

获取从站当前同步模式。FreeRun / SmSynchron 可查询,但不是 DC 同步。DC 同步仅 DcSynchron / DcSynchron01,要求 0x1C32/0x1C33 = 2 且 DCActive == 0x0300。

示例:

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

DC 参数设置​

ConfigureDC()​

bool 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)​

// Slave 成员重载(与上方周期数值形态同名重载)
bool ConfigureDC(DcSyncMode mode, uint32_t sync0CycleNs = 0,
uint32_t sync1CycleNs = 0, int32_t shiftNs = 0) const;

// darra::SlaveDC 命名空间下的等价自由函数
bool SlaveDC::ConfigureDC(dll_t& dll, uint16_t mi, uint16_t si,
DcSyncMode mode,
uint32_t sync0CycleNs = 0,
uint32_t sync1CycleNs = 0,
int32_t shiftNs = 0);

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

成员重载与自由函数等价

Slave 类除"周期数值"形态外还有接收 DcSyncMode 的成员重载,内部调 SlaveDC::ConfigureDC()。自由函数形态需显式传入 dll_t& / 主站索引 / 从站索引,适合不经过 Slave 对象的调用点。

参数:

  • dll (dll_t&) — DLL 引用 (可由 slave.Dll() 获取)
  • mi (uint16_t) — 主站索引 (master.MasterNumber())
  • si (uint16_t) — 从站索引 (1-based)
  • mode (DcSyncMode) — 同步模式
  • sync0CycleNs / sync1CycleNs — 仅在 DcSynchron / DcSynchron01 模式下有效
  • shiftNs — 仅在 DC 模式下有效

示例:

using namespace darra;
auto& s = master.GetSlave(1);
uint16_t mi = master.MasterNumber();

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

// SM 事件同步 (大多数 IO 模块默认)
SlaveDC::ConfigureDC(s.Dll(), mi, 1, DcSyncMode::SmSynchron);

// 自由运行 (不使用 DC 同步)
SlaveDC::ConfigureDC(s.Dll(), mi, 1, 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; // 是否在 DC SYNC0 同步窗口内。FreeRun 下恒为 false,不得当成已同步
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()​

重置同步窗口统计,Slave 成员与 SlaveDC 自由函数两种入口等价:

slave.ResetSyncWindowStats();

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

DC 同步丢失事件​

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

master.Events().OnDCSyncLost([](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;
}