跳到主要内容

从站属性与方法

所有从站函数使用 1-based 索引(slave_index 从 1 开始)。

函数签名约定

所有 C API 函数第一个参数为 master_index,第二个为 slave_index。 下表中函数签名省略这两个参数,完整调用形式如: dll.GetSlaveName(master, slave, buf, size)

属性

类别函数返回类型读写说明
基本标识GetSlaveName(buf, size)int只读设备名称(从 EEPROM 读取),返回实际长度
GetSlaveDeviceName(buf, size)int只读驱动/设备名称(从 SDO 0x1008 读取)
设备信息GetSlaveVendorId()uint32_t只读制造商 ID(从 SII EEPROM 读取)
GetSlaveProductCode()uint32_t只读产品 ID
GetSlaveRevision()uint32_t只读修订版本号
GetSlaveSerialNumber()uint32_t只读序列号(从 SII EEPROM 读取)
GetSlaveVendorName(buf, size)int只读制造商名称(从 ESI 文件读取),返回实际长度
GetSlaveDtype()uint16_t只读设备类型标识
GetSlaveHasMDP()BOOL只读是否支持模块化设备配置文件(ETG.5001)
GetSlaveBlockLRW()BOOL只读LRW 逻辑读写操作阻止标志
地址GetSlaveConfigAddr()uint16_t只读物理配置地址
GetSlaveAliasAddr()uint16_t只读别名地址
GetSlaveSIIIndex()uint16_t只读SII EEPROM 配置索引
状态GetSlaveState()uint8_t只读从站当前 EtherCAT 状态
GetSlaveALStatusCode()uint16_t只读AL Status Code 错误码
GetSlaveIsLost()BOOL只读从站是否丢失(断开连接)
拓扑GetSlaveTopologyType()uint8_t只读拓扑类型 (0=无链接, 1=端点, 2=中间, 3=分支, 4=交叉)
GetSlaveParentStation()uint16_t只读父从站的站地址
GetSlaveParentPort()uint8_t只读父端口号
GetSlaveEntryPort()uint8_t只读入口端口号
GetSlaveConsumedPorts()uint8_t只读激活端口位掩码
GetSlavePhysicalType()uint8_t只读物理端口类型
PDO 数据GetSlaveInputBits() / GetSlaveOutputBits()uint16_t只读输入/输出数据位数
GetSlaveInputBytes() / GetSlaveOutputBytes()uint32_t只读输入/输出数据字节数
GetSlaveInputOffset() / GetSlaveOutputOffset()uint32_t只读输入/输出数据偏移
GetSlaveInputStartBit() / GetSlaveOutputStartBit()uint8_t只读输入/输出起始位
EEPROMGetSlaveEep8Byte()uint8_t只读EEPROM 寻址模式(1=8字节, 0=4字节)
GetSlaveEepPDI()uint8_t只读物理设备接口(PDI)类型
GetSlaveEbusCurrent()int16_t只读E-bus 电流消耗(mA)
ESI/配置GetSlaveHasEsi(mi, si)BOOL只读是否已加载 ESI 文件
GetSlaveEsiVersion(mi, si, buf, size)int只读ESI 版本号,返回实际长度
邮箱GetSlaveMailboxProto()uint16_t只读支持的邮箱协议类型(位掩码)
GetSlaveMailboxWriteLength()uint16_t只读邮箱发送缓冲区大小
GetSlaveMailboxReadLength()uint16_t只读邮箱接收缓冲区大小
GetSlaveMailboxReadOffset() / GetSlaveMailboxWriteOffset()uint16_t只读邮箱读/写偏移
GetSlaveMbxCount()uint8_t只读邮箱协议计数器
协议详情GetSlaveCoEDetails()uint8_t只读CoE 协议功能标志
GetSlaveEoEDetails()uint8_t只读EoE 协议功能标志
GetSlaveFoEDetails()uint8_t只读FoE 协议详情
GetSlaveSoEDetails()uint8_t只读SoE 协议详情
FMMUGetSlaveFMMU0Function()uint8_t只读FMMU0 功能类型(bit 0=输出, bit 1=输入)
GetSlaveFMMU1Function()uint8_t只读FMMU1 功能类型
GetSlaveFMMU2Function()uint8_t只读FMMU2 功能类型
GetSlaveFMMU3Function()uint8_t只读FMMU3 功能类型
DCGetSlaveHasDC()BOOL只读是否支持 DC
GetSlaveDCActive()uint16_t只读DC 激活状态(0=禁用, 非0=已激活)
GetSlaveDCCycle0() / GetSlaveDCCycle1()int32_t只读SYNC0/SYNC1 周期(纳秒)
GetSlaveDCShift()int32_t只读相位偏移(纳秒)
GetSlavePDelay()int32_t只读帧从主站到达此从站的传播延迟(纳秒)
GetSlaveDCNext() / GetSlaveDCPrevious()uint16_t只读DC 下一个/上一个从站索引
GetSlaveDCParentPort()int32_t只读DC 父端口
GetSlaveDCReceiveTimeA/B/C/D()int32_t只读端口 A/B/C/D 接收时间(纳秒)
配置GetSlaveGroup() / SetSlaveGroup(group)uint8_t读写从站分组号(0-7)
GetSlaveOptional() / SetSlaveOptional(opt)BOOL读写可选从站标记
GetSlaveSupportsFrameRepeat() / SetSlaveSupportsFrameRepeat(val)BOOL读写帧重复功能支持(ETG.1500 5.4.3)
冗余GetSlaveRedundancyActivated(mi, si)BOOL只读冗余是否激活
GetSlavePrimaryLinkBroken(mi, si)BOOL只读主链路是否断开
GetSlaveSecondaryLinkBroken(mi, si)BOOL只读副链路是否断开
标志GetSlaveOpOnlyFlag()BOOL只读OpOnly 标志(ETG.1500),从站仅在 OP 状态下可用
GetSlaveDeviceEmulationFlag()BOOL只读设备仿真标志
SetSlaveErrorAck(ack)BOOL确认从站错误(清除 AL Status Code)
拓扑扩展GetSlaveActivePorts(mi, si)uint8_t只读激活端口位掩码
GetSlaveParent(mi, si)uint16_t只读父从站索引

