跳到主要内容

事件

通过注册回调函数订阅所有主站级事件。

回调注册时机

回调注册函数是全局的,在 Initialize() 之前或之后都可以调用。回调函数在 DLL 内部线程中触发。

从站级事件

从站 PDO 数据变化通知、FSoE 安全事件请参考 从站事件

自动日志

所有事件(PDO 周期回调除外)触发时系统均自动记录日志,无论是否订阅。确保关键运行状态不会被静默丢失。

PDO 周期同步回调(RegisterProcessDataCyclicCallbackSync)为高频回调,不记录日志。

PDO 数据通路已纯内核化

PDO 过程数据收发为纯内核 RT 路径 + 内核共享内存指针零拷贝。应用层直接轮询过程映像指针读写 PDO,不再有逐周期回调应用层的机制。因此异步周期 PDO 回调与"输入数据变化"回调已停用,本 SDK 不再提供。需要当周期实时控制(如 FSoE)时使用 RegisterProcessDataCyclicCallbackSync(同步回调,在 PDO 线程内执行)。

功能概览

类别函数名 (dll.XXX)说明自动日志
PDO 周期RegisterPDOCyclicSyncPDO 周期同步回调(PDO 线程内执行,供 FSoE 等实时控制)
状态变化RegisterStateChangeSync / Async从站状态变化
热插拔RegisterDiscoverySync / Async从站发现 / 移除
RegisterSlaveIdentityMismatch从站身份不符
紧急事件RegisterEmergencyCoE Emergency 紧急消息
PDO 丢帧RegisterPDOFrameLoss连续丢帧通知(按组独立跟踪)
DC 同步丢失SetDCSyncLostCallbackDC 同步偏差超限
冗余RegisterRedundancyChanged冗余模式变化
RegisterSlavePortLinkChanged从站端口 link 变化(P0-P3)
PreOP 重配RegisterPreOpReconfig从站 PreOP 重配置
日志SetLogCallbackDLL 内部日志
崩溃SetCrashCallback崩溃通知

回调类型定义

typedef void (*log_callback_t)(int category, const char* message);
typedef void (*crash_callback_t)(int exception_code, const char* message);
typedef void (*pdo_cyclic_callback_t)(uint16_t master_index);
typedef void (*state_change_callback_t)(uint16_t master_index, uint16_t slave_index,
int old_state, int new_state);
typedef void (*emergency_callback_t)(uint16_t master_index, uint16_t slave_index,
uint16_t error_code, uint16_t error_reg, uint8_t b1, uint16_t w1, uint16_t w2);
typedef void (*discovery_callback_t)(uint16_t master_index, uint16_t slave_index,
BOOL is_found);
typedef void (*frame_loss_callback_t)(uint16_t master_index, uint8_t group,
uint32_t consecutive_lost, uint32_t total_lost);
typedef void (*redundancy_changed_callback_t)(uint16_t master_index,
int old_mode, int new_mode);
typedef void (*preop_reconfig_callback_t)(uint16_t master_index, uint16_t slave_index);
typedef void (*dc_sync_lost_callback_t)(uint16_t master_index, uint16_t slave_index,
int diff_ns);
typedef void (*identity_mismatch_callback_t)(uint16_t master_index, uint16_t slave_index,
uint32_t expected_vendor, uint32_t expected_product, uint32_t expected_revision,
uint32_t actual_vendor, uint32_t actual_product, uint32_t actual_revision);
typedef void (*port_link_changed_callback_t)(uint16_t master_index, uint16_t slave_index,
uint8_t port, BOOL is_up);
C 特有语法糖

注册回调后通常需要在 Initialize / SetNetwork / SetStateSequence 等返回值上做错误判断, C 可用 EC_TRY(...) macro 简化错误检查, 失败统一 goto fail 集中清理. 详见 错误处理宏.

PDO 周期回调

RegisterPDOCyclicSync

每个 PDO 周期触发一次(同步模式)。回调在 PDO 实时线程内直接执行,延迟最低,供 FSoE 等当周期实时控制使用。回调必须快速返回。

static void on_pdo_cyclic(uint16_t master_index) {
uint16_t status = PDOReadInputU16(master_index, 1, 0);
}
dll.RegisterPDOCyclicSync(on_pdo_cyclic);
注意

同步回调中不要执行耗时操作(如 SDO 读写、文件 I/O、锁等待),否则会阻塞实时线程导致丢帧。

异步周期 PDO 回调已停用

PDO 过程数据已是纯内核 RT 收发 + 内核共享内存指针零拷贝。常规 PDO 数据访问请在应用循环中直接调用 PDOReadInput* / PDOWriteOutput* 轮询过程映像指针,不需要回调;异步周期 PDO 回调已移除。

状态事件

RegisterStateChangeAsync

