跳到主要内容

EoE (Ethernet over EtherCAT)

EoE 协议通过 EtherCAT 总线实现以太网通信,支持配置 IP/MAC/DNS 地址和以太网帧收发,符合 ETG.1000.6 和 ETG.1020 标准。

EoE 可用性检查

通过 GetSlaveEoEDetails(master, slave) 返回值判断从站是否支持 EoE(非 0 表示支持)。

IP 配置

EOESetIP()

BOOL EOESetIP(uint16_t master_index, uint16_t slave, uint8_t port,
uint32_t ip, uint32_t mask, uint32_t gateway, int timeout);

设置从站的 IP 地址、子网掩码和网关。

参数:

  • master_index (uint16_t) — 主站索引
  • slave (uint16_t) — 从站索引
  • port (uint8_t) — 端口号(通常为 0)
  • ip (uint32_t) — IP 地址(网络字节序)
  • mask (uint32_t) — 子网掩码
  • gateway (uint32_t) — 默认网关
  • timeout (int) — 超时时间(微秒)

返回值:

  • BOOL — 成功返回 TRUE

EOEGetIP()

BOOL EOEGetIP(uint16_t master_index, uint16_t slave, uint8_t port,
uint32_t* ip, uint32_t* mask, uint32_t* gateway, int timeout);

获取从站的 IP 地址、子网掩码和网关。

参数:

  • ip (uint32_t*) — 输出 IP 地址
  • mask (uint32_t*) — 输出子网掩码
  • gateway (uint32_t*) — 输出网关

返回值:

  • BOOL — 成功返回 TRUE

MAC 配置

EOESetMAC()

BOOL EOESetMAC(uint16_t master_index, uint16_t slave, uint8_t port,
const uint8_t* mac, int timeout);

设置从站的 MAC 地址。

参数:

  • mac (const uint8_t*) — MAC 地址(6 字节)
  • timeout (int) — 超时时间(微秒)

返回值:

  • BOOL — 成功返回 TRUE

EOEGetMAC()

BOOL EOEGetMAC(uint16_t master_index, uint16_t slave, uint8_t port,
uint8_t* mac, int timeout);

获取从站的 MAC 地址。

参数:

  • mac (uint8_t*) — 输出 MAC 地址缓冲区(至少 6 字节)

返回值:

  • BOOL — 成功返回 TRUE

DNS 配置

EOESetDNS()

BOOL EOESetDNS(uint16_t master_index, uint16_t slave, uint8_t port,
uint32_t dns_ip, const char* dns_name, int timeout);

设置从站的 DNS 服务器。

参数:

  • dns_ip (uint32_t) — DNS 服务器 IP 地址
  • dns_name (const char*) — DNS 名称
  • timeout (int) — 超时时间(微秒)

返回值:

  • BOOL — 成功返回 TRUE

EOEGetDNS()

BOOL EOEGetDNS(uint16_t master_index, uint16_t slave, uint8_t port,
uint32_t* dns_ip, char* dns_name, int timeout);

获取从站的 DNS 配置。

参数:

  • dns_ip (uint32_t*) — 输出 DNS IP 地址
  • dns_name (char*) — 输出 DNS 名称缓冲区

返回值:

  • BOOL — 成功返回 TRUE

完整参数读写

EOESetFullParam()

BOOL EOESetFullParam(uint16_t master_index, uint16_t slave, uint8_t port,
uint32_t ip, uint32_t mask, uint32_t gateway,
const uint8_t* mac, uint32_t dns_ip, const char* dns_name, int timeout);

一次性设置完整的网络参数(IP + MAC + DNS)。

EOEGetFullParam()

BOOL EOEGetFullParam(uint16_t master_index, uint16_t slave, uint8_t port,
uint32_t* ip, uint32_t* mask, uint32_t* gateway,
uint8_t* mac, uint32_t* dns_ip, char* dns_name, int timeout);

一次性获取完整的网络参数。

以太网帧收发

EOESendFrame()

BOOL EOESendFrame(uint16_t master_index, uint16_t slave, uint8_t port,
const uint8_t* data, int size, int timeout);

发送以太网帧。

参数:

  • data (const uint8_t*) — 帧数据
  • size (int) — 帧大小
  • timeout (int) — 超时时间(微秒)

返回值:

  • BOOL — 成功返回 TRUE

