第二讲:环境搭建——交叉编译工具链安装、Linux内核头文件准备、CMake工程创建、第一个Hello SOEM程序

各位同学,欢迎来到第二讲。

上一讲我们聊了SOEM的整体架构和它能干什么。今天咱们要动真格的了——把开发环境搭起来,然后跑通第一个SOEM程序。

环境搭建这事儿,说简单也简单,说坑也多。我这些年帮不少团队搭过环境,见过有人卡在工具链版本上三天搞不定。所以这一讲,我会把每一步的关键点都点出来。

2.1 交叉编译工具链安装

做嵌入式开发,交叉编译是绕不开的。说白了,就是你在PC上写代码,编译出来的程序要在ARM板子上跑。

第一步:选择工具链

我个人习惯用Linaro提供的GCC工具链,稳定且社区活跃。以ARM Cortex-A系列为例,常用的版本是:

  • gcc-arm-linux-gnueabihf —— 针对ARMv7-A,硬浮点
  • aarch64-linux-gnu —— 针对ARMv8-A,64位

第二步:安装

在Ubuntu 20.04/22.04上,直接apt安装就行:

sudo apt update
sudo apt install gcc-arm-linux-gnueabihf
sudo apt install gcc-aarch64-linux-gnu

安装完成后,验证一下:

arm-linux-gnueabihf-gcc --version
注意: 我曾经遇到过一个问题——apt源里的工具链版本太老,编译SOEM时某些POSIX接口不兼容。建议用Linaro官网的预编译包,或者用ARM官方的GNU Arm Embedded Toolchain。

第三步:设置环境变量

我习惯把工具链路径加到~/.bashrc里:

export CROSS_COMPILE=arm-linux-gnueabihf-
export CC=${CROSS_COMPILE}gcc
export CXX=${CROSS_COMPILE}g++

然后source ~/.bashrc让它生效。

2.2 Linux内核头文件准备

SOEM在Linux上跑,需要内核头文件来编译。为什么呢?因为SOEM底层要调用socketioctl这些系统调用,需要知道内核的数据结构定义。

方法一:使用目标板的内核头文件

这是最稳妥的做法。把板子上的/usr/include或内核源码里的include/uapi目录拷贝到你的开发机上。

方法二:使用通用内核头文件

如果只是编译SOEM应用层,用通用头文件也够用:

sudo apt install linux-libc-dev-armhf-cross
小技巧: 我建议你直接拿目标板Linux源码里的头文件。因为有些板子(比如TI的AM335x)会修改部分内核头文件,用通用的可能会编译不过。

2.3 CMake工程创建

SOEM官方用的是CMake构建系统。咱们也用它,方便管理。

第一步:安装CMake

sudo apt install cmake cmake-curses-gui

第二步:创建工程目录结构

我习惯这样组织:

soem_hello/
├── CMakeLists.txt
├── src/
│   └── main.c
├── lib/
│   └── soem/          # SOEM源码
└── build/

第三步:编写CMakeLists.txt

这里我直接给一个能用的模板:

cmake_minimum_required(VERSION 3.10)
project(soem_hello C)

# 设置交叉编译
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

# 指定工具链
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)

# 添加SOEM源码
add_subdirectory(lib/soem)

# 添加可执行文件
add_executable(soem_hello src/main.c)

# 链接SOEM库
target_link_libraries(soem_hello soem)
核心要点: 交叉编译时,一定要通过CMAKE_C_COMPILER指定编译器,否则CMake会默认用gcc,编译出来的东西在ARM板子上跑不了。

2.4 第一个Hello SOEM程序

好了,环境搭好了,咱们写个最简单的程序,验证一下SOEM能不能用。

代码:

// src/main.c
#include <stdio.h>
#include <soem/ethercat.h>

int main(int argc, char *argv[])
{
    printf("Hello SOEM!\n");
    printf("SOEM Version: %s\n", ec_version);
    
    // 初始化EtherCAT
    if (ec_init("eth0") > 0)
    {
        printf("EtherCAT initialized on eth0\n");
        ec_close();
    }
    else
    {
        printf("Failed to initialize EtherCAT\n");
        return -1;
    }
    
    return 0;
}

编译:

cd build
cmake ..
make

编译成功后,你会得到一个soem_hello可执行文件。用file命令看看:

file soem_hello

输出应该是类似:

soem_hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked

看到ARM字样,说明交叉编译成功了。

避坑指南: 我曾经遇到过一个问题——编译出来的程序在板子上运行时报错No such file or directory。查了半天,发现是动态链接库路径不对。解决办法:要么静态编译,要么把libsoem.so拷贝到板子的/usr/lib下。

2.5 本章知识体系

下面这张图,帮你理清今天讲的内容:

环境搭建知识体系 交叉编译工具链 arm-linux-gnueabihf-gcc Linux内核头文件 linux-libc-dev-armhf-cross CMake工程创建 CMakeLists.txt配置 Hello SOEM程序 ec_init()验证 cmake .. && make soem_hello (ARM ELF可执行文件)

这张图把今天的内容串起来了。你想想看,从工具链到内核头文件,再到CMake工程,最后编译出能在ARM板子上跑的Hello程序——这就是一个完整的嵌入式开发流程。

2.6 验证与测试

把编译好的soem_hello拷贝到板子上:

scp soem_hello root@192.168.1.100:/root/

在板子上运行:

./soem_hello

如果看到:

Hello SOEM!
SOEM Version: 1.4.0
EtherCAT initialized on eth0

恭喜你,环境搭建成功了!

提示: 如果板子上没有eth0网口,或者网口名字不一样(比如end0),记得把代码里的"eth0"改成实际的名字。我遇到过一块板子叫enp1s0,当时找了半天才找到原因。

好了,这一讲的内容就到这里。环境搭好了,下一讲我们就可以真正开始写SOEM的应用了。


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