设备类型标识 (Dtype)

  • 0 Undefined — 未定义
  • 1 Static — 静态设备,无IO映射,如EK1100耦合器
  • 2 InputNoMailbox — 输入设备(无邮箱)
  • 3 OutputNoMailbox — 输出设备(无邮箱)
  • 4 InputWithMailbox — 输入设备(有邮箱)
  • 5 OutputWithMailbox — 输出设备(有邮箱)
  • 6 IONoMailbox — 输入输出设备(无邮箱)
  • 7 IOWithMailbox — 输入输出设备(有邮箱)

邮箱协议类型 (MbxProto)

  • 0x01 AoE — ADS over EtherCAT
  • 0x02 EoE — Ethernet over EtherCAT
  • 0x03 CoE — CANopen over EtherCAT
  • 0x04 FoE — File over EtherCAT
  • 0x05 SoE — Servo over EtherCAT
  • 0x0F VoE — Vendor over EtherCAT

CoE 协议功能标志 (CoEDetails)

  • 0x01 — 支持 SDO
  • 0x02 — 支持 SDO Info
  • 0x04 — 支持 PDO Assign
  • 0x08 — 支持 PDO Config
  • 0x10 — 支持 Startup
  • 0x20 — 支持 Complete Access

EoE 协议功能标志 (EoEDetails)

  • 0x01 — 支持发送帧
  • 0x02 — 支持接收帧
  • 0x04 — 支持设置 IP 参数
  • 0x08 — 支持获取 IP 参数

状态切换

BOOL SetSlaveStateWithTimeout(uint16_t master_index, uint16_t slave_index,
int target_state, uint32_t timeout_ms);

设置从站 EtherCAT 状态(带超时)。用于手动恢复单个从站或将从站切换到指定状态。状态切换遵循 EtherCAT 标准状态机流程,协议层自动处理中间状态。

参数:

  • master_index (uint16_t) — 主站索引
  • slave_index (uint16_t) — 从站索引(1-based)
  • target_state (int) — 目标状态(EC_STATE_INIT / EC_STATE_PRE_OP / EC_STATE_SAFE_OP / EC_STATE_OPERATIONAL)
  • timeout_ms (uint32_t) — 超时时间(毫秒)

示例:

/* 手动恢复单个从站到 OP */
if (dll.GetSlaveState(master, 1) != EC_STATE_OPERATIONAL) {
dll.SetSlaveStateWithTimeout(master, 1, EC_STATE_OPERATIONAL, 5000);
}

从站 IO 区属性

PDO 输入/输出区在过程数据映像中的位置与大小,用于零拷贝指针偏移计算和 PDO 字段定位。

GetSlaveInputBits() / GetSlaveOutputBits()

uint16_t GetSlaveInputBits (uint16_t master, uint16_t slave);
uint16_t GetSlaveOutputBits(uint16_t master, uint16_t slave);

输入 / 输出 PDO 区的总位数(bit)。

GetSlaveInputBytes() / GetSlaveOutputBytes()

uint32_t GetSlaveInputBytes (uint16_t master, uint16_t slave);
uint32_t GetSlaveOutputBytes(uint16_t master, uint16_t slave);

输入 / 输出 PDO 区的字节数 (bits / 8,向上取整)。

