跳到主要内容

从站属性与方法

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

函数签名约定

所有 C API 函数第一个参数为 master_index,第二个为 slave_index。 下表中函数签名省略这两个参数,完整调用形式如: 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)BOOL只读制造商名称(从 ESI 文件读取),成功返回 TRUE
GetSlaveHasMDP()BOOL只读是否支持模块化设备配置文件(ETG.5001)
GetSlaveBlockLRW()BOOL只读LRW 逻辑读写操作阻止标志
地址GetSlaveConfigAddr()uint16_t只读物理配置地址
GetSlaveAliasAddr()uint16_t只读别名地址
状态GetSlaveState()uint8_t只读从站当前 EtherCAT 状态(缓存读,返回 EC_STATE_* 枚举语义)
GetSlaveALStatusCode()uint16_t只读AL Status Code 错误码(缓存读)
DarraEcat_GetSlaveStateLive()uint8_t只读从站状态实时直读(强制刷新,抢 ESC 端口锁,禁止高频调用)
DarraEcat_GetSlaveALStatusCodeLive()uint16_t只读AL Status Code 实时直读(强制刷新,禁止高频调用)
GetSlaveIsLost()BOOL只读从站是否丢失(断开连接)
诊断镜像GetSlaveWcState()uint8_t只读该从站本周期对工作计数器(WKC)的贡献状态(1=贡献 / 0=未贡献 / 0xFF=未知,薄读零帧),详见 WKC 贡献语义
GetSlaveAlStatusMirror()uint16_t只读内核 AL 状态镜像 16bit(低字节=AL State 含 Error 位,高字节=AL Status Code,薄读零帧)
GetSlaveMailboxHealth()uint8_t只读该从站邮箱健康度(0=Unknown, 1=Healthy, 2=Degraded,薄读零帧)
GetSlaveFreeRunDemoted()uint8_t只读该从站是否被迫从 DC 降级到 FreeRun(非 0=已降级,薄读零帧)
GetSlaveHealthDegradedCount()uint32_t只读该从站邮箱半失效连续累计计数(≈秒数,0=健康)
RecoverSlaveMailboxHealth()BOOL只写手动触发该从站邮箱半失效修复(内核通常自动修复)
拓扑GetSlaveTopologyType()uint8_t只读拓扑类型 (0=无链接, 1=端点, 2=中间, 3=分支, 4=交叉)
GetSlaveParentStation()uint16_t只读父从站索引(1-based,0=父节点是主站;命名为历史遗留,实际是 index 非 station address)
GetSlaveParentPort()uint8_t只读父端口号
GetSlaveEntryPort()uint8_t只读入口端口号
GetSlaveConsumedPorts()uint8_t只读已使用端口位掩码(consumed ports)
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)BOOL只读ESI 版本号(写入 buf),成功返回 TRUE
邮箱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 AssignActivate 原码(0=未激活; 0x0100=SYNC0 位单带、 0x0300=SYNC0、 0x0700=SYNC0+SYNC1)。已同步判据 = (值 & 0x0300)≠0 且 DCCycle0>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只读历史命名有误:实际读 SII General Flags bit1 = NotLRW(不支持 LRW),不是 OpOnly 位。真 OpOnly(Flags bit0)读 SII General Category 的 flags。返回 FALSE 也可能是从站未找到/主站未初始化
GetSlaveDeviceEmulationFlag()BOOL只读设备仿真标志
SetSlaveErrorAck(set_ack)BOOL只写确认从站错误(清除 AL Status Code)
拓扑扩展GetSlaveActivePorts(mi, si)uint8_t只读激活端口位掩码
GetSlaveParent(mi, si)uint16_t只读父从站索引
C 特有语法糖

C 可用 ec_vendor / ec_product / ec_state inline 短名替代 GetSlaveVendorId / GetSlaveProductCode / GetSlaveState 等长名 (零开销, 编译期完全 inline). 详见 inline 包装.

WKC 贡献状态 (GetSlaveWcState)​

GetSlaveWcState(mi, si) 返回该从站本周期对工作计数器(WKC)的贡献状态(薄读零帧,内核 per-slave 镜像):

  • 0 NotContributed — 该从站此刻没在贡献 WKC(疑似掉站 / 热插拔恢复中)。这不是 master 故障,从站修复后内核镜像自然回到 1。
  • 1 Contributed — 该从站正常贡献 WKC,即聚合 WKC 完整(actual == expected,亦即缺口 GetWcDeficit(mi) == 0)时各从站逐字节比对都"满"。
  • 0xFF Unknown — 内核镜像未填充(契约未就绪),诚实暴露,不臆造为"已贡献"。
R1 可靠性铁律

