跳到主要内容

从站分组

从站分组功能允许将从站分配到不同的组(0-7,0=未分配),每组可配置独立的 PDO 周期分频器,实现不同组以不同频率交换数据。

典型应用场景:

  • 伺服驱动(组1):默认组,每周期发送,31.25us 32KHz
  • IO 模块(组2):每4周期发送一次,125us 8000Hz
  • 传感器(组3):每6400周期发送一次,200ms 5Hz
核心简化
  • 无需手动启用组 -- 映射时未分组从站自动归入组 1,空组自动跳过 PDO 收发
  • 属性式分频器 -- 通过 master.Divider(group, value) 直接设置
  • 每组独立收发 -- 每个组对应独立的 PDO 数据报文

快速开始​

EtherCATMaster master(dll);
master.SetNetwork("\\Device\\NPF_{...}");
master.Build();

// 1. 分配从站到组(必须在 SAFE_OP 之前)
// 从站 1~2 不设置(group=0)—— 映射时自动归入组 1(默认组,每周期发送)
master.GetSlave(3).Group(2); // IO 模块 -> 组2
master.GetSlave(4).Group(2);
master.GetSlave(5).Group(3); // 传感器 -> 组3

// 2. 设置组分频器(组级配置只接受组号 1-7)
master.Divider(2, 4); // 组2: 每4周期发送
master.Divider(3, 10); // 组3: 每10周期发送
// 组1(默认组)分频器默认=1,无需额外设置

// 3. 切换到 OP -- 按组分频器收发,空组自动跳过
master.SetState(EcState::OP);
master.Start();

// 4. 查看组状态
printf("活跃组数: %d\n", master.ActiveGroupCount());

组语义​

组号含义
0未分配标记 -- 不设置时保持 0;进入 SAFE_OP 映射前自动归入组 1,不独立收发
1默认组 -- 未分组从站最终都在此组,分频器默认 1(每周期)
2-7显式分组 -- 用户手动分配,组号无需连续
备注
  • slave.Group() 可写 0-7;组级配置与查询(Divider、组使能、组 WKC、组从站数)只接受 1-7,对组 0 设置无效
  • 组号可以不连续(如 2, 4, 6),系统会跳过空组
  • 进入 SafeOp 映射时未分组从站归入组 1、各组分频器默认补 1;空组不生成 PDO 数据段

从站组分配​

通过从站对象的 Group() 方法设置组归属。

slave.Group()​

uint8_t Group() const;
void Group(uint8_t grp);

获取或设置从站的组归属(0-7,0=未分配,映射前自动归入组 1)。必须在 SAFE_OP 之前设置。

示例:

// 分配从站到组
master.GetSlave(1).Group(1); // 显式分到组1
master.GetSlave(2).Group(2); // 显式分到组2
// master.GetSlave(3) 不设置(group=0,映射时归入组 1)

// 读取从站所在组
uint8_t group = master.GetSlave(1).Group();

主站级组属性​

Divider()​

uint8_t Divider(uint8_t group) const;
void Divider(uint8_t group, uint8_t value);

获取或设置指定组的 PDO 周期分频器(1-255)。分频器值 N 表示该组每 N 个周期交换一次 PDO 数据。组号只接受 1-7(组 0 是未分配标记,设置无效);各组分频器默认 1。

  • 1 -- 每周期发送(1000Hz,主周期 1ms 时)
  • 2 -- 每2周期发送(500Hz)
  • 4 -- 每4周期发送(250Hz)
  • 10 -- 每10周期发送(100Hz)

示例:

// 设置分频器(组号 1-7)
master.Divider(2, 4); // 组2: 每4周期
master.Divider(3, 10); // 组3: 每10周期
// 组1(默认组)分频器默认=1(每周期),无需设置

// 读取分频器
uint8_t div = master.Divider(3); // 10

ActiveGroupCount()​

uint8_t ActiveGroupCount() const;

获取当前有从站分配的活跃组数量。通过扫描所有从站的组归属计算。

示例:

printf("活跃组数: %d\n", master.ActiveGroupCount());

