概述
SOEM 是一个让你的应用程序发送和接收 EtherCAT 数据帧 的软件库。它不包含 维持 EtherCAT 网络处于运行状态的自动逻辑,但它提供了实现该功能所需的全部基础模块。
在 SOEM 中,用户应用程序必须负责处理以下任务:
- 读写过程数据(Process Data)
- 保持本地 I/O 数据与全局 I/O 映射同步
- 检测 SOEM 上报的错误
- 处理 SOEM 上报的错误
下面的章节提供基础示例,展示如何启动 SOEM、如何使用过程数据以及如何检查错误。用户需要根据自己的应用修改这些示例。
创建上下文(Context)
SOEM 需要一个上下文(context) 来保存协议状态。
所有 SOEM 函数都必须传入这个上下文。
上下文由应用程序定义:
C++
static ecx_contextt ctx;
配置
1. 初始化网卡
应用启动后,必须配置网卡(NIC)作为 EtherCAT 通信接口。
- 简单配置:调用
ecx_init() - 需要线缆冗余 :调用
ecx_init_redundant()
函数返回值**>0** 表示成功:
C++
if (ecx_init(&ctx, iface) <= 0) { /* 错误处理 */
}
2. 初始化网络配置
SOEM 是面向嵌入式的轻量级主站库,仅支持运行时配置 。
调用ecx_config_init() 触发配置:
C++
if (ecx_config_init(&ctx) <= 0) { /* 错误处理 */
}
SOEM 会:
- 枚举网络上的所有从站
- 初始化从站
- 请求所有从站进入PreOP 状态
返回值是找到的从站数量,成功时至少为 1。
3. 验证从站配置
配置完成后,可以检查ecx_context中的slavelist 确认从站是否正确:
C++
#define EK1100_1 1
#define EL4001_1 2
#define NUMBER_OF_SLAVES 9
uint32 verify_network_configuration(ecx_contextt* ctx) {
if (ctx->slavecount < NUMBER_OF_SLAVES) return 0;
if (strcmp(ctx->slavelist[EK1100_1].name, "EK1100"))
return 0;
else if (strcmp(ctx->slavelist[EL4001_1].name, "EL4001"))
return 0;
return 1;
}
4. 映射 I/O 内存
调用ecx_config_map() 将从站映射到 IOmap:
C
uint8_t IOmap[IOMAP_SIZE];
size = ecx_config_map(&ctx, IOmap);
if (size > IOMAP_SIZE) {
/* 错误:缓冲区溢出 */
}
在这个调用中,SOEM 会自动计算工作计数器 WKC 。
工作计数器代表有多少从站响应了 EtherCAT 帧,是检测网络错误的核心依据。
应用程序应保存预期的 WKC:
TypeScript
expectedWKC = ctx.group[0].outputsWKC * 2 + ctx.group[0].inputsWKC;
5. 配置分布式时钟 DC
调用ecx_configdc() 配置 EtherCAT 时钟并测量从站传输延迟:
C
ecx_configdc(&ctx);
6. 等待从站进入 SafeOP
C
ecx_statecheck(&ctx, 0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE * 4);
7. 发送初始过程数据
进入运行态前,必须先发送一次过程数据:
C
ecx_send_processdata(&ctx);
ecx_receive_processdata(&ctx, EC_TIMEOUTRET);
8. 切换到 OPERATIONAL 运行态
通过 0 号从站广播切换所有从站:
C
ctx.slavelist[0].state = EC_STATE_OPERATIONAL;
ecx_writestate(&ctx, 0);
等待所有从站切换完成:
C
chk = 200;
do {
ecx_send_processdata(&ctx);
ecx_receive_processdata(&ctx, EC_TIMEOUTRET);
ecx_statecheck(&ctx, 0, EC_STATE_OPERATIONAL, 50000);
} while (chk-- && (ctx.slavelist[0].state != EC_STATE_OPERATIONAL));
if (ctx.slavelist[0].state !=
EC_STATE_OPERATIONAL) { /* 错误:从站无法进入运行态 */
}
9. 周期运行循环(必须)
进入运行态后,必须持续发送过程数据,否则从站看门狗会触发并退回 SafeOP。
C++
for (;;) {
os_usleep(5000); // 周期
// 5msecx_send_processdata(&ctx);wkc=ecx_receive_processdata(&ctx,EC_TIMEOUTRET);if(wkc!=expectedWKC){/*
// 错误:通信异常 */}}
小提示
- 如果你需要在其他线程执行 XoE 命令(CoE、FoE、EoE),请参考官方指南。
- 如果需要控制周期抖动,可以将内部时钟与EtherCAT 分布式时钟(DC) 同步。