EOEReceiveFrame()

BOOL EOEReceiveFrame(uint16_t master_index, uint16_t slave, uint8_t port,
uint8_t** data, int* size, int timeout);

接收以太网帧。

参数:

  • data (uint8_t**) — 输出帧数据指针,需调用 FreeMemory() 释放
  • size (int*) — 输出帧大小

返回值:

  • BOOL — 成功返回 TRUE

带时间戳的发送

eoe_send_frame_with_timestamp()

BOOL eoe_send_frame_with_timestamp(uint16_t master_index, uint16_t slave,
uint8_t port,
const uint8_t* data, int size,
uint64_t timestamp_ns,
int timeout);

发送以太网帧, 同时附带应用层时间戳 (单位纳秒, 通常使用 DC 时钟基准, 见 GetMasterDCTime)。

不同于普通 EOESendFrame, 该函数将时间戳写入 EoE 帧扩展头, 适合需要"发出时刻"精确同步的场景, 如 PTP gateway / 工业相机触发 / 多从站时间标记日志。

参数:

  • master_index (uint16_t) — 主站索引
  • slave (uint16_t) — 从站索引
  • port (uint8_t) — 端口号 (通常为 0)
  • data (const uint8_t*) — 帧数据
  • size (int) — 帧大小
  • timestamp_ns (uint64_t) — 时间戳 (纳秒, 2000 纪元), 通常取自 GetMasterDCTime
  • timeout (int) — 超时 (微秒)

返回值:

  • BOOL — 成功返回 TRUE

示例:

#include "ethercat_advanced.h"

uint64_t now = dll.GetMasterDCTime(master);
eoe_send_frame_with_timestamp(master, 1, 0, frame, sizeof(frame),
now, 5000000);
从站支持

仅当从站 EoE 协议栈实现 ETG.1000.6 EoE 扩展头解析时, 时间戳才会被消费; 普通从站会按标准帧处理, 时间戳被静默丢弃。

地址过滤器

EOESetAddressFilter()

BOOL EOESetAddressFilter(uint16_t master_index, uint16_t slave, uint8_t port,
uint8_t filter_count, const uint8_t* mac_list, int timeout);

设置 MAC 地址过滤器列表。

参数:

  • filter_count (uint8_t) — 过滤器数量
  • mac_list (const uint8_t*) — MAC 地址列表(每个 6 字节)

EOEGetAddressFilter()

BOOL EOEGetAddressFilter(uint16_t master_index, uint16_t slave, uint8_t port,
uint8_t* filter_count, uint8_t* mac_list, int max_count, int timeout);

获取当前的 MAC 地址过滤器列表。

参数:

  • filter_count (uint8_t*) — 输出过滤器数量
  • mac_list (uint8_t*) — 输出 MAC 地址缓冲区
  • max_count (int) — 最大过滤器数

Ping 测试 (ethercat_advanced.h)

eoe_ping()

ping_result_t eoe_ping(dll_t* dll, uint16_t master, uint16_t slave,
uint8_t port, uint32_t target_ip, int timeout_ms);

通过 EoE 发送 ICMP Ping 并等待响应。

参数:

  • dll (dll_t*) — DLL 实例
  • target_ip (uint32_t) — 目标 IP 地址
  • timeout_ms (int) — 超时时间(毫秒)

返回值:

  • ping_result_t — Ping 结果

相关结构:

typedef struct {
BOOL success; /* 是否成功 */
uint32_t rtt_us; /* 往返时间 (微秒) */
uint8_t ttl; /* TTL */
} ping_result_t;

ARP 缓存管理 (ethercat_advanced.h)

eoe_arp_set()

void eoe_arp_set(uint32_t ip, const uint8_t mac[6]);

设置 ARP 缓存条目。

eoe_arp_clear()

void eoe_arp_clear(void);

清除所有 ARP 缓存。

eoe_arp_lookup()

BOOL eoe_arp_lookup(uint32_t ip, uint8_t mac[6]);

查找 ARP 缓存条目。

返回值:

  • BOOL — 找到返回 TRUE

EoE 异步接收钩子

EOEReceiveFrame 是同步 (阻塞) 收帧, 在高频应用里需要轮询线程; 使用 receive hook 后, PDO 主循环线程在收到 EoE Fragment Data 帧时直接回调用户函数, 无需轮询。对齐 C# DLL.dx_eoe_set_receive_hook / dx_eoe_clear_receive_hook (老 DLL 未导出对应符号时返回 FALSE)。