从站 EtherCAT 状态变化时触发。由 DLL 后台线程监控并回调。始终自动记录日志。

回调参数: master_index, slave_index (1-based), old_state, new_state (EC_STATE_*)。

static void on_state_change(uint16_t mi, uint16_t si, int old_s, int new_s) {
printf("从站 %d: %d -> %d\n", si, old_s, new_s);
uint16_t err = dll.GetSlaveALStatusCode(mi, si);
if (err) printf(" 错误码: 0x%04X\n", err);
}
dll.RegisterStateChangeAsync(on_state_change);

RegisterStateChangeSync

同上,同步模式。在实时线程内执行,回调必须快速返回。

热插拔事件

RegisterDiscoveryAsync

从站离线或上线时触发。热插拔断开时 PDO 线程内置恢复状态机自动恢复从站。始终自动记录日志。

回调参数: master_index, slave_index (1-based), is_found (TRUE=上线, FALSE=离线)。

static void on_discovery(uint16_t mi, uint16_t si, BOOL is_found) {
printf("从站 %d %s\n", si, is_found ? "上线" : "离线");
}
dll.RegisterDiscoveryAsync(on_discovery);

RegisterDiscoverySync

同上,同步模式。

RegisterSlaveIdentityMismatch

从站断电重插后身份不符时触发:识别状态机读取到的 Vendor/Product 与配置不匹配,或 Revision 低于配置(向后兼容:实际 Revision ≥ 配置值视为匹配)。始终自动记录日志。

触发后从站进入 IDENT_REJECTED 状态,不会自动重探测(防止错设备反复刷屏)。操作员检查/更换设备后需调用 AcknowledgeSlaveReplacement 让 SDK 重新探测。

回调参数: master_index, slave_index, 期望/实际 vendor/product/revision 各 3 个 uint32_t

static void on_identity_mismatch(uint16_t mi, uint16_t si,
uint32_t ev, uint32_t ep, uint32_t er,
uint32_t av, uint32_t ap, uint32_t ar)
{
printf("从站 %d 身份不符: 期望 Vendor=0x%08X, 实际 Vendor=0x%08X\n", si, ev, av);
/* UI 弹窗提示后调用 AcknowledgeSlaveReplacement(mi, si) */
}
dll.RegisterSlaveIdentityMismatch(on_identity_mismatch);
去重机制

同一从站进入 IDENT_REJECTED 状态仅触发一次事件。调用 AcknowledgeSlaveReplacement 后重置探测,身份仍不匹配会再次触发。

异常事件

RegisterEmergency

CoE Emergency 紧急消息事件。从站固件检测到错误时发送,数据格式遵循 CANopen Emergency 协议(CiA 301)。始终自动记录日志。

回调参数: master_index, slave_index, error_code (CANopen Emergency Error Code), error_reg (对象 0x1001), b1/w1/w2 (制造商特定数据)。

常见 Emergency Error Code: 0x1000通用 / 0x2000电流 / 0x3000电压 / 0x4000温度 / 0x5000硬件 / 0x6000软件 / 0x8000通信监控。

static void on_emcy(uint16_t mi, uint16_t si,
uint16_t code, uint16_t reg, uint8_t b1, uint16_t w1, uint16_t w2)
{
printf("从站 %d EMCY: 错误码=0x%04X, 寄存器=0x%02X\n", si, code, reg);
}
dll.RegisterEmergency(on_emcy);

RegisterPDOFrameLoss

PDO 连续丢帧事件。当连续丢帧达到阈值时触发,单次丢帧仅计数不触发。每组独立跟踪。始终自动记录日志。

回调参数: master_index, group (0-7), consecutive_lost, total_lost

static void on_frame_loss(uint16_t mi, uint8_t grp, uint32_t cons, uint32_t total) {
printf("组 %d 丢帧: 连续=%u, 累计=%u\n", grp, cons, total);
}
dll.RegisterPDOFrameLoss(on_frame_loss);

SetDCSyncLostCallback

DC 同步丢失事件。当从站从同步→失同步(超出阈值)时触发一次,持续超出不重复触发。始终自动记录日志。

回调参数: master_index, slave_index, diff_ns (当前与参考时钟的时间差)。

dll.SetSyncWindowThreshold(master, 500);  /* 500ns */
static void on_dc_lost(uint16_t mi, uint16_t si, int diff) {
printf("从站 %d DC 同步丢失: 偏差 %dns\n", si, diff);
}
dll.SetDCSyncLostCallback(on_dc_lost);

冗余事件

RegisterRedundancyChanged

冗余运行模式发生变化时触发。old_mode/new_mode 对应冗余模式枚举值(0=Inactive, 1=Dual, 2=Degraded)。始终自动记录日志。

