VoE (Vendor over EtherCAT)
VoE 协议允许设备厂商在 EtherCAT 邮箱中传输自定义私有数据,用于实现非标准的厂商特定功能(Mailbox Type 0x0F)。
功能检测
VOEIsSupported()
BOOL VOEIsSupported(uint16_t master_index, uint16_t slave);
检查从站是否支持 VoE 协议。
参数:
master_index(uint16_t) — 主站索引slave(uint16_t) — 从站索引
返回值:
BOOL— 支持返回TRUE
结构化收发
VOESend()
BOOL VOESend(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) — 厂商 IDvendor_type(uint16_t) — 厂商自定义类型data(const uint8_t*) — 数据size(int) — 数据大小timeout(int) — 超时时间(微秒)
返回值:
BOOL— 成功返回TRUE
VOEReceive()
BOOL VOEReceive(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*) — 输出厂商 IDvendor_type(uint16_t*) — 输出厂商自定义类型data(uint8_t**) — 输出数据指针,需调用FreeMemory()释放size(int*) — 输出数据大小timeout(int) — 超时时间(微秒)
返回值:
BOOL— 成功返回TRUE
原始收发
VOESendRaw()
BOOL VOESendRaw(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
VOEReceiveRaw()
BOOL VOEReceiveRaw(uint16_t master_index, uint16_t slave,
uint8_t** data, int* size, int timeout);
接收原始 VoE 数据(不含厂商头部)。
参数:
data(uint8_t**) — 输出数据指针,需调用FreeMemory()释放size(int*) — 输出数据大小
返回值:
BOOL— 成功返回TRUE
异步通知 (ethercat_advanced.h)
VoE 不像 CoE 那样有标准的事件机制, 但厂商常需要"从站主动推送" → "应用回调" 的语义。SDK 通过周期性轮询 VOEReceiveRaw 实现, 应用层只需注册回调就能在数据到达时收到通知。
voe_notification_cb_t
typedef void (*voe_notification_cb_t)(
uint16_t master, uint16_t slave,
const uint8_t* data, int size,
void* user_data);
回调参数:
master(uint16_t) — 主站索引slave(uint16_t) — 从站索引data(const uint8_t*) — 收到的 VoE 原始数据 (仅回调期间有效)size(int) — 数据大小user_data(void*) — 注册时传入的上下文指针
voe_notification_register()
int voe_notification_register(dll_t* dll,
uint16_t master, uint16_t slave,
voe_notification_cb_t callback, void* user_data);
为指定从站注册 VoE 数据到达回调。同一从站重复注册会覆盖旧回调。
返回值:
int— 0 成功, 非 0 失败
voe_notification_unregister()
int voe_notification_unregister(dll_t* dll,
uint16_t master, uint16_t slave);
取消注册指定从站的 VoE 回调。
voe_notification_set_poll_interval()
void voe_notification_set_poll_interval(uint32_t interval_ms);
设置后台轮询周期 (默认 100ms)。值越小响应越快, 但邮箱占用越高。
voe_notification_start() / voe_notification_stop()
int voe_notification_start(dll_t* dll, uint16_t master);
int voe_notification_stop(dll_t* dll, uint16_t master);
启动/停止后台轮询线程。Register 后必须调用 Start 才会有回调; Unregister 后建议在所有从站都已取消时调用 Stop 释放线程。
示例:
#include "ethercat_advanced.h"
static void on_voe_data(uint16_t mi, uint16_t si,
const uint8_t* data, int size, void* user)
{
printf("VoE 推送: slave=%u, %d 字节\n", si, size);
/* data 仅回调期间有效, 需要持久化请立即复制 */
}
int main(void)
{
dll_t dll;
LOAD_DLL(&dll, "Darra.Core.dll");
uint16_t master = dll.Initialize();
/* ... 初始化省略 ... */
/* 设 50ms 轮询 */
voe_notification_set_poll_interval(50);
/* 监听从站 1 / 2 */
voe_notification_register(&dll, master, 1, on_voe_data, NULL);
voe_notification_register(&dll, master, 2, on_voe_data, NULL);
/* 启动轮询线程 */
voe_notification_start(&dll, master);
/* ... 业务循环 ... */
/* 停止并清理 */
voe_notification_unregister(&dll, master, 1);
voe_notification_unregister(&dll, master, 2);
voe_notification_stop(&dll, master);
dll.Dispose(master);
UNLOAD_DLL(&dll);
return 0;
}
回调线程
回调在 advanced.h 内部的轮询线程上调用, 与主程序异步。访问共享数据请使用互斥锁; 不要在回调内调用阻塞式 VOESend/Receive, 会与轮询自身竞争邮箱。
完整示例
#define DYNAMIC_LOAD
#include "ethercat.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;
/* 检查 VoE 支持 */
if (!dll.VOEIsSupported(master, slave)) {
printf("从站不支持 VoE\n");
dll.Dispose(master);
UNLOAD_DLL(&dll);
return 1;
}
/* 发送结构化数据 */
uint8_t cmd[] = {0x01, 0x02, 0x03, 0x04};
if (dll.VOESend(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.VOEReceive(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.VOESendRaw(master, slave, raw, sizeof(raw), 5000000);
uint8_t* raw_resp = NULL;
int raw_size = 0;
if (dll.VOEReceiveRaw(master, slave, &raw_resp, &raw_size, 5000000)) {
printf("原始响应: %d 字节\n", raw_size);
dll.FreeMemory(raw_resp);
}
dll.Dispose(master);
UNLOAD_DLL(&dll);
return 0;
}