配置
启动参数
启动参数 (Startup Parameters) 是主站在 ESM 状态转换前后自动写入从站的 SDO/寄存器序列, 符合 ETG.1500 PO->SO Init 命令规范. 适用于厂商特定初始化序列 (如配置 PDO 映射、设置工作模式、写入电机参数等), 用户态无需手写状态机, SDK 在 SetStateSequence 期间按 transition+priority 顺序自动应用.
AddStartupParameter()
int AddStartupParameter(uint16_t master_index, uint16_t slave_index,
const ec_startup_param_t* param);
为指定从站追加一条启动参数, 参数会缓存到从站槽位, 状态转换时按 priority 顺序应用.
参数:
master_index(uint16_t) — 主站索引slave_index(uint16_t) — 从站索引 (1-based)param(const ec_startup_param_t*) — 启动参数描述
返回值:
int— 0=成功, 非 0=失败 (从站不存在 / 槽位已满 / 参数非法)
相关结构:
#pragma pack(push, 1)
typedef struct {
uint16_t index; /* SDO 索引 */
uint8_t sub_index; /* SDO 子索引 */
uint8_t data[256]; /* 参数数据 (EC_STARTUP_PARAM_MAX_DATA) */
uint16_t data_len; /* 数据长度 (字节) */
uint8_t transition; /* 转换标识 (EC_TRANS_*) */
uint8_t timing; /* 执行时机 (EC_TIMING_*) */
uint16_t priority; /* 执行优先级 (数值越小越先执行) */
uint8_t complete_access; /* 完整访问 (CA=1 写入整个索引) */
uint8_t is_register_write; /* 1=寄存器写入, 0=SDO 写入 */
} ec_startup_param_t;
#pragma pack(pop)
transition:EC_TRANS_IP=0(Init->PreOp),EC_TRANS_PS=1(PreOp->SafeOp),EC_TRANS_SO=2(SafeOp->OP),EC_TRANS_OS=3,EC_TRANS_SP=4,EC_TRANS_PI=5timing:EC_TIMING_BEFORE=0(转换前执行),EC_TIMING_AFTER=1(转换后执行)- 单从站最多
EC_MAX_STARTUP_PARAMS=128条
必须在 SetStateSequence/SetStateWithStartup 之前添加完毕. 已经进入 OP 后再添加的条目本周期不会生效, 需重新跑状态转换.
示例:
/* PreOp 之前写入电机操作模式 = 8 (CSP, CiA402) */
ec_startup_param_t p = {0};
p.index = 0x6060;
p.sub_index = 0x00;
p.data[0] = 8;
p.data_len = 1;
p.transition = EC_TRANS_PS; /* PreOp -> SafeOp */
p.timing = EC_TIMING_BEFORE;
p.priority = 100;
p.complete_access = 0;
p.is_register_write = 0;
if (dll.AddStartupParameter(master, 1, &p) != 0)
printf("启动参数追加失败\n");
ApplyStartupParameters()
int ApplyStartupParameters(uint16_t master_index, uint16_t slave_index,
uint8_t transition, uint8_t timing);
立即对指定从站应用匹配 (transition + timing) 的启动参数, 用于手动驱动场景 (跳过 SetStateSequence 自动管线时).
参数:
master_index(uint16_t) — 主站索引slave_index(uint16_t) — 从站索引 (1-based)transition(uint8_t) — 状态转换标识 (EC_TRANS_*)timing(uint8_t) — 执行时机 (EC_TIMING_BEFORE/EC_TIMING_AFTER)
返回值:
int— 已成功应用的参数数量, 负数表示失败
绝大多数应用走 SetStateSequence 即可, 框架自动在每次转换前后调用本函数. 只有需要在自定义状态机里精确控制时机时才手动调用.
示例:
/* 手动: PreOp -> SafeOp 之前应用 EC_TRANS_PS / BEFORE 段 */
int n = dll.ApplyStartupParameters(master, 1, EC_TRANS_PS, EC_TIMING_BEFORE);
printf("从站1 应用了 %d 条启动参数\n", n);
ApplyStartupParametersAll()
int ApplyStartupParametersAll(uint16_t master_index, uint8_t transition, uint8_t timing);
对全部从站批量应用匹配 (transition + timing) 的启动参数, 等价于循环 ApplyStartupParameters.
参数:
master_index(uint16_t) — 主站索引transition(uint8_t) — 状态转换标识 (EC_TRANS_*)timing(uint8_t) — 执行时机 (EC_TIMING_BEFORE/EC_TIMING_AFTER)
返回值:
int— 已成功应用的参数总数, 负数表示失败
示例:
/* 进 OP 之前对所有从站应用 PS/BEFORE 段 */
dll.ApplyStartupParametersAll(master, EC_TRANS_PS, EC_TIMING_BEFORE);
dll.SetStateWithTimeout(master, EC_STATE_SAFE_OP, 5000);
dll.ApplyStartupParametersAll(master, EC_TRANS_PS, EC_TIMING_AFTER);
相关辅助
ClearStartupParameters(mi, si)→int— 清除指定从站的全部启动参数GetStartupParameterCount(mi, si)→int— 查询指定从站当前缓存的启动参数数量AddStartupParameterBatch(mi, si, params, count)→int— 一次性批量追加 (避免循环单条调用开销)
JSON 配置加载
LoadConfigJson()
int LoadConfigJson(uint16_t master_index, const char* json_str);
从 JSON 字符串加载配置(含从站启动参数)。返回加载的参数数量。
AutoConfigureSM()
int AutoConfigureSM(uint16_t master_index, uint16_t slave_index);
根据启动参数自动计算 SM2/SM3。slave_index=0 时处理所有从站。
一步初始化
详见 初始化 - EcInit。
日志控制
void SetPDOLogging(BOOL enable);
void SetMailboxLogging(BOOL enable);
void SetDebugLogging(BOOL enable);
日志类别:
typedef enum {
LOG_CATEGORY_ERROR = 0,
LOG_CATEGORY_WARNING = 1,
LOG_CATEGORY_MESSAGE = 2,
LOG_CATEGORY_MAILBOX = 3,
LOG_CATEGORY_PDO = 4,
LOG_CATEGORY_DEBUG = 5
} log_category_t;
互斥锁保护
void SetMutexProtection(uint16_t master_index, BOOL enable);
BOOL GetMutexProtection(uint16_t master_index);
互斥锁保护开关。控制 PDO 读写时是否自动加锁确保线程安全。
- 启用(默认) — 框架自动加锁保证线程安全
- 禁用 — 关闭自动互斥锁,由用户自行管理线程安全,减少锁竞争开销
禁用互斥锁保护后,用户需要自行确保 PDO 数据的线程安全。适用于对延迟有极致要求且能自行管理并发的场景。
IOmap 锁定
void LockIOmap(uint16_t master_index);
void UnlockIOmap(uint16_t master_index);
手动锁定/解锁 IOmap,用于多轴批量原子写入。当 SetMutexProtection(mi, FALSE) 时,需要手动调用此函数保证 PDO 数据的原子性。
三种线程安全模式对比:
| 模式 | 设置 | 延迟 | 适用场景 |
|---|---|---|---|
| 自动互斥锁 | SetMutexProtection(mi, TRUE)(默认) | ~200ns | 通用场景,无需关心线程安全 |
| 手动锁 | SetMutexProtection(mi, FALSE) + LockIOmap/UnlockIOmap | 多轴插补,需要批量原子写入 | |
| 无锁 | SetMutexProtection(mi, FALSE),不调用 Lock | 0 | 单线程写入,保证无竞争 |
多轴联动插补时,多个从站的目标位置必须在同一 PDO 周期内同时生效。使用手动 IOmap 锁可以保证一批写入操作在一次 PDO 帧中原子提交。
示例(多轴插补批量写入):
// 关闭自动互斥锁
SetMutexProtection(mi, FALSE);
// PDO 回调中批量写入
void pdo_callback(uint16_t mi) {
LockIOmap(mi);
// 所有写入在同一把锁内完成,保证原子性
WriteSlaveOutput(mi, 1, target_pos1_data, 4);
WriteSlaveOutput(mi, 2, target_pos2_data, 4);
WriteSlaveOutput(mi, 3, target_pos3_data, 4);
UnlockIOmap(mi);
}
PDO 周期配置
LoopCycle
uint32_t config_get_loop_cycle(uint16_t master);
void config_set_loop_cycle(uint16_t master, uint32_t cycle_ns);
PDO 交换周期时间,单位:纳秒(ns)。
主站 RT 线程(WDK 内核定时器驱动)以此周期执行 PDO 帧的发送和接收。该值决定了主站与从站之间过程数据的刷新频率 -- 值越小,数据刷新越快,但对系统实时性要求也越高。WDK 驱动最低支持 31.25μs。
Windows 系统建议不低于 125,000ns(125us)。WDK 驱动完全支持 31.25us,更小的值在 Windows 上没有意义,应用层无法做到更高频的写入。
示例:
config_set_loop_cycle(mi, 1000000); /* 1ms = 1,000,000ns */
config_set_loop_cycle(mi, 500000); /* 500us = 500,000ns */
config_set_loop_cycle(mi, 250000); /* 250us = 250,000ns */
config_set_loop_cycle(mi, 125000); /* 125us = 125,000ns(Windows 推荐最小值) */
DC 漂移补偿自动完成,无需额外配置。
邮箱报文(SDO 读写、FoE 文件传输、紧急消息等)自动附带在 PDO 周期中,不占用额外帧。
邮箱响应延迟: 约 2-3 个 PDO 周期。如 LoopCycle=1ms,SDO 读取约需 2-3ms。
通信配置属性
以下配置通过 master/config.h 中的内联函数读写,底层通过 ec_State 结构体指针直接访问字段。
使用这些函数需要包含 master/config.h,该头文件已被 ethercat.h 自动包含。
| 类别 | 函数 (Get/Set) | 类型 | 读写 | 默认值 | 说明 |
|---|---|---|---|---|---|
| PDO 周期 | config_get/set_loop_cycle(master) | uint32_t | 读写 | 1000000 | PDO 交换周期(纳秒) |
| 帧优先级 | config_get/set_frame_high_priority(master) | BOOL | 读写 | FALSE | 帧 OS 级优先级(Linux: SO_PRIORITY, Windows: 线程优先级) |
| PDO 优化 | config_get/set_overlapping_groups(master) | BOOL | 读写 | FALSE | FMMU 逻辑地址重叠模式(TI ESC),仅 INIT/PREOP 设置 |
| config_get/set_packed_mode(master) | BOOL | 读写 | FALSE | bit 紧密 PDO 映射(Beckhoff EL1008/EL2008),仅 INIT/PREOP 设置 | |
| 帧重复 | config_get/set_frame_repeat_count(master) | uint8_t | 读写 | 1 | 每周期 PDO 帧重复次数(1=禁用, 2-3=ETG.1500 5.4.3) |
| 丢帧检测 | config_get/set_pdo_frame_loss_threshold(master) | uint16_t | 读写 | 3 | 连续丢帧阈值,达到时触发 PDOFrameLoss 回调 |
| 自适应超时 | config_get/set_adaptive_timeout(master) | BOOL | 读写 | FALSE | 根据网络 RTT 动态调整 PDO 超时值 |
| 状态超时 | config_get/set_timeout_init_to_preop(master) | uint32_t | 读写 | 3000 | INIT->PREOP 超时(毫秒, ETG.1020) |
| config_get/set_timeout_preop_to_safeop(master) | uint32_t | 读写 | 10000 | PREOP->SAFEOP 超时(毫秒) | |
| config_get/set_timeout_safeop_to_op(master) | uint32_t | 读写 | 5000 | SAFEOP->OP 超时(毫秒) | |
| 看门狗 | config_get/set_pd_watchdog_ms(master) | uint16_t | 读写 | 0 | 过程数据看门狗超时(毫秒, 0=从站默认, ETG.1000.4) |
| config_get/set_pdi_watchdog_ms(master) | uint16_t | 读写 | 0 | PDI 看门狗超时(毫秒, 0=从站默认) | |
| VLAN | config_get/set_vlan_id(master) | uint16_t | 读写 | 0 | VLAN ID(0=禁用, 1-4095, IEEE 802.1Q),仅 WDK 模式 |
| config_get/set_vlan_priority(master) | uint8_t | 读写 | 0 | VLAN 优先级 PCP(0-7, 7 最高),需先设置 VlanId | |
| UDP | SetUdpMode(master, enable) / GetUdpMode(master) | BOOL | 读写 | FALSE | UDP 帧传输模式(ETG.1500 5.3.7) |
| IsUdpAvailable(master) | BOOL | 只读 | - | UDP 帧传输是否可用 |
帧优先级
BOOL config_get_frame_high_priority(uint16_t master);
void config_set_frame_high_priority(uint16_t master, BOOL enabled);
OS 层面的帧发送优先级提升。不插入 VLAN 标签,与 VLAN 功能独立。
PDO 优化
/* Overlapping Groups (TI ESC 重叠模式) */
BOOL config_get_overlapping_groups(uint16_t master);
void config_set_overlapping_groups(uint16_t master, BOOL enabled);
/* Packed PDO 映射 (bit-level IO 模块) */
BOOL config_get_packed_mode(uint16_t master);
void config_set_packed_mode(uint16_t master, BOOL enabled);
Overlapping Groups 和 Packed Mode 仅在 INIT 或 PRE_OP 状态下设置,SafeOp/OP 状态下修改无效。
帧重复 (ETG.1500 5.4.3)
uint8_t config_get_frame_repeat_count(uint16_t master);
void config_set_frame_repeat_count(uint16_t master, uint8_t count);
每个 PDO 周期发送相同帧的次数。count=1 禁用(默认),count=2-3 启用。需要从站支持帧重复(通过 SetSlaveSupportsFrameRepeat 标记)。
PDO 丢帧阈值
uint16_t config_get_pdo_frame_loss_threshold(uint16_t master);
void config_set_pdo_frame_loss_threshold(uint16_t master, uint16_t threshold);
连续丢帧数达到此阈值时触发 PDOFrameLoss 回调。默认值为 3。
自适应超时
BOOL config_get_adaptive_timeout(uint16_t master);
void config_set_adaptive_timeout(uint16_t master, BOOL enabled);
启用后根据网络往返时间(RTT)动态调整 PDO 超时值,适用于网络延迟波动较大的场景。
状态转换超时 (ETG.1020)
/* INIT -> PREOP (默认 3000ms) */
uint32_t config_get_timeout_init_to_preop(uint16_t master);
void config_set_timeout_init_to_preop(uint16_t master, uint32_t timeout_ms);
/* PREOP -> SAFEOP (默认 10000ms) */
uint32_t config_get_timeout_preop_to_safeop(uint16_t master);
void config_set_timeout_preop_to_safeop(uint16_t master, uint32_t timeout_ms);
/* SAFEOP -> OP (默认 5000ms) */
uint32_t config_get_timeout_safeop_to_op(uint16_t master);
void config_set_timeout_safeop_to_op(uint16_t master, uint32_t timeout_ms);
状态转换超时控制。在 SetStateSequence 或 SetStateWithTimeout 前设置,用于调整等待从站响应的最大时间。
过程数据看门狗 (ETG.1000.4)
/* 过程数据看门狗 (默认 0=从站默认值) */
uint16_t config_get_pd_watchdog_ms(uint16_t master);
void config_set_pd_watchdog_ms(uint16_t master, uint16_t timeout_ms);
/* PDI 看门狗 (默认 0=从站默认值) */
uint16_t config_get_pdi_watchdog_ms(uint16_t master);
void config_set_pdi_watchdog_ms(uint16_t master, uint16_t timeout_ms);
全局看门狗超时配置。设置为 0 时使用从站 EEPROM 中的默认值。与从站级看门狗 SetSlaveWatchdog 独立。
UDP 帧传输 (ETG.1500 5.3.7)
BOOL SetUdpMode(uint16_t master_index, BOOL enable);
BOOL GetUdpMode(uint16_t master_index);
BOOL IsUdpAvailable(uint16_t master_index);
启用 UDP 帧传输模式。IsUdpAvailable 检查当前环境是否支持 UDP 传输。
VLAN 配置 (IEEE 802.1Q)
uint16_t config_get_vlan_id(uint16_t master);
void config_set_vlan_id(uint16_t master, uint16_t vlan_id);
uint8_t config_get_vlan_priority(uint16_t master);
void config_set_vlan_priority(uint16_t master, uint8_t priority);
大部分标准 EtherCAT 从站(Beckhoff EK/EL 系列等)不支持 VLAN 帧。启用 VLAN 会导致帧头从 14 扩展到 18 字节,不兼容的从站直接丢弃帧(WKC=0)。仅在确认所有从站支持 802.1Q 时才启用。
- 仅 WDK 驱动模式下生效
- 需要在
SetNetwork之前设置VlanId VlanPriority需要VlanId为非零值才能生效
配置示例
dll_t dll;
LOAD_DLL(&dll, "Darra.Core.dll");
uint16_t mi = dll.Initialize();
/* 设置状态转换超时 */
config_set_timeout_init_to_preop(mi, 5000);
config_set_timeout_preop_to_safeop(mi, 15000);
config_set_timeout_safeop_to_op(mi, 8000);
/* 启用帧优先级 */
config_set_frame_high_priority(mi, TRUE);
/* 设置帧重复 (需要从站支持) */
config_set_frame_repeat_count(mi, 2);
/* 设置过程数据看门狗 100ms */
config_set_pd_watchdog_ms(mi, 100);
/* 启用自适应超时 */
config_set_adaptive_timeout(mi, TRUE);
/* 设置丢帧检测阈值 */
config_set_pdo_frame_loss_threshold(mi, 5);
dll.SetNetwork(mi, adapter, "");
dll.SetStateSequence(mi, EC_STATE_OPERATIONAL, 15000);
扫描配置
ScanRevisionMatch
uint32_t config_get_scan_revision_match(uint16_t master);
void config_set_scan_revision_match(uint16_t master, uint32_t mode);
扫描从站时的版本匹配策略。控制 Build 过程中扫描到的从站与 DENI 配置中从站的版本号匹配严格程度。
0Exact — 精确匹配 VendorID + ProductID + RevisionID1CompatibleHigher — 兼容匹配: 扫描到的 RevisionID >= 配置的 RevisionID2IgnoreRevision — 忽略 RevisionID,仅匹配 VendorID + ProductID(默认)
DC 配置
DriftCompensation
BOOL config_get_drift_compensation(uint16_t master);
void config_set_drift_compensation(uint16_t master, BOOL enabled);
DC 漂移补偿开关。启用后主站周期性检测 DC 系统时间偏差并写入补偿值 (0x0920)。默认关闭。
超时配置最佳实践
EtherCAT 通信涉及的多种超时字段彼此独立, 对网络异常的检测灵敏度和恢复时延有直接影响. 下表汇总常用字段、典型默认值与推荐范围, 实际部署时按照网络长度 / 干扰水平 / 实时性优先级三维平衡选取.
关键字段速查:
| 字段 | 单位 | 默认 | 推荐范围 | 影响 |
|---|---|---|---|---|
| loop_cycle | ns | 1,000,000 (1ms) | 125,000 ~ 4,000,000 (125us ~ 4ms) | PDO 周期, 决定刷新率与 CPU 负载 |
| timeout_init_to_preop | ms | 3000 | 3000 ~ 10000 | INIT->PREOP 等待时间 (慢从站 / EEPROM 加载) |
| timeout_preop_to_safeop | ms | 10000 | 10000 ~ 30000 | PREOP->SAFEOP 等待时间 (含 SM/FMMU/PDO 映射, 大网络可加大) |
| timeout_safeop_to_op | ms | 5000 | 5000 ~ 15000 | SAFEOP->OP 等待时间 |
| pd_watchdog_ms | ms | 0 (从站默认 ≈100) | loop_cycle × 10 ~ 100 | 从站丢失主站连接后回退 SafeOp 的时间 |
| pdi_watchdog_ms | ms | 0 (从站默认) | 0 ~ 200 | 从站本地固件停止访问 ESC 的检测窗口 |
何时调大 / 调小
调大 适用场景:
- 长电缆 / 跨机柜 / 跨车间 -- 物理传输延迟大, 偶发 RTT 抖动需缓冲
- 高 EMI / 焊接 / 大功率变频器附近 -- 噪声造成偶发丢帧, 重试空间需放宽
- 跨 VLAN / TSN 复合网络 -- 经过交换机会引入排队抖动
- 大网络 (>50 从站) --
timeout_preop_to_safeop必须放大, 否则 SafeOp 转换失败 - 慢从站 (FoE 大文件下载 / EEPROM 写入) -- 邮箱 / Init->PreOp 都要拉长
调小 适用场景:
- 快速故障检测优先 -- 工业紧急停车场景, 看门狗调到 5×loop_cycle, 50ms 内检测到丢帧
- 桌面短电缆 / 实验室 -- RTT 极低, 看门狗可下探到 20~50ms
- RT 严苛 (≤ 250µs 周期) -- 整体时序紧凑, 任何超时都要短
默认 / 实验室 / 工业 / 高 EMI 四档示例
/* (1) SDK 默认 — 适合标准短电缆 + 桌面 ~ 普通车间 */
config_set_loop_cycle(mi, 1000000); /* 1ms */
config_set_timeout_preop_to_safeop(mi, 10000);
config_set_pd_watchdog_ms(mi, 0); /* 从站默认 (~100ms) */
/* (2) 实验室高速 — 短线 + 严苛 RT */
config_set_loop_cycle(mi, 250000); /* 250us */
config_set_timeout_preop_to_safeop(mi, 5000);
config_set_pd_watchdog_ms(mi, 20); /* 极快检测 */
/* (3) 工业典型 — 长电缆 + 大网络 */
config_set_loop_cycle(mi, 1000000);
config_set_timeout_preop_to_safeop(mi, 20000); /* 大网络放宽 */
config_set_pd_watchdog_ms(mi, 200);
/* (4) 高 EMI / 焊接现场 — 容忍偶发丢帧 */
config_set_loop_cycle(mi, 2000000); /* 2ms */
config_set_timeout_preop_to_safeop(mi, 30000);
config_set_pd_watchdog_ms(mi, 500);
config_set_adaptive_timeout(mi, TRUE); /* 自适应吸收抖动 */
- 超时太大 -- 真实故障也要等到超时才上报, 应用层告警 / 安全停机延迟变长
- 超时太小 -- 偶发抖动被误判成故障, 频繁触发
PDOFrameLoss/StateChange噪声告警 - 看门狗 < loop_cycle × 数倍 -- 正常运行也会触发, 从站反复回退 SafeOp
- 调整后建议跑 24h 通讯压力测试再上线
启用 config_set_adaptive_timeout(mi, TRUE) 后, SDK 自动按真实 RTT 计算 PDO 单帧超时,
无需手动收紧 loop_cycle. 上面的 watchdog / state transition 字段仍需按场景配置.