ESI 文件管理
ESI (EtherCAT Slave Information) 文件包含从站完整配置信息: 设备标识、对象字典、PDO 映射、DC 配置、启动参数等。
C SDK 通过两套 API 处理 ESI:
esi_dll_*系列 (utils/esi_ext.h) 在 DLL 上动态绑定EcEsi_*入口,完成"加载/绑定/启动参数注册/DC 模式查询"utils/esi.h与utils/eni.h提供独立解析器,可在不依赖 DLL 的情况下解析 ESI/ENI XML
utils/esi_ext.h 中 DLL 包装函数统一带 esi_dll_ 前缀,与 utils/esi.h 托管 XML 解析器的 esi_load_file() 区分(后者签名为 esi_load_file(const char*, DarraEsiDeviceInfo*),见下文 独立 ESI 解析)。
DLL 绑定
#include "utils/esi_ext.h"
esi_ctx_t esi = {0};
if (esi_init(&esi, &dll) == 0) {
/* DLL 未导出 EcEsi_* (老版 DLL) */
}
esi_init 在已加载的 dll_t 上绑定 EcEsi_* 函数指针。至少 LoadFile + BindToSlave 可用才算成功(返回 1=全部绑定成功,0=失败)。
ESI 加载
esi_dll_load_file()
int esi_dll_load_file(const esi_ctx_t* ctx, const char* file_path);
加载单个 ESI XML 文件,返回该文件中包含的 Device 数(0 表示失败)。
esi_dll_load_directory()
int esi_dll_load_directory(const esi_ctx_t* ctx, const char* dir_path);
加载目录下所有 .xml / .ESI 文件,返回累计 Device 数。
esi_dll_clear()
void esi_dll_clear(const esi_ctx_t* ctx);
清空 ESI 缓存。
esi_dll_get_loaded_count()
int esi_dll_get_loaded_count(const esi_ctx_t* ctx);
返回当前已加载的 ESI 文件数。
从站绑定
esi_dll_bind_to_slave()
int esi_dll_bind_to_slave(const esi_ctx_t* ctx,
uint16_t master_index, uint16_t slave_index,
const char* file_path);
将指定 ESI 文件绑定到从站。slave_index 1-based。返回 >0 成功,0 失败,<0 参数错误。
esi_dll_auto_match_all()
int esi_dll_auto_match_all(const esi_ctx_t* ctx, uint16_t master_index);
按从站身份(VendorId/ProductCode/Revision)自动匹配 ESI 仓库中的设备并绑定,返回成功匹配的从站数。
esi_dll_register_startup_parameters()
int esi_dll_register_startup_parameters(const esi_ctx_t* ctx,
uint16_t master_index, uint16_t slave_index);
读取从站绑定的 ESI 中 <InitCmd> 段,转换为启动参数并加入主站启动参数列表。需要在 PreOp 之前调用,返回注册的参数数量(<0 失败)。
esi_dll_apply_all_slaves()
int esi_dll_apply_all_slaves(const esi_ctx_t* ctx, uint16_t master_index);
对所有已绑定 ESI 的从站执行 esi_dll_register_startup_parameters + ApplyStartupParameters,一次完成扫描-绑定-注册-应用全流程。
ESI DC 同步模式查询
ESC 硬件 DC bit (GetSlaveHasDC()) 仅表示芯片支持 DC,不代表 ESI 声明该从站应启用 DC。EL6022 / EK1110 / GCAN-8200 等从站 ESC 报 DC capable 但 ESI 无 <Dc> OpMode,强行启用 DC 会让从站卡在 SafeOP→OP。启用 DC 前应同时检查 GetSlaveHasDC() 与本组接口。
esi_dll_get_device_dc_sync_mode()
int esi_dll_get_device_dc_sync_mode(const esi_ctx_t* ctx,
uint16_t master_index, uint16_t slave_index);
查询从站 ESI 声明的 DC 同步模式。返回值:
| 返回值 | 含义 |
|---|---|
| -2 | 参数非法 / 从站未绑定 ESI Device(调用方应退化为 GetSlaveHasDC) |
| -1 | ESI 未声明 <Dc> 节点(从站不用 DC,应跑 FreeRun) |
| 0 | ESI <Dc> 存在但无 OpMode → FreeRun |
| 1 | SM-Sync(启用 SM 但 AssignActivate 无 0x100) |
| 2 | DC-Sync0 |
| 3 | DC-Sync0 + Sync1 |
esi_dll_has_esi_dc_sync()
int esi_dll_has_esi_dc_sync(const esi_ctx_t* ctx, const dll_t* dll,
uint16_t master_index, uint16_t slave_index);
便捷判定: ESI 是否声明此从站使用 DC 同步。get_device_dc_sync_mode >= 2 返回 1,0/1/-1 返回 0,未绑定(-2)时退化为 GetSlaveHasDC 硬件位保守判定。返回 1=ESI 声明 DC 同步,0=未声明。
slave/dc.h 中的 slave_has_esi_dc_sync() 是同一语义的标准 API 封装,内部即走 EcEsi_GetDeviceDcSyncMode。详见 DC 同步。
从站 ESI 查询
GetSlaveHasEsi()
BOOL GetSlaveHasEsi(uint16_t master_index, uint16_t slave_index);
检查从站是否已加载 ESI 文件。
SetSlaveEsiFile()
BOOL SetSlaveEsiFile(uint16_t master_index, uint16_t slave_index, const char* file_path);
为从站显式指定 ESI 文件。
GetSlaveEsiVersion()
BOOL GetSlaveEsiVersion(uint16_t master_index, uint16_t slave_index,
char* buf, int buf_size);
获取从站 ESI 版本字符串。
GetSlaveVendorName()
BOOL GetSlaveVendorName(uint16_t master_index, uint16_t slave_index,
char* buf, int buf_size);
获取从站厂商名称(从 ESI 文件中读取)。
GetSlaveHasMDP()
BOOL GetSlaveHasMDP(uint16_t master_index, uint16_t slave_index);
检查从站是否为 MDP 模块化设备。
独立 ESI 解析 (utils/esi.h)
不依赖 DLL,可在 SDK 任意层解析 ESI XML 文件:
esi_load_file (XML 解析版)
int esi_load_file(const char* filePath, DarraEsiDeviceInfo* info);
解析 ESI XML 文件,提取设备信息、SM/PDO/DC/邮箱配置到 DarraEsiDeviceInfo。返回 0 成功,-1 文件打开失败,-2 XML 解析失败,-3 未找到设备节点。
esi_apply_startup()
int esi_apply_startup(void* dll, uint16_t mi, uint16_t si, const char* filePath);
解析 ESI XML 中的 InitCmd 元素,转换为启动参数并通过 DLL 写入从站。返回成功写入的参数数量。
esi_match_revision()
bool esi_match_revision(uint32_t actual, uint32_t expected, int strategy);
ESI 版本号匹配。strategy 取值:
ESI_MATCH_EXACT (0)— 精确匹配 RevisionNoESI_MATCH_MAJOR (1)— 仅匹配高 16 位(主版本)ESI_MATCH_IGNORE (2)— 忽略 RevisionNo
eeprom_crc()
uint8_t eeprom_crc(const uint8_t* data, int length);
计算 EEPROM 数据 CRC-8 校验值(ETG.2010 标准)。
eeprom_validate_crc()
bool eeprom_validate_crc(const uint8_t* data, int length);
验证 EEPROM 数据末尾的 CRC 字节是否匹配。length 包含 CRC 字节。
ESI 数据结构
typedef struct {
uint32_t vendorId;
uint32_t productId;
uint32_t revisionId;
char productName[128];
char vendorName[128];
char groupType[64];
DarraEsiCoEDetails coeDetails;
bool supportsCoE;
bool supportsSoE;
bool supportsFoE;
bool supportsEoE;
bool supportsVoE;
DarraEsiDcConfiguration dcConfig;
DarraEsiSmInfo syncManagers[8];
int smCount;
DarraEsiPdoConfiguration pdoConfig;
DarraEsiMailboxTimeout mailboxTimeout;
DarraEsiEepromConfiguration eepromConfig;
int ebusCurrent; /* E-Bus 电流 (mA) */
} DarraEsiDeviceInfo;
ENI 解析 (utils/eni.h)
ENI (EtherCAT Network Information) 是 ETG.2100 定义的整网拓扑/PDO/DC 配置 XML,可在调用 LoadENI() 之前先解析校验。
eni_load()
int eni_load(const char* filePath, DarraEniConfiguration* config);
解析 ETG.2100 ENI XML 到 DarraEniConfiguration。返回 0 成功,-1 文件不可读,-2 XML 格式错误,-3 根节点不是 EtherCATConfig。
eni_save()
int eni_save(const char* filePath, const DarraEniConfiguration* config);
将摘要结构序列化为 ETG.2100 兼容的 ENI XML。返回 0 成功,-1 输入参数错误,-2 写入失败。
typedef struct {
char name[128];
int cycleTimeUs;
int dcCycleTimeUs;
int expectedSlaveCount;
} DarraEniMasterConfig;
typedef struct {
int physicalAddress;
int autoIncAddress;
uint32_t vendorId;
uint32_t productCode;
uint32_t revisionNumber;
char name[128];
int dcAssignActivate;
int sm2Offset;
int sm2Length;
int sm3Offset;
int sm3Length;
} DarraEniSlaveConfig;
typedef struct {
DarraEniMasterConfig master;
DarraEniSlaveConfig slaves[256];
int slaveCount;
char version[16];
} DarraEniConfiguration;
完整示例
#include "ethercat.h"
#include "utils/esi_ext.h"
int main(void)
{
dll_t dll;
LOAD_DLL(&dll, "Darra.Core.dll");
/* 1. 绑定 ESI 函数指针 */
esi_ctx_t esi = {0};
esi_init(&esi, &dll);
/* 2. 加载 ESI 库 */
int dev_count = esi_dll_load_directory(&esi, "C:/EtherCAT/ESI");
printf("已加载 %d 个 Device\n", dev_count);
/* 3. 创建主站 + 扫描 */
uint16_t mi = dll.Initialize();
dll.SetNetwork(mi, "\\Device\\NPF_{...}", "");
/* 4. 自动匹配 ESI 到从站 */
int matched = esi_dll_auto_match_all(&esi, mi);
printf("已匹配 %d 个从站的 ESI\n", matched);
/* 5. 注册并应用所有启动参数 */
int applied = esi_dll_apply_all_slaves(&esi, mi);
printf("应用了 %d 条启动参数\n", applied);
/* 6. 进入 OP */
dll.SetStateSequence(mi, EC_STATE_OPERATIONAL, 10000);
dll.Start(mi);
dll.Dispose(mi);
UNLOAD_DLL(&dll);
return 0;
}