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()— 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 表示仅 SYNC0shiftNs— 相位偏移(纳秒)
示例:
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;
}