3. EtherCAT

EtherCAT(Ethernet Control Automation Technology)是一种高性能实时以太网通信协议,用于在工业自动化领域中进行实时控制和通信。

IgH EtherCAT是运⾏于Linux系统的开源EtherCAT主站程序,IgH EtherCAT主站通过构建Linux字符设备, 应⽤程序通过对字符设备的访问实现与EtherCAT主站模块的通信。

IgH EtherCAT开发包配套EtherCAT⼯具,该⼯具提供各种可在Linux⽤户层运⾏的命令,可直接实现对从站的访问和设置, 如设置从站地址、显⽰总线配置、显⽰PDO数据、读写SDO参数等。

LubanCat板卡使用IgH EtherCAT可作为EtherCAT主站使用。目前已对使用rk3588、rk3588s、rk3576的LubanCat板卡做了适配验证。

注解

仅支持Linux kernel-6.1版本的LubanCat SDK和系统镜像

3.1. 运行环境组件编译

EtherCAT整体分为四个部分,分别是内核,驱动,用户态还有EtherCAT应⽤。以下将从这四部分对EtherCat主站环境的搭建做说明。

EtherCat环境的适配需要编译Linux内核和EtherCAT-IgH源码,为了便于操作,可以借助板卡配套SDK进行编译。 所以在进行EtherCat环境适配编译之前,需要先从网盘配套资料中下载 LubanCat_Linux_Gen_Full_SDK 并搭建SDK编译环境。 有关SDK编译环境搭建的详细说明见文档: 《 嵌入式Linux镜像构建与部署 -> LubanCat_Gen_SDK

3.1.1. 修改内核并编译

EtherCAT IgH需要保证⾼实时性,因此使用Linux-RT实时内核。我们已经为LubanCat板卡适配好了实时内核, 以下内容仅对实时内核的修改编译和安装做说明,有关实时内核的详细信息,请参考 RT-Linux 章节。

在Linux-RT实时内核的基础上,还需要进行一些修改来进一步适配EtherCAT功能并优化。

Linux内核源码仓库地址为:https://github.com/LubanCat/kernel

rk3576和rk3588对应的实时内核分支为:lbc-develop-6.1-rt36

可在SDK根目录执行以下命令,将普通Linux内核源码替换为Linux-RT内核源码并打上EtherCat补丁:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#删除旧内核源码
rm -rf kernel-6.1/

#拉取新内核源码并保存到kernel-6.1目录,只拉取最新一次提交,指定lbc-develop-6.1-rt36分支
git clone --depth=1 -b lbc-develop-6.1-rt36 https://github.com/LubanCat/kernel.git kernel-6.1

# 进入内核源码目录
cd kernel-6.1

# 打EtherCat补丁
git am ../external/ethercat_igh/0001-add-support-ethercat-for-lubancat.patch