GetSlaveInputOffset() / GetSlaveOutputOffset()

uint32_t GetSlaveInputOffset (uint16_t master, uint16_t slave);
uint32_t GetSlaveOutputOffset(uint16_t master, uint16_t slave);

输入 / 输出 PDO 区在主站 IOmap 中的字节偏移。多从站 PDO 顺序排列,此值用于跨从站定位。

GetSlaveInputStartBit() / GetSlaveOutputStartBit()

uint8_t GetSlaveInputStartBit (uint16_t master, uint16_t slave);
uint8_t GetSlaveOutputStartBit(uint16_t master, uint16_t slave);

输入 / 输出 PDO 区在所属字节内的起始位(0-7)。仅对位对齐 PDO 有意义,字节对齐 PDO 始终为 0。

示例:

uint16_t in_bits  = dll.GetSlaveInputBits(master, 1);
uint32_t in_bytes = dll.GetSlaveInputBytes(master, 1);
uint32_t in_off = dll.GetSlaveInputOffset(master, 1);
uint8_t in_sbit = dll.GetSlaveInputStartBit(master, 1);
printf("从站1 输入 PDO: %u bit, %u byte, IOmap 偏移=%u, 起始位=%u\n",
in_bits, in_bytes, in_off, in_sbit);

从站 SII 属性

EEPROM (SII) 提供的设备身份与配置信息。SDK 在 config_init 阶段已自动读取并缓存到从站结构。

函数返回类型说明
GetSlaveVendorId(mi, si)uint32_t制造商 ID(ETG 分配)
GetSlaveProductCode(mi, si)uint32_t产品 ID(厂商分配)
GetSlaveRevision(mi, si)uint32_t修订版本号
GetSlaveSerialNumber(mi, si)uint32_t序列号
GetSlaveSIIIndex(mi, si)uint16_tEEPROM 配置索引
GetSlaveEep8Byte(mi, si)uint8_tEEPROM 寻址模式(1=8 字节, 0=4 字节)
GetSlaveEepPDI(mi, si)uint8_t物理设备接口(PDI)类型
GetSlaveEbusCurrent(mi, si)int16_tE-bus 电流消耗(mA)
GetSlaveDtype(mi, si)uint16_t设备类型标识(见 Dtype 枚举)
GetSlaveBlockLRW(mi, si)BOOLLRW 逻辑读写阻止标志
GetSlavePhysicalType(mi, si)uint8_t物理端口类型
GetSlaveCoEDetails(mi, si)uint8_tCoE 协议功能标志位掩码
GetSlaveFoEDetails(mi, si)uint8_tFoE 协议详情位掩码
GetSlaveEoEDetails(mi, si)uint8_tEoE 协议功能标志位掩码
GetSlaveSoEDetails(mi, si)uint8_tSoE 协议详情位掩码
GetSlaveHasMDP(mi, si)BOOL是否支持 MDP(ETG.5001)

详见 设备类型标识 (Dtype) / CoE 协议功能标志 / EoE 协议功能标志

提示

原始 EEPROM 字节读写见 EEPROM (SII) 访问。常规字段无需手动读 EEPROM,SDK 已封装为上述属性。

从站 Mailbox 属性

邮箱(SyncManager 0/1)配置与协议支持。

函数返回类型说明
GetSlaveMailboxProto(mi, si)uint16_t支持的邮箱协议位掩码(AoE/EoE/CoE/FoE/SoE/VoE)
GetSlaveMailboxWriteLength(mi, si)uint16_t邮箱发送缓冲区大小(字节)
GetSlaveMailboxWriteOffset(mi, si)uint16_t邮箱写 SyncManager 在 ESC 内存的偏移
GetSlaveMailboxReadLength(mi, si)uint16_t邮箱接收缓冲区大小(字节)
GetSlaveMailboxReadOffset(mi, si)uint16_t邮箱读 SyncManager 在 ESC 内存的偏移
GetSlaveMbxCount(mi, si)uint8_t邮箱协议计数器(next-counter,3 bit 循环)

详见 邮箱协议类型 (MbxProto)

示例:

uint16_t proto = dll.GetSlaveMailboxProto(master, 1);
if (proto & 0x04)
printf("从站1 支持 CoE\n");

uint16_t wr_len = dll.GetSlaveMailboxWriteLength(master, 1);
uint16_t rd_len = dll.GetSlaveMailboxReadLength(master, 1);
printf("邮箱: 写 %u 字节 / 读 %u 字节\n", wr_len, rd_len);

过程数据看门狗

SetSlaveWatchdog()

BOOL SetSlaveWatchdog(uint16_t master_index, uint16_t slave_index, uint32_t timeout_ms);

设置从站过程数据看门狗超时。从站在超时时间内未收到过程数据帧时触发看门狗错误(ALStatusCode 0x001B)。

