跳到主要内容

从站分组

从站分组功能允许将从站分配到不同的组(1-7;group=0 表示未分配,映射前自动归入组 1),每组可配置独立的 PDO 周期分频器,实现不同组以不同频率交换数据。

典型应用场景(主周期 31.25µs,WDK 驱动最低支持):

  • 伺服驱动(组1):默认组(未显式分组的从站自动归入),每周期发送,31.25µs 32KHz
  • IO 模块(组2):每4周期发送一次,125µs 8000Hz
  • 传感器(组3):每6400周期发送一次,200ms 5Hz
核心简化
  • 无需手动启用组 — 系统在进入 SafeOp 时自动启用有从站的组,禁用空组
  • 每组独立收发 — 每个组对应独立的 PDO 数据报文

快速开始​

uint16_t master = Initialize();
SetNetwork(master, "\\Device\\NPF_{GUID}", "");

/* 1. 分配从站到组(必须在 SAFE_OP 之前) */
SetSlaveGroup(master, 3, 1); /* 从站 3 → 组1 */
SetSlaveGroup(master, 4, 1);
SetSlaveGroup(master, 5, 2);

/* 2. 设置组分频器 */
SetGroupCycleDivider(master, 1, 4); /* 组1: 每4周期 */
SetGroupCycleDivider(master, 2, 10); /* 组2: 每10周期 */
/* 未显式分组的从站自动归入组1 */

/* 3. 切换到 OP — 系统自动启用有从站的组 */
SetStateSequence(master, EC_STATE_OPERATIONAL, 10000);
Start(master);

组语义​

组号含义
0未分配标记 — SetSlaveGroup 传 0 表示未分配;映射前自动归入组 1,组查询/配置接口不接受 0
1-7有效 PDO 组 — 用户手动分配,组号无需连续;未显式分组的从站也归入组 1
备注
  • 有效组范围为 1-7(组查询/配置接口不接受 0),每个组独立收发 PDO 数据
  • 组号可以不连续(如 1, 3, 5),系统会跳过空组
  • 进入 SafeOp 时自动启用有从站的组,禁用空组

从站组分配​

BOOL    SetSlaveGroup(uint16_t master_index, uint16_t slave_index, uint8_t group);
uint8_t GetSlaveGroup(uint16_t master_index, uint16_t slave_index);
uint8_t GetActiveGroupCount(uint16_t master_index);
uint16_t GetGroupSlaveCount(uint16_t master_index, uint8_t group);
uint16_t GetGroupExpectedWKC(uint16_t master_index, uint8_t group);

SetSlaveGroup() 必须在 SAFE_OP 之前调用(group 可传 0-7,0=未分配)。GetSlaveGroup() 读取从站所属组号。GetActiveGroupCount() 返回当前活跃组数量,GetGroupSlaveCount() 返回组内从站数量,GetGroupExpectedWKC() 返回组的期望 WKC。后三者中带 group 参数的接口只接受 1-7。

组分频器​

BOOL    SetGroupCycleDivider(uint16_t master_index, uint8_t group, uint8_t divider);
uint8_t GetGroupCycleDivider(uint16_t master_index, uint8_t group);

设置组的 PDO 周期分频器(1-255)。例如 divider=4 表示该组每 4 个主周期执行一次 PDO 交换。

分频器与频率对照(主周期 1ms 时):

  • 1 — 1000Hz(每周期)
  • 2 — 500Hz
  • 4 — 250Hz
  • 10 — 100Hz
  • 100 — 10Hz

示例:

SetGroupCycleDivider(master, 1, 4);   /* 组1: 每4周期 */
SetGroupCycleDivider(master, 2, 10); /* 组2: 每10周期 */

自动启用/禁用​

进入 SafeOp 状态时,系统自动扫描每个组(1-7)是否有从站:

  • 有从站的组 → 自动启用,确保分频器至少为 1
  • 空组 → 自动禁用,跳过 PDO 收发

无需手动调用 SetGroupEnabled,组号也无需连续。

BOOL SetGroupEnabled(uint16_t master_index, uint8_t group, BOOL enabled);
BOOL GetGroupEnabled(uint16_t master_index, uint8_t group);

仅在需要手动覆盖系统自动管理时使用。

组诊断​

每组(1-7)独立跟踪 PDO 丢帧统计 + 实际 WKC + 组级累计计数。

uint16_t GetGroupExpectedWKC(uint16_t master_index, uint8_t group);
uint16_t DarraEcat_GetGroupActualWKC(uint16_t master_index, uint8_t group);
int DarraEcat_GetGroupDiag(uint16_t master_index, uint8_t group,
uint16_t* out_consecutive_miss,
uint32_t* out_total_frames,
uint32_t* out_mismatch_frames);

DarraEcat_GetGroupActualWKC — 该组上一次 PDO 交换收到的 WKC。每组一个 LRW 报文, 各组 actual_wkc 独立维护。WKC 不等于 GetGroupExpectedWKC() 时本帧视为该组丢帧。

DarraEcat_GetGroupDiag — 返回 1 成功、0 失败(group 须为 1-7)。三项组级累计计数 (任一输出指针可为 NULL):

  • out_consecutive_miss — 当前连续丢帧数 (任意一帧 WKC 匹配后清零)
  • out_total_frames — 该组累计 TX 帧数 (重启 / ResetDiagnostics 后清零)
  • out_mismatch_frames — WKC 不匹配累计帧数, 与 total 的比值即组级丢帧率

示例:

/* 查询组 1 的诊断 */
uint16_t miss = 0; uint32_t total = 0, mismatch = 0;
if (DarraEcat_GetGroupDiag(master, 1, &miss, &total, &mismatch)) {
uint16_t exp = GetGroupExpectedWKC(master, 1);
uint16_t act = DarraEcat_GetGroupActualWKC(master, 1);
printf("组1: WKC=%u/%u, 连续丢=%u, 累计=%u/%u (%.3f%%)\n",
act, exp, miss, mismatch, total,
total ? 100.0 * mismatch / total : 0.0);
}

