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 : int {
FreeRun = 0, // 自由运行模式(无同步)
SmSynchron = 1, // SyncManager 事件同步(SM-Sync)
DcSynchron = 2, // DC 同步(仅 SYNC0)
DcSynchron01 = 3 // DC 同步(SYNC0 + SYNC1)
};
CurrentDcSyncMode()
DcSyncMode CurrentDcSyncMode() const;
获取从站当前的 DC 同步模式。
示例:
auto mode = slave.CurrentDcSyncMode();
if (mode == DcSyncMode::DcSynchron) {
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);
SlaveDC::ConfigureDC(DcSyncMode)
// darra::SlaveDC 命名空间下的自由函数
void 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 类的 ConfigureDC() 成员只有"周期数值"形态 (ConfigureDC(sync0CycleNs, sync1CycleNs, shiftNs)),
没有接收 DcSyncMode 参数的成员重载。按同步模式配置需走 darra::SlaveDC::ConfigureDC() 自由函数,
显式传入 dll_t& / 主站索引 / 从站索引。
参数:
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; // 是否在同步窗口内
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 同步丢失事件
通过主站事件用 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;
}