跳到主要内容

初始化

推荐方式

强烈建议使用 Darra 配置工具导出的 DENI 文件进行初始化。
DENI 文件包含完整的网络配置(网口、从站、PDO 映射、DC 设置等),可确保配置的一致性和正确性。
使用 DENI 文件管理设备硬件设施将获得更好的兼容性与通用性。

创建主站​

C SDK 直接调用同名函数:#include "ethercat.h" 之后即可用,无句柄、无函数指针表。主站索引 master_index 作为所有后续 API 的第一个参数。

Initialize()​

uint16_t Initialize(void);
uint16_t InitializeSpecificMaster(uint16_t master_index);
int GetMaxMasterInstances(void);

Initialize() 创建一个新的主站实例并返回其索引(>0 成功,0 失败)。InitializeSpecificMaster() 初始化指定索引的主站(不允许索引 0)。GetMaxMasterInstances() 返回允许的最大主站实例数。

SetNetwork()​

uint16_t SetNetwork(uint16_t master_index, const char* if_primary, const char* if_secondary);

绑定网卡。if_secondary 传空字符串 "" 表示单网卡模式。

参数:

  • master_index (uint16_t) — 主站索引
  • if_primary (const char*) — 主网卡名称(Windows: \\Device\\NPF_{GUID},Linux: eth0)
  • if_secondary (const char*) — 冗余网卡名称("" 表示无冗余)

返回值:

  • uint16_t — 发现的从站数量,0 或 0xFFFF 表示失败

示例:

uint16_t master = Initialize();
uint16_t slave_count = SetNetwork(master, "\\Device\\NPF_{GUID}", "");
printf("发现 %d 个从站\n", slave_count);

资源释放​

Dispose()​

BOOL Dispose(uint16_t master_index);
void FreeMemory(void* ptr);
void EmergencyCloseNics(void);
  • Dispose() 释放主站实例,返回是否成功。调用后 master_index 不可再使用。
  • FreeMemory() 释放 SDK 中标注「调用方 FreeMemory」的返回值(如 GetRedundancyStatus() 返回的状态结构)。GetSerialNumber() 等只读借用字符串禁止用它释放(静态/缓存内存,释放会崩溃)。
  • EmergencyCloseNics() 紧急关闭所有网卡句柄,在进程异常退出(信号处理 / atexit)时调用。

多实例管理​

每个主站实例管理一条独立的 EtherCAT 总线,使用不同的网卡。Initialize() 多次调用即可创建多个实例。

多实例要求
  • 每个实例必须使用不同的网卡(同一网卡不能被多个实例同时使用)
  • CPU 核心由 SDK 自动分配,不同实例占用不同核心
  • Mailbox Gateway 端口自动按实例索引偏移
  • 授权与日志在所有实例间共享
uint16_t master1 = Initialize();
SetNetwork(master1, "\\Device\\NPF_{GUID_A}", "");

uint16_t master2 = Initialize();
SetNetwork(master2, "\\Device\\NPF_{GUID_B}", "");

SetStateSequence(master1, EC_STATE_OPERATIONAL, 10000);
SetStateSequence(master2, EC_STATE_OPERATIONAL, 10000);

Dispose(master1);
Dispose(master2);

网络扫描​

网卡枚举范围

默认只列出已绑定 DarraRT_Eth 的物理有线口。虚拟网卡、WiFi、TAP 不进列表。ASIX 等 USB 有线网卡按物理有线口保留。未绑定口不要当作主口候选(条目中的 boundProtocol)。

GetNetworkInfo()​

int   GetNetworkInfo(BOOL is_redundant, BOOL need_slaves_num);
void* GetNetworksPointer(void);

GetNetworkInfo() 枚举网卡并返回数量。默认列表只含已绑定的物理有线口。need_slaves_num=TRUE 时同时为每张口填从站数;FALSE 时仅列举。GetNetworksPointer() 返回内部网卡列表指针,无需释放。每条记录含 boundProtocol(非 0 = 已绑定 DarraRT_Eth)。

QuickSlaveCount()​

int QuickSlaveCount(const char* adapter_name);
int GetRingSlaveCount(void);
int QuickFindRedundantPairBatch(const char** adapter_names, int adapter_count,
int* primary_idx, int* secondary_idx,
int* out_ring_count, int* out_total_count);
  • QuickSlaveCount() — DENI / 下拉数量:不读 EEPROM,仅 BRD,返回从站个数。
  • GetRingSlaveCount() — 上一次冗余扫描中环上的从站数。
  • QuickFindRedundantPairBatch() — 在多张 NIC 中批量探测冗余对,输出主/副适配器索引 (primary_idx / secondary_idx) 以及环上从站数 (out_ring_count) 与总从站数 (out_total_count)。
