跳到主要内容

Slave 从站

通过 master.GetSlave(index) 获取从站引用(1-based 索引)。

概述

Slave 类封装了对单个 EtherCAT 从站的所有操作,包括状态管理、协议访问、IO 数据读写等。从站对象由 EtherCATMaster 创建和管理,不可直接构造。

所有协议子对象(CoE、SoE、FoE 等)采用延迟初始化策略,首次访问时自动创建。

快速开始

索引访问

#include "ethercat.hpp"
using namespace darra;

auto& slave = master.GetSlave(1); // 第1个从站(1-based)
int count = master.SlaveCount(); // 从站总数

按组查询

using namespace darra::ethercat;
uint16_t groupCount = master_other::get_group_slave_count(master, 0); // 默认组从站数
提示

组相关属性详见 从站分组

地址访问

uint16_t addr = slave.ConfigAddr();        // 物理配置地址
uint16_t alias = slave.AliasAddress(); // 别名地址

标识访问

std::string name = slave.Name();           // 设备名称
uint32_t vid = slave.VendorId(); // 厂商 ID
uint32_t pid = slave.ProductId(); // 产品 ID
uint32_t rev = slave.RevId(); // 修订号
uint32_t sn = slave.SerialNumber(); // 序列号

// 读取状态
EcState state = slave.State();

printf("VendorID=0x%08X ProductID=0x%08X\n", vid, pid);

协议访问

所有协议接口通过成员方法延迟获取,首次调用时自动创建:

auto& slave = master.GetSlave(1);

// 各协议接口
CoE& coe = slave.GetCoE(); // CoE (CANopen over EtherCAT)
SoE& soe = slave.GetSoE(); // SoE (Servo over EtherCAT)
FoE& foe = slave.GetFoE(); // FoE (File over EtherCAT)
EoE& eoe = slave.GetEoE(); // EoE (Ethernet over EtherCAT)
AoE& aoe = slave.GetAoE(); // AoE (ADS over EtherCAT)
VoE& voe = slave.GetVoE(); // VoE (Vendor over EtherCAT)
FSoE& fsoe = slave.GetFSoE(); // FSoE (功能安全)
CiA402& cia402 = slave.GetCiA402(); // CiA 402 (伺服驱动)
协议方法说明文档
CoEslave.GetCoE()SDO 对象字典访问CoE
SoEslave.GetSoE()SERCOS IDN 参数SoE
FoEslave.GetFoE()文件传输FoE
EoEslave.GetEoE()以太网隧道EoE
AoEslave.GetAoE()Beckhoff ADSAoE
VoEslave.GetVoE()厂商特定协议VoE
FSoEslave.GetFSoE()功能安全协议FSoE
CiA 402slave.GetCiA402()伺服状态机CiA 402

从站属性与方法

详细的属性列表、状态管理、看门狗、DC 配置等,参见 属性与方法

PDO 数据读写

通过零拷贝指针映射访问 PDO 数据:

auto& slave = master.GetSlave(1);

// 零拷贝指针
void* input = slave.InputDataPointer();
void* output = slave.OutputDataPointer();

// 结构体映射
#pragma pack(push, 1)
struct ServoOutput { uint16_t controlWord; int32_t targetPosition; };
struct ServoInput { uint16_t statusWord; int32_t actualPosition; };
#pragma pack(pop)

auto* out = reinterpret_cast<ServoOutput*>(output);
auto* in = reinterpret_cast<ServoInput*>(input);
if (out && in) {
out->controlWord = 0x000F;
printf("位置: %d\n", in->actualPosition);
}

或使用直接读写:

// 读取输入数据到缓冲区
uint8_t buf[64];
int bytes = slave.ReadInputDirect(buf, sizeof(buf));

// 写入输出数据
uint8_t data[] = {0x0F, 0x00, 0, 0, 0, 0};
slave.WriteOutputDirect(data, sizeof(data));

详见 PDO 输入输出

PDO 直接读写

通过零拷贝指针访问 PDO 数据, 详见 PDO 输入输出

EMCY 紧急消息

EtherCAT 从站在检测到异常时会发送 EMCY (Emergency) 紧急消息。SDK 提供两套接口:

  • SDO 0x1003 历史 — coe.GetEmergencyHistory() / ClearEmergencyHistory()
  • 实时缓存 — darra::ethercat::slave_emcy 命名空间下的自由函数
using namespace darra::ethercat;

int n = slave_emcy::emcy_count(dll, mi, si);
auto records = slave_emcy::emcy_history(dll, mi, si);
for (auto& r : records) {
printf("EMCY t=%u ms code=0x%04X reg=0x%02X\n",
r.timestamp_ms, r.error_code, r.error_register);
}
slave_emcy::clear_emcy(dll, mi, si);

详见 CoE - 紧急消息历史

拓扑查询

EtherCAT 通过每个 ESC 的端口连接关系自然形成线性 / 环 / 分支拓扑。SDK 在进入 OP 状态后可以从 DLL 一次性拉取所有节点的父子关系, 缓存到 TopologyTree, 再做 DFS / BFS / 深度查询等。

using namespace darra::ethercat;

auto tree = BuildTopology(master.Dll(), master.MasterNumber());
if (!tree.IsBuilt()) return;

// 打印根 (有 N 张主网卡或冗余环时可能多个根)
for (auto rootIdx : tree.RootSlaves()) {
auto* root = tree.FindNode(rootIdx);
if (root) printf("根: Slave[%u] ports=0x%02X\n", root->SlaveIndex, root->ActivePorts);
}

// DFS / BFS 整棵树, visitor 返回 false 立即中止
tree.DFS([&](const TopologyNode& n, int depth) {
printf("%*sSlave[%u] addr=%s 子=%zu\n",
depth * 2, "", n.SlaveIndex, n.Address.c_str(), n.Children.size());
return true;
});

相关结构:

struct TopologyNode {
uint16_t SlaveIndex; // 从站索引 (1-based)
uint16_t ConfigAddr; // 配置地址
std::string Address; // 配置地址十六进制字符串 (如 "0x1001")
uint16_t ParentIndex; // 父节点索引, 0 表示根节点
uint8_t EntryPort; // 入口端口 (0-3)
uint8_t ActivePorts; // 活动端口位图 (Bit0..Bit3 对应 P0..P3)
uint8_t Topology; // 0=无链接 / 1=端点 / 2=中间 / 3=分支 / 4=交叉
int Depth; // 到根节点的深度
std::vector<uint16_t> Children; // 子节点索引列表
};

便捷方法: IsRoot() / ActivePortCount() / GetPortType(port) / EntryPortType()TopologyTree 提供 RootSlaves() / GetChildren() / GetChildCount() / FindNode() / DFS() / BFS()

完整示例

#include "ethercat.hpp"
using namespace darra;

int main() {
EtherCATMaster master(dll);
master.SetNetwork("\\Device\\NPF_{...}")
.SetENI("config.deni")
.Build();

// 遍历所有从站
for (uint16_t i = 1; i <= master.SlaveCount(); ++i) {
auto& slave = master.GetSlave(i);
printf("[%d] VID=0x%08X PID=0x%08X 状态=%d\n",
i, slave.VendorId(), slave.ProductId(),
static_cast<int>(slave.State()));
}

// 使用协议
auto& slave1 = master.GetSlave(1);
auto data = slave1.GetCoE().SDORead(0x1018, 0x01);
if (!data.empty()) {
uint32_t vendor_id;
std::memcpy(&vendor_id, data.data(), 4);
printf("VendorID: 0x%08X\n", vendor_id);
}

return 0;
} // 析构自动 Stop + Dispose