ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

EtherCAT主站开发实战:IGH在RK3568平台的移植与同步机制解析

EtherCAT主站开发实战:IGH在RK3568平台的移植与同步机制解析 看到EtherCAT主站开发这几个字我第一反应是想起自己入坑时的场景。当时接了个伺服同步项目甲方开口就是EtherCAT我嘴上说没问题心里其实还在琢磨主站到底要自己写多少东西。翻了几天IGH源码和那堆英文手册后才发现这事儿和以前做Modbus、CANopen的思维方式完全不一样你不需要去管每个从站的具体命令交互但必须理解帧如何在从站间流转数据如何在正确的时间点被写入物理存储区。这篇笔记是系列第一篇主要记录我从零开始在RK3568 Linux平台上把IGH主站跑通、把第一个IO从站拖到OP状态的全过程包括选型逻辑、编译移植、抓包分析、FMMU和同步管理器理解以及几个让我折腾到半夜的坑。适合正准备入坑EtherCAT主站开发、或者已经在看IGH源码但被概念淹没的朋友。1. 入坑前的清醒认知EtherCAT主站到底在做什么1.1 主站不是发命令这么简单很多人对现场总线的理解还停留在主站轮询、从站应答的问答模式EtherCAT彻底打破了这套玩法。它的核心特征是主站只负责往网线上发送标准以太网帧帧里面的每一个子报文会在物理层途经各个从站时被从站硬件在飞行中读取或写入对应数据然后继续往下一个从站传递最后一个从站把帧返还给主站。这意味着主站的工作不是逐台设备交互而是三件事组织子报文、维护总线状态、管理时间同步。组织子报文你需要在应用层配置好每个从站需要交换哪几个字节然后主站把这些信息映射到帧里的特定位置每个周期重复发送。维护总线状态从站有四个状态INIT、PRE-OP、SAFE-OP、OP主站要通过配置邮箱通信、过程数据通信等步骤把从站一层层推到运行态。管理时间同步伺服同步这类应用要求所有从站在同一个时间基准上采样这就要靠分布式时钟DC机制主站作为参考时钟来协调。不把这三件事想清楚后面看IGH源码基本等于看天书。我一开始就是陷在代码里出不来后来退回来先把主站的职责拆开再去看代码思路一下就顺了。1.2 为什么我选了IGH而不是商业方案EtherCAT主站方案市面上不少商业的有倍福TwinCAT、Acontis、KPA等开源的则是IGHIgH EtherCAT Master一统天下。我选IGH的理由很实际无授权成本商业主站一套授权几万块起步IGH开源学习阶段完全免费。源码透明出问题可以直接翻代码对做技术的人来説这点太重要了。你不需要去猜某个行为背后的设计意图翻一翻master.c和fsm_*.c就清楚了。平台适配广IGH基于Linux内核模块运行理论上任何有标准以太网口的Linux设备都能跑这也是瑞芯微RK3568这类嵌入式平台能用的前提。IGH的缺点也很明显它本身只提供协议引擎不含运动控制、PLC运行时这些上层业务实时性也要靠你配合RT补丁、绑核等系统调优来保证。换句话说IGH给你的是一把好枪开枪打哪个靶子是你自己的事。1.3 认清楚你要写的是什么刚开始我犯过一个方向性错误以为主站开发是要把EtherCAT协议栈从零撸一遍。后来看明白了IGH已经把协议栈的绝大部分实现了包括状态机、CoE邮箱、FMMU映射、DC同步等你需要写的是三部分平台相关代码让IGH能在你的板卡和内核上编译、加载、绑定正确的网卡。应用层逻辑周期性任务里往输出缓存区写数据、从输入缓存区读数据以及处理状态切换。系统配置与调优实时线程优先级、CPU绑核、网卡中断优化保证周期任务稳定。认清这一点之后整个学习路径就清晰了先跑通环境再理解帧再玩转FMMU和SM最后调实时性。2. 环境准备在RK3568上把IGH源码变成可用驱动2.1 先说平台RK3568和它的GMAC我用的板子是正点原子RK3568核心是四核Cortex-A55板载千兆以太网口GMAC。有人会问用RK3568做EtherCAT主站性能够不够实测下来跑1ms周期、带十几个从站完全没问题如果做更高速的同步应用建议把周期压到250us甚至125us这时候CPU绑核和中断优化就要做扎实了这我们在第5章细聊。网卡方面有个关键点IGH最稳定的是Intel系列网卡比如82574L、I210因为它们的驱动e1000e/igb对EtherCAT支持最成熟。但RK3568板载的是瑞芯微自己的GMAC驱动IGH对它的支持是通过ec_generic这个通用驱动来兜底的。ec_generic不代表性能差它用的是Linux内核的发送/接收路径实际测试抖动比原生驱动略大但初期学习完全够用。2.2 内核实时化没有RT的EtherCAT主站是残废EtherCAT的核心是等时同步周期任务如果Linux内核没有实时化任务调度抖动可能达到几十毫秒级别这会让伺服同步彻底乱套。我首选的是PREEMPT_RT补丁也就是把标准内核替换为实时内核。从交叉编译角度看RK3568用SDK里的内核源码开启以下配置CONFIG_PREEMPT_RTy CONFIG_HZ_1000y CONFIG_NO_HZ_FULLy这里踩过一个不小的坑如果直接用Ubuntu发行版内核PREEMPT_RT需要手动打补丁但RK3568的SDK内核本身已经做了大量BSP适配直接在SDK内核上打补丁容易冲突。正确做法是先把SDK内核正常编译一遍确认没问题之后再在menuconfig里打开实时选项重新编译。我用的SDK是Rockchip官方Linux SDK内核版本5.10PREEMPT_RT的补丁是和SDK匹配的。编译命令大致如下实际路径按你自己的SDK调整cd kernel export ARCHarm64 export CROSS_COMPILEaarch64-linux-gnu- make rockchip_linux_defconfig make menuconfig # 进入 Kernel Features - Preemption Model - Fully Preemptible Kernel (RT) # 进入 Timers subsystem - Timer tick handling - Full dynticks system make -j8 dtbs Image modules第一次编译RT内核时我图省事没改CONFIG_HZ的值用的是默认100Hz结果后面跑周期任务时计时精度一塌糊涂。后来上网查到的经验是EtherCAT主站周期的毫秒级任务建议用1000Hz改了之后明显稳定很多。另外NO_HZ_FULL全动态时钟是把每个CPU的周期时钟停掉由用户态调度器按需唤醒这对绑核后的RT线程也有帮助。2.3 编译IGH从GitHub拉源码到模块加载IGH源码在GitHub上可以直接拉到我这里用的是稳定版本1.5.2git clone https://gitlab.com/etherlab.org/ethercat.git -b stable-1.5 cd ethercat编译IGH需要准备好autoconf、automake、libtool这些工具如果没有要先装。IGH的configure脚本一个重要参数是指定内核源码目录./bootstrap ./configure --prefix/opt/etherlab \ --with-linux-dir/path/to/your/kernel \ --enable-generic \ --disable-e1000e \ --disable-igb make sudo make install编译时要注意--with-linux-dir必须指向你正在运行的那套内核的源码目录而且内核版本要一致否则装进去模块无法加载。我一开始用SDK内核源码编译IGH但板子上跑的是另一个版本的内核加载ec_master时直接报版本不匹配后来痛定思痛先把RT内核在板子上跑起来再回头编译IGH问题就没了。IGH编译完成后默认装到/opt/etherlab。接下来配置网卡绑定。IGH通过/etc/ethercat.conf决定用哪块网卡MASTER0_DEVICEaa:bb:cc:dd:ee:ff DEVICE_MODULESgenericMASTER0_DEVICE填板载网卡的实际MAC地址DEVICE_MODULES指定驱动类型我们用generic。然后加载模块sudo modprobe ec_master这时候执行/opt/etherlab/sbin/ethercat master如果输出主站版本号和网卡信息说明IGH已经跑起来了。2.4 验证主站是否就绪一个容易忽略的动作很多教程到这里就直接跳到连接从站了但我建议你先做一件事确认主站能不能看到总线上的从站。sudo /opt/etherlab/sbin/ethercat slaves没有接从站时输出是空的这是正常的。如果这时候你已经接了从站但命令报错多半是权限问题。我用的方法是把ethercat命令加sudo省事。如果还想更省事可以在/etc/udev/rules.d/里给EtherCAT设备加个规则让普通用户也能访问字符设备节点。提示IGH在编译时默认的安装路径是/opt/etherlab这个路径不会自动加入PATH你每次敲命令要么全路径要么做个软链到/usr/local/sbin。3. 打通第一帧理解EtherCAT报文如何找到从站3.1 抓包工具和0x88A4跑通环境后下一步一定是抓包看帧。这里我强烈建议用tcpdump Wireshark组合板子上用tcpdump抓PC上用Wireshark分析。抓包之前先明确一点EtherCAT报文是承载在标准以太网帧里的它的EtherType是固定的0x88A4。所以抓包过滤条件写这个值就行sudo tcpdump -i eth0 -s 0 -w ecat.pcap ether proto 0x88a4板载网卡抓自己的收发数据没问题但要注意IGH的主站模块发送帧时是直接通过网卡驱动往下走的不走内核协议栈所以tcpdump能不能抓到取决于你的网卡驱动和libpcap能不能看到原始帧。实测下来RK3568的GMAC驱动可以抓到。用Wireshark打开抓包文件展开EtherCAT报文你会看到结构大概长这样以太网帧头EtherCAT头子报文1子报文2...FCS具体到二进制EtherCAT头里有报文长度和类型字段子报文则是核心每个子报文有命令字节、索引、寻址信息、数据区、工作计数器WKC和状态位。3.2 寻址方式一段旅程的开始EtherCAT子报文要找到目标从站靠的是寻址机制主要有两种位置寻址从站从上电起就在总线里有个物理顺序编号比如第一个从站是0第二个是1。配置阶段主站用位置寻址访问某个特定从站。节点寻址每个从站有唯一的站地址通过EEPROM配置可以根据这个地址访问。运行阶段一般用节点寻址。IGH在启动时自动完成从位置寻址到节点寻址的过渡。你抓包时会看到开头有几个用位置寻址的帧后面就切换成节点寻址了。如果这时候从站地址没配好总线状态切换会报错这也是新手常见问题。3.3 状态机从站是怎样被一步步催熟的EtherCAT从站有四个状态主站的启动过程本质上是把从站从INIT推到OP状态能做什么INIT只能做最基本的通信初始化PRE-OP邮箱通信可用可以配置参数但是过程数据还没开始SAFE-OP过程数据开始更新但从站输出端还处于安全状态比如伺服不使能OP完全运行输出正常生效如果过程中任何一步配置失败从站会停留在失败状态并报告错误码。IGH里状态切换不是一步到位的而是通过状态机一步步走。你可以用命令手动切sudo /opt/etherlab/sbin/ethercat states -s op sudo /opt/etherlab/sbin/ethercat states # 查看当前状态第一次切到OP的时候只有切换命令发出去了不代表成功了一定要看从站的实际状态。我发现很多人卡在这一步的原因是从站的EEPROM信息或PDO映射没配置好切换失败但没注意到。3.4 实操抓包看状态切换过程我建议你做一个实验不接任何从站只启动主站抓包然后接上从站再抓包对比两次的帧内容。接上从站后你会看到主站周期性发送带有FPRD位置读、APRD节点读等命令的帧。这个实验能帮你直观理解主站发起帧、从站即时响应的过程。实际上IGH周期性发送的帧格式不复杂先是几个寻址读命令获取从站状态然后是一个或多个过程数据读写命令最后可能还有邮箱轮询命令。子报文里的WKC工作计数器会告诉你从站有没有正确处理这个报文比如读操作期望WKC1写操作期望WKC1复杂操作会有别的值。抓包时看到WKC不符合预期基本可以断定对应的从站操作失败了。4. FMMU和SM是主站开发者的必修课4.1 FMMU主站视角的虚拟内存映射FMMUFieldbus Memory Management Unit现场总线内存管理单元这个名字起的很有迷惑性但它的作用其实和CPU的MMU非常像把从站内部不连续的物理寄存器/存储区映射到主站应用层的连续逻辑地址空间。举个例子一个伺服驱动器从站内部可能把这些数据分散在不同的位置控制字在0x6040目标转速在0x6042状态字在0x6041。如果没有FMMU主站每次都要针对不同的寄存器地址发起独立的读写请求效率极低。有了FMMU主站可以申请一段连续的逻辑地址比如0x1000到0x1007然后配置FMMU把0x6040映射到0x10000x6042映射到0x10020x6041映射到0x1004这样一帧就能把数据全部收发掉。IGH处理FMMU的方式很优雅你不需要对着寄存器地址硬算它通过PDO映射配置自动生成FMMU参数。你需要关心的只是从站的PDO配置。4.2 SyncManager给数据交换装上红绿灯SMSyncManager同步管理器解决了另一个问题主站和从站之间的数据交换不能随意乱来需要协调好方向、触发时机和缓冲区。一个典型从站有四个SM通道SM通道用途SM0邮箱写主站到从站用于CoE等配置SM1邮箱读从站到主站用于上传诊断、SDO响应SM2过程数据输出主站到从站即控制字、目标位置等SM3过程数据输入从站到主站即状态字、实际位置等IGH在初始化阶段会自动配置SM你需要做的是告诉IGH每个PDO的映射关系然后IGH会计算每个SM通道里包含哪些数据并自动生成FMMU映射。所以FMMU和SM说到底是配对的SM负责这个数据什么时候通过哪个方向走FMMU负责这个数据在主站内存的哪个位置。4.3 实操用ethercat pdo查看从站映射如果你手上有一个IO从站比如倍福EK1100耦合器加EL2008数字量输出模块可以用IGH命令查看它的PDO信息sudo /opt/etherlab/sbin/ethercat pdo -m 0-m 0是指主站0如果你只有一个主站可写可不写。这条命令会列出从站当前配置的PDO包括SM通道、PDO索引、子索引Entry和数据类型。以EL2008为例它的过程数据通常是8位数字量输出映射到一个SM2通道里面一个Entry就搞定。这时候如果你在IGH的应用代码里定义了ec_pdo_entry_info_t数组并把它注册到主站IGH就会自动根据这些配置生成FMMU应用层直接操作一块连续内存就能控制从站了。4.4 第一个可跑的Demo点亮从站输出理论铺垫够了直接上代码。IGH自带一个示例程序simple_test位于examples/simple_test/它做的事情很单一周期性给从站发送数据。但我的建议是不要直接用而是基于它写一个点灯程序这样你能清楚看到数据和实际输出的对应关系。我自己的写法是注册一个PDO入口设为8位输出然后周期任务里每200ms翻转一次这8位的值同时从输入方向读回一个8位值如果从站支持的话。IGH应用代码的核心结构大致是这样的#include ecrt.h static ec_master_t *master NULL; static ec_domain_t *domain NULL; static uint8_t *domain_pd NULL; // 定义PDO映射信息 ec_pdo_entry_info_t slave_0_pdos[] { {0x7000, 0x01, 8}, // 输出8位 {0x6000, 0x01, 8}, // 输入8位 }; ec_pdo_info_t slave_0_pdo_info[] { {0x1600, 1, slave_0_pdos 0}, // 输出PDO {0x1a00, 1, slave_0_pdos 1}, // 输入PDO }; ec_sync_info_t slave_0_syncs[] { {2, EC_DIR_OUTPUT, 1, slave_0_pdo_info 0}, {3, EC_DIR_INPUT, 1, slave_0_pdo_info 1}, {0xff} };然后在初始化阶段把从站的sync_info注册进域master ecrt_request_master(0); domain ecrt_master_create_domain(master); ecrt_master_slave_config(master, 0, 0, VENDOR_ID, PRODUCT_CODE, slave_0_syncs);这里面的VENDOR_ID和PRODUCT_CODE要从从站的EEPROMSII读取可以用ethercat slaves -v命令查。不同厂家的IO模块产品码都不一样想偷懒的话IGH源码里很多从站的配置范例可以直接改。周期任务里domain_pd就是域的数据指针你按偏移量写数据就能影响从站EC_WRITE_U8(domain_pd out_off, value); ecrt_domain_queue(domain); ecrt_master_send(master);跑起来之后用万用表或LED观察从站输出口的变化你会深切体会到从定义PDO到输出翻转这一整条链路是怎么回事。5. 周期抖动与分布式时钟主站实时性验证5.1 为什么抖动是EtherCAT主站的生死线很多EtherCAT主站方案能跑但不代表跑得稳。对伺服应用来说周期任务的稳定性直接决定了运动控制的品质。如果主站在1ms周期任务里的实际唤醒间隔忽大忽小比如这一轮1.1ms下一轮0.9ms伺服插补出来的轨迹就会抖电机噪音变大甚至触发跟踪误差报警。所以主站跑通OP之后第一件事不是写复杂的控制算法而是测抖动。我的测法很土但有效在周期任务里记录相邻两次调度的实际间隔累计几百次然后看最大值、最小值和标准差。static struct timespec ts_last, ts_now; clock_gettime(CLOCK_MONOTONIC, ts_now); int64_t delta (ts_now.tv_sec - ts_last.tv_sec) * 1000000000LL (ts_now.tv_nsec - ts_last.tv_nsec); ts_last ts_now; // 记录delta1ms周期下正常RT配置的抖动应该在几十us以内优秀配置能做到个位数us。如果抖动超过200us趁早去优化系统配置别先想着调伺服参数。5.2 分布式时钟DC到底在同步什么严格来说EtherCAT的等时同步靠的是DC机制而不是简单的主站周期发送。DC的核心思路是主站或指定的参考从站会生成一个系统时间通过每个周期发送的帧把时间信息广播给所有从站从站硬件根据传播延迟补偿后各自在同一个时刻锁存输入、更新输出。IGH里DC的实现在master.c和fsm_coe.c这些文件里作为使用者你只需要在初始化时选择是否启用DC同步。如果你用的是RK3568这类嵌入式平台我的经验是先用SM同步模式把功能跑通再切到DC模式看效果。DC对时钟漂移的补偿算法比较复杂一旦配置不当从站状态会出现随机跳变排查起来很痛苦。5.3 我踩过的一个坑网卡中断合并导致周期性漂移这个坑是我在调抖动时撞上的花了大半天才定位清楚。现象是周期任务的平均唤醒间隔非常准但偶尔会跳出一次2~3ms的延迟而且没有明显规律。一开始我以为是RT线程优先级问题试过调nice值、用SCHED_FIFO、绑核到CPU3都没根治。后来用/proc/interrupts看网卡中断分布发现网卡中断被分到了多个CPU上而且中断被合并了——也就是说网卡驱动在收到一帧后并不会立刻通知应用层而是攒了一批再中断这就把周期性帧的到达时间拉偏了。解决方法是两件事一是固定中断亲和性让网卡中断只走一个CPU比如CPU2二是关闭网卡中断合并interrupt coalescing。IGH的标准做法是直接用ec_generic驱动时在网卡驱动层关闭合并开关具体命令看网卡型号sudo ethtool -C eth0 rx-usecs 0 tx-usecs 0跑完这两步再测抖动最大值一下就压下来了。这里有个细节如果你的网卡驱动不支持ethtool关闭合并就要改IGH对应的网卡驱动源码把NIC的合并参数在初始化时清零。IGH源码里e1000e和igb驱动都有类似的处理函数。5.4 给RT配置的几条实战建议跑通OP、解决掉中断合并问题后我又做了一些系统层面的优化实测效果明显CPU隔离在内核启动参数里加isolcpus2,3把CPU2和CPU3从普通调度器中隔离出来专门跑RT任务。中断亲和性用/proc/irq/xx/smp_affinity把网卡中断固定到CPU0或CPU1避免和RT任务抢CPU。关闭调频RK3568默认可能有DVFS调频CPU频率变化会导致最坏执行时间不可控。我在跑实时任务时把CPU定频到最高档。使用实时线程IGH的周期任务用pthread_create创建设置SCHED_FIFO和优先级比如90。代码里加mlockall(MCL_CURRENT | MCL_FUTURE)锁页避免页缺失导致延迟。这些建议听起来都是老生常谈但每一条都是我实际踩过坑之后验证过的。拿出一下午时间系统调一遍比后面天天被偶发卡顿折磨要划算得多。6. 总结一下这阶段学到的东西老实说EtherCAT主站开发的学习曲线比传统现场总线陡很多但当你把状态机、FMMU、SM、DC这几个核心概念串起来之后再看公开资料和源码会轻松很多。这第一篇笔记里我从选型聊到环境搭建从帧结构聊到同步机制核心想表达的其实是主站开发的重点不是把代码跑起来而是搞清楚每一帧数据在网络里穿梭时从站硬件和IGH软件分别在什么时机、以什么方式处理了哪些字节。虽然我还在入门阶段但有几个碎片经验值得分享遇到状态切换失败不要急着怀疑硬件先用抓包软件看实际帧IGH的日志默认比较简陋开启--enable-debug编译可以输出详细的调试信息排查问题效率高很多所有配置改动都要有一套可复现的检查清单不然调优调到最后连自己都忘了改过什么。下一步我打算进入伺服驱动部分用汇川或者台达的驱动器做DSP402标准运动控制的Demo到时候继续更新这个系列。有一起在EtherCAT主站这条路上摸索的朋友欢迎在评论区交流各自的踩坑经历。
返回列表