两条扫描不要混
  • 快速计数:QuickSlaveCount()(DENI)。
  • 完整扫描(选用从站):ScanSlaveInfo() + GetScannedSlaveInfo()。count > 0 时不得当成 0 个从站。

ReadSlaveInfo()​

int  ReadSlaveInfo(const char* adapter_name);
int GetScannedSlaveCount(void);
BOOL GetScannedSlaveInfo(int index,
uint32_t* vendor_id, uint32_t* product_code,
uint32_t* revision, uint32_t* serial,
char* name, int name_size,
uint16_t* config_addr, uint16_t* alias_addr,
uint16_t* parent, uint8_t* topology, uint8_t* activeports,
uint8_t* entryport, uint8_t* parentport, uint8_t* ptype);

ReadSlaveInfo() 扫描指定网卡上所有从站并缓存详情,返回从站数量。配合 GetScannedSlaveCount() 与 GetScannedSlaveInfo() 按索引读取每个从站的厂商 ID / 产品代码 / 拓扑信息等(index 0-based)。

示例:

int count = ReadSlaveInfo("\\Device\\NPF_{GUID}");
for (int i = 0; i < count; i++) {
uint32_t vid, pid, rev, serial;
char name[128];
uint16_t cfg_addr, alias, parent;
uint8_t topo, ports, entry, pport, ptype;
if (GetScannedSlaveInfo(i, &vid, &pid, &rev, &serial,
name, sizeof(name), &cfg_addr, &alias,
&parent, &topo, &ports, &entry, &pport, &ptype)) {
printf("[%d] %s (VID=0x%08X, PID=0x%08X)\n", i + 1, name, vid, pid);
}
}

AbortScan()​

void AbortScan(void);
void ResetScanAbort(void);
void AbortNetwork(void);
void ResetAbortNetwork(void);

中止/重置正在进行的网络扫描操作。AbortScan() 用于关闭 UI 窗口或取消操作时快速中断阻塞的扫描;AbortNetwork() 作用于网络层(绑定/通讯)的取消。

一步初始化 (EcInit)​

EcInit()​

uint16_t EcInit(const char* json_config);
uint16_t EcInitFromFile(const char* json_file_path);
void EcClose(uint16_t master_index);

EcInit() 通过 JSON 字符串一步完成 Initialize + SetNetwork + LoadConfig + SetStateSequence + Start。EcInitFromFile() 从文件读取 JSON。EcClose() 一步关闭并释放,配对使用。

JSON 配置:

{
"adapter": "\\Device\\NPF_{GUID}",
"redundant_adapter": "",
"cycle_time_us": 1000,
"dc_sync0_ns": 1000000,
"target_state": "OP",
"slaves": []
}

字段说明:

  • adapter (必选) — 主网卡名称
  • redundant_adapter (可选) — 冗余网卡名称(空字符串=单网卡)
  • cycle_time_us (可选) — PDO 周期,默认 1000
  • dc_sync0_ns (可选) — DC SYNC0 周期,0=不配置 DC
  • target_state (可选) — 目标状态("OP"/"SAFEOP"/"PREOP")
  • slaves (可选) — 从站启动参数配置数组

ENI/DENI 配置​

ethercat.h 自动包含 utils/xml.h。用户 API 是 deni_load / eni_load / xml_apply_config,以及把已解析结构写回文件的 deni_save / eni_save。保存配置 / 启动快照在主站自愈窗口内采用有限等待(秒级上限,短轮询),超时返回错误,不会长时间挂起。

不是用户 API

SaveXML / save_xml_configuration / load_and_apply_xml_configuration 不是 C SDK 用户 API:ethercat.h 伞形头不导出,utils/xml.h 也不再声明。活主站导出仅 GUI 内部使用(internal/xml_live.h)。

deni_load()​

int deni_load(const char* path,
DarraMasterXmlConfig* master_out,
DarraSlaveXmlConfig* slaves_out,
int max_slaves);

从 DENI 文件加载配置。根元素 DarraEtherCATConfig。

参数:

  • path (const char*) — DENI XML 文件路径
  • master_out (DarraMasterXmlConfig*) — 主站配置输出(调用前清零)
  • slaves_out (DarraSlaveXmlConfig*) — 从站配置数组
  • max_slaves (int) — 数组容量