参数:

  • timeout_ms (uint32_t) — 超时时间(毫秒),0 = 禁用,最大 6553ms
备注

应在 SafeOp 或 OP 状态下调用。

SetSlavePdiWatchdog()

BOOL SetSlavePdiWatchdog(uint16_t master_index, uint16_t slave_index, uint32_t timeout_ms);

设置从站 PDI 看门狗超时。PDI 看门狗监控从站本地应用(微控制器固件)是否正常运行。

SetAllSlaveWatchdog() / SetAllSlavePdiWatchdog()

int SetAllSlaveWatchdog(uint16_t master_index, uint32_t timeout_ms);
int SetAllSlavePdiWatchdog(uint16_t master_index, uint32_t timeout_ms);

批量设置所有从站的看门狗超时。返回成功设置的从站数量。

GetSlaveWatchdogConfig()

BOOL GetSlaveWatchdogConfig(uint16_t master_index, uint16_t slave_index, ec_watchdog_config_t* config);

读取从站看门狗当前配置。

typedef struct {
uint16_t wd_divider; /* 看门狗分频器 */
uint16_t wd_time_pdi; /* PDI 看门狗超时值(分频器单位) */
uint16_t wd_time_pd; /* 过程数据看门狗超时值(分频器单位) */
} ec_watchdog_config_t;

GetSlaveWatchdogStatus()

BOOL GetSlaveWatchdogStatus(uint16_t master_index, uint16_t slave_index, ec_watchdog_status_t* status);

读取从站看门狗运行状态。

typedef struct {
uint8_t wd_status; /* SM 看门狗状态 (0=正常, 1=已过期) */
uint8_t wd_counter; /* SM 看门狗计数器(过期累计次数) */
uint16_t wd_divider; /* 当前分频器值 */
uint16_t wd_time_pd; /* 当前过程数据看门狗值 */
} ec_watchdog_status_t;

示例:

/* 设置 100ms 超时 */
dll.SetSlaveWatchdog(master, 1, 100);

/* 读取配置 */
ec_watchdog_config_t config;
if (dll.GetSlaveWatchdogConfig(master, 1, &config)) {
printf("PD超时: %u x %.2f us\n", config.wd_time_pd, config.wd_divider * 0.04);
}

/* 检查状态 */
ec_watchdog_status_t status;
if (dll.GetSlaveWatchdogStatus(master, 1, &status)) {
if (status.wd_status)
printf("看门狗过期! 计数: %d\n", status.wd_counter);
}

/* 禁用看门狗 */
dll.SetSlaveWatchdog(master, 1, 0);

从站验证

BOOL GetSlaveIdentity(uint16_t master_index, uint16_t slave_index, ec_slave_identity_t* identity);
BOOL VerifySlaveIdentity(uint16_t master_index, uint16_t slave_index,
ec_slave_identity_t* expected, BOOL check_rev, BOOL check_serial);
BOOL VerifyAllSlaveIdentities(uint16_t master_index, ec_slave_identity_t* expected_identities,
uint32_t slave_count, BOOL check_revision, BOOL check_serial,
uint64_t* mismatch_mask);
BOOL SetSlaveOptional(uint16_t master_index, uint16_t slave_index, BOOL is_optional);
BOOL GetSlaveOptional(uint16_t master_index, uint16_t slave_index);
typedef struct {
uint32_t VendorId;
uint32_t ProductCode;
uint32_t RevisionNo;
uint32_t SerialNo;
} ec_slave_identity_t;

热插拔重配置

BOOL GetSlaveNeedsStartupReconfig(uint16_t master_index, uint16_t slave_index);
void ClearSlaveNeedsStartupReconfig(uint16_t master_index, uint16_t slave_index);

ESM 超时 (ETG.1020)

BOOL GetSlaveEsmTimeouts(uint16_t master_index, uint16_t slave_index, ec_esm_timeouts_t* timeouts);
BOOL SetSlaveEsmTimeouts(uint16_t master_index, uint16_t slave_index, ec_esm_timeouts_t* timeouts);
typedef struct {
uint32_t IP, PS, SO, OS, SP, PI, BI, IB;
} ec_esm_timeouts_t;

ESI 相关

BOOL GetSlaveHasEsi(uint16_t master, uint16_t slave);
BOOL SetSlaveEsiFile(uint16_t master, uint16_t slave, const char* name);
BOOL GetSlaveEsiVersion(uint16_t master, uint16_t slave, char* buf, int buf_size);
BOOL GetSlaveHasMDP(uint16_t master, uint16_t slave);
BOOL GetSlaveVendorName(uint16_t master, uint16_t slave, char* buf, int buf_size);

启动参数

