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