返回值:

  • int — ≥0 从站数量;-1 参数无效;-2 文件打开失败;-3 XML 解析失败;-4 根元素无效

eni_load()​

int eni_load(const char* path,
DarraMasterXmlConfig* master_out,
DarraSlaveXmlConfig* slaves_out,
int max_slaves);

从标准 ENI 文件加载(根元素 EtherCATConfig)。签名与返回值同 deni_load。

xml_apply_config()​

int xml_apply_config(const DarraMasterXmlConfig* master_config,
uint16_t master_index);

将 deni_load / eni_load 解析出的主站配置下发到主站。须在 SetNetwork 之前(INIT)调用。

参数:

  • master_config (const DarraMasterXmlConfig*) — 主站 XML 配置
  • master_index (uint16_t) — 主站索引

返回值:

  • int — 0 成功;-1 参数无效;-2 主站状态不可读 / 薄壳入口不可用

下发项包括周期、CPU 亲和、冗余、UDP、IOmap 锁、VLAN/帧优先级、PDO 优化、状态超时、看门狗。

示例:

DarraMasterXmlConfig cfg = {0};
DarraSlaveXmlConfig slaves[XML_MAX_SLAVES];
int n = deni_load("C:\\EtherCAT\\MyProject.deni", &cfg, slaves, XML_MAX_SLAVES);
if (n < 0) { fprintf(stderr, "DENI 加载失败 %d\n", n); return 1; }

uint16_t master = Initialize();
if (xml_apply_config(&cfg, master) != 0) {
fprintf(stderr, "配置下发失败\n");
Dispose(master);
return 1;
}
SetNetwork(master, adapter, "");

deni_save() / eni_save()​

int deni_save(const char* path,
const DarraMasterXmlConfig* master,
const DarraSlaveXmlConfig* slaves,
int slave_count);
int eni_save(const char* path,
const DarraMasterXmlConfig* master,
const DarraSlaveXmlConfig* slaves,
int slave_count);

把已解析的配置结构写回 DENI / ENI 文件。这是离线结构体序列化,不是把活主站 Dump 成 XML。

返回值:

  • int — 0 成功,负值失败

版本与授权​

GetSerialNumber()​

/* 返回只读借用字符串(内部静态/缓存),禁止 FreeMemory */
char* GetSerialNumber(void);
char* GetDeviceName(void);
char* GetUserEmail(void);

获取设备序列号 / 设备名称 / 用户邮箱。返回字符串为只读借用(内部静态 / 缓存单例),不要调用 FreeMemory() 释放(释放会崩溃),也不要改写。授权 API 已于 2026-09-16 从 SDK 移除。

示例:

char* serial = GetSerialNumber();
if (serial) {
printf("设备序列号: %s\n", serial); /* 只读借用,勿 FreeMemory */
}

完整示例​

使用 ENI/DENI 文件(推荐)​

#include "ethercat.h"
#include <stdio.h>

int main(void)
{
DarraMasterXmlConfig cfg = {0};
DarraSlaveXmlConfig slaves[XML_MAX_SLAVES];
int n = deni_load("C:\\EtherCAT\\MyProject.deni", &cfg, slaves, XML_MAX_SLAVES);
if (n < 0) { return 1; }

uint16_t master = Initialize();
if (master == 0) { return 1; }
if (xml_apply_config(&cfg, master) != 0) {
Dispose(master); return 1;
}

uint16_t ret = SetNetwork(master, "\\Device\\NPF_{GUID}", "");
if (ret == 0 || ret == 0xFFFF) {
Dispose(master); return 1;
}
printf("发现 %d 个从站\n", ret);

if (!SetStateSequence(master, EC_STATE_OPERATIONAL, 10000)) {
Dispose(master); return 1;
}
Start(master);

getchar();

Stop(master);
Dispose(master);
return 0;
}

动态扫描网口​

int adapter_count = GetNetworkInfo(FALSE, TRUE);
printf("发现 %d 个网络适配器\n", adapter_count);

int slave_count = ReadSlaveInfo("\\Device\\NPF_{GUID}");
for (int i = 0; i < slave_count; i++) {
/* 通过 GetScannedSlaveInfo() 读取详情 */
}

uint16_t master = Initialize();
SetNetwork(master, "\\Device\\NPF_{GUID}", "");
SetStateSequence(master, EC_STATE_OPERATIONAL, 10000);
Start(master);

一步初始化​

uint16_t master = EcInitFromFile("config.json");
if (master == 0) { return 1; }

/* ... 业务逻辑 ... */

EcClose(master);