int AddStartupParameter(uint16_t master_index, uint16_t slave_index,
const ec_startup_param_t* param);
int AddStartupParameterBatch(uint16_t master_index, uint16_t slave_index,
const ec_startup_param_t* params, int count);
int ClearStartupParameters(uint16_t master_index, uint16_t slave_index);
int GetStartupParameterCount(uint16_t master_index, uint16_t slave_index);
int ApplyStartupParameters(uint16_t master_index, uint16_t slave_index,
uint8_t transition, uint8_t timing);
int ApplyStartupParametersAll(uint16_t master_index, uint8_t transition, uint8_t timing);
typedef struct {
uint16_t index; /* SDO 索引 */
uint8_t sub_index; /* SDO 子索引 */
uint8_t data[EC_STARTUP_PARAM_MAX_DATA]; /* 参数数据 */
uint16_t data_len; /* 数据长度 (字节) */
uint8_t transition; /* 转换标识 (EC_TRANS_*) */
uint8_t timing; /* 执行时机 (EC_TIMING_*) */
uint16_t priority; /* 执行优先级 (数值越小越先执行) */
uint8_t complete_access; /* 完整访问标志 (CA=1 写入整个索引) */
uint8_t is_register_write; /* 1=寄存器写入, 0=SDO写入 */
} ec_startup_param_t;

状态转换标识:

常量说明
EC_TRANS_IP0Init -> PreOp
EC_TRANS_PS1PreOp -> SafeOp
EC_TRANS_SO2SafeOp -> OP
EC_TRANS_OS3OP -> SafeOp
EC_TRANS_SP4SafeOp -> PreOp
EC_TRANS_PI5PreOp -> Init

执行时机:

常量说明
EC_TIMING_BEFORE0转换之前执行
EC_TIMING_AFTER1转换之后执行

冗余状态

BOOL GetSlaveRedundancyActivated(uint16_t master, uint16_t slave);
BOOL GetSlavePrimaryLinkBroken(uint16_t master, uint16_t slave);
BOOL GetSlaveSecondaryLinkBroken(uint16_t master, uint16_t slave);

拓扑

int      GetTopology(uint16_t master, ec_topology_node_t* out, int max);
int TopologyBuild(uint16_t master_index, ec_topology_node_t* out_nodes, int max_nodes);
int TopologyGetChildren(uint16_t master_index, uint16_t parent_index,
uint16_t* out_children, int max_children);
int TopologyGetRoots(uint16_t master_index, uint16_t* out_roots, int max_roots);
uint8_t GetSlaveActivePorts(uint16_t master, uint16_t slave);
uint16_t GetSlaveParent(uint16_t master, uint16_t slave);
typedef struct {
uint16_t slave_index;
uint16_t config_addr;
uint16_t parent_index;
uint8_t entry_port;
uint8_t active_ports;
uint8_t topology;
uint8_t port_type;
} ec_topology_node_t;

ESC 寄存器访问 (高级)

直接读写从站 ESC (EtherCAT Slave Controller) 寄存器,用于故障诊断自定义 ESC 操作底层调试。协议层走 FPRD/FPWR 数据报,自动 primary → secondary → APWR 三级回退。

高级 API

正常使用 SDK 时无需调用 — 状态切换/PDO/邮箱等流程 SDK 已自动配置寄存器。此 API 用于深度诊断特殊场景(例如读取错误计数器、强制端口策略、调试 ESI 烧写不生效等)。

寄存器定义见 ETG.1000.4 §6 / ETG.1000.6 §5(公开标准),例如:

寄存器说明
0x0000Type / Revision / Build(设备类型)
0x0030AL Control(主站发起状态请求)
0x0130AL Status(从站当前状态)
0x0134AL Status Code(错误码)
0x0300-0x030F端口 0-3 错误计数器
0x0400-0x043F看门狗配置/计数

ReadSlaveRegister()

BOOL ReadSlaveRegister(uint16_t master_index, uint16_t slave_index,
uint16_t reg_addr, uint8_t* data, uint32_t len);

读取从站 ESC 寄存器(FPRD)。

参数:

  • master_index (uint16_t) — 主站索引
  • slave_index (uint16_t) — 从站索引(1-based)
  • reg_addr (uint16_t) — 寄存器地址
  • data (uint8_t*) — [out] 接收缓冲区(至少 len 字节)
  • len (uint32_t) — 读取字节数(1/2/4 等)

返回值:

  • BOOL — 成功返回 TRUE

WriteSlaveRegister()

BOOL WriteSlaveRegister(uint16_t master_index, uint16_t slave_index,
uint16_t reg_addr, uint8_t* data, uint32_t len);

写入从站 ESC 寄存器(FPWR)。

