当前位置: 首页 > java >正文

EtherCAT开源主站 SOEM 2.0 最新源码在嵌入式 Linux 下的移植与编译

EtherCAT 作为工业自动化领域的主流现场总线协议,因其高实时性和高带宽被广泛应用。而 SOEM(Simple Open EtherCAT Master)则是开源社区中最受欢迎的 EtherCAT 主站协议栈之一。本文将以 SOEM 2.0 最新源码为例,详细介绍其在嵌入式 Linux 平台下的移植与编译流程,并结合实际问题给出解决方案,助力开发者高效上手 EtherCAT 主站开发。


文章目录

    • 一、SOEM 项目结构简介
    • 二、samples 目录下的示例程序作用
    • 三、嵌入式 Linux 下的移植与编译流程
      • 1. 安装交叉编译工具链(如果已有非必须)
      • 2. 编写 CMake 工具链文件
      • 3. 配置 CMake
      • 4. 编译
      • 5. 可执行文件部署
    • 四、常见编译报错与解决方案
      • 1. 找不到 eniconv.py
    • 五、跨平台移植建议
    • 六、移植到stm32单片机涉及的内容
      • 1、移植需要的步骤
      • 2、移植需要修改或新增的文件
      • 3、移植流程简述
      • 4、常见移植注意事项
      • 5、移植总结
    • 参考资源

一、SOEM 项目结构简介

github地址:https://github.com/OpenEtherCATsociety/SOEM

SOEM 项目结构清晰,便于移植和二次开发。

源码目录结构介绍:

soem/
├── cmake/                # CMake 构建相关脚本和工具
│   ├── AddENI.cmake
│   ├── Linux.cmake
│   ├── Windows.cmake
│   └── tools/
├── contrib/              # 贡献的代码,包含不同操作系统的适配层
│   ├── cmake/
│   ├── osal/             # OS Abstraction Layer(操作系统抽象层)
│   ├── oshw/             # OS Hardware Layer(硬件抽象层)
│   └── test/
├── include/              # 头文件,主要对外接口和数据结构定义
│   └── soem/
├── osal/                 # OS Abstraction Layer 的主目录
│   ├── linux/
│   ├── rtk/
│   ├── win32/
│   └── osal.h            # OSAL 的主头文件
├── oshw/                 # OS Hardware Layer 的主目录
│   ├── linux/
│   ├── rtk/
│   ├── win32/
│   └── win32/wpcap/      # Windows 下的 WinPcap 相关头文件和库
├── samples/              # 示例程序,演示 SOEM 的用法
│   ├── ec_sample/
│   ├── eepromtool/
│   ├── eni_test/
│   ├── eoe_test/
│   ├── firm_update/
│   ├── simple_ng/
│   └── slaveinfo/
├── scripts/              # 辅助脚本(如 ENI 文件转换)
├── src/                  # SOEM 核心源代码
├── CMakeLists.txt        # CMake 主构建脚本
├── CMakePresets.json     # CMake 构建预设
├── LICENSE.md            # 许可证
├── README.md             # 项目说明

主要目录说明如下:

  • cmake/:跨平台构建脚本,支持多操作系统和工具链。
  • contrib/:第三方贡献的适配代码,包括不同操作系统的 OSAL(操作系统抽象层)和 OSHW(硬件抽象层)。
  • include/soem/:SOEM 的公共头文件,定义主要数据结构和 API。
  • osal/oshw/:分别为操作系统和硬件抽象层的具体实现,按平台分类(如 linux、win32)。
  • samples/:丰富的示例程序,涵盖 EtherCAT 主站开发的常见场景。
  • src/:SOEM 协议栈核心源代码。
  • scripts/:实用脚本,如 ENI 文件转换工具 eniconv.py

src目录下为SOEM2.0核心的协议栈源码。

各个文件的功能介绍:

文件名作用简介主要内容说明
ec_base.cEtherCAT 基础功能实现提供帧的发送/接收、底层数据处理等基础操作,供其他模块调用
ec_coe.cCoE(CANopen over EtherCAT)协议实现实现 SDO/PDO 通信、对象字典访问、参数配置等 CANopen 协议相关功能
ec_config.cEtherCAT 网络配置相关功能负责从站扫描、初始化、配置、分布式时钟同步等主站自动化配置流程
ec_dc.c分布式时钟(DC)功能实现实现主站与从站之间的高精度时钟同步、调整和管理
ec_eoe.cEoE(Ethernet over EtherCAT)协议实现支持以太网帧在 EtherCAT 网络中的透传,实现以太网数据通信
ec_foe.cFoE(File over EtherCAT)协议实现支持主站与从站之间的文件传输,如固件升级、配置文件下发等
ec_main.cSOEM 主流程与核心控制逻辑负责主站初始化、主循环、状态机管理,是协议栈的核心调度中心
ec_print.c调试与信息打印功能实现提供数据包、状态、错误等信息的格式化输出,便于开发调试
ec_soe.cSoE(Servo over EtherCAT)协议实现支持伺服驱动器参数访问和控制,适用于需要 SoE 协议的 EtherCAT 设备

这些文件共同组成了 SOEM 协议栈的核心功能模块,各自负责 EtherCAT 协议的不同部分。

二、samples 目录下的示例程序作用

samples 目录下每个子目录都是一个独立的示例程序,覆盖了 EtherCAT 主站开发的常见需求:

  • ec_sample:基础 EtherCAT 主站示例,适合新手快速入门。
  • eepromtool:EEPROM 操作工具,用于从站底层参数的读写。
  • eni_test:ENI 文件解析与测试,适合基于 ENI 文件的网络配置。
  • eoe_test:Ethernet over EtherCAT 协议测试,实现以太网帧透传。
  • firm_update:从站固件升级示例,适合远程维护场景。
  • simple_ng:极简新一代主站示例,便于快速集成。
  • slaveinfo:从站信息查询工具,常用于调试和设备信息采集。

samples 目录总结

samples 目录下的每个子目录都是一个独立的示例程序,覆盖了 EtherCAT 主站开发的常见场景:

  • 基础通信(ec_sample、simple_ng)
  • 从站信息查询(slaveinfo)
  • EEPROM 操作(eepromtool)
  • ENI 文件支持(eni_test)
  • EOE 协议(eoe_test)
  • 固件升级(firm_update)

这些示例有助于开发者快速上手 SOEM,并根据实际需求进行二次开发。

三、嵌入式 Linux 下的移植与编译流程

1. 安装交叉编译工具链(如果已有非必须)

以 ARM 平台为例:

sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf

如果已经有了交叉编译工具链,则此步骤非必须。

2. 编写 CMake 工具链文件

在项目根目录新建 toolchain-arm.cmake

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)

我的交叉编译工具链toolchain.cmake文件如下:

# 交叉编译工具链配置文件
# 用于嵌入式Linux系统和RISC-V MCU的交叉编译# 设置系统名称
set(CMAKE_SYSTEM_NAME Linux)# 设置处理器架构变量,可以通过命令行参数传入
# 例如: cmake -DTARGET_ARCH=arm ..
if(NOT DEFINED TARGET_ARCH)set(TARGET_ARCH "arm" CACHE STRING "Target architecture (arm or riscv)")
endif()# 设置ARM工具链路径
set(ARM_TOOLCHAIN_PATH "/opt/fsl-imx-x11/4.1.15-2.1.0/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi")
# 设置RISC-V工具链路径
set(RISCV_TOOLCHAIN_PATH "/home/zh1an/tronlong/tina5.0_v1.0/rtos/lichee/rtos/tools/riscv64-elf-x86_64-20201104")# 根据目标架构设置主工具链
if(${TARGET_ARCH} STREQUAL "arm")# ARM Linux工具链配置set(CMAKE_C_COMPILER ${ARM_TOOLCHAIN_PATH}/arm-poky-linux-gnueabi-gcc)set(CMAKE_CXX_COMPILER ${ARM_TOOLCHAIN_PATH}/arm-poky-linux-gnueabi-g++)set(CMAKE_FIND_ROOT_PATH /opt/fsl-imx-x11/4.1.15-2.1.0/sysroots/cortexa7hf-neon-poky-linux-gnueabi/)set(CMAKE_SYSTEM_PROCESSOR arm)# 设置额外的编译标志set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv7-a -mfloat-abi=hard -mfpu=neon" CACHE STRING "" FORCE)set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv7-a -mfloat-abi=hard -mfpu=neon" CACHE STRING "" FORCE)# 设置链接器set(CMAKE_LINKER ${CMAKE_C_COMPILER})set(CMAKE_AR ${ARM_TOOLCHAIN_PATH}/arm-poky-linux-gnueabi-ar)set(CMAKE_RANLIB ${ARM_TOOLCHAIN_PATH}/arm-poky-linux-gnueabi-ranlib)elseif(${TARGET_ARCH} STREQUAL "riscv")# RISC-V工具链配置 (C906核心)set(CMAKE_C_COMPILER ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-gcc)set(CMAKE_CXX_COMPILER ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-g++)set(CMAKE_FIND_ROOT_PATH ${RISCV_TOOLCHAIN_PATH}/riscv64-unknown-elf)set(CMAKE_SYSTEM_PROCESSOR riscv)# 设置RISC-V特定的编译标志 (C906核心)set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=rv64gcv0p7 -mabi=lp64d" CACHE STRING "" FORCE)set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=rv64gcv0p7 -mabi=lp64d" CACHE STRING "" FORCE)# 设置链接器set(CMAKE_LINKER ${CMAKE_C_COMPILER})set(CMAKE_AR ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-ar)set(CMAKE_RANLIB ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-ranlib)else()message(FATAL_ERROR "不支持的目标架构: ${TARGET_ARCH}. 请使用 'arm' 或 'riscv'")
endif()# 定义ARM工具链变量,供CMakeLists.txt使用
set(ARM_C_COMPILER ${ARM_TOOLCHAIN_PATH}/arm-linux-gnueabi-gcc)
set(ARM_CXX_COMPILER ${ARM_TOOLCHAIN_PATH}/arm-linux-gnueabi-g++)
set(ARM_AR ${ARM_TOOLCHAIN_PATH}/arm-linux-gnueabi-ar)
set(ARM_RANLIB ${ARM_TOOLCHAIN_PATH}/arm-linux-gnueabi-ranlib)
set(ARM_C_FLAGS "-march=armv7-a -mfloat-abi=hard -mfpu=neon")
set(ARM_CXX_FLAGS "-march=armv7-a -mfloat-abi=hard -mfpu=neon")# 定义RISC-V工具链变量,供CMakeLists.txt使用
set(RISCV_C_COMPILER ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-gcc)
set(RISCV_CXX_COMPILER ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-g++)
set(RISCV_AR ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-ar)
set(RISCV_RANLIB ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-ranlib)
set(RISCV_C_FLAGS "-march=rv64gcv0p7 -mabi=lp64d")
set(RISCV_CXX_FLAGS "-march=rv64gcv0p7 -mabi=lp64d")# 设置查找规则
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)# 禁用系统库路径
set(CMAKE_SKIP_RPATH TRUE)# 设置交叉编译环境的库和头文件搜索路径
set(CMAKE_SYSROOT ${CMAKE_FIND_ROOT_PATH}) 

3. 配置 CMake

mkdir build
cd build
cmake ../ -DCMAKE_TOOLCHAIN_FILE=../toolchain1.cmake   -DTARGET_ARCH=arm  -DSOEM_BUILD_SAMPLES=ON  -DENICONV=../scripts/eniconv.py -DCMAKE_INSTALL_PREFIX=${HOME}/arm_install

如在目标板上直接编译,可省略 -DCMAKE_TOOLCHAIN_FILE

4. 编译

make -j$(nproc)

在这里插入图片描述

5. 可执行文件部署

