1. 背景与意义

1.1 Igh-Ethetcat

EtherCAT作为工业自动化领域广泛应用的实时以太网协议,在其开源实现中,IGH EtherCAT是目前Linux系统中最成熟的开源EtherCAT主站实现。作为工业自动化领域广泛应用的实时以太网协议解决方案,它具有以下核心特性:

  • 实时性能:支持μs级周期通信,满足大多数工业控制场景需求。

  • 模块化架构:包含主站内核模块(ec_master)、通用设备驱动(ec_generic)及多种网卡专用驱动。

  • 完整协议支持:实现EtherCAT状态机、分布式时钟(DC)、SDO/PDO通信等核心功能。

  • 用户态接口:通过字符设备提供IOCTL接口,应用程序可配置从站和交换过程数据。

典型应用场景包括运动控制、PLC替代方案以及任何需要硬实时工业通信的场合。

1.2 Xenomai 双内核实时操作系统

1.2.1 Xenomai3

Xenomai3是Linux的实时扩展框架,其核心设计目标是提供确定性的实时响应能力,其提供Native、POSIX、RTDM等多种编程接口。并且目前Igh-Ethetcat已经可以支持使用Xenomai3来提高自己的实时性,通过RTDM接口来支持实时的ethercat实时主站设备。

随着Xenomai4 EVL(Emerald Virtual Layer)框架的发布,其用户态实时驱动架构为实时系统开发提供了新的可能。本文将探讨将IGH EtherCAT主站移植到Xenomai4 EVL环境的技术路径与实践经验。

2. 环境准备

2.1 硬件平台

  • 测试平台:树莓派5 B

  • 网卡:Intel 82576

  • EtherCAT从站设备:STM32F407ZG+LAN9252

2.2 软件环境

3. 移植关键步骤

对于移植igh-ethercat的思路可以查看我们之前的代码粗读视频https://www.bilibili.com/video/BV1eJDCY2EzB/,其中一小部分(如对rtdm设备改动的设计)的内容已被废弃,但是总的思路上还是一致的。我们接下来主要从代码方面详细介绍下此次移植的改动。

3.1 网卡模块适配

igh-ethercat对许多网卡的驱动提供了适配以加强实时性,代码见devices/目录下。

具体对于我们选用的Intel 82576网卡来说,会使用igb驱动,而目前的igb驱动支持中尚未提供linux-6.6版本的驱动支持,所以要先移植驱动。

调用devices/igb/update.sh后,脚本会将当前kernel(即上文中的rpi-linux6.6.47-evl)目录下的对应网卡驱动代码粘到当前igb目录下并根据对应版本重命名,之后会将之前最新版本的原始网卡驱动和适配网卡驱动的diff打到当前的代码上。

鉴于我们所选用版本的网卡驱动与就驱动并无实质性变化,所以只需要将diff中名称有问题的头文件改对即可。

3.2 Master内核模块适配

Xenomai4框架下的驱动设备改造详见https://evlproject.org/core/kernel-api/,相对于xenomai3以及其他使用RTDM接口的系统来说,我们无需再添加一个实时的驱动设备,一个EVL的实时驱动设备就是一个linux的通用驱动设备。

具体到我们的代码改动上,对于Master内核模块来说,我们只需要改动open、release并加入一个oob_ioctl的File operation即可。其中对于open、release两个操作的改动在于将设备数据与evl file进行绑定和解绑,是一个evl对驱动设备的要求。


 /** File operation callbacks for the EtherCAT character device.
  */
 static struct file_operations eccdev_fops = {
     .owner          = THIS_MODULE,
     .open           = eccdev_open,
     .release        = eccdev_release,
     .unlocked_ioctl = eccdev_ioctl,
 #ifdef EVL
     .oob_ioctl      = oob_eccdev_ioctl,
 #endif
     .mmap           = eccdev_mmap
 };

oob_ioctl则是真正需要添加的实时操作,我们需要将之前的操作中需要实时化的部分(如发送、接收数据包)改进这个接口,而对于初始化之类的非实时操作则仍保留在原来的unlocked_ioctl中。得益于上述其他实时操作系统的适配,尽管系统的实时设备接口有所不同,但是将操作分为实时和非实时这两个部分的思想却是统一的。在之前分好的实时操作和非实时操作上加入适当的条件编译即可。