参数:

  • master_index (uint16_t) — 主站索引
  • slave_index (uint16_t) — 从站索引(1-based)
  • reg_addr (uint16_t) — 寄存器地址
  • data (uint8_t*) — 写入数据
  • len (uint32_t) — 字节数

返回值:

  • BOOL — 成功返回 TRUE

示例:

/* 读取 AL Status (0x0130, 2 字节) */
uint8_t al_status[2] = {0};
if (dll.ReadSlaveRegister(master, 1, 0x0130, al_status, 2)) {
uint16_t state = al_status[0] | (al_status[1] << 8);
printf("AL Status = 0x%04X (state=%u, err=%s)\n",
state, state & 0x0F, (state & 0x10) ? "yes" : "no");
}

/* 读取 AL Status Code (0x0134, 错误码) */
uint8_t al_code[2] = {0};
dll.ReadSlaveRegister(master, 1, 0x0134, al_code, 2);
uint16_t code = al_code[0] | (al_code[1] << 8);
printf("AL Status Code = 0x%04X\n", code);

/* 写 AL Control = 0x04 (请求 SafeOp) */
uint8_t al_ctrl[2] = { 0x04, 0x00 };
dll.WriteSlaveRegister(master, 1, 0x0030, al_ctrl, 2);

EEPROM (SII) 访问

读写从站 SII EEPROM(Slave Information Interface)。EEPROM 存储 vendor_id/product_code/revision_no/serial_no 等设备身份信息。SDK 在 config_init 阶段已自动读取,应用一般无需直接访问。

危险操作

EEPROM 写入慎用 — 写错可能导致从站身份信息错乱,严重时永久 brick 从站,需厂家工具恢复。仅在烧写 alias 地址、修复出厂数据被误覆盖、厂商授权的参数烧录场景使用。EEPROM 写需要从站处于 Init/PreOp 状态。

SIIReadWord()

int SIIReadWord(uint16_t master_index, uint16_t slave_index,
uint16_t word_addr, uint16_t* out_value);

读取单个 EEPROM word(16-bit)。返回 1 成功,0 失败。

SIIWriteWord()

int SIIWriteWord(uint16_t master_index, uint16_t slave_index,
uint16_t word_addr, uint16_t value);

写入单个 EEPROM word(16-bit)。返回 1 成功,0 失败。

/* 读 vendor_id (EEPROM word 0x08, 0x09 — 32-bit) */
uint16_t vid_lo = 0, vid_hi = 0;
dll.SIIReadWord(master, 1, 0x08, &vid_lo);
dll.SIIReadWord(master, 1, 0x09, &vid_hi);
uint32_t vendor_id = ((uint32_t)vid_hi << 16) | vid_lo;
printf("VendorID = 0x%08X\n", vendor_id);

/* 写 alias address (EEPROM word 0x04, 必须 Init/PreOp) */
dll.SIIWriteWord(master, 1, 0x04, 0x0001);
推荐方式

常规身份字段建议直接用 GetSlaveVendorId() / GetSlaveProductCode() / GetSlaveRevision() / GetSlaveSerialNumber(),SDK 已自动从 EEPROM 缓存。

DL Port 端口控制

直接读写 ESC 的 DL Port Control 寄存器 (0x0101),用于端口故障注入测试冗余/环拓扑的手动诊断

高级 API

普通应用不要使用这组 API——它会强制关闭某个物理端口,影响拓扑路由。仅用于冗余验证、线缆故障复现、诊断脚本等场景。

ESC 有 4 个物理端口 P0 / P1 / P2 / P3DL Port Control 寄存器的值定义如下:

DLPORT 值行为
0x00Auto — 所有端口由 ESC 自动管理(默认)
0x03关闭 P0(注入故障)
0x0C关闭 P1
0x30关闭 P2
0xC0关闭 P3

SDK 自动采用 primary → secondary → APWR 三级回退写入路径,即使 P0 关闭后仍能通过副网口/广播恢复。

WriteSlaveDLPORT()

BOOL WriteSlaveDLPORT(uint16_t master_index, uint16_t slave_index, uint8_t dlport_value);

写入从站 DL Port 控制寄存器(0x0101)。

参数:

  • master_index (uint16_t) — 主站索引
  • slave_index (uint16_t) — 从站索引(1-based)
  • dlport_value (uint8_t) — DLPORT 值(见上表)

返回值:

  • BOOL — 成功返回 TRUE

ReadSlaveDLPORT()

BOOL ReadSlaveDLPORT(uint16_t master_index, uint16_t slave_index, uint8_t* dlport_value);

读取从站 DL Port 控制寄存器的当前值。

参数:

  • master_index (uint16_t) — 主站索引
  • slave_index (uint16_t) — 从站索引(1-based)
  • dlport_value (uint8_t*) — [out] 当前 DLPORT 值

返回值:

  • BOOL — 成功返回 TRUE