static void on_redundancy(uint16_t mi, int old_mode, int new_mode) {
printf("冗余模式: %d -> %d\n", old_mode, new_mode);
}
dll.RegisterRedundancyChanged(on_redundancy);

RegisterSlavePortLinkChanged

从站 ESC 端口物理链路变化时触发(P0-P3 之一断开或恢复)。每秒诊断周期检测 DL Status 寄存器的 link bit,从 1->0 触发"断开",从 0->1 触发"恢复"。始终自动记录日志。

用于精确定位故障线缆段(相邻从站的对向端口同时报断 = 中间线缆故障)。

回调参数: master_index, slave_index, port (0-3 对应 P0-P3), is_up (TRUE=恢复, FALSE=断开)。

static void on_port_link(uint16_t mi, uint16_t si, uint8_t port, BOOL is_up) {
printf("从站 %d P%d link %s\n", si, port, is_up ? "恢复" : "断开");
}
dll.RegisterSlavePortLinkChanged(on_port_link);
配合断点定位

GetBreakPoints() 提供聚合后的故障点视图(断线 + CRC 故障)。SlavePortLinkChanged 是原始事件源,适合做实时告警。详见 主站诊断

清除事件订阅

C SDK 暂未实现 ClearAllEvents

dll_t 结构体中虽有 ClearAllEvents 字段,但该函数当前版本未实现,绑定后字段保持 NULL,直接调用 dll.ClearAllEvents(...) 会触发空指针调用崩溃,请勿调用。

C SDK 中无需手动逐个清除回调:调用 Dispose(master) 时 SDK 会整体释放该主站实例并停止所有回调线程。若需在不销毁主站的前提下停止某类回调,可重新注册一个空回调或在回调体内自行加开关标志。

线程安全

事件回调在 DLL 内部线程上触发,非 UI 线程。访问共享数据时需要使用互斥锁或原子操作。

注意

同步回调 (*Sync) 在实时 PDO 线程中直接执行,不要执行耗时操作(如 SDO 读写、文件 I/O、锁等待),否则会阻塞实时线程导致丢帧。 状态变化 / 热插拔等异步回调 (*Async) 在独立线程中执行,延迟略高但更安全。

/* 线程安全示例 — 使用互斥锁保护共享数据 */
#include <pthread.h>

static pthread_mutex_t data_lock = PTHREAD_MUTEX_INITIALIZER;
static int shared_counter = 0;

static void on_pdo_cyclic(uint16_t mi) {
pthread_mutex_lock(&data_lock);
shared_counter++;
pthread_mutex_unlock(&data_lock);
}

完整示例

#define DYNAMIC_LOAD
#include "ethercat.h"
#include <stdio.h>

static void on_log(int cat, const char* msg) {
const char* tags[] = {"错误","警告","消息","邮箱","PDO","调试"};
printf("[%s] %s\n", (cat >= 0 && cat <= 5) ? tags[cat] : "?", msg);
}
static void on_pdo_cyclic(uint16_t mi) { /* IO 读写 */ }
static void on_state_change(uint16_t mi, uint16_t si, int o, int n) {
printf("从站 %d: %d -> %d\n", si, o, n);
}
static void on_discovery(uint16_t mi, uint16_t si, BOOL found) {
printf("从站 %d %s\n", si, found ? "上线" : "离线");
}
static void on_emcy(uint16_t mi, uint16_t si,
uint16_t c, uint16_t r, uint8_t b, uint16_t w1, uint16_t w2) {
printf("从站 %d EMCY 0x%04X\n", si, c);
}
static void on_frame_loss(uint16_t mi, uint8_t g, uint32_t c, uint32_t t) {
printf("组 %d 丢帧: 连续=%u\n", g, c);
}
static void on_dc_lost(uint16_t mi, uint16_t si, int d) {
printf("从站 %d DC 失步 %dns\n", si, d);
}

int main(void) {
dll_t dll;
LOAD_DLL(&dll, "DarraEtherCAT.dll");

dll.SetLogCallback(on_log);
dll.RegisterPDOCyclicSync(on_pdo_cyclic);
dll.RegisterStateChangeAsync(on_state_change);
dll.RegisterDiscoveryAsync(on_discovery);
dll.RegisterEmergency(on_emcy);
dll.RegisterPDOFrameLoss(on_frame_loss);
dll.SetDCSyncLostCallback(on_dc_lost);

uint16_t master = dll.Initialize();
dll.SetNetwork(master, "\\Device\\NPF_{GUID}", "");
dll.SetStateSequence(master, EC_STATE_OPERATIONAL, 10000);
dll.Start(master);

getchar();

dll.Stop(master);
dll.Dispose(master); /* Dispose 整体停止所有回调线程 */
UNLOAD_DLL(&dll);
return 0;
}