编译完成后,samples 目录下的各示例程序会在 build/samples/xxx/ 生成对应可执行文件,拷贝到目标设备即可运行。

我编译构建输出的内容如下:
在这里插入图片描述


四、常见编译报错与解决方案

在这里插入图片描述
原因:CMake 没找到 scripts/eniconv.py 脚本。因为上述构建,启用了构建自带的samples,其中有个ENI工具的测试(sample-eni.c)需要用到这个脚本。

解决方法:
将 scripts 目录加入 PATH:

export PATH=$PATH:/path/to/soem/scripts

或在 CMake 配置时指定脚本路径:

  cmake .. -DENICONV=/path/to/soem/scripts/eniconv.py

1. 找不到 eniconv.py

报错:

Could not find ENICONV using the following names: eniconv.py

原因:CMake 没找到 scripts/eniconv.py 脚本。

解决方法

  • scripts 目录加入 PATH:
    export PATH=$PATH:/path/to/soem/scripts
    
  • 或在 CMake 配置时指定脚本路径:
    cmake .. -DENICONV=/path/to/soem/scripts/eniconv.py
    

五、跨平台移植建议

  • SOEM 2.0 结构清晰,适合嵌入式 Linux 平台移植。
  • 编译前务必理清依赖关系,尤其是 Python 脚本和 ENI 工具的路径问题。
  • 遇到 CMake 报错时,仔细检查路径设置和环境变量,绝大多数问题都能快速定位解决。
  • 推荐优先参考 samples 目录下的示例程序,结合实际需求进行裁剪和二次开发。

六、移植到stm32单片机涉及的内容

将 SOEM 2.0 移植到 STM32 等嵌入式平台,主要涉及操作系统适配硬件驱动适配编译环境配置。下面详细说明移植步骤和需要修改的文件:

1、移植需要的步骤

  1. 操作系统抽象层(OSAL)适配
    • SOEM 通过 OSAL 屏蔽不同操作系统的差异。你需要为 STM32 平台实现一套 OSAL(如基于裸机、FreeRTOS、RT-Thread 等)。
  2. 硬件抽象层(OSHW)适配
    • SOEM 通过 OSHW 屏蔽不同网卡/硬件平台的差异。你需要为 STM32 的以太网 MAC(如 RMII/GMII)实现底层收发驱动。
  3. 编译环境与工具链配置
    • 配置适合 STM32 的交叉编译工具链(如 arm-none-eabi-gcc)。
    • 编写或修改 Makefile/CMakeLists.txt 以适配 STM32 工程结构。
  4. 内存与性能优化
    • 根据 STM32 的 RAM/Flash 资源,裁剪不必要的功能,优化内存分配。

2、移植需要修改或新增的文件

目录/文件说明与操作
contrib/osal/新增 stm32/ 目录,实现 STM32 平台的 osal.c 和 osal_defs.h
contrib/oshw/新增 stm32/ 目录,实现 STM32 平台的 nicdrv.c、nicdrv.h、oshw.c、oshw.h
osal/如需全局适配,可在此新增或修改 osal.h
oshw/如需全局适配,可在此新增或修改 oshw.h
CMakeLists.txt 或 Makefile增加对 STM32 平台的编译选项、源文件路径、交叉编译工具链等配置
samples/可根据 STM32 资源,移植或精简示例程序

3、移植流程简述

  1. 实现 OSAL(操作系统抽象层)

    • 主要包括互斥锁、延时、内存分配等函数。
    • 参考 contrib/osal/linux/contrib/osal/win32/,仿照实现 contrib/osal/stm32/
  2. 实现 OSHW(硬件抽象层)

    • 主要包括以太网帧的收发、网卡初始化等。
    • 参考 contrib/oshw/linux/contrib/oshw/win32/,仿照实现 contrib/oshw/stm32/
    • 需要调用 STM32 HAL/LL 库的以太网驱动(如 ETH DMA、PHY 配置等)。
  3. 配置编译环境

    • 新建适合 STM32 的 Makefile 或 CMake 工程,指定交叉编译器和目标架构。
    • 配置头文件和源文件路径,确保编译时能找到 STM32 适配的 OSAL/OSHW。
  4. 裁剪和优化

    • 移除不需要的协议模块(如 EoE、FoE、SoE 等),只保留核心 EtherCAT 功能。
    • 优化内存分配,避免动态分配,适应 STM32 的内存限制。
  5. 移植和测试示例程序

    • 选择 samples/simple_ngsamples/ec_sample 进行移植,作为移植验证入口。