示例:

/* 模拟 P1 端口故障 (测试冗余切换) */
BOOL ok = dll.WriteSlaveDLPORT(master, 1, 0x0C);
printf("关闭从站 1 的 P1: %s\n", ok ? "成功" : "失败");

/* 读回确认 */
uint8_t dlport = 0;
dll.ReadSlaveDLPORT(master, 1, &dlport);
printf("当前 DLPORT = 0x%02X\n", dlport);

/* 故障恢复后还原 */
dll.WriteSlaveDLPORT(master, 1, 0x00); /* 恢复 Auto */

SII 完整访问 (Category / 控制权 / 字符串解析)

SIIReadWord / SIIWriteWord 之上的高层 API: Category 查找/读取/枚举、控制权 Acquire/Release、Strings/General Category 特化解析。对应 ETG.1000.6 §6 + ETG.2010 SII Category 规范, 也是 protocol_codes.h SII 解析 的纯 SDK 派发入口。

int SIIAcquire           (uint16_t master_index, uint16_t slave_index);
int SIIRelease (uint16_t master_index, uint16_t slave_index);
int SIIReadControlReg (uint16_t master_index, uint16_t slave_index, uint8_t* out_ctrl);

int SIIFindCategory (uint16_t master_index, uint16_t slave_index,
uint16_t cat_type,
uint32_t* out_byte_offset, uint32_t* out_byte_size);

int SIIReadCategory (uint16_t master_index, uint16_t slave_index,
uint16_t cat_type,
uint8_t* buffer, uint32_t max_bytes,
uint32_t* out_actual_bytes);

int SIIEnumerateCategories(uint16_t master_index, uint16_t slave_index,
uint16_t* out_types, int max_count);

int SIIGetStrings (uint16_t master_index, uint16_t slave_index,
char* buffer, uint32_t max_bytes);

int SIIGetStringByIndex (uint16_t master_index, uint16_t slave_index,
uint8_t string_index,
char* buffer, uint32_t max_bytes);

int SIIGetGeneralInfo (uint16_t master_index, uint16_t slave_index,
sii_general_info_t* out);

控制权管理 (SIIAcquire / SIIRelease / SIIReadControlReg) — EEPROM 在 PDI / 主站之间二选一, ESC 0x0500 寄存器记录当前持有方。SIIWriteWord 内部已自动切换主站持有再切回, 用户只在批量写场景才需要显式 Acquire/Release 包住整段写入避免 PDI 中途抢回。SIIReadControlReg 输出 bit0: 0=主站持有, 1=PDI 持有。

Category 访问 (SIIFindCategory / SIIReadCategory / SIIEnumerateCategories) — Category Type 见 ec_sii_category_t 枚举 (10=Strings / 20=DataTypes / 30=General / 40=FMMU / 41=SyncManager / 50=TxPDO / 51=RxPDO / 60=DC / ≥0x1000=Vendor / 0xFFFF=End)。SIIFindCategory 返回数据起始字节偏移与字节长度 (不含 4 字节 header), SIIReadCategory 一次性把 Category 数据拷到调用者缓冲, SIIEnumerateCategories 列出从站所有 Category 类型用于诊断。

字符串解析SIIGetStrings 返回 \0 分隔的字符串拼接 (整体以 \0\0 结尾), SIIGetStringByIndex 直接拿第 N 条 (1-based, 对应 General 里的 GroupName / Name / ImageName / OrderName 索引)。

SIIGetGeneralInfo — 解析 General Category (类型 30) 到结构体, 一次拿到 CoE/FoE/EoE/SoE 详情位、E-bus 电流、物理端口、SysMan Class 等设备级元数据 (ETG.2010 表格)。

示例:

/* 1. 列出所有 Category */
uint16_t cats[32];
int n = dll.SIIEnumerateCategories(master, 1, cats, 32);
for (int i = 0; i < n; i++) {
printf("Category %u (%s)\n", cats[i], EcSii_GetCategoryName(cats[i]));
}

/* 2. 解析 General Category */
sii_general_info_t info;
if (dll.SIIGetGeneralInfo(master, 1, &info)) {
printf("CoE 详情=0x%02X, FoE 详情=0x%02X, E-bus 电流=%u mA\n",
info.coe_details, info.foe_details, info.current_on_ebus);
}

/* 3. 读 Order String (idx 来自 General) */
char order[64];
dll.SIIGetStringByIndex(master, 1, info.order_idx, order, sizeof(order));
printf("OrderName: %s\n", order);

/* 4. 批量写 alias 段 (ESC 0x04, 4 个 word) */
dll.SIIAcquire(master, 1);
dll.SIIWriteWord(master, 1, 0x04, 0x0001);
dll.SIIWriteWord(master, 1, 0x05, 0x0000);
dll.SIIRelease(master, 1);
Category 写入

