跳到主要内容

EoE (Ethernet over EtherCAT)

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

EoE 可用性检查

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

函数命名

EoE 邮箱配置/收发在 C SDK 中以 dx_eoe_* 前缀提供。动态加载模式经 dll.dx_eoe_set_ip(...) 调用,静态链接模式直接调 dx_eoe_set_ip(...)

IP 配置

dx_eoe_set_ip()

BOOL dx_eoe_set_ip(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

dx_eoe_get_ip()

BOOL dx_eoe_get_ip(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 配置

dx_eoe_set_mac()

BOOL dx_eoe_set_mac(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

dx_eoe_get_mac()

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

获取从站的 MAC 地址。

参数:

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

返回值:

  • BOOL — 成功返回 TRUE

DNS 配置

dx_eoe_set_dns()

BOOL dx_eoe_set_dns(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

dx_eoe_get_dns()

BOOL dx_eoe_get_dns(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

完整参数读写

dx_eoe_set_full_param()

BOOL dx_eoe_set_full_param(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)。

dx_eoe_get_full_param()

BOOL dx_eoe_get_full_param(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);

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

以太网帧收发

dx_eoe_send_frame()

BOOL dx_eoe_send_frame(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

dx_eoe_receive_frame()

BOOL dx_eoe_receive_frame(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

带时间戳的发送 (slave/eoe.h)

eoe_send_frame_with_timestamp()

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

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

不同于普通 dx_eoe_send_frame, 该函数置 EoE 帧 frameinfo1.TIME_APPEND 标志附带时间戳, 适合需要"发出时刻"精确同步的场景, 如 PTP gateway / 工业相机触发 / 多从站时间标记日志。DLL 导出 dx_eoe_send_frame_ex 时透传; 老 DLL 未导出时退化为帧末尾追加 4 字节后走 dx_eoe_send_frame

参数:

  • dll (dll_t*) — DLL 实例
  • master_index (uint16_t) — 主站索引
  • slave (uint16_t) — 从站索引
  • port (uint8_t) — 端口号 (通常为 0)
  • data (const uint8_t*) — 帧数据
  • size (int) — 帧大小
  • timestamp (uint32_t) — 时间戳 (32 位, 取自 DC 时钟低 32 位)
  • timeout (int) — 超时 (微秒)

返回值:

  • BOOL — 成功返回 TRUE

示例:

#include "slave/eoe.h"

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

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

地址过滤器

dx_eoe_set_address_filter()

BOOL dx_eoe_set_address_filter(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 字节)

dx_eoe_get_address_filter()

BOOL dx_eoe_get_address_filter(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 异步接收钩子

dx_eoe_receive_frame 是同步 (阻塞) 收帧, 在高频应用里需要轮询线程; 使用 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 master_index,
uint16_t slave,
const uint8_t* frame_data,
int frame_size);

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

eoe_set_receive_hook — 注册全 master 共享的回调; 每个 EoE Fragment Data 帧 (任意 slave) 都会触发一次, 调用方按 master_index / slave 自行分发。回调签名固定为 4 参数 (无 port / user_data 形参), 需要传递上下文请用全局或静态变量。

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

示例:

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

/* 回调无 user_data 形参, 上下文用全局/静态变量传递 */
static ctx_t g_ctx = {0};

static void on_eoe_recv(uint16_t master_index, uint16_t slave,
const uint8_t* data, int size) {
if (size >= 0 && (size_t)size <= sizeof(g_ctx.buf)) {
memcpy(g_ctx.buf, data, (size_t)size);
g_ctx.len = size;
g_ctx.got_frame = 1; /* 唤醒应用线程 */
}
}

eoe_set_receive_hook(&dll, master, on_eoe_recv);

/* 应用线程 */
while (running) {
if (g_ctx.got_frame) {
process_frame(g_ctx.buf, g_ctx.len);
g_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, "DarraEtherCAT.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.dx_eoe_get_ip(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.dx_eoe_get_mac(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.dx_eoe_set_ip(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.dx_eoe_send_frame(master, slave, 0, frame, sizeof(frame), 5000000);

uint8_t* recv_data = NULL;
int recv_size = 0;
if (dll.dx_eoe_receive_frame(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;
}