从站属性与方法
所有从站函数使用 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 | 只读 | 输入/输出起始位 | |
| EEPROM | GetSlaveEep8Byte() | 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 协议详情 | |
| FMMU | GetSlaveFMMU0Function() | uint8_t | 只读 | FMMU0 功能类型(bit 0=输出, bit 1=输入) |
| GetSlaveFMMU1Function() | uint8_t | 只读 | FMMU1 功能类型 | |
| GetSlaveFMMU2Function() | uint8_t | 只读 | FMMU2 功能类型 | |
| GetSlaveFMMU3Function() | uint8_t | 只读 | FMMU3 功能类型 | |
| DC | GetSlaveHasDC() | 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)
0Undefined — 未定义1Static — 静态设备,无IO映射,如EK1100耦合器2InputNoMailbox — 输入设备(无邮箱)3OutputNoMailbox — 输出设备(无邮箱)4InputWithMailbox — 输入设备(有邮箱)5OutputWithMailbox — 输出设备(有邮箱)6IONoMailbox — 输入输出设备(无邮箱)7IOWithMailbox — 输入输出设备(有邮箱)
邮箱协议类型 (MbxProto)
0x01AoE — ADS over EtherCAT0x02EoE — Ethernet over EtherCAT0x03CoE — CANopen over EtherCAT0x04FoE — File over EtherCAT0x05SoE — Servo over EtherCAT0x0FVoE — Vendor over EtherCAT
CoE 协议功能标志 (CoEDetails)
0x01— 支持 SDO0x02— 支持 SDO Info0x04— 支持 PDO Assign0x08— 支持 PDO Config0x10— 支持 Startup0x20— 支持 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_t | EEPROM 配置索引 |
| GetSlaveEep8Byte(mi, si) | uint8_t | EEPROM 寻址模式(1=8 字节, 0=4 字节) |
| GetSlaveEepPDI(mi, si) | uint8_t | 物理设备接口(PDI)类型 |
| GetSlaveEbusCurrent(mi, si) | int16_t | E-bus 电流消耗(mA) |
| GetSlaveDtype(mi, si) | uint16_t | 设备类型标识(见 Dtype 枚举) |
| 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) |
详见 设备类型标识 (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 循环) |
示例:
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_IP | 0 | Init -> PreOp |
| EC_TRANS_PS | 1 | PreOp -> SafeOp |
| EC_TRANS_SO | 2 | SafeOp -> OP |
| EC_TRANS_OS | 3 | OP -> SafeOp |
| EC_TRANS_SP | 4 | SafeOp -> PreOp |
| EC_TRANS_PI | 5 | PreOp -> Init |
执行时机:
| 常量 | 值 | 说明 |
|---|---|---|
| EC_TIMING_BEFORE | 0 | 转换之前执行 |
| EC_TIMING_AFTER | 1 | 转换之后执行 |
冗余状态
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 三级回退。
正常使用 SDK 时无需调用 — 状态切换/PDO/邮箱等流程 SDK 已自动配置寄存器。此 API 用于深度诊断和特殊场景(例如读取错误计数器、强制端口策略、调试 ESI 烧写不生效等)。
寄存器定义见 ETG.1000.4 §6 / ETG.1000.6 §5(公开标准),例如:
| 寄存器 | 说明 |
|---|---|
| 0x0000 | Type / Revision / Build(设备类型) |
| 0x0030 | AL Control(主站发起状态请求) |
| 0x0130 | AL Status(从站当前状态) |
| 0x0134 | AL 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——它会强制关闭某个物理端口,影响拓扑路由。仅用于冗余验证、线缆故障复现、诊断脚本等场景。
ESC 有 4 个物理端口 P0 / P1 / P2 / P3,DL Port Control 寄存器的值定义如下:
| DLPORT 值 | 行为 |
|---|---|
| 0x00 | Auto — 所有端口由 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);
当前 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.Topology | GetSlaveTopologyType / GetSlaveActivePorts / GetSlaveConsumedPorts / GetSlaveEntryPort / GetSlaveParent / GetSlaveParentPort / GetSlaveParentStation / GetSlavePhysicalType |
Slave.Dc | GetSlaveHasDC / GetSlaveDCActive / GetSlaveDCCycle0 / GetSlaveDCCycle1 / GetSlaveDCShift / GetSlaveDCNext / GetSlaveDCPrevious / GetSlaveDCParentPort / GetSlaveDCReceiveTimeA/B/C/D / GetSlavePDelay |
Slave.Mailbox | GetSlaveMailboxProto / GetSlaveMailboxWriteLength / GetSlaveMailboxWriteOffset / GetSlaveMailboxReadLength / GetSlaveMailboxReadOffset / GetSlaveMbxCount |
Slave.Metadata (SII 缓存) | GetSlaveSIIIndex / GetSlaveDtype / GetSlaveBlockLRW / GetSlaveEep8Byte / GetSlaveEepPDI / GetSlaveEbusCurrent / GetSlaveCoEDetails / GetSlaveFoEDetails / GetSlaveEoEDetails / GetSlaveSoEDetails / GetSlaveHasMDP / GetSlaveHasEsi / GetSlaveEsiVersion / GetSlaveVendorName |
Slave.Identity | GetSlaveVendorId / GetSlaveProductCode / GetSlaveRevision / GetSlaveSerialNumber / GetSlaveConfigAddr / GetSlaveAliasAddr / GetSlaveName / GetSlaveDeviceName / GetSlaveIdentity (struct 输出) |
Slave.Runtime | GetSlaveState / GetSlaveALStatusCode / GetSlaveIsLost / GetSlaveOpOnlyFlag / GetSlaveDeviceEmulationFlag / GetSlaveGroup / GetSlaveOptional / GetSlaveSupportsFrameRepeat |
Slave.SmFmmu | GetSlaveFMMU0Function / GetSlaveFMMU1Function / GetSlaveFMMU2Function / GetSlaveFMMU3Function (FMMU 功能位) |
Slave.Capabilities | 由 GetSlaveCoEDetails / GetSlaveFoEDetails / GetSlaveEoEDetails / GetSlaveSoEDetails / GetSlaveHasDC / GetSlaveHasMDP / GetSlaveMailboxProto 组合派生 |
Slave.Redundancy | GetSlaveRedundancyActivated / GetSlavePrimaryLinkBroken / GetSlaveSecondaryLinkBroken |
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));
}