ESI 文件管理
ESI (EtherCAT Slave Information) 文件包含从站完整配置信息: 设备标识、对象字典、PDO 映射、DC 配置、启动参数等。
C SDK 通过两套 API 处理 ESI:
esi_dll_*系列 (utils/esi_ext.h) 把薄壳EcEsi_*入口装进esi_ctx_t,完成"加载/绑定/启动参数注册/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 解析)。
上下文装填
#include "utils/esi_ext.h"
esi_ctx_t esi = {0};
esi_init(&esi); /* 装填薄壳 EcEsi_* 入口, 返回 1 */
esi_init 把薄壳的 EcEsi_* 入口装进 esi_ctx_t, 返回 1=装填成功,0=ctx 为空。无需任何 DLL 句柄。
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);
对主站所有从站执行 AutoMatchAll 自动匹配绑定 + 给每个匹配从站注册 ESI 启动参数(对齐 EcEsi_ApplyAllSlaves),返回总注册参数数。需先 esi_dll_load_directory / esi_dll_load_file 建好 ESI 库。
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 → FreeRun1— SM-Sync(启用 SM 但 AssignActivate 无 0x100)2— DC-Sync03— DC-Sync0 + Sync1
esi_dll_has_esi_dc_sync()
int esi_dll_has_esi_dc_sync(const esi_ctx_t* ctx,
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(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,可在调用 xml_apply_config() 下发到主站之前先解析校验。
eni_config_load()
int eni_config_load(const char* filePath, DarraEniConfiguration* config);
解析 ETG.2100 ENI XML 到 DarraEniConfiguration。返回 0 成功,-1 文件不可读,-2 XML 格式错误,-3 根节点不是 EtherCATConfig。
eni_config_save()
int eni_config_save(const char* filePath, const DarraEniConfiguration* config);
将摘要结构序列化为 ETG.2100 兼容的 ENI XML。返回 0 成功,-1 输入参数错误,-2 XML 构建或文件写入失败。
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)
{
/* 1. 装填 ESI 入口 */
esi_ctx_t esi = {0};
esi_init(&esi);
/* 2. 加载 ESI 库 */
int dev_count = esi_dll_load_directory(&esi, "C:/EtherCAT/ESI");
printf("已加载 %d 个 Device\n", dev_count);
/* 3. 创建主站 + 扫描 */
uint16_t mi = Initialize();
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 */
SetStateSequence(mi, EC_STATE_OPERATIONAL, 10000);
Start(mi);
Dispose(mi);
return 0;
}