当前 SIIWriteWord 仅支持单 word 写, 没有提供 SIIWriteCategory (写 Category 需要重排链表 + Checksum 重算, 容易 brick)。整 Category 修改建议用厂商烧录工具或 ESI 重新烧录。

从站子结构访问 (ec_slavet 字段直读)

C SDK 把 C# Slave.Topology / Slave.Dc / Slave.Mailbox / Slave.Metadata / Slave.Identity / Slave.Runtime / Slave.SmFmmu 这些子对象按字段拆成独立的 GetSlaveXxx(master_index, slave_index) 函数, 直接返回 ec_slavet 内部字段。这种风格便于 P/Invoke / ctypes / JNA 平铺绑定。

下表是按 C# 子对象分组的字段映射, 每个函数完整签名为 GetSlaveXxx(uint16_t master_index, uint16_t slave_index), 返回类型见前面 属性 表。

C# 子对象C SDK 直读函数
Slave.TopologyGetSlaveTopologyType / GetSlaveActivePorts / GetSlaveConsumedPorts / GetSlaveEntryPort / GetSlaveParent / GetSlaveParentPort / GetSlaveParentStation / GetSlavePhysicalType
Slave.DcGetSlaveHasDC / GetSlaveDCActive / GetSlaveDCCycle0 / GetSlaveDCCycle1 / GetSlaveDCShift / GetSlaveDCNext / GetSlaveDCPrevious / GetSlaveDCParentPort / GetSlaveDCReceiveTimeA/B/C/D / GetSlavePDelay
Slave.MailboxGetSlaveMailboxProto / GetSlaveMailboxWriteLength / GetSlaveMailboxWriteOffset / GetSlaveMailboxReadLength / GetSlaveMailboxReadOffset / GetSlaveMbxCount
Slave.Metadata (SII 缓存)GetSlaveSIIIndex / GetSlaveDtype / GetSlaveBlockLRW / GetSlaveEep8Byte / GetSlaveEepPDI / GetSlaveEbusCurrent / GetSlaveCoEDetails / GetSlaveFoEDetails / GetSlaveEoEDetails / GetSlaveSoEDetails / GetSlaveHasMDP / GetSlaveHasEsi / GetSlaveEsiVersion / GetSlaveVendorName
Slave.IdentityGetSlaveVendorId / GetSlaveProductCode / GetSlaveRevision / GetSlaveSerialNumber / GetSlaveConfigAddr / GetSlaveAliasAddr / GetSlaveName / GetSlaveDeviceName / GetSlaveIdentity (struct 输出)
Slave.RuntimeGetSlaveState / GetSlaveALStatusCode / GetSlaveIsLost / GetSlaveOpOnlyFlag / GetSlaveDeviceEmulationFlag / GetSlaveGroup / GetSlaveOptional / GetSlaveSupportsFrameRepeat
Slave.SmFmmuGetSlaveFMMU0Function / GetSlaveFMMU1Function / GetSlaveFMMU2Function / GetSlaveFMMU3Function (FMMU 功能位)
Slave.Capabilities由 GetSlaveCoEDetails / GetSlaveFoEDetails / GetSlaveEoEDetails / GetSlaveSoEDetails / GetSlaveHasDC / GetSlaveHasMDP / GetSlaveMailboxProto 组合派生
Slave.RedundancyGetSlaveRedundancyActivated / GetSlavePrimaryLinkBroken / GetSlaveSecondaryLinkBroken
高级: 直接拿 ec_slavet 指针

GetSlave(master_index, slave_index) 返回 void* 指向内部 ec_slavet 结构, 转型后可一次性读所有字段而不必逐个跨 FFI 调用。仅对 SDK 开发者开放, 字段布局可能随版本调整, 应用代码请用上面具名函数。

示例:

/* 一次拿到从站 1 的运行时 + 拓扑 + DC 状态 */
uint16_t s = 1;
printf("从站 %u: state=0x%02X, AL=0x%04X, IsLost=%d\n",
s, dll.GetSlaveState(master, s),
dll.GetSlaveALStatusCode(master, s),
dll.GetSlaveIsLost(master, s));

printf("拓扑: parent=%u port=%u, entry=%u, ports=0x%X\n",
dll.GetSlaveParent(master, s),
dll.GetSlaveParentPort(master, s),
dll.GetSlaveEntryPort(master, s),
dll.GetSlaveActivePorts(master, s));

if (dll.GetSlaveHasDC(master, s)) {
printf("DC: Cycle0=%d ns, Shift=%d ns, PDelay=%d ns\n",
dll.GetSlaveDCCycle0(master, s),
dll.GetSlaveDCShift(master, s),
dll.GetSlavePDelay(master, s));
}