从站分组
从站分组功能允许将从站分配到不同的组(0-7),每组可配置独立的 PDO 周期分频器,实现不同组以不同频率交换数据。
典型应用场景:
- 伺服驱动(组0):默认组,每周期发送,31.25us 32KHz
- IO 模块(组1):每4周期发送一次,125us 8000Hz
- 传感器(组2):每6400周期发送一次,200ms 5Hz
核心简化
- 无需手动启用组 — 系统在进入 SafeOp 时自动启用有从站的组,禁用空组
- 属性式分频器 — 通过
master.Divider(group, value)直接设置 - 每组独立收发 — 每个组对应独立的 PDO 数据报文
快速开始
try (EtherCATMaster master = EtherCATMaster.create()) {
master.SetNetwork("\\Device\\NPF_{...}");
// 1. 分配从站到组(必须在 SAFE_OP 之前)
// master.getSlave(0~1) 不设置,保持 group=0(默认组,每周期发送)
master.getSlave(2).Group((byte) 1); // IO 模块 -> 组1
master.getSlave(3).Group((byte) 1);
master.getSlave(4).Group((byte) 2); // 传感器 -> 组2
// 2. 设置组分频器
master.Divider((byte) 1, (byte) 4); // 组1: 每4周期发送
master.Divider((byte) 2, (byte) 10); // 组2: 每10周期发送
// 组0 默认分频器=1,无需额外设置
// 3. 切换到 OP — 系统自动启用有从站的组
master.setState(EcState.OP);
master.Start();
// 4. 查看组状态
System.out.println("活跃组: " + master.ActiveGroupCount());
}
组语义
| 组号 | 含义 |
|---|---|
| 0 | 默认组 — 未显式分组的从站自动归入此组 |
| 1-7 | 显式分组 — 用户手动分配,组号无需连续 |
备注
- 有效组范围为 0-7(共 8 组),每个组独立收发 PDO 数据
- 组号可以不连续(如 1, 3, 5),系统会跳过空组
- 进入 SafeOp 时自动启用有从站的组,禁用空组
从站组分配
slave.Group() / slave.Group(byte)
public byte Group()
public void Group(byte group)
获取或设置从站的组归属(0-7)。必须在 SAFE_OP 之前设置。
示例:
// 分配从站到组
master.getSlave(0).Group((byte) 1); // 显式分到组1
master.getSlave(1).Group((byte) 2); // 显式分到组2
// master.getSlave(2).Group() 不设置,保持 0(默认组)
// 读取从站所在组
byte group = master.getSlave(0).Group();
组分频器
master.Divider(byte group) / master.Divider(byte group, byte value)
public byte Divider(byte group)
public void Divider(byte group, byte value)
获取或设置组的 PDO 周期分频器 (1-255)。
1— 每周期发送(1000Hz,主周期 1ms 时)2— 每2周期发送(500Hz)4— 每4周期发送(250Hz)10— 每10周期发送(100Hz)
示例:
// 设置分频器
master.Divider((byte) 1, (byte) 4); // 组1: 每4周期
master.Divider((byte) 2, (byte) 10); // 组2: 每10周期
// 读取分频器
byte div = master.Divider((byte) 2); // 10
活跃组数量
master.ActiveGroupCount()
public byte ActiveGroupCount()
获取活跃组数量(扫描从站集合计算)。
分频器频率计算
分频器值与实际频率的关系:
| 主周期 | 分频器 | 实际频率 | 适用场景 |
|---|---|---|---|
| 1ms (1000Hz) | 1 | 1000 Hz | 伺服驱动 |
| 1ms (1000Hz) | 4 | 250 Hz | IO 模块 |
| 1ms (1000Hz) | 10 | 100 Hz | 传感器 |
| 1ms (1000Hz) | 200 | 5 Hz | 慢速监控 |
| 31.25us (32KHz) | 1 | 32000 Hz | 高速伺服 |
| 31.25us (32KHz) | 4 | 8000 Hz | IO 模块 |
| 31.25us (32KHz) | 6400 | 5 Hz | 慢速传感器 |
公式: 实际频率 = 主周期频率 / 分频器值
自动启用/禁用
进入 SafeOp 状态时,系统自动扫描每个组(0-7)是否有从站:
- 有从站的组 -> 自动启用,确保分频器至少为 1
- 空组 -> 自动禁用,跳过 PDO 收发
无需手动启用组,组号也无需连续。
组诊断
每组独立跟踪 PDO 丢帧统计。
按组查询丢帧
// 查询指定组的丢帧统计
int[] stats = master.Diagnostics().getPDO().getFrameLossStats(1); // 组1
System.out.printf("组1: 累计丢帧=%d, 连续=%d%n", stats[0], stats[1]);
int[] stats2 = master.Diagnostics().getPDO().getFrameLossStats(2); // 组2
System.out.printf("组2: 累计丢帧=%d%n", stats2[0]);
PDOFrameLoss 事件
PDO 连续丢帧事件的 group 参数标识哪个组发生了丢帧:
master.Events().addPDOFrameLossListener((masterIndex, group, consecutive, total) -> {
System.out.printf("组 %d 丢帧: 连续=%d, 累计=%d%n", group, consecutive, total);
if (group == 0) {
// 伺服组丢帧,紧急处理
System.out.println("警告: 伺服组通信异常!");
}
});
配置时序
INIT -> PRE_OP -> SAFE_OP -> OP
| | |
| | +-- 组配置已锁定,PDO 开始按组发送
| |
| +-- 设置从站组归属: slave.Group(N)
| 设置组分频器: master.Divider(N, divider)
| (进入 SafeOp 时自动启用有从站的组)
|
+-- 初始化主站
警告
slave.Group()在进入 SAFE_OP 时生效- 组分频器建议在初始化之后、OP 之前设置
- 进入 SAFE_OP 后组配置自动锁定
完整示例
try (EtherCATMaster master = EtherCATMaster.create()) {
master.SetNetwork("\\Device\\NPF_{...}");
// 1. 分配从站到组
// master.getSlave(0~2) 不设置,保持 group=0(伺服驱动,默认组)
master.getSlave(3).Group((byte) 1); // IO 模块 -> 组1
master.getSlave(4).Group((byte) 1);
master.getSlave(5).Group((byte) 2); // 传感器 -> 组2
// 2. 设置组分频器
master.Divider((byte) 1, (byte) 4); // 组1: 250Hz
master.Divider((byte) 2, (byte) 10); // 组2: 100Hz
// 组0 默认分频器=1(1000Hz),无需设置
// 3. 设置周期
master.Config().LoopCycle(1_000_000); // 1ms
// 4. PDO 丢帧监控(每组独立)
master.Events().addPDOFrameLossListener((masterIndex, group, consecutive, total) -> {
System.out.printf("组 %d 丢帧: 连续=%d, 累计=%d%n", group, consecutive, total);
});
// 5. 启动 -- 自动启用有从站的组
master.setState(EcState.OP);
master.Start();
// 6. 查看组状态
System.out.println("活跃组: " + master.ActiveGroupCount());
// 7. 遍历从站,显示组归属
for (Slave slave : master.Slaves()) {
System.out.printf("从站 %d: 组 %d%n", slave.SlaveNum(), slave.Group());
}
// 8. 周期回调中按组处理
master.Events().addProcessDataCyclicAsyncListener((masterIndex) -> {
// 组0 的伺服控制
for (Slave servo : master.Slaves()) {
if (servo.Group() == 0) {
// 伺服控制逻辑
}
}
});
// 9. 查询每组丢帧统计
MasterDiagnosticsInfo.MasterPDODiagnostics pdo = master.Diagnostics().getPDO();
int[] g0 = pdo.getFrameLossStats(0);
int[] g1 = pdo.getFrameLossStats(1);
System.out.printf("组0 丢帧: %d, 组1 丢帧: %d%n", g0[0], g1[0]);
}