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就三层:
- 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来抓包。
步骤:
- 先安装 WinPcap 或 Npcap(推荐Npcap,记得勾选“Install in WinPcap API-compatible Mode”)
- 下载SOEM源码,用CMake生成Visual Studio工程
- 编译生成
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