/* 兼容入口: 按组 (0-7) 的 PDO 丢帧累计 */
uint32_t consec, t, max_consec;
GetPDOFrameLossStats(master, 1, &t, &consec, &max_consec);

PDOFrameLoss 事件的 group 参数标识哪个组发生了丢帧:

static void on_frame_loss(uint16_t mi, uint8_t group, uint32_t consecutive, uint32_t total)
{
printf("组 %d 丢帧: 连续=%u, 累计=%u\n", group, consecutive, total);
}
RegisterPDOFrameLossCallback(on_frame_loss);

配置时序​

INIT → PRE_OP → SAFE_OP → OP
│ │ │
│ │ └─ 组配置已锁定,PDO 开始按组发送
│ │
│ └─ SetSlaveGroup(master, slave, group)
│ SetGroupCycleDivider(master, group, divider)
│ (进入 SafeOp 时自动启用有从站的组)
│
└─ Initialize / SetNetwork
警告
  • SetSlaveGroup() 在进入 SAFE_OP 时生效
  • 组分频器建议在 SetNetwork() 之后、SetStateSequence() 之前设置
  • 进入 SAFE_OP 后组配置自动锁定

Hot Connect 组 (ETG.1020)​

Hot Connect 是 EtherCAT 标准 (ETG.1020 §8.3) 定义的"可选从站段"机制。 在主站启动时, Hot Connect 组内的从站可以缺席,主站不会因此停在 INIT,而会跳过该段并仍然把其他从站推到 OP;现场后续把可选从站接入时,主站可报 Offline/Online,不会自动进 OP,应用层要自己处理状态机。

典型场景: 装配线/物流台车工件附带 IO 模块、巡检车断电脱离、移动机械臂末端工具切换。

与普通分组的区别
  • SetSlaveGroup 把从站划入 PDO 周期分频组(实时控制层)
  • Hot Connect 组是状态机/拓扑层概念, 决定该段是否"必须存在",与 PDO 分频组正交
#define EC_HOTCONNECT_MAX_GROUPS 32

typedef enum {
EC_HOTCONNECT_UNKNOWN = -1,
EC_HOTCONNECT_ABSENT = 0,
EC_HOTCONNECT_PRESENT = 1
} ec_hotconnect_status_t;

#pragma pack(push, 1)
typedef struct {
uint16_t group_id;
uint16_t alias_address; /* ESC 0x0012, 必须非 0 且组内唯一 */
uint32_t vendor_id; /* 0=不校验 */
uint32_t product_code; /* 0=不校验 */
int32_t is_present;
uint16_t detected_slave_idx;
uint16_t reserved;
} ec_hotconnect_group_t;
#pragma pack(pop)

int HotConnectAddGroup(uint16_t master_index, uint16_t group_id,
uint16_t alias_address, uint32_t vendor_id, uint32_t product_code);
int HotConnectRemoveGroup(uint16_t master_index, uint16_t group_id);
int HotConnectGetGroupStatus(uint16_t master_index, uint16_t group_id);
int HotConnectEnumerate(uint16_t master_index, ec_hotconnect_group_t* out_buf, int max);
void HotConnectClearAll(uint16_t master_index);
int HotConnectGetGroupCount(uint16_t master_index);

HotConnectAddGroup() 必须在 SetNetwork / 扫描前调用。group_id 同主站下唯一(1..65535)。HotConnectGetGroupStatus() 返回 EC_HOTCONNECT_PRESENT(1)、EC_HOTCONNECT_ABSENT(0)、EC_HOTCONNECT_UNKNOWN(-1)。

Alias 必须烧录

Alias Address (ESC 0x0012) 必须预先烧录到从站 EEPROM 中, 非 0 且组内唯一。 Alias=0 的从站不支持 Hot-Connect。

配合身份不符回调

插入身份不匹配的设备会通过 RegisterSlaveIdentityMismatchCallback 触发, 操作员检查/更换设备后再调用 AcknowledgeSlaveReplacement 重置识别状态机。

完整示例​

#include "ethercat.h"
#include <stdio.h>

static void on_frame_loss(uint16_t mi, uint8_t group, uint32_t consecutive, uint32_t total) {
printf("组 %d 丢帧: 连续=%u, 累计=%u\n", group, consecutive, total);
}

int main(void) {
uint16_t master = Initialize();
SetNetwork(master, "\\Device\\NPF_{GUID}", "");

/* 1. 分配从站到组 */
SetSlaveGroup(master, 3, 1); /* IO 模块 */
SetSlaveGroup(master, 4, 1);

/* 2. 设置组分频器 */
SetGroupCycleDivider(master, 1, 4); /* 组1: 250Hz */

/* 3. 设置周期 */
SetMasterLoopCycleTime(master, 1000000); /* 1ms */

/* 4. 注册回调 */
RegisterPDOFrameLossCallback(on_frame_loss);

/* 5. 启动 */
SetStateSequence(master, EC_STATE_OPERATIONAL, 10000);
Start(master);

/* 6. 查看组状态(组接口只接受 1-7;未分组从站自动归入组 1) */
printf("活跃组数: %d\n", GetActiveGroupCount(master));
printf("组 1: %d 个从站, 分频=%d\n",
GetGroupSlaveCount(master, 1), GetGroupCycleDivider(master, 1));
printf("组 2: %d 个从站, 分频=%d\n",
GetGroupSlaveCount(master, 2), GetGroupCycleDivider(master, 2));

getchar();
Stop(master);
Dispose(master);
return 0;
}