C SDK 概述
纯 C 原生接口,用于开发 EtherCAT 主站应用,适合嵌入式与资源受限设备。
安装
SDK 包 darra_ethercat_c-win-x64.zip:从 下载页面 获取,或直链 https://download.darra.xyz/ethercat/c/darra_ethercat_c-win-x64.zip (SHA256 校验文件 darra_ethercat_c-win-x64.zip.sha256 同目录)。
| 路径 | 内容 |
|---|---|
bin/ | Darra.Core.dll + DarraEtherCAT.dll(运行时) |
include/ | 全部头文件(伞形入口 ethercat.h) |
lib/ | darra_ethercat.lib + darra-ethercat-advanced.lib + mxml4.lib |
examples/ | basic.c / highlevel.c / cia402.c |
| 包根 | CMakeLists.txt / build.bat / build.sh(build.bat Release examples 构建示例) |
链接薄壳导入库 darra_ethercat.lib(MSVC 由 ethercat.h 的 #pragma comment(lib, ...) 自动带上);用到 advanced 层时再链 darra-ethercat-advanced.lib + mxml4.lib。运行时 DarraEtherCAT.dll 必须与可执行文件同目录或在 PATH 中,它会在首次调用时自行加载 Darra.Core.dll。
# MSVC(lib\ 为包内库目录)
cl /Fe:my_app.exe /I include main.c /link /LIBPATH:lib darra_ethercat.lib darra-ethercat-advanced.lib mxml4.lib
# GCC/MinGW
gcc main.c -I include -o my_app.exe lib/darra_ethercat.lib lib/darra-ethercat-advanced.lib lib/mxml4.lib
调用方不需要任何 DLL 句柄:所有 native 入口直接调用同名函数(薄壳稳定语义导出 DarraEcat_*,模块头提供同名 inline 转发)。
#include "ethercat.h"
int main(void) {
uint16_t master = Initialize(); /* 直接调用, 无 dll_t 成员写法 */
/* ... */
Dispose(master);
return 0;
}
环境要求
- 操作系统 — Windows 7+(推荐 Windows 10/11)。Linux 尚未公开发布(SDK 与驱动未发布)
- 编译器 — MSVC 2019+、GCC 7+、Clang 10+,C11 或更高
- 权限 — 管理员 / root(实时网络访问)
- 实时内核驱动 — 随官方安装包提供(下载),微秒级实时、无需专用硬件,见 Windows 驱动
快速开始
- 打开 Darra 配置工具,扫描 EtherCAT 网络
- 配置从站参数(PDO 映射、DC 设置等)
- 导出 DENI 配置文件
- 在代码中加载 DENI 文件初始化主站
#include <stdio.h>
#include <ethercat.h>
int main(void) {
/* ★ 推荐方式: 加载 DENI 文件
(DENI 是 XML: deni_load 解析, xml_apply_config 在 SetNetwork 前下发配置) */
DarraMasterXmlConfig cfg = {0};
DarraSlaveXmlConfig slaves[XML_MAX_SLAVES];
if (deni_load("C:\\EtherCAT\\MyProject.deni", &cfg, slaves, XML_MAX_SLAVES) < 0) {
fprintf(stderr, "DENI 加载失败\n"); return 1;
}
uint16_t m = Initialize();
if (m == 0) return 1;
if (xml_apply_config(&cfg, m) != 0) { Dispose(m); return 1; }
SetNetwork(m, "\\Device\\NPF_{GUID}", ""); /* 配置工具里选的那张网卡 */
SetStateSequence(m, EC_STATE_OPERATIONAL, 10000);
Start(m); /* 已进 OP, 这里直接通信 */
int n = 0;
char* sw = dx_sdo_read(m, 1, 0x6041, 0x00, FALSE, &n);
if (sw && n >= 2) {
printf("Statusword=0x%04X\n", *(uint16_t*)sw);
}
if (sw) FreeMemory(sw);
EcClose(m); /* 一步关闭: Stop + Dispose */
return 0;
}
备选方式: 自动搜索网卡 (无 DENI 时)
不使用 DENI 时, 用 GetNetworkInfo() 自动扫描所有网卡, 取有从站的那张, 不必硬编码 \Device\NPF_{GUID}:
#include <stdio.h>
#include <ethercat.h>
int main(void) {
/* 扫所有 NIC 并探测从站数 (耗时 ~500ms-1.5s) */
int n = GetNetworkInfo(FALSE, TRUE);
ec_network_info_t* nets = (ec_network_info_t*)GetNetworksPointer(); /* 内部指针, 无需释放 */
/* 选有从站的 NIC */
uint16_t m = Initialize();
int found = 0;
for (int i = 0; i < n; i++) {
if (nets[i].slaveNum > 0) {
printf("找到: %s (从站 %d 台)\n", (const char*)nets[i].desc, nets[i].slaveNum);
SetNetwork(m, (const char*)nets[i].name, "");
found = 1;
break;
}
}
if (!found) { fprintf(stderr, "未发现 EtherCAT 网络\n"); Dispose(m); return 1; }
SetStateSequence(m, EC_STATE_OPERATIONAL, 10000);
Start(m);
/* ... 通信 ... */
Stop(m);
Dispose(m);
return 0;
}
DENI 用于生产环境(一行加载全套参数),自动搜索用于演示 / 工具类应用。
核心概念:
- 主站索引 —
uint16_t标识主站实例,Initialize()返回(>0 成功) - 无句柄调用 — native 入口全部经薄壳直接调用同名函数;
dll_t/LOAD_DLL只是编译兼容外壳,dll.FuncName()成员写法已废除、不再编译 - 多实例 — 每个主站实例管一条独立总线,用不同网卡,CPU 核心可独立分配
高级 API
ethercat_advanced.h 提供一组高级封装,简化常见流程:
| 类别 | API 函数 / 头文件 | 读写 | 说明 |
|---|---|---|---|
| 一步初始化 | EcInit / EcInitFromFile / EcClose | 只写 | JSON 配置一步完成主站初始化 |
| 状态链转换 | SetStateSequence | 只写 | 自动执行多步状态转换链 |
| 启动参数管线 | AddStartupParameter / ApplyStartupParameters | 只写 | 批量添加启动参数并一次性应用 |
| CiA 402 驱动状态机 | CiA402_Enable / CiA402_SetMode | 只写 | 一步使能 / 故障复位 / 状态解析 |
| EMCY 紧急消息缓冲 | EmcyGetCount / EmcyGetHistory | 只读 | 读取从站紧急消息历史 |
| PDO 钉页 / 整帧快照与写入 | GetIO / CopySlaveInputPdo · CopySlaveOutputPdo / WriteSlaveOutputPdo | 读写 | GetIO 进 OP 映射一次钉页(金路径);Copy 是快照;WriteSlaveOutputPdo 只收单一 owner 完整帧 |
| 邮箱网关 | gateway_create / gateway_start / gateway_stop / gateway_get_statistics | 读写 | ETG.8200 UDP 网关(master/gateway.h,darra-ethercat-advanced) |
| ENI/DENI | deni_load / eni_load / xml_apply_config | 只写 | 加载配置并下发主站(utils/xml.h,经 ethercat.h 包含;SaveXML 不是用户 API)。保存配置 / 启动快照在主站自愈窗口内采用有限等待(秒级上限,短轮询),超时返回错误,不会长时间挂起 |
| 拓扑查询 | TopologyBuild / TopologyGetRoots / TopologyGetChildren | 只读 | 构建并查询从站网络拓扑树 |
| OD 树遍历 | od_load() | 只读 | 加载完整对象字典树 |
| MDP 模块发现 | mdp_discover() / mdp_get_detected_module_list() | 只读 | 模块化设备扫描 |
| MDP 自动编排 | DarraEcat_MDPAutoEnumerate | 只写 | 模块化设备进 OP 自动配置 |
| 异步邮箱事务 | ec_mbx_submit_async / ec_mbx_wait | 读写 | 非阻塞邮箱事务管线 |
| 从站实时状态 | DarraEcat_GetSlaveStateLive / DarraEcat_GetSlaveALStatusCodeLive | 只读 | 当周期从站状态 / AL 错误码直读 |
| FSoE 管理器 | fsoe_manager_create() | 只写 | 多连接管理 |
| EoE Ping | eoe_ping() | 只写 | ICMP Ping |
| PDO 变化监视器 | pdo_monitor_create() | 只写 | 输入数据变化检测 |
快速示例:
/* 一步使能伺服驱动器 (slave=1, max_retries=50) */
CiA402_Enable(master, 1, 50);
/* PDO 快照与整帧写入 (金路径是 GetIO 一次钉页, 见 [PDO 输入输出](./slave/pdo-io)) */
uint8_t probe = 0;
uint32_t copied = 0, required = 0;
CopySlaveInputPdo(master, 1, &probe, sizeof(probe), &copied, &required); /* 探长度: 返回 2 (缓冲不足), required 为权威长度 */
uint8_t* in = (uint8_t*)malloc(required);
if (in && ec_pdo_io_is_ok(CopySlaveInputPdo(master, 1, in, required, &copied, &required))) {
/* in 为完整输入帧快照, 不随总线变 */
}
free(in);
uint32_t out_copied = 0, out_required = 0;
CopySlaveOutputPdo(master, 1, &probe, sizeof(probe), &out_copied, &out_required); /* 输出帧长度 */
uint8_t* out = (uint8_t*)calloc(1, out_required);
if (out) {
uint32_t written = 0;
WriteSlaveOutputPdo(master, 1, out, out_required, &written, &out_required);
free(out);
}
/* 读取紧急消息历史 (调用者提供 ec_emcy_record_t 缓冲, 返回写入条数) */
ec_emcy_record_t records[16];
int n = EmcyGetHistory(master, 1, records, 16);
/* 拓扑查询 */
uint16_t children[64];
int cn = TopologyGetChildren(master, 1, children, 64);
C 特有语法糖 (C99+)
<sugar/sugar.h> 提供一组可选的 macros + inline 包装,零运行时成本,不影响标准 API。详见 C 特有语法糖,包含:结构指定初始化 ({ .cycle_ns = 1000000 }) / 遍历宏 EC_FOREACH_SLAVE(mi, i) / 错误处理 EC_TRY · EC_TRY_LOG · EC_RETURN_IF_FAIL · EC_GOTO_IF_FAIL / inline 短名 getter ec_vendor · ec_product / GCC/Clang cleanup EC_AUTO_DISPOSE / 周期预设 EC_PRESET_CYCLE_1MS / C11 _Generic 类型分发。
错误码本地化
AL Status 描述支持中英双语,默认英文:
darra_driver_version_t drv;
darra_get_driver_version(&drv); // 内核驱动版本
const char* en = AL_StatusCode_GetDescription(code); // English (默认)
版本兼容
当前 SDK 版本 2.11.0。Core / 驱动 / GUI 同号升级;MINOR / MAJOR 必须重装配套驱动 —— 详见 SDK 版本兼容。
版本以官方安装包为准,应用层无需查询 DLL 版本。授权状态见运行日志。