从站分组
从站分组功能允许将从站分配到不同的组 (0-7),每组可配置独立的 PDO 周期分频器,实现不同组以不同频率交换数据。
典型应用场景:
- 伺服驱动(组0):默认组,每周期发送,31.25us 32KHz
- IO 模块(组1):每4周期发送一次,125us 8000Hz
- 传感器(组2):每6400周期发送一次,200ms 5Hz
- 无需手动启用组 — 系统在进入 SafeOp 时自动启用有从站的组,禁用空组
- 属性式分频器 — 通过
set_group_cycle_divider()直接设置 - 每组独立收发 — 每个组对应独立的 PDO 数据报文
快速开始
let mut master = EtherCATMaster::from_json_file("config.json")?;
// 1. 分配从站到组(必须在 SAFE_OP 之前)
// slave 1~2 不设置,保持 group=0(默认组,每周期发送)
master.slave(3).set_group(1); // IO 模块 -> 组1
master.slave(4).set_group(1);
master.slave(5).set_group(2); // 传感器 -> 组2
// 2. 设置组分频器
master.set_group_cycle_divider(1, 4); // 组1: 每4周期发送
master.set_group_cycle_divider(2, 10); // 组2: 每10周期发送
// 组0 默认分频器=1,无需额外设置
// 3. 切换到 OP — 系统自动启用有从站的组
master.set_state(EcState::Operational)?;
// 4. 查询组信息
println!("组1: {} 个从站", master.group_slave_count(1));
println!("组2: {} 个从站", master.group_slave_count(2));
组语义
| 组号 | 含义 |
|---|---|
| 0 | 默认组 — 未显式分组的从站自动归入此组 |
| 1-7 | 显式分组 — 用户手动分配,组号无需连续 |
- 有效组范围为 0-7(共 8 组),每个组独立收发 PDO 数据
- 组号可以不连续(如 1, 3, 5),系统会跳过空组
- 进入 SafeOp 时自动启用有从站的组,禁用空组
从站组分配
slave.group() / slave.set_group()
pub fn group(&self) -> u8
pub fn set_group(&self, group: u8) -> Result<()>
获取或设置从站的组归属 (0-7)。必须在 SAFE_OP 之前设置。
示例:
// 分配从站到组
master.slave(1).set_group(1); // 显式分到组1
master.slave(2).set_group(2); // 显式分到组2
// master.slave(3) 不设置,保持 0(默认组)
// 读取从站所在组
let group = master.slave(1).group();
group_slave_count()
pub fn group_slave_count(&self, group: u8) -> u16
获取组内从站数量。
active_group_count()
pub fn active_group_count(&self) -> u8
获取活跃组数量(扫描从站集合计算)。
group_expected_wkc()
pub fn group_expected_wkc(&self, group: u8) -> u16
获取组的期望 WKC。
组分频器
set_group_cycle_divider()
pub fn set_group_cycle_divider(&self, group: u8, divider: u8) -> bool
设置组的 PDO 周期分频器 (1-255)。
1— 每周期发送 (1000Hz,主周期 1ms 时)2— 每2周期发送 (500Hz)4— 每4周期发送 (250Hz)10— 每10周期发送 (100Hz)
示例:
// 设置分频器
master.set_group_cycle_divider(1, 4); // 组1: 每4周期
master.set_group_cycle_divider(2, 10); // 组2: 每10周期
// 组0 默认分频器=1(每周期),无需设置
set_group_enabled()
pub fn set_group_enabled(&self, group: u8, enabled: bool) -> bool
启用或禁用组。通常无需手动调用,系统在进入 SafeOp 时自动管理。
自动启用/禁用
进入 SafeOp 状态时,系统自动扫描每个组 (0-7) 是否有从站:
- 有从站的组 -> 自动启用,确保分频器至少为 1
- 空组 -> 自动禁用,跳过 PDO 收发
无需手动调用 set_group_enabled(),组号也无需连续。
组诊断
每组独立跟踪 PDO 丢帧统计。
按组查询丢帧
// 查询指定组的丢帧统计
let stats = master.diagnostics_info().pdo().frame_loss_stats(1);
println!("组1: 累计丢帧={}, 连续={}", stats.total_lost, stats.consecutive_lost);
let stats2 = master.diagnostics_info().pdo().frame_loss_stats(2);
println!("组2: 累计丢帧={}", stats2.total_lost);
组级 WKC 诊断
每个 PDO 组对应一个独立报文、一个独立 WKC。组的实际 WKC、连续不足次数、累计帧数等诊断字段保存在主站状态结构的 group_config_raw 字节区中 (8 组 × 20 字节),通过 EcGroupConfigHelper 静态方法解析。
组期望 WKC 是配置期确定的固定真值,永不下调。get_group_actual_wkc() < get_group_expected_wkc() 表示该组有从站掉了 —— 这是如实的故障反映,应报警 + 诊断,而非降低期望值掩盖。从站修复后实际 WKC 自然回升匹配固定期望值。
EcGroupConfigHelper
EcGroupConfigHelper 是一组静态方法,对原始字节切片 (group_config_raw) 按组 (0-7) 解析。
| 方法 | 返回类型 | 说明 |
|---|---|---|
get_group_enabled(raw, group) | bool | 组是否启用 |
get_group_cycle_divider(raw, group) | u8 | 组周期分频器 |
get_group_expected_wkc(raw, group) | u16 | 组期望 WKC (固定真值) |
get_group_slave_count(raw, group) | u16 | 组内从站数 |
get_group_frame_repeat_eligible(raw, group) | bool | 组帧重复资格 (ETG.1500 5.4.3) |
get_group_actual_wkc(raw, group) | u16 | 组实际 WKC (本周期 receive_processdata_group 结果) |
get_group_consecutive_miss(raw, group) | u16 | 组连续 WKC 不足次数 (actual < expected) |
get_group_total_frames(raw, group) | u32 | 组累计 TX 帧数 |
get_group_mismatch_frames(raw, group) | u32 | 组累计 WKC 不匹配帧数 |
set_group_enabled / set_group_cycle_divider / set_group_frame_repeat_eligible 为对应写方法,接受 &mut [u8]。
每组 20 字节布局:
[enabled:1][cycle_divider:1][expected_wkc:2][slave_count:2]
[frame_repeat_eligible:1][reserved:1][actual_wkc:2][consecutive_miss:2]
[total_frames:4][mismatch_frames:4]
示例:
use darra_ethercat::EcGroupConfigHelper;
// raw 为主站状态结构的 group_config_raw 字节区 (160 字节 = 8 组 × 20)
fn report_group_health(raw: &[u8]) {
for g in 0..8u8 {
let slaves = EcGroupConfigHelper::get_group_slave_count(raw, g);
if slaves == 0 { continue; } // 跳过空组
let expected = EcGroupConfigHelper::get_group_expected_wkc(raw, g);
let actual = EcGroupConfigHelper::get_group_actual_wkc(raw, g);
let miss = EcGroupConfigHelper::get_group_consecutive_miss(raw, g);
if actual < expected {
println!("⚠ 组 {}: WKC {}/{} (连续不足 {} 次) — 有从站掉了",
g, actual, expected, miss);
} else {
println!("组 {}: WKC {}/{} 正常", g, actual, expected);
}
}
}
组级 WKC 诊断属于底层/高级接口,需要持有主站状态结构的原始字节区。常规组级偏差监控推荐用 master.diagnostics_info() 与 pdo().frame_loss_stats(group) (见上);EcGroupConfigHelper 用于需要精确逐组 WKC 真值的诊断工具或自定义监控面板。
PDOFrameLoss 事件
PDO 连续丢帧事件的 group 参数标识哪个组发生了丢帧:
master.events().on_pdo_frame_loss(|_, group, consecutive, total| {
println!("组 {} 丢帧: 连续={}, 累计={}", group, consecutive, total);
if group == 0 {
// 伺服组丢帧,紧急处理
println!("警告: 伺服组通信异常!");
}
});
配置时序
INIT -> PRE_OP -> SAFE_OP -> OP
| | |
| | +- 组配置已锁定,PDO 开始按组发送
| |
| +- 设置从站组归属: slave.set_group(n)
| 设置组分频器: set_group_cycle_divider(n, divider)
| (进入 SafeOp 时自动启用有从站的组)
|
+- 初始化主站
slave.set_group()在进入 SAFE_OP 时生效- 组分频器建议在初始化之后、
set_state(Operational)之前设置 - 进入 SAFE_OP 后组配置自动锁定
完整示例
let mut master = EtherCATMaster::from_json_file("config.json")?;
// 1. 分配从站到组
// slave 1~3 不设置,保持 group=0(伺服驱动,默认组)
for i in 4..=5 {
master.slave(i).set_group(1); // 后2个从站 -> 组1(IO模块)
}
// 其余从站保持 group=0(默认组,每周期发送)
// 2. 设置组分频器
master.set_group_cycle_divider(1, 4); // 组1: 250Hz
// 组0 默认分频器=1(1000Hz),无需设置
// 3. 周期
master.config().set_loop_cycle(1_000_000); // 1ms
// 4. PDO 丢帧监控(每组独立)
master.events().on_pdo_frame_loss(|_, group, consecutive, _| {
println!("组 {} 丢帧: 连续={}", group, consecutive);
});
// 5. 启动 — 自动启用有从站的组
master.set_state(EcState::Operational)?;
// 6. 查看组状态
println!("活跃组: {}", master.active_group_count());
println!("组0: {} 个从站", master.group_slave_count(0));
println!("组1: {} 个从站", master.group_slave_count(1));
// 7. 周期回调 (同步, 供 FSoE 等实时控制; PDO 数据通路为内核共享内存指针轮询)
master.events().on_pdo_cyclic_sync(|_| {
// 仅做快速 PDO 读写
});