typedef void (*eoe_frame_cb_t)(uint16_t slave_index,
uint8_t port,
const uint8_t* frame_data,
int frame_size,
void* user_data);

BOOL eoe_set_receive_hook (dll_t* dll, uint16_t master_index, eoe_frame_cb_t cb);
BOOL eoe_clear_receive_hook(dll_t* dll, uint16_t master_index);

/* 带 DC 时间戳的发送 (高级, 自动 frameinfo1.TIME_APPEND) */
BOOL eoe_send_frame_with_timestamp(dll_t* dll,
uint16_t master_index, uint16_t slave, uint8_t port,
const uint8_t* data, int size,
uint32_t timestamp, int timeout);

eoe_set_receive_hook — 注册全 master 共享的回调; 每个 EoE Fragment Data 帧 (任意 slave / port) 都会触发一次, 调用方按 slave_index / port 自行分发。

回调线程模型
  • 回调在 PDO 主循环线程 内同步执行, 必须立即拷贝 frame_data 后返回, 不要阻塞 (Sleep / 文件 IO / 等待锁) — 否则直接拖累整个总线
  • frame_data 指向 SDK 内部缓冲, 回调返回后立即释放, 不可在回调外继续访问
  • 同一 master 同时只能注册一个 hook, 重复 set 后注册者覆盖前一个
  • 老 DLL 未导出对应符号时返回 FALSE, 应用应回退到 EOEReceiveFrame 轮询模式

示例:

typedef struct { volatile int got_frame; uint8_t buf[1500]; int len; } ctx_t;

static void on_eoe_recv(uint16_t slave, uint8_t port,
const uint8_t* data, int size, void* ud) {
ctx_t* c = (ctx_t*)ud;
if (size <= sizeof(c->buf)) {
memcpy(c->buf, data, size);
c->len = size;
c->got_frame = 1; /* 唤醒应用线程 */
}
}

ctx_t ctx = {0};
eoe_set_receive_hook(&dll, master, on_eoe_recv /*, &ctx — 通过全局指针传递 */);

/* 应用线程 */
while (running) {
if (ctx.got_frame) {
process_frame(ctx.buf, ctx.len);
ctx.got_frame = 0;
} else {
Sleep(1);
}
}

eoe_clear_receive_hook(&dll, master);

完整示例

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

int main(void)
{
dll_t dll;
LOAD_DLL(&dll, "Darra.Core.dll");
uint16_t master = dll.Initialize();
dll.SetNetwork(master, "\\Device\\NPF_{...}", "");
dll.SetStateSequence(master, EC_STATE_PRE_OP, 5000);

uint16_t slave = 1;

/* 读取当前 IP 配置 */
uint32_t ip, mask, gw;
if (dll.EOEGetIP(master, slave, 0, &ip, &mask, &gw, 5000000)) {
printf("IP: %d.%d.%d.%d\n",
ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF);
}

/* 读取 MAC */
uint8_t mac[6];
if (dll.EOEGetMAC(master, slave, 0, mac, 5000000)) {
printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}

/* 设置新 IP */
uint32_t new_ip = 0x6401A8C0; /* 192.168.1.100 */
uint32_t new_mask = 0x00FFFFFF; /* 255.255.255.0 */
uint32_t new_gw = 0x0101A8C0; /* 192.168.1.1 */
dll.EOESetIP(master, slave, 0, new_ip, new_mask, new_gw, 5000000);

/* Ping 测试 */
ping_result_t ping = eoe_ping(&dll, master, slave, 0, new_gw, 5000);
if (ping.success) {
printf("Ping 成功: RTT=%u us, TTL=%d\n", ping.rtt_us, ping.ttl);
}

/* 帧收发 */
uint8_t frame[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 目标 MAC */
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* 源 MAC */
0x08, 0x00}; /* EtherType: IP */
dll.EOESendFrame(master, slave, 0, frame, sizeof(frame), 5000000);

uint8_t* recv_data = NULL;
int recv_size = 0;
if (dll.EOEReceiveFrame(master, slave, 0, &recv_data, &recv_size, 5000000)) {
printf("收到帧: %d 字节\n", recv_size);
dll.FreeMemory(recv_data);
}

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