本接口仅"如实反映总线现实", 不参与任何 WKC 篡改 / 迁就逻辑。逐从站贡献位是聚合 WKC 缺口(GetWcDeficit = expected - actual)的逐站分解;缺口大于 0 表示有从站此刻未贡献,应报警 + 诊断,不下调 expected、不停 OP。配套主站级薄读:GetWcDeficit(mi) / GetWkcActualMirror(mi) / GetWkcExpectedMirror(mi)。

邮箱协议类型 (MbxProto)​

MbxProto() 返回 SII mbx_proto 能力位图(ECT_MBXPROT_*),一位一个协议,可直接与 ECT_MBXPROT_* 做位与判断:

  • 0x0001 AoE — ADS over EtherCAT
  • 0x0002 EoE — Ethernet over EtherCAT
  • 0x0004 CoE — CANopen over EtherCAT
  • 0x0008 FoE — File over EtherCAT
  • 0x0010 SoE — Servo over EtherCAT
  • 0x0020 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 state, uint32_t timeout_ms);

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

参数:

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

示例:

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

从站 IO 区属性​

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

参数(本组 GetSlaveInput* / GetSlaveOutput* 共用):

  • master / slave (uint16_t) — 主站索引 / 从站索引(1-based)

返回值:

  • GetSlaveInputBits / GetSlaveOutputBits — uint16_t 位数
  • GetSlaveInputBytes / GetSlaveOutputBytes — uint32_t 字节数(bits / 8 向上取整)
  • GetSlaveInputOffset / GetSlaveOutputOffset — uint32_t IOmap 起始字节偏移
  • GetSlaveInputStartBit / GetSlaveOutputStartBit — uint8_t 所属字节内的起始位(0-7)

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  = GetSlaveInputBits(master, 1);
uint32_t in_bytes = GetSlaveInputBytes(master, 1);
uint32_t in_off = GetSlaveInputOffset(master, 1);
uint8_t in_sbit = 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只读序列号
GetSlaveEep8Byte(mi, si)uint8_t只读EEPROM 寻址模式(1=8 字节, 0=4 字节)
GetSlaveEepPDI(mi, si)uint8_t只读物理设备接口(PDI)类型
GetSlaveEbusCurrent(mi, si)int16_t只读E-bus 电流消耗(mA)
GetSlaveBlockLRW(mi, si)BOOL只读LRW 逻辑读写阻止标志
GetSlavePhysicalType(mi, si)uint8_t只读物理端口类型
GetSlaveCoEDetails(mi, si)uint8_t只读CoE 协议功能标志位掩码
GetSlaveFoEDetails(mi, si)uint8_t只读FoE 协议详情位掩码
GetSlaveEoEDetails(mi, si)uint8_t只读EoE 协议功能标志位掩码
GetSlaveSoEDetails(mi, si)uint8_t只读SoE 协议详情位掩码
GetSlaveHasMDP(mi, si)BOOL只读是否支持 MDP(ETG.5001)

详见 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 = GetSlaveMailboxProto(master, 1);
if (proto & 0x04)
printf("从站1 支持 CoE\n");

uint16_t wr_len = GetSlaveMailboxWriteLength(master, 1);
uint16_t rd_len = 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)。

参数:

  • master_index / slave_index (uint16_t) — 主站索引 / 从站索引(1-based)
  • timeout_ms (uint32_t) — 超时时间(毫秒),0 = 禁用,最大 6553ms

返回值:

  • BOOL — 成功 TRUE
备注

应在 SafeOp 或 OP 状态下调用。

SetSlavePdiWatchdog()​

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

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

参数:

  • master_index / slave_index (uint16_t) — 主站索引 / 从站索引(1-based)
  • timeout_ms (uint32_t) — 超时时间(毫秒),0 = 禁用

返回值:

  • BOOL — 成功 TRUE

SetAllSlaveWatchdog() / SetAllSlavePdiWatchdog()​

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

批量设置所有从站的看门狗超时。

参数:

  • master_index (uint16_t) — 主站索引
  • timeout_ms (uint32_t) — 超时时间(毫秒),0 = 禁用

返回值:

  • int — 成功设置的从站数量

GetSlaveWatchdogConfig()​

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

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

参数:

  • master_index / slave_index (uint16_t) — 主站索引 / 从站索引(1-based)
  • config (ec_watchdog_config_t*) — [out] 当前配置

返回值:

  • BOOL — 成功 TRUE

相关结构:

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);

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

参数:

  • master_index / slave_index (uint16_t) — 主站索引 / 从站索引(1-based)
  • status (ec_watchdog_status_t*) — [out] 当前运行状态

返回值:

  • BOOL — 成功 TRUE

相关结构:

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 超时 */
SetSlaveWatchdog(master, 1, 100);

