跳到主要内容

从站事件

所有事件回调包含 master_indexslave_index 参数,由调用方自行过滤。

完整回调注册列表详见 事件

协议专属事件

FSoE 安全事件通过独立回调注册,请参考 FSoE

功能概览

类别注册函数说明
状态变化RegisterSlaveStateChangeCallbackSync/Async从站 EtherCAT 状态变化
紧急消息RegisterEmergencyEventCallbackCoE Emergency 紧急消息
热插拔RegisterSlaveDiscoveryCallbackSync/Async从站上线/离线
DC 同步丢失SetDCSyncLostCallbackDC 同步偏差超过阈值
输入数据变化RegisterInputDataChangedCallback输入 PDO 数据变化
PreOP 重配置RegisterSlavePreOpReconfigCallback热插拔后自动恢复

状态变化

void RegisterSlaveStateChangeCallbackSync(state_change_callback_t callback);
void RegisterSlaveStateChangeCallbackAsync(state_change_callback_t callback);

从站 EtherCAT 状态变化通知。

回调类型:

typedef void (*state_change_callback_t)(uint16_t master_index, uint16_t slave_index,
int old_state, int new_state);

紧急消息

void RegisterEmergencyEventCallback(emergency_callback_t callback);

CoE Emergency 消息通知。

回调类型:

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);

回调参数说明:

  • error_code (uint16_t) — 错误代码(CANopen Emergency Error Code)
  • error_reg (uint16_t) — 错误寄存器(对象 0x1001)
  • b1 (uint8_t) — 制造商特定数据(字节 3)
  • w1 (uint16_t) — 制造商特定数据(字节 4-5)
  • w2 (uint16_t) — 制造商特定数据(字节 6-7)

从站发现/移除(热插拔)

void RegisterSlaveDiscoveryCallbackSync(discovery_callback_t callback);
void RegisterSlaveDiscoveryCallbackAsync(discovery_callback_t callback);

从站上线/离线通知。

回调类型:

typedef void (*discovery_callback_t)(uint16_t master_index, uint16_t slave_index, BOOL is_found);

DC 同步丢失

void SetDCSyncLostCallback(dc_sync_lost_callback_t callback);

DC 同步偏差超过阈值时通知。

回调类型:

typedef void (*dc_sync_lost_callback_t)(uint16_t master_index, uint16_t slave_index, int diff_ns);

输入 PDO 数据变化

void RegisterInputDataChangedCallback(input_changed_callback_t callback);

输入 PDO 数据发生变化时通知。DLL 层自动检测每个从站的输入数据变化,仅当输入数据与上一周期不同时触发回调。

回调类型:

typedef void (*input_changed_callback_t)(uint16_t master_index,
const uint8_t* changed_slave_bits, uint16_t changed_count);

回调参数说明:

  • master_index (uint16_t) — 主站索引
  • changed_slave_bits (const uint8_t*) — 变化从站位图(每个 bit 对应一个从站,1=变化)
  • changed_count (uint16_t) — 变化的从站数量
零开销
  • 无变化时:不触发回调,零性能开销
  • 有变化时:通过位图高效传递,一次回调覆盖所有变化从站

示例:

static void on_input_changed(uint16_t mi, const uint8_t* bits, uint16_t count) {
/* 检查从站1是否有变化 (从站索引 1-based, bit 0 = 从站1) */
if (bits[0] & 0x01) {
/* 读取从站1的输入数据 */
uint16_t status = dll.PDOReadInputU16(mi, 1, 0);
printf("从站1 状态字变化: 0x%04X\n", status);
}
}

PreOP 重配置

void RegisterSlavePreOpReconfigCallback(preop_reconfig_callback_t callback);

从站需要 PreOP 重配置时通知(热插拔后自动恢复)。

回调类型:

typedef void (*preop_reconfig_callback_t)(uint16_t master_index, uint16_t slave_index);

线程安全

同步 vs 异步回调

同步回调 (*Sync) 在实时 PDO 线程中直接执行,不要执行耗时操作(如文件IO、网络请求、锁竞争)。 异步回调 (*Async) 在独立线程中执行,延迟略高但更安全。

回调中避免执行耗时操作。如需处理大量数据,将数据放入队列异步处理:

static void on_state_sync(uint16_t mi, uint16_t si, int old_s, int new_s) {
/* 快速入队,不做耗时操作 */
enqueue_event(mi, si, old_s, new_s);
}

完整示例

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

/* ===== 状态事件 ===== */
static void on_state(uint16_t mi, uint16_t si, int old_s, int new_s) {
printf("从站 %d 状态变化: %d -> %d\n", si, old_s, new_s);
}

/* ===== 热插拔事件 ===== */
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 code, uint16_t reg, uint8_t b1, uint16_t w1, uint16_t w2) {
printf("从站 %d 紧急消息: 错误码=0x%04X, 寄存器=0x%04X\n", si, code, reg);
}

/* ===== DC 同步丢失事件 ===== */
static void on_dc_sync_lost(uint16_t mi, uint16_t si, int diff_ns) {
printf("从站 %d DC 同步丢失: 偏差 %d ns\n", si, diff_ns);
}

/* ===== 输入数据变化事件 ===== */
static void on_input_changed(uint16_t mi, const uint8_t* bits, uint16_t count) {
printf("输入数据变化: %d 个从站\n", count);
}

/* ===== PreOP 重配置事件 ===== */
static void on_preop_reconfig(uint16_t mi, uint16_t si) {
printf("从站 %d 需要 PreOP 重配置\n", si);
}

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

/* 注册所有从站事件回调 */
if (dll.RegisterStateChangeSync) dll.RegisterStateChangeSync(on_state);
if (dll.RegisterDiscoverySync) dll.RegisterDiscoverySync(on_discovery);
if (dll.RegisterEmergency) dll.RegisterEmergency(on_emcy);
if (dll.SetDCSyncLostCallback) dll.SetDCSyncLostCallback(on_dc_sync_lost);
if (dll.RegisterInputChanged) dll.RegisterInputChanged(on_input_changed);
if (dll.RegisterPreOpReconfig) dll.RegisterPreOpReconfig(on_preop_reconfig);

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

printf("按回车退出...\n");
getchar();

dll.Stop(master);
dll.Dispose(master);
UNLOAD_DLL(&dll);
return 0;
}