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.c | EtherCAT 基礎功能實現 | 提供幀的發送/接收、底層數據處理等基礎操作,供其他模塊調用 |
ec_coe.c | CoE(CANopen over EtherCAT)協議實現 | 實現 SDO/PDO 通信、對象字典訪問、參數配置等 CANopen 協議相關功能 |
ec_config.c | EtherCAT 網絡配置相關功能 | 負責從站掃描、初始化、配置、分布式時鐘同步等主站自動化配置流程 |
ec_dc.c | 分布式時鐘(DC)功能實現 | 實現主站與從站之間的高精度時鐘同步、調整和管理 |
ec_eoe.c | EoE(Ethernet over EtherCAT)協議實現 | 支持以太網幀在 EtherCAT 網絡中的透傳,實現以太網數據通信 |
ec_foe.c | FoE(File over EtherCAT)協議實現 | 支持主站與從站之間的文件傳輸,如固件升級、配置文件下發等 |
ec_main.c | SOEM 主流程與核心控制邏輯 | 負責主站初始化、主循環、狀態機管理,是協議棧的核心調度中心 |
ec_print.c | 調試與信息打印功能實現 | 提供數據包、狀態、錯誤等信息的格式化輸出,便于開發調試 |
ec_soe.c | SoE(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、移植需要的步驟
- 操作系統抽象層(OSAL)適配
- SOEM 通過 OSAL 屏蔽不同操作系統的差異。你需要為 STM32 平臺實現一套 OSAL(如基于裸機、FreeRTOS、RT-Thread 等)。
- 硬件抽象層(OSHW)適配
- SOEM 通過 OSHW 屏蔽不同網卡/硬件平臺的差異。你需要為 STM32 的以太網 MAC(如 RMII/GMII)實現底層收發驅動。
- 編譯環境與工具鏈配置
- 配置適合 STM32 的交叉編譯工具鏈(如 arm-none-eabi-gcc)。
- 編寫或修改 Makefile/CMakeLists.txt 以適配 STM32 工程結構。
- 內存與性能優化
- 根據 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、移植流程簡述
-
實現 OSAL(操作系統抽象層)
- 主要包括互斥鎖、延時、內存分配等函數。
- 參考
contrib/osal/linux/
或contrib/osal/win32/
,仿照實現contrib/osal/stm32/
。
-
實現 OSHW(硬件抽象層)
- 主要包括以太網幀的收發、網卡初始化等。
- 參考
contrib/oshw/linux/
或contrib/oshw/win32/
,仿照實現contrib/oshw/stm32/
。 - 需要調用 STM32 HAL/LL 庫的以太網驅動(如 ETH DMA、PHY 配置等)。
-
配置編譯環境
- 新建適合 STM32 的 Makefile 或 CMake 工程,指定交叉編譯器和目標架構。
- 配置頭文件和源文件路徑,確保編譯時能找到 STM32 適配的 OSAL/OSHW。
-
裁剪和優化
- 移除不需要的協議模塊(如 EoE、FoE、SoE 等),只保留核心 EtherCAT 功能。
- 優化內存分配,避免動態分配,適應 STM32 的內存限制。
-
移植和測試示例程序
- 選擇
samples/simple_ng
或samples/ec_sample
進行移植,作為移植驗證入口。
- 選擇
4、常見移植注意事項
- STM32 以太網驅動需支持“原始幀”收發(Raw Ethernet),不能走 TCP/IP 協議棧。
- 需保證中斷/輪詢方式下的實時性,避免丟包。
- 可能需要移植 lwIP 的 RAW API 或直接操作 ETH HAL/LL 驅動。
- 調試時建議先用 PC 端抓包工具(如 Wireshark)配合分析。
5、移植總結
SOEM 2.0 移植到 STM32 的主要步驟:
- 新增并實現 STM32 平臺的 OSAL 和 OSHW 適配層(
contrib/osal/stm32/
、contrib/oshw/stm32/
)。 - 配置交叉編譯環境,修改編譯腳本。
- 移植并測試示例程序,逐步完善和優化。
結語
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