4、常见移植注意事项

  • STM32 以太网驱动需支持“原始帧”收发(Raw Ethernet),不能走 TCP/IP 协议栈。
  • 需保证中断/轮询方式下的实时性,避免丢包。
  • 可能需要移植 lwIP 的 RAW API 或直接操作 ETH HAL/LL 驱动。
  • 调试时建议先用 PC 端抓包工具(如 Wireshark)配合分析。

5、移植总结

SOEM 2.0 移植到 STM32 的主要步骤:

  1. 新增并实现 STM32 平台的 OSAL 和 OSHW 适配层(contrib/osal/stm32/contrib/oshw/stm32/)。
  2. 配置交叉编译环境,修改编译脚本。
  3. 移植并测试示例程序,逐步完善和优化。

结语

SOEM 2.0 的移植和编译并不复杂,关键在于理解其目录结构和构建流程。希望本文能帮助你顺利在嵌入式 Linux 平台上部署 EtherCAT 主站应用,开启高效、稳定的工业自动化之路!如有更多问题,欢迎留言交流。

参考资源

https://github.com/lipoyang/SOEM4Mbed
https://os.mbed.com/users/EasyCAT/code/EasyCAT_LAB/
https://openethercatsociety.github.io/doc/soes/tutorial_8txt.html

http://www.xdnf.cn/news/15325.html

相关文章:

  • 【读书笔记】《C++ Software Design》第九章:The Decorator Design Pattern
  • LeetCode 1156.单字符重复子串的最大长度
  • 代码部落 20250713 CSP-J复赛 模拟赛
  • 婚后才明白,原来结婚真需要一点冲动!
  • 时序预测 | Matlab代码实现VMD-TCN-GRU-MATT变分模态分解时间卷积门控循环单元多头注意力多变量时序预测
  • (一)SAP Group Reporting (GR) 集团财务合并解决方案套件概述
  • java 基本数据类型所对应的包装类
  • 暑期自学嵌入式——Day01(C语言阶段)
  • C++中顶层const与底层const
  • 【开源项目】网络诊断告别命令行!NetSonar:开源多协议网络诊断利器
  • 【研报复现】开源证券:均线的收敛与发散
  • 从 Manifest V2 升级到 Manifest V3:常见问题与解决方案
  • exe文件图标修改器 - exe图标提取器(ico、png) - 修改360文件夹的图标为windows自带的图标
  • # 通过wifi共享打印机只有手动翻页正反打印没有自动翻页正反打印,而通过网线连接的主机电脑可以自动翻页正反打印
  • 设计模式:软件开发的高效解决方案(单例、工厂、适配器、代理)
  • 预处理器完整功能介绍和示例演示(LESS/SCSS)
  • DMDIS文件到数据库
  • 并查集 UnionFind Test01
  • 什么是RAG(Retrieval-Augmented Generation)?一文读懂检索增强生成
  • websocket连接时发生未知错误
  • SAP顾问职位汇总(第28周)
  • 快速生成 Android 的 Splash 的 9 Patch 图片
  • phpMyAdmin:一款经典的MySQL在线管理工具又回来了
  • DNS解析过程和nmap端口扫描
  • 【STM32实践篇】:F407 时钟系统
  • MacOS使用Multipass快速搭建轻量级k3s集群
  • Spring Boot 安全登录系统:前后端分离实现
  • ERA5的UV合并成矢量并按时间维度转为nc或tif
  • 【版本控制】Perforce Helix Core (P4V) 完全入门指南(含虚幻引擎实战)
  • Spring Boot 集成 Spring Security 完整示例