跳到主要内容

VoE (Vendor over EtherCAT)

VoE 协议允许设备厂商在 EtherCAT 邮箱中传输自定义私有数据,用于实现非标准的厂商特定功能(Mailbox Type 0x0F)。

函数命名

VoE 邮箱收发在 C SDK 中以 dx_voe_* 前缀提供。动态加载模式经 dll.dx_voe_send(...) 调用,静态链接模式直接调 dx_voe_send(...)

功能检测

dx_voe_is_supported()

BOOL dx_voe_is_supported(uint16_t master_index, uint16_t slave);

检查从站是否支持 VoE 协议。

参数:

  • master_index (uint16_t) — 主站索引
  • slave (uint16_t) — 从站索引

返回值:

  • BOOL — 支持返回 TRUE

结构化收发

dx_voe_send()

BOOL dx_voe_send(uint16_t master_index, uint16_t slave, uint32_t vendor_id,
uint16_t vendor_type, const uint8_t* data, int size, int timeout);

发送带有厂商标识的 VoE 数据包。

参数:

  • master_index (uint16_t) — 主站索引
  • slave (uint16_t) — 从站索引
  • vendor_id (uint32_t) — 厂商 ID
  • vendor_type (uint16_t) — 厂商自定义类型
  • data (const uint8_t*) — 数据
  • size (int) — 数据大小
  • timeout (int) — 超时时间(微秒)

返回值:

  • BOOL — 成功返回 TRUE

dx_voe_receive()

BOOL dx_voe_receive(uint16_t master_index, uint16_t slave, uint32_t* vendor_id,
uint16_t* vendor_type, uint8_t** data, int* size, int timeout);

接收带有厂商标识的 VoE 数据包。

参数:

  • vendor_id (uint32_t*) — 输出厂商 ID
  • vendor_type (uint16_t*) — 输出厂商自定义类型
  • data (uint8_t**) — 输出数据指针,需调用 FreeMemory() 释放
  • size (int*) — 输出数据大小
  • timeout (int) — 超时时间(微秒)

返回值:

  • BOOL — 成功返回 TRUE

原始收发

dx_voe_send_raw()

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

发送原始 VoE 数据(不含厂商头部)。

参数:

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

返回值:

  • BOOL — 成功返回 TRUE

dx_voe_receive_raw()

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

接收原始 VoE 数据(不含厂商头部)。

参数:

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

返回值:

  • BOOL — 成功返回 TRUE

异步通知 (slave/voe.h)

VoE 不像 CoE 那样有标准的事件机制, 但厂商常需要"从站主动推送" → "应用回调" 的语义。slave/voe.h 通过后台监听线程实现, 应用层只需注册订阅就能在数据到达时收到通知。对应底层接口 dx_voe_start_notification_listener / dx_voe_register_notification 等由 advanced 层封装, 应用层无需直接调用。

voe_notification_cb_t

typedef void (*voe_notification_cb_t)(uint16_t slave,
uint32_t vendor_id,
uint16_t vendor_type,
const uint8_t* data,
uint32_t data_size,
void* user_data);

回调参数:

  • slave (uint16_t) — 从站索引
  • vendor_id (uint32_t) — 厂商 ID
  • vendor_type (uint16_t) — 厂商自定义类型
  • data (const uint8_t*) — 收到的 VoE 数据 (仅回调期间有效)
  • data_size (uint32_t) — 数据大小
  • user_data (void*) — 注册时传入的上下文指针

voe_start_notification_listener()

int voe_start_notification_listener(uint16_t master_index);

启动指定主站的 VoE 通知监听线程。返回 1=成功, 0=FFI 未就绪 / 失败。

voe_register_notification()

int voe_register_notification(uint16_t master_index, uint16_t slave,
uint32_t vendor_id, uint16_t vendor_type,
voe_notification_cb_t cb, void* user_data);

注册一个 VoE 通知订阅 (按 vendor_id / vendor_type 过滤)。返回 >= 0 的订阅索引, 负值表示失败。

voe_unregister_notification()

int voe_unregister_notification(int subscription_index);

按订阅索引注销订阅。

voe_stop_notification_listener()

int voe_stop_notification_listener(void);

停止监听线程。

voe_is_notification_listening()

int voe_is_notification_listening(void);

查询监听线程是否在运行 (1=运行中)。

示例:

#include "slave/voe.h"

static void on_voe_data(uint16_t si, uint32_t vid, uint16_t vtype,
const uint8_t* data, uint32_t size, void* user)
{
printf("VoE 推送: slave=%u, VendorID=0x%08X, Type=0x%04X, %u 字节\n",
si, vid, vtype, size);
/* data 仅回调期间有效, 需要持久化请立即复制 */
}

int main(void)
{
dll_t dll;
LOAD_DLL(&dll, "DarraEtherCAT.dll");
uint16_t master = dll.Initialize();
/* ... 初始化省略 ... */

/* 启动监听线程 */
voe_start_notification_listener(master);

/* 注册从站 1 / 2 的订阅 */
int sub1 = voe_register_notification(master, 1, 0x12345678, 0x0001, on_voe_data, NULL);
int sub2 = voe_register_notification(master, 2, 0x12345678, 0x0001, on_voe_data, NULL);

/* ... 业务循环 ... */

/* 停止并清理 */
voe_unregister_notification(sub1);
voe_unregister_notification(sub2);
voe_stop_notification_listener();

dll.Dispose(master);
UNLOAD_DLL(&dll);
return 0;
}
回调线程

回调在 advanced 层内部的监听线程上调用, 与主程序异步。访问共享数据请使用互斥锁; 不要在回调内调用阻塞式 dx_voe_send / dx_voe_receive, 会与监听自身竞争邮箱。

完整示例

#define DYNAMIC_LOAD
#include "ethercat.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;

/* 检查 VoE 支持 */
if (!dll.dx_voe_is_supported(master, slave)) {
printf("从站不支持 VoE\n");
dll.Dispose(master);
UNLOAD_DLL(&dll);
return 1;
}

/* 发送结构化数据 */
uint8_t cmd[] = {0x01, 0x02, 0x03, 0x04};
if (dll.dx_voe_send(master, slave, 0x12345678, 0x0001, cmd, sizeof(cmd), 5000000)) {
printf("VoE 发送成功\n");
}

/* 接收响应 */
uint32_t vid;
uint16_t vtype;
uint8_t* resp = NULL;
int resp_size = 0;
if (dll.dx_voe_receive(master, slave, &vid, &vtype, &resp, &resp_size, 5000000)) {
printf("VoE 响应: VendorID=0x%08X, Type=0x%04X, %d 字节\n", vid, vtype, resp_size);
for (int i = 0; i < resp_size; i++)
printf("%02X ", resp[i]);
printf("\n");
dll.FreeMemory(resp);
}

/* 原始帧收发 */
uint8_t raw[] = {0xAA, 0xBB, 0xCC};
dll.dx_voe_send_raw(master, slave, raw, sizeof(raw), 5000000);

uint8_t* raw_resp = NULL;
int raw_size = 0;
if (dll.dx_voe_receive_raw(master, slave, &raw_resp, &raw_size, 5000000)) {
printf("原始响应: %d 字节\n", raw_size);
dll.FreeMemory(raw_resp);
}

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