get_group_slave_count()​

namespace darra::ethercat::master_other {
uint16_t get_group_slave_count(const EtherCATMaster& m, uint8_t group);
}

获取指定组当前包含的从站数量。这是 darra::ethercat::master_other 命名空间下的自由函数, 不是 EtherCATMaster 的成员方法。

参数:

  • m (const EtherCATMaster&) — 主站引用
  • group (uint8_t) — 组号 (1-7;组 0 恒返回 0)

返回值:

  • uint16_t — 该组中已分配的从站个数, 空组返回 0

示例:

using namespace darra::ethercat::master_other;

for (uint8_t g = 1; g < 8; ++g) { // 组级查询只接受 1-7
uint16_t cnt = get_group_slave_count(master, g);
if (cnt) printf("组 %u: %u 个从站\n", g, cnt);
}
C++ SDK 无按组取从站对象的 API

C++ SDK 不提供 GetGroupSlave(group, slotIndex) 之类"按组 + 组内序号直接取 Slave&"的入口。要按组遍历从站, 用全网 1-based 索引遍历并按 slave.Group() 过滤:

// 遍历组 1 (默认组) 的所有从站
for (uint16_t i = 1; i <= master.SlaveCount(); ++i) {
auto& s = master.GetSlave(i); // GetSlave 始终用全网 1-based 索引
if (s.Group() == 1) {
printf("组1 从站 %u 状态: %d\n", i, static_cast<int>(s.State()));
}
}

自动启用/禁用​

进入 SafeOp 的映射阶段,未分组从站(Group()=0)自动归入组 1,各组(1-7)分频器默认补 1:

  • 有从站 / 有 PDO 数据段的组 -> 按分频器参与收发
  • 空组 -> 不生成数据段,跳过 PDO 收发

无需手动调用启用/禁用(master_other::set_group_enabled 是显式多组开关),组号也无需连续。

组诊断​

每组独立跟踪 PDO 丢帧统计。

按组查询丢帧​

auto& diag = master.GetDiagnostics();

// 查询指定组的丢帧统计 (GetPDOFrameLossStats 在 Diagnostics 类上)
auto stats = diag.GetPDOFrameLossStats(1); // 组1
printf("组1: 累计丢帧=%u, 连续=%u\n", stats.total_lost, stats.consecutive_lost);

auto stats2 = diag.GetPDOFrameLossStats(2); // 组2
printf("组2: 累计丢帧=%u\n", stats2.total_lost);

默认参数 (group = 0)​

// 不传参数时 group 默认 0 ——组 0 是未分配标记、无 PDO 数据段,
// 默认组的丢帧统计在映射后归入的组 1 上
auto g1 = master.GetDiagnostics().GetPDOFrameLossStats(1);
printf("组1 (默认组) 丢帧: %u\n", g1.total_lost);

组 WKC 诊断 (2.5.x 新增)​

每个组独立跟踪工作计数器 (WKC)。组 WKC 访问器位于 darra::ethercat::master_other 命名空间,按组返回 EtherCAT 可靠性铁律下的真实值——expected_wkc 是拓扑固定真值永不篡改,actual_wkc 是本周期实测值,actual < expected 表示该组有从站掉线。

namespace darra::ethercat::master_other {
// 组期望 WKC (拓扑固定真值, 不随劣化总线下调)
uint16_t get_group_expected_wkc(const EtherCATMaster& m, uint8_t group);
// 组实际 WKC (本周期 receive_processdata_group 实测)
uint16_t get_group_actual_wkc(const EtherCATMaster& m, uint8_t group);
// 组 WKC 诊断三元组: 连续不足次数 / 累计 TX 帧数 / 累计不匹配帧数
bool get_group_diag(const EtherCATMaster& m, uint8_t group,
uint16_t* consecutive_miss,
uint32_t* total_frames,
uint32_t* mismatch_frames);
// 上述三项的单值封装
uint16_t get_group_consecutive_miss(const EtherCATMaster& m, uint8_t group);
uint32_t get_group_total_frames(const EtherCATMaster& m, uint8_t group);
uint32_t get_group_mismatch_frames(const EtherCATMaster& m, uint8_t group);
}