补丁文件存放在SDK的external/ethercat_igh/0001-add-support-ethercat-for-lubancat.patch。以下是对补丁文件内容的说明, 节选部分内容,以LubanCat-3IO为例,其他板卡类似的部份不再单独说明,实际补丁内容以补丁文件中的为准

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
diff --git a/arch/arm64/boot/dts/rockchip/rk3576-linux.dtsi b/arch/arm64/boot/dts/rockchip/rk3576-linux.dtsi
index 37b76f2e3cf62..069586ee2e5b3 100644
--- a/arch/arm64/boot/dts/rockchip/rk3576-linux.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3576-linux.dtsi
@@ -12,7 +12,7 @@ aliases {
    };

    chosen: chosen {
# 更新启动参数,隔离cpu7资源给EtherCat单独使用
-           bootargs = "earlycon=uart8250,mmio32,0x2ad40000 console=ttyFIQ0 root=PARTUUID=614e0000-0000 rw rootwait rcupdate.rcu_expedited=1 rcu_nocbs=all";
+           bootargs = "earlycon=uart8250,mmio32,0x2ad40000 console=ttyFIQ0 root=PARTUUID=614e0000-0000 isolcpus=7 nohz_full=7 rcu_nocbs=7 rw rootwait rcupdate.rcu_expedited=1";
    };

    fiq_debugger: fiq-debugger {
diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
index 9dd309efd801a..1ccc1a45f561f 100644
--- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
@@ -295,7 +295,7 @@ cpu_l0: cpu@0 {
            operating-points-v2 = <&cluster0_opp_table>;
            #cooling-cells = <2>;
            dynamic-power-coefficient = <120>;
# 关闭CPU休眠,所有核心都要修改
-                   cpu-idle-states = <&CPU_SLEEP>;
+                   // cpu-idle-states = <&CPU_SLEEP>;
        };

@@ -4402,7 +4402,7 @@ pcie1_intc: legacy-interrupt-controller {
    };

    gmac0: ethernet@2a220000 {
# 使用ethercat专用的网口驱动
-           compatible = "rockchip,rk3576-gmac", "snps,dwmac-4.20a";
+           compatible = "rockchip,rk3576-gmac-ethercat", "snps,dwmac-4.20a";
        reg = <0x0 0x2a220000 0x0 0x10000>;
        interrupts = <GIC_SPI 293 IRQ_TYPE_LEVEL_HIGH>,
                <GIC_SPI 298 IRQ_TYPE_LEVEL_HIGH>;
@@ -4441,13 +4441,15 @@ gmac0_stmmac_axi_setup: stmmac-axi-config {
        };

        gmac0_mtl_rx_setup: rx-queues-config {
# 支持TSN功能
-                   snps,rx-queues-to-use = <1>;
+                   snps,rx-queues-to-use = <2>;
            queue0 {};
+                   queue1 {};
        };

        gmac0_mtl_tx_setup: tx-queues-config {
# 支持TSN功能
-                   snps,tx-queues-to-use = <1>;
+                   snps,tx-queues-to-use = <2>;
            queue0 {};
+                   queue1 {};
        };
    };

diff --git a/arch/arm64/boot/dts/rockchip/uEnv/rk3576/uEnvLubanCat3IO.txt b/arch/arm64/boot/dts/rockchip/uEnv/rk3576/uEnvLubanCat3IO.txt
index 864a95f909703..13fe62ea125b7 100644
--- a/arch/arm64/boot/dts/rockchip/uEnv/rk3576/uEnvLubanCat3IO.txt
+++ b/arch/arm64/boot/dts/rockchip/uEnv/rk3576/uEnvLubanCat3IO.txt
@@ -1,6 +1,6 @@
uname_r=
size=0x1000000
# 更新启动参数,隔离cpu7资源给EtherCat单独使用
-cmdline="earlyprintk console=ttyFIQ0 console=tty1 consoleblank=0 loglevel=7 rootwait rw rootfstype=ext4"
+cmdline="earlyprintk console=ttyFIQ0 consoleblank=0 loglevel=7 rootwait rw rootfstype=ext4 isolcpus=7 nohz_full=7 rcu_nocbs=7"

enable_uboot_overlays=1
#overlay_start

修改完成后,在SDK的顶层目录执行下面的命令编译生成内核deb更新包,方便后续使用

1
2
#编译内核deb包
./build.sh kerneldeb

编译出来的linux-headers-6.1.99-rt36-rk3576和linux-image-6.1.99-rt36-rk3576内核deb包传到板卡备用

3.1.2. 用户态文件和驱动编译

用户态主要是两个⽂件,ethercat和libethercat.so,⼀个是EtherCAT IgH的调试⼯具,⼀个是EtherCAT IgH的动态库,⽤来提供⽤户层接口。

驱动部分主要是ec_master.ko和⼀些RK优化后的ko。ec_master.ko是⼀个Linux内核模块,⽤于⽀持EtherCAT主站的功能, 这个模块负责管理EtherCAT总线上的通信,实现主站与从站之间的数据交换和同步。ec_stmmac.ko是RK优化过实时性的网口驱动。

EtherCAT IgH的源码保存在SDK的external/ethercat_igh目录下,此源码根据开源项目修改优化得来,可以直接编译使用。

在使用下面的命令时需要注意,/home/dev/LubanCat_Linux_SDK是SDK目录的绝对路径,要在使用时替换为用户SDK的实际路径。 如果不确定,可以使用cd命令进入SDK顶层目录,使用pwd命令获取当前目录的绝对路径。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 进入ethercat_igh目录
cd external/ethercat_igh

# 导出编译器路径到环境变量
export PATH=/home/dev/LubanCat_Linux_SDK/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH

# 初始化构建环境
./bootstrap
./configure --prefix=/home/dev/LubanCat_Linux_SDK/external/ethercat_igh/output --host=aarch64-none-linux-gnu --with-linux-dir=/home/dev/LubanCat_Linux_SDK/kernel-6.1 --enable-8139too=no --enable-stmmac=yes --enable-generic=no --enable-wildcards=yes

# 编译
make -j8

# 将编译生成的内容安装到output目录
make install systemdsystemunitdir=/home/dev/LubanCat_Linux_SDK/external/ethercat_igh/output

# 编译内核外部模块
make modules ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- -C /home/dev/LubanCat_Linux_SDK/kernel-6.1 M=$PWD -j32

编译生成的用户态文件保存在ethercat_igh的output目录下,生成的ko模块文件保存在master/ec_master.ko和devices/stmmac/ec_stmmac.ko

将output目录和ec_master.ko、ec_stmmac.ko传输到板卡中备用。

3.1.3. EtherCat应用编译

EtherCAT IgH应⽤需要根据具体的伺服器来编写,demo目录下的ec_master_test_CD02.c是一个用于测试LubanCat板卡连接 SERVOTRONIX-CDHD2S 伺服驱动器EtherCat性能的示例程序, 在external/ethercat_igh/demo目录下调用./build.sh脚本编译,得到ec_master_test_CD02可执行文件,此测试程序可以用于rk3588板卡和rk3576板卡。

将ec_master_test_RK3588_CD02传输到板卡备用

3.2. 板卡运行环境搭建

板卡运行环境推荐使用debian12 lite版本,无桌面版本占用资源少,实时性更高。

3.2.1. 获取网卡MAC地址

启动板卡,此时板卡还没有安装实时内核更新包,由于后面会用到网卡的MAC地址,此时需要记录一下。 如果是多网口,此时建议把所有网口的MAC地址都记录一下,有时候设备树里的网口顺序会和系统里的网口顺序不一致, 后面搭建好环境就看不到网卡MAC地址了,记错了会很麻烦。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
ifconfig

eth0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether 1a:04:65:66:d0:a0  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 564  bytes 94196 (91.9 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        device interrupt 57

eth1: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether 1e:04:65:66:d0:a0  txqueuelen 1000  (Ethernet)
        RX packets 645  bytes 58599 (57.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 725  bytes 65614 (64.0 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        device interrupt 59

两个网口的MAC地址分别是1a:04:65:66:d0:a0和1e:04:65:66:d0:a0

3.2.2. 配置用户态环境

进入传输到板卡中的output目录,将传输到板卡中的用户态文件复制到系统目录中

1
2
3
4
5
6
# 进入output目录
cd output/

# 复制库和可执行程序
cp -fv lib/libethercat.* /usr/lib/
cp -fv bin/ethercat /usr/bin/

3.2.3. 安装实时内核

进入板卡上存放内核更新包的位置,运行下面的命令将修改后的实时内核更新到板卡

1
2
3
4
5
6
7
8
9
# 卸载旧内核
apt remove linux-headers-6.1.99-rk3576 linux-image-6.1.99-rk3576

# 安装实时内核和内核头文件
dpkg -i linux-image-6.1.99-rt36-rk3576_*_arm64.deb
dpkg -i linux-headers-6.1.99-rt36-rk3576_*_arm64.deb

# 重启
reboot

安装完成后重启板卡,实时内核就会生效

3.2.4. 加载内核模块

ec_master和ec_stmmac是使用ethercat_igh编译的内核模块,需要手动加载。

main_devices=后面是EtherCAT网口对应的mac地址,根据前面的步骤记录, 我们记录了两个网卡的MAC地址,现在将两个网口中的其中一个作为EtherCat使用,并在设备树中设置修改为使用专用驱动。

使用ifconfig查看网络接口,此时只有eth0,排除eth0网卡的MAC地址,另一个MAC地址就是要设置为EtherCat的网卡地址。

1
2
3
4
5
6
7
8
9
ifconfig

eth0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether 1e:04:65:66:d0:a0  txqueuelen 1000  (Ethernet)
        RX packets 134  bytes 13288 (12.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 159  bytes 14002 (13.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        device interrupt 96

一定要添加并确认地址正确

1
2
insmod ec_master.ko main_devices=1a:04:65:66:d0:a0
insmod ec_stmmac.ko

3.2.5. 简单测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 主站状态
ethercat master

# 输出识别到的从站
ethercat slaves

#如果通讯成功,就会显⽰出识别到的从站位置信息,别名和型号,如下:
0 0(别名):0(位置) PREOP  +  CD02 EtherCAT Drive (CoE)

#输出pdo信息,这些pdo信息只是默认的,具体要根据需求,参考伺服驱动器⼿册来编写
ethercat pdos

#以c语⾔的形式输出pdo信息
ethercat cstruct

3.2.6. 板卡性能优化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 调整soc为性能模式,cpu时钟运行在最高频率
echo performance | tee $(find /sys/ -name *governor)

# 关闭网络时间同步功能,防止与TSN争抢系统时钟
# 关闭ntp同步时间(debian)
systemctl stop ntpsec
systemctl disable ntpsec

# 关闭chrony时间同步(buildroot)
killall chronyd
ls /etc/init.d/S*chronyd | xargs rm

# 开启TSN与系统时钟同步
# 查看EtherCat网口对应的ptp设备
ls -l /sys/class/ptp

# 同步系统时间和gmac ptp时间
apt install linuxptp
phc2sys -s /dev/ptp1 -c CLOCK_REALTIME -O 0 &

3.3. 性能测试

运行demo测试示例,可以测试EtherCat主站与从站通信时的周期抖动和调度延迟。

用网线连接LubanCat板卡的EtherCAT口和伺服驱动器的EtherCAT输入接口,此处连接 SERVOTRONIX-CDHD2S 伺服驱动器。 由于EtherCat从站配置有差异,需要单独适配示例程序,此处以编译好的ec_master_test_CD02为例,在板卡上运行。

1
2
3
# 绑定CPU核心为资源隔离的核心运行示例程序
# -c 指定绑定的核心
./ec_master_test_CD02 -c 7

3.3.1. 测试结果和说明

测试程序运行时,当出现一个周期抖动值时,打印输出周期时间和抖动值,并记录此时的值作为当前最大值。 每个测试周期进行300000次测试循环,在一个测试周期中,每当出现新的最大抖动值都将打印输出,也就是打印信息输出了当前测试周期的最大最大周期抖动。

以下测试结果选取10个连续的测试周期,统计这10个测试周期中的最小周期时间、最大周期时间和最大周期抖动值。

3.3.1.1. RK3588(LubanCat-5IO)测试结果

设定周期时间

最小周期时间

最大周期时间

最大周期抖动

1000μs

996042ns

1004792ns

4.8μs

500μs

495250ns

505167ns

5.2μs

125μs

122792ns

128042ns

3.0μs

3.3.1.2. RK3576(LubanCat-3IO)测试结果

设定周期时间

最小周期时间

最大周期时间

最大周期抖动

1000μs

986315ns

1012564ns

13.7μs

500μs

489696ns

509304ns

10.3μs

125μs

117189ns

134740ns

9.7μs