/* 读取配置 */
ec_watchdog_config_t config;
if (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 (GetSlaveWatchdogStatus(master, 1, &status)) {
if (status.wd_status)
printf("看门狗过期! 计数: %d\n", status.wd_counter);
}

/* 禁用看门狗 */
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);

实时诊断镜像 (薄读零帧)​

内核 RT 线程每周期(WKC 异常立即)维护 per-slave 诊断缓存,下列 getter 直接读 native,不收发帧(零帧),不二次缓存,读到即此刻总线现实,无需刷新。旧 DLL 未导出时返回兜底值(0xFF=Unknown / 0),诚实暴露不臆造。

/* per-slave 薄读 */
uint8_t GetSlaveWcState(uint16_t master, uint16_t slave); /* 1=贡献 / 0=未贡献 / 0xFF=未知 */
uint16_t GetSlaveAlStatusMirror(uint16_t master, uint16_t slave); /* 低字节=AL State(含 Error 位), 高字节=AL Status Code */
uint8_t GetSlaveMailboxHealth(uint16_t master, uint16_t slave); /* 0=Unknown, 1=Healthy, 2=Degraded */
uint8_t GetSlaveFreeRunDemoted(uint16_t master, uint16_t slave); /* 非 0=被迫降级到 FreeRun */
uint32_t GetSlaveHealthDegradedCount(uint16_t master, uint16_t slave);/* 邮箱半失效连续累计计数 (≈秒数) */
BOOL RecoverSlaveMailboxHealth(uint16_t master, uint16_t slave); /* 手动触发邮箱半失效修复 */

/* master 级 WKC 镜像薄读 (逐站贡献位的聚合视图) */
uint16_t GetWcDeficit(uint16_t master); /* expected - actual (只读诊断, 不据此改 expected) */
uint16_t GetWkcActualMirror(uint16_t master); /* 实测 WKC (总线此刻真值) */
uint16_t GetWkcExpectedMirror(uint16_t master); /* 期望 WKC (配置期固定真值, 概念上不可变) */

WKC 贡献语义见 WKC 贡献状态 (GetSlaveWcState)。

示例:

uint16_t s = 1;
if (GetSlaveWcState(master, s) == 0) { /* 0 = NotContributed */
uint16_t al = GetSlaveAlStatusMirror(master, s);
printf("从站 %u 此刻未贡献 WKC(疑似掉站),AL Code=0x%02X\n", s, (al >> 8) & 0xFF);
}
if (GetSlaveMailboxHealth(master, s) == 2) /* 2 = Degraded */
printf("从站 %u 在 OP 但邮箱半失效, 已持续 %us\n", s, GetSlaveHealthDegradedCount(master, s));

拓扑​

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 (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};
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 };
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)。

参数:

  • master_index / slave_index (uint16_t) — 主站索引 / 从站索引(1-based)
  • word_addr (uint16_t) — EEPROM word 地址
  • out_value (uint16_t*) — [out] 读出的 16 位值

返回值:

  • int — 1 成功 / 0 失败

SIIWriteWord()​

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

写入单个 EEPROM word(16-bit)。

参数:

  • master_index / slave_index (uint16_t) — 主站索引 / 从站索引(1-based)
  • word_addr (uint16_t) — EEPROM word 地址
  • value (uint16_t) — 待写入的 16 位值

返回值:

  • int — 1 成功 / 0 失败

示例:

/* 读 vendor_id (EEPROM word 0x08, 0x09 — 32-bit) */
uint16_t vid_lo = 0, vid_hi = 0;
SIIReadWord(master, 1, 0x08, &vid_lo);
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 段 (EEPROM word 0x04/0x05, 必须 Init/PreOp;单字写免 Acquire,此处 Acquire/Release 为批量写整段持有示例) */
DarraEcat_SIIAcquire(master, 1);
SIIWriteWord(master, 1, 0x04, 0x0001);
SIIWriteWord(master, 1, 0x05, 0x0000);
DarraEcat_SIIRelease(master, 1);
推荐方式

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

DL Port 端口控制​

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

高级 API

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

ESC 有 4 个物理端口 P0 / P1 / P2 / P3,DL 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 = WriteSlaveDLPORT(master, 1, 0x0C);
printf("关闭从站 1 的 P1: %s\n", ok ? "成功" : "失败");

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

/* 故障恢复后还原 */
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 DarraEcat_SIIAcquire           (uint16_t master_index, uint16_t slave_index);
int DarraEcat_SIIRelease (uint16_t master_index, uint16_t slave_index);
int DarraEcat_SIIReadControlReg (uint16_t master_index, uint16_t slave_index, uint8_t* out_ctrl);

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