示例:

using namespace darra::ethercat::master_other;

for (uint8_t g = 1; g < 8; ++g) { // 组级查询只接受 1-7
uint16_t expected = get_group_expected_wkc(master, g);
if (expected == 0) continue; // 空组
uint16_t actual = get_group_actual_wkc(master, g);
printf("组 %u WKC: %u / %u", g, actual, expected);
if (actual < expected)
printf(" <- 该组有从站掉线 (连续不足 %u 周期)", get_group_consecutive_miss(master, g));
printf("\n");
}
WKC 如实反映现实

组 actual < expected 表示该组有从站掉了 — SDK 报警 + 诊断,不下调 expected、不停 OP。从站修复后 actual 自然回升匹配固定的 expected,报警自动消除。

PDOFrameLoss 事件​

PDO 连续丢帧事件的 group 参数标识哪个组发生了丢帧 (用 OnPDOFrameLoss() 注册):

master.Events().OnPDOFrameLoss([](uint16_t masterIndex, uint8_t group,
uint32_t consecutive, uint32_t total) {
printf("组 %d 丢帧: 连续=%u, 累计=%u\n", group, consecutive, total);

if (group == 1) {
// 默认组 (伺服) 丢帧,紧急处理
printf("警告: 伺服组通信异常!\n");
}
});

配置时序​

INIT -> PRE_OP -> SAFE_OP -> OP
| | |
| | +-- 组配置已锁定,PDO 开始按组发送
| |
| +-- 设置从站组归属: slave.Group(N)
| 设置组分频器: master.Divider(N, divider)
| (映射时未分组从站归入组 1,分频器默认补 1)
|
+-- 初始化主站
警告
  • slave.Group() 在进入 SAFE_OP 时生效
  • 组分频器建议在 Build() 之后、SetState(OP) 之前设置
  • 进入 SAFE_OP 后组配置自动锁定

完整示例​

#include "ethercat.hpp"
#include <cstdio>

using namespace darra;

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

printf("发现 %d 个从站\n", master.SlaveCount());

// 1. 分配从站到组
// 从站 1~2 不设置(伺服驱动)—— 映射时归入组 1(默认组)
for (int i = 3; i <= 4; i++)
master.GetSlave(i).Group(2); // IO 模块 -> 组2
master.GetSlave(5).Group(3); // 传感器 -> 组3

// 2. 设置组分频器
master.Divider(2, 4); // 组2: 250Hz
// 组1(默认组)分频器默认=1(1000Hz),无需设置

// 3. 周期
master.LoopCycle(1000000); // 1ms

// 4. PDO 丢帧监控(每组独立)
master.Events().OnPDOFrameLoss([](uint16_t mi, uint8_t group,
uint32_t consecutive, uint32_t total) {
printf("组 %d 丢帧: 连续=%u\n", group, consecutive);
});

// 5. 启动 -- 按组分频器收发,空组自动跳过
master.SetState(EcState::OP);
master.Start();

// 6. 查看组状态
printf("活跃组: %d\n", master.ActiveGroupCount());

// 7. 周期回调中按组处理
master.Events().SetPDOCallbackSync([&](uint16_t mi) {
// 组1(默认组)的伺服控制
std::vector<uint8_t> inFrame;
darra::SlavePDO::CopyInputFrame(dll, master.MasterNumber(), 1, inFrame);
});

printf("运行中... 按 Enter 停止\n");
getchar();

} catch (const ethercat::DarraException& e) {
printf("错误: %s\n", e.what());
return -1;
}
return 0;
}

性能建议​

场景推荐配置
伺服驱动(高实时性)组1(默认组), 分频=1
数字 IO 模块组2, 分频=4~10
模拟传感器组3, 分频=10~100
慢速设备(温度传感器等)组4+, 分频=100+
提示

分频器设置越大,该组占用的总线带宽越低。对于不需要高频更新的设备,适当增大分频器可以减少总线负载,提升实时性能。