SOEM2.0 API教程(一)

概述

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) 同步。