EoE (Ethernet over EtherCAT)
EoE 协议通过 EtherCAT 总线实现以太网通信,支持配置 IP/MAC/DNS 地址和以太网帧收发,符合 ETG.1000.6 和 ETG.1020 标准。
通过 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 纪元), 通常取自GetMasterDCTimetimeout(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;
}