#ifdef EC_IOCTL_RTDM
 long ec_ioctl_rtdm_rt
 #elif defined(EVL)
 long oob_ec_ioctl
 #else
 long ec_ioctl
 #endif
         (
         ec_master_t *master, /**< EtherCAT master. */
         ec_ioctl_context_t *ctx, /**< Device context. */
         unsigned int cmd, /**< ioctl() command identifier. */
         void *arg /**< ioctl() argument. */
         )
 -----------------------------
 #ifdef EC_IOCTL_RTDM
 long ec_ioctl_rtdm_nrt
 #elif defined(EVL)
 long ec_ioctl
 #else
 static long ec_ioctl_nrt
 #endif
         (
         ec_master_t *master, /**< EtherCAT master. */
         ec_ioctl_context_t *ctx, /**< Device context. */
         unsigned int cmd, /**< ioctl() command identifier. */
         void *arg /**< ioctl() argument. */
         )

再加上一点其他代码规范化相关的改动,master部分就改造完成了。

3.3 用户态接口调整

既然并没有使用RTDM设备,并且将实时操作抽象成了一个新的oob_ioctl,那么我们的lib库肯定也需要有相应的改变才可以,具体来说就是把原来统一调用的ioctl的实时操作部分改用oob_ioctl。启发于libevl中部分测试对oob_ioctl的使用,对于非实时的lib库的实现我们不做改动,而对于实时操作,则使用一个条件编译使其在不同的编译条件下调用不同的ioctl,由于不同cmd下ioctl的调用和实现是一一对应的,我们不用担心遗漏或者多余改动的问题。

#ifdef EVL
 #define do_ioctl oob_ioctl
 #else
 #define do_ioctl ioctl
 #endif
 
 int ecrt_master_send(ec_master_t *master)
 {
     int ret;
 
     ret = ioctl(master->fd, EC_IOCTL_SEND, NULL);
     ret = do_ioctl(master->fd, EC_IOCTL_SEND, NULL);
     if (EC_IOCTL_IS_ERROR(ret)) {
         return -EC_IOCTL_ERRNO(ret);
     }
     return 0;
 }

3.4 与libevl一起使用

libevl的使用见https://evlproject.org/core/user-api/,在将当前线程attach到evl上之前,我们可以进行一些初始化工作如申请主站和从站配置,等到主站激活之后,我们可以将当前线程attach到evl之上,之后尽可能只使用实时的lib库函数防止程序从实时状态退出(也可以通过设置evl线程模式使程序在退出实时状态时马上崩溃,并以此修改相关代码以获得一个完全实时的程序)。

4. 性能测试

循环 睡眠到指定时间 运行读取从站数据的任务 获取当前的时间戳 将指定时间设置为上次指定时间的1ms之后 使用获得相邻两个时间戳之间的差值与设定时间间隔的绝对差值来分析结果

对于我们的初步改造,将使用如上伪代码所示的一个简单的测试程序来大致测量性能的提升,在我们给出的平台上,使用1ms为周期,每周期从从站中读一份数据。linux和evl内核使用同一份config,设置cpu3为隔离cpu并使用taskset将程序跑在该cpu上,分别测试stress-ng下的表现。最终得到的结果如下。

4.1 基准测试结果

测试项

evl

linux

stress-evl

stress-linux

平均延迟(ns)

177

362

285

286

最大延迟(μs)

37.8

54.3

37.5

64.8

延迟方差(ns^2)

17836

306419

232064

536134

5. 总结与展望

本文成功实现了IGH EtherCAT主站向Xenomai4 EVL环境的移植,实测表明简单移植后在xenomai4的表现已经显著优于linux内核,但是仍有待与其他方案如preempt-rt/Xenomai3的实测结构进行比较,后续也具有较大的优化空间。

目前的下一步计划如下:

  1. configure脚本的evl支持

  2. igh-ethercat的内核线程的实时性改造。

  3. 使用更加精确的方式进行数据的测量和统计

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