int DarraEcat_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 DarraEcat_SIIEnumerateCategories(uint16_t master_index, uint16_t slave_index,
uint16_t* out_types, int max_count);

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

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

int DarraEcat_SIIGetGeneralInfo (uint16_t master_index, uint16_t slave_index,
void* out); /* out 指向 ec_sii_general_info_t */

控制权管理 (DarraEcat_SIIAcquire / DarraEcat_SIIRelease / DarraEcat_SIIReadControlReg) — EEPROM 在 PDI / 主站之间二选一, ESC 0x0500 寄存器记录当前持有方。SIIWriteWord 内部已自动取得主站持有并在写后把控制权还给 PDI,单字写免手动 Acquire/Release;显式 DarraEcat_SIIAcquire / DarraEcat_SIIRelease 用于批量写整段持有,避免 PDI 中途抢回。DarraEcat_SIIReadControlReg 输出 bit0: 0=主站持有, 1=PDI 持有。

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

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

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

示例:

/* 1. 列出所有 Category */
uint16_t cats[32];
int n = DarraEcat_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 */
ec_sii_general_info_t info;
if (DarraEcat_SIIGetGeneralInfo(master, 1, &info)) {
printf("CoE 详情=0x%02X, FoE 详情=0x%02X, E-bus 电流=%d mA\n",
info.coe_details, info.foe_details, info.ebus_current_ma);
}

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

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

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

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

C SDK 把从站各子域(拓扑 / DC / 邮箱 / 元数据 / 标识 / 运行时 / SM-FMMU / 能力 / 冗余)按字段拆成独立的 GetSlaveXxx(master_index, slave_index) 函数, 直接返回对应的从站内部字段。这种风格便于各类语言的平铺式绑定。

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

从站子域C SDK 直读函数读写
拓扑GetSlaveTopologyType / GetSlaveActivePorts / GetSlaveConsumedPorts / GetSlaveEntryPort / GetSlaveParent / GetSlaveParentPort / GetSlaveParentStation / GetSlavePhysicalType (注: GetSlaveParentStation 实际是从站索引 1-based, 不是 station address; 0 表示父节点是主站)只读
DCGetSlaveHasDC / GetSlaveDCActive / GetSlaveDCCycle0 / GetSlaveDCCycle1 / GetSlaveDCShift / GetSlaveDCNext / GetSlaveDCPrevious / GetSlaveDCParentPort / GetSlaveDCReceiveTimeA/B/C/D / GetSlavePDelay只读
邮箱GetSlaveMailboxProto / GetSlaveMailboxWriteLength / GetSlaveMailboxWriteOffset / GetSlaveMailboxReadLength / GetSlaveMailboxReadOffset / GetSlaveMbxCount只读
元数据 (SII 缓存)GetSlaveBlockLRW / GetSlaveEep8Byte / GetSlaveEepPDI / GetSlaveEbusCurrent / GetSlaveCoEDetails / GetSlaveFoEDetails / GetSlaveEoEDetails / GetSlaveSoEDetails / GetSlaveHasMDP / GetSlaveHasEsi / GetSlaveEsiVersion / GetSlaveVendorName只读
标识GetSlaveVendorId / GetSlaveProductCode / GetSlaveRevision / GetSlaveSerialNumber / GetSlaveConfigAddr / GetSlaveAliasAddr / GetSlaveName / GetSlaveDeviceName / GetSlaveIdentity (struct 输出)只读
运行时GetSlaveState / GetSlaveALStatusCode / GetSlaveIsLost / GetSlaveOpOnlyFlag / GetSlaveDeviceEmulationFlag / GetSlaveGroup / GetSlaveOptional / GetSlaveSupportsFrameRepeat只读(Group / Optional / SupportsFrameRepeat 配 Set 写入)
SM-FMMUGetSlaveFMMU0Function / GetSlaveFMMU1Function / GetSlaveFMMU2Function / GetSlaveFMMU3Function (FMMU 功能位)只读
能力由 GetSlaveCoEDetails / GetSlaveFoEDetails / GetSlaveEoEDetails / GetSlaveSoEDetails / GetSlaveHasDC / GetSlaveHasMDP / GetSlaveMailboxProto 组合派生只读
冗余GetSlaveRedundancyActivated / GetSlavePrimaryLinkBroken / GetSlaveSecondaryLinkBroken只读
诊断镜像 (薄读零帧)GetSlaveWcState / GetSlaveAlStatusMirror / GetSlaveMailboxHealth / GetSlaveFreeRunDemoted / GetSlaveHealthDegradedCount / RecoverSlaveMailboxHealth只读(RecoverSlaveMailboxHealth 为只写)
高级: 直接拿从站结构指针

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

示例:

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

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

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