2、SOEM库入门:SOEM是什么、SOEM的源码结构、在Linux/Windows下的编译与安装、第一个扫描从站的程序

好,咱们正式开始动手了。

上一章我们聊了EtherCAT的基本原理,说白了就是主站怎么跟从站唠嗑。那这一章,我们就来聊聊主站这边具体怎么实现。我个人最常用的开源主站方案,就是SOEM。

2.1 SOEM是什么?

SOEM,全称是 Simple Open EtherCAT Master。名字很直白——简单、开源、EtherCAT主站。

它是由一个叫RT-Labs的团队开发的,后来贡献给了开源社区。你想想看,一个能跑在Linux、Windows甚至裸机上的EtherCAT主站,代码量还不大,这多香啊。

我在项目中遇到过好几次,客户非要低成本方案,又不想买商业主站。这时候SOEM就是救命稻草。它不依赖任何商业库,纯C语言实现,移植性极强。

SOEM的核心特点:

  • 纯C语言,无外部依赖
  • 支持Linux、Windows、RTOS、裸机
  • 支持EtherCAT标准协议(CoE、FoE、SoE等)
  • 支持DC(分布式时钟)同步
  • 代码量约2万行,适合学习和二次开发

嗯,这里要注意:SOEM不是万能的。它不支持EtherCAT冗余(比如环网),也不支持热插拔。但做单主站、单网段的运动控制,完全够用。

2.2 SOEM的源码结构

我第一次看SOEM源码时,说实话有点懵。文件夹不少,但别怕,核心就几个。

咱们先画个图,把SOEM的骨架看清楚。

SOEM 源码结构图 应用层(你的程序) SOEM API(osal/ethercat.h) 核心协议层(ethercatconfig.c / ethercatmain.c / ethercatcoe.c ...) 配置、状态机、CoE、FoE、DC同步 底层驱动层(osal/nicdrv.c / ethercattype.h) 网络接口驱动(Linux: socket / Windows: winpcap) 调用方向 osal/ ethercat.h src/ osal/ nicdrv.c

说白了,SOEM就三层:

  • osal/:操作系统抽象层。里面放了网络驱动(nicdrv.c)和系统接口(timer、thread等)。你换平台,主要改这里。
  • src/:核心协议栈。包括配置、状态机、邮箱通信等。这部分你基本不用动。
  • ethercat.h:对外API头文件。你写应用时,就包含这个头文件,调用里面的函数。

我记得刚开始学SOEM时,总想搞懂src里每个函数。后来发现没必要。你只需要知道几个关键API怎么用就行。其他的,用到再查。

2.3 在Linux/Windows下的编译与安装

编译SOEM其实很简单。我分别说说两个平台。

2.3.1 Linux下编译

Linux下最省事。SOEM官方推荐用CMake。

# 下载源码
git clone https://github.com/OpenEtherCATsociety/SOEM.git
cd SOEM

# 创建build目录
mkdir build && cd build

# 配置
cmake ..

# 编译
make

# 安装(可选)
sudo make install

编译完成后,会在build目录下生成静态库 libsoem.a。你写程序时链接这个库就行。

小提示:如果你在Linux上跑,记得用root权限或者给网卡设置raw socket权限。不然打不开套接字。我曾经在这上面卡了半小时,后来发现是权限问题。

2.3.2 Windows下编译

Windows下稍微麻烦点,因为SOEM底层依赖WinPcap或Npcap来抓包。

步骤:

  1. 先安装 WinPcapNpcap(推荐Npcap,记得勾选“Install in WinPcap API-compatible Mode”)
  2. 下载SOEM源码,用CMake生成Visual Studio工程
  3. 编译生成 soem.lib

具体命令:

# 假设你已经安装了CMake和VS
mkdir build && cd build
cmake .. -G "Visual Studio 16 2019"
cmake --build . --config Release

注意:Windows下调试时,记得关闭防火墙。WinPcap的驱动有时会被防火墙拦截,导致收不到从站的数据帧。我遇到过好几次,折腾半天发现是防火墙在捣乱。

2.4 第一个扫描从站的程序

好,理论说完了,咱们写个真正的程序。目标:扫描总线上挂了几个从站,打印出它们的信息。

代码不长,我直接贴出来:

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

int main(int argc, char *argv[])
{
    char ifname[] = "eth0";  // 你的网卡名,Linux下用ip link查看
    int i, n_slaves;

    // 1. 初始化SOEM
    if (ec_init(ifname) == 0) {
        printf("初始化失败!检查网卡 %s\n", ifname);
        return -1;
    }
    printf("SOEM初始化成功,网卡: %s\n", ifname);

    // 2. 扫描从站
    n_slaves = ec_config_init(FALSE);
    if (n_slaves == 0) {
        printf("没有发现从站!\n");
        ec_close();
        return -1;
    }
    printf("发现 %d 个从站\n", n_slaves);

    // 3. 打印每个从站信息
    for (i = 1; i <= n_slaves; i++) {
        printf("从站 %d: 厂商ID=0x%08X, 产品码=0x%08X\n",
               i,
               ec_slave[i].eep_man,   // 厂商ID
               ec_slave[i].eep_id);   // 产品码
    }

    // 4. 关闭连接
    ec_close();
    printf("程序结束\n");
    return 0;
}

编译命令(Linux下):

gcc -o scan_slaves scan_slaves.c -lsoem -lpthread

运行前,记得把网线接好,从站上电。然后执行:

sudo ./scan_slaves

如果一切正常,你会看到类似这样的输出:

SOEM初始化成功,网卡: eth0
发现 3 个从站
从站 1: 厂商ID=0x00000002, 产品码=0x0A320000
从站 2: 厂商ID=0x00000002, 产品码=0x0A320000
从站 3: 厂商ID=0x00000002, 产品码=0x0A320000
程序结束

嗯,这里要注意:ec_config_init(FALSE) 这个函数,参数传 FALSE 表示只扫描,不配置PDO映射。如果你要实际控制电机,后面得再调用 ec_config_map()ec_configdc()。这个我们后面章节再细讲。

避坑指南:我曾经在扫描从站时,发现总是少一个。后来排查发现,是网线接触不良。EtherCAT对物理层要求很高,线缆松动或者屏蔽不好,就会丢帧。建议用质量好的工业以太网线。

好了,这一章我们搞定了SOEM的入门。你手头有硬件的话,赶紧试试。没有硬件也别急,下一章我们会讲怎么用模拟从站来调试。


公众号:蓝海资料掘金营,微信deep3321