伺服驱动内嵌EtherNet/IP通信方案设计与SPI链路适配实践 做伺服驱动开发这几年我接过不少“客户指定要EtherNet/IP通信”的项目。一开始的方案很朴素在主控DSP里直接移植协议栈后来发现这件事远比想象中麻烦。EtherNet/IP协议栈要跑TCP/IP加CIP对象模型还要响应扫描器的隐式报文主控的电流环和速度环被打断得七零八落板上资源也被吃得很紧。后来我换了一条路通信协议栈放到独立的嵌入式小板上主控和小板之间用SPI通讯主控只负责把位置、速度、转矩这些数据“递”过去至于CIP对象、组包解包、以太网收发全部交给小板处理。这个方案就是标题里说的“伺服内嵌EtherNet/IP方案”。这块嵌入式小板的核心是厂商提供的SPI通讯固件Demo程序。Demo本身能跑但直接烧进产品里肯定不行——硬件引脚复用不一样SPI传输速率和帧结构要按伺服周期来定中断优先级要跟电流环错开通信看门狗的逻辑也要重新设计。我这次做的就是把Demo程序完整适配到实际控制板上的过程。文章里我会把方案架构、SPI链路的搭建方式、Demo适配改造的具体步骤、EtherNet/IP协议栈调试的坑以及我踩过的一些典型问题都整理出来。适合谁来读正在做伺服、变频器、PLC远程IO这类工业设备通信扩展的嵌入式工程师或者想把EtherNet/IP加进自己产品但又不确定该从哪儿下手的同行。文章偏实操原理部分只讲跟工程决策直接相关的剩下全是能直接抄作业的内容。1. 为什么伺服里要塞一块“嵌入式小板”先回答一个最基础的问题EtherNet/IP是标准工业以太网协议伺服主控芯片的算力也不算差为什么非要单独搞一块小板1.1 运动控制主控的实时性困局伺服主控的活儿其实很单调但要求极高。以常见的三闭环控制为例电流环的周期一般是125us8kHz甚至62.5us16kHz速度环和位置环在此基础上分频得到。每个周期内主控要做的事情包括采样编码器数据、执行电流环PID、更新PWM占空比、处理速度环和位置环结果再加上各种保护逻辑过流、过压、过温。这些任务加起来留给其他事情的时间窗口非常有限。如果你在主控里再塞一个EtherNet/IP协议栈情况就变了。EtherNet/IP的隐式报文Implicit Message是周期性实时数据到达频率可能到1kHz甚至更高每一次报文到达都要求CPU立刻响应显式报文Explicit Message虽然不要求那么高的实时性但也要处理TCP连接、CIP对象请求、EDS参数读写。一套完整协议栈在单片机上的资源开销通常要占掉Cortex-M4级别芯片的30%-50%性能还要配RTOS。这个开销对通信模块来说无所谓但对刚跑完电流环、马上要进下个周期的伺服主控来说是致命的。1.2 通信协议栈的资源账我们算一笔具体的账。一个功能完整的EtherNet/IP Adapter从站协议栈包含这几部分以太网MAC/PHY驱动、轻量级TCP/IP协议栈或者用厂商提供的协议栈库、CIP对象模型Identity、Connection Manager、Assembly对象还要根据设备类型决定要不要支持Position对象、Motion对象、EDS参数管理、DLR环网协议如果要做Device Level Ring以及CIP SyncIEEE 1588/精确时间协议用于多轴同步。这些代码编译之后Flash占用通常在300KB到500KB之间RAM占用也要几十KB。CIP Sync的时间戳处理要求纳秒级精度必须靠硬件以太网MAC的时间戳功能配合普通软件模拟做不到。如果还要做双端口交换机功能的DLR硬件上得集成两个以太网口。这些资源对于伺服主控的DSP或者MCU来说基本上是越界的。所以“内嵌小板”这个方案本质上是把通信复杂度剥离出去。主控只需要管好自己的一亩三分地——电机控制通信的脏活累活全交给小板。主控和小板之间用SPI这种简单可靠的接口交换数据。对于伺服产品的整体设计来说好处非常明显主控软件不用频繁改换一种总线Profinet、EtherCAT、Powerlink就是换一块不同的小板SPI接口协议保持不变产品平台化能力立刻上来了。1.3 三种方案对比直跑、模块、小板市面上做伺服EtherNet/IP通信其实有三种常见路线。第一种是主控直跑协议栈前面说了实时性容易被拖垮而且后续升级维护都很痛苦。第二种是外购成熟的通信模块比如Anybus模块好处是即插即用、可靠性高坏处是贵一个模块的成本可能抵得上一小块pcb加上协议栈授权费而且模块和你自己的硬件是物理分立的结构上要占空间产品一量产成本就压不下来。第三种就是我们用的嵌入式小板方案小板是一块独立的PCB上面有一颗Cortex-M4/M7级别的处理器、以太网PHY芯片、SPI接口、以及电源电路。小板通过排针或者板对板连接器插在伺服主板上SPI的从机模式挂在主控下面。你需要自主设计的就是小板的固件在这个项目里就是对厂商Demo程序的适配和两端的数据帧协议。这条路最平衡——成本可控开发量适中灵活性也好。小板出了通信问题可以单独换不会殃及主控。2. 硬件接口设计与SPI通讯链路搭建方案定下来了下一步是啃硬骨头——SPI通讯链路。这块直接决定后面的Demo适配难度和通信稳定性值得花心思。2.1 SPI主从拓扑与速率选择SPI链路谁来当主机这个看起来简单的问题背后是控制逻辑的取舍。我们的做法是主控当SPI主机小板当从机。原因是伺服主控是整个系统的节拍发生器电流环、速度环的位置更新都有固定的周期数据交换必须跟控制周期对齐。主控主动发起传输就能保证每个控制周期结束后立刻把指令送给小板从小板拿回实际反馈不会出现“小板想发数据但主控没空请它”的尴尬。如果要按“主控从机、小板主机”来做也是可以的但那样小板要先知道主控的控制周期节拍要么通过专门的GPIO信号告诉它要么让它靠定时器猜设计复杂度会明显上去。我建议没有特殊理由就保持主控SPI主机这个方案。速率方面Demo程序默认的SPI速率通常是1Mbps或者2Mbps这只能算“能用”不是“够用”。伺服运行时的数据量不是很大但周期很短举个例子位置/速度/转矩指令各4字节状态反馈4字节加上控制和状态字来回一次数据交换大概32到64个字节。如果SPI速率为6Mbps一帧32字节的传输时间大约50us左右可以接受。但如果你把SPI速率提到12Mbps甚至更高从机小板的CPU就可能来不及处理——它一边要从SPI FIFO搬数据一边要跑协议栈还要应答以太网报文处理不过来就会丢数据。所以速率的选取是个平衡题。我最终用的是6MbpsSPI模式3CPOL1, CPHA1这个组合在Demo里最常见也经过了厂商的一致性测试直接沿用最稳。2.2 主从机之间的数据帧结构设计SPI只是管道真正决定通信质量的是管道里跑的数据帧结构。Demo程序里默认的帧结构很简单大概率只有几个字节的原始数据循环这在实际产品里不够用。我的做法是设计两个固定长度的数据块主机→从机控制数据32字节帧头、帧序号、控制字、目标位置、目标速度、目标转矩、模式字、CRC校验。从机→主机状态数据32字节帧头、帧序号、状态字、实际位置、实际速度、实际转矩、驱动器报警码、CRC校验。为什么不用变长帧变长帧处理起来灵活但解析复杂还容易出粘包和半包问题。固定长度帧在SPI这种“一主一从”的确定性链路下最简单从机收到32字节就知道整包数据齐了解析逻辑O(1)完成不占用小板太多算力。帧序号和数据一致性要特别注意。SPI是异步通信数据传输过程中主控完全可能被中断打断导致主机发出的数据帧在某个字节上发生错位。加上帧序号和CRC之后从机一旦发现序号跳变或者CRC不对就丢弃整帧并返回上一个有效周期的数据主控看到序号不对也能自己判断是否需要重发或者标记链路异常。这比试图在物理层保证“绝对不传错”要实际得多。2.3 流控、DMA和同步机制SPI从机最怕什么主机连续发数据从机还没把上一帧处理完下一帧就来了。Demo程序在低速率下还不容易出问题一旦速率提上去中断方式收数据就会超额。我在小板上把SPI接收改成了DMA模式配合双缓冲乒乓结构DMA正在往缓冲A写数据时主控的核心可以处理缓冲B里上一帧的协议数据下一次传输反过来。这样数据接收和处理解耦从机不丢帧主机也无须等待。还有一个常被忽略的细节从机数据就绪信号。SPI从机是“被动”接收的主机不知道从机当前写好的数据是否已经更新。我们在硬件上额外引出一根GPIO从机在每帧状态数据准备好后拉高表示“可以读”主机看到这个信号再从SPI读取。没这根信号主机就只能靠固定延时去读多等不少时间而且还可能读到重复数据。Demo程序里如果没做这根线建议自己加改动不大但整体通信可靠性能上一个台阶。3. Demo程序适配改造的完整实操下面进入正题聊聊Demo程序适配改造的过程。这部分我按实际项目推进的顺序写尽量把每个步骤“为什么这么做”讲清楚。3.1 拿到Demo后的第一步梳理工程结构厂商给的Demo程序压缩包解开后通常就是这么几个目录Doc说明文档、Library编译好的协议栈库文件、Project工程文件、App应用层示例代码、BSP板级支持包。第一步不是改代码而是先把原始Demo编译一遍烧录到小板里面跑起来确认基础功能正常。怎么确认把小板的以太网口接到交换机上用网线连电脑把电脑的IP设置成和小板同一个网段。如果Demo程序做得好小板固件默认会开启DHCP或者有一个默认IP比如192.168.1.10。然后用官方的EDS文件在Rockwell的RSLogix/Studio 5000里建一个设备拉进IO树试试能不能建连。能连上说明Demo的协议栈、以太网驱动、基本CIP对象都是好的问题只出在我们后面要改动的部分连不上先查IP配置和网线别急着动代码。这一步很重要它能给你一个“基线”在这个基线之上做的每一步改动都可以用“是否还能连上、连接是否稳定”来验证。我见过太多人拿不到基线就直接改改完发现通信不通排查半天也不知道是协议栈的问题还是自己改动引入的问题。3.2 BSP层适配引脚、时钟和中断优先级Demo程序通常是基于某款官方开发板写的引脚复用表不一定和你手里的小板对得上。你需要对照小板原理图逐一核查SPI引脚SCK、MISO、MOSI、CS分别接在哪个GPIO复用功能SPI1还是SPI2。以太网PHYRMII接口引脚还有PHY的复位引脚PORST和中断引脚INT。状态LED对应引脚要改后面排查问题全靠这几个灯。调试串口如果是板载调试口把日志输出绑到串口上方便调试。时钟树小板的晶振频率可能跟Demo开发板不同PLL配置也要改。中断优先级这块我踩过坑。小板的CPU要处理SPI接收DMA中断和以太网中断。以太网中断的优先级应该略高于普通任务但不能高于伺服主控在SPI链路上要求的最坏响应时间。我的经验是以太网中断设为中等优先级SPI DMA完成中断设为较高优先级因为它是小板和主控之间的“心跳”一旦SPI帧收晚了主控那边可能直接报通信故障。同时中断服务函数里只做数据搬运和置标志绝不在中断里跑协议栈代码协议栈放在主循环或RTOS任务里处理这样能避免中断嵌套导致的不可控延迟。3.3 SPI从机驱动适配与数据映射Demo程序里的SPI驱动如果是查询方式的建议直接改成DMA中断的方式理由前面已经说了。适配时的关键点第一SPI从机模式下的DMA接收长度。SPI从机的DMA接收长度必须预设定为帧长比如上面的示例是32字节。主机每次CS拉低、开始时钟传输时从机DMA会自动接收32字节。接收完成后产生DMA传输完成中断在中断里切换乒乓缓冲置“新数据到达”标志。这里有一个小窍门置标志之前先判断帧头是否是预期的固定值如果是垃圾数据直接丢弃不置标志避免协议层处理一堆无效包。第二数据映射表。Demo程序里通常会有一个Assembly对象示例它负责把EtherNet/IP侧的输入/输出数据映射到协议栈内的缓冲区。你需要把这块缓冲区和SPI收到的数据做对应。比如从PLC下来的目标位置4字节从Assembly对象的output区取出放进SPI发送缓冲区偏移地址XSPI从主控收到的实际位置放进Assembly对象的input区。这个映射关系建议单独用一个结构体管理写清楚注释后面EDS文件也要用同样的偏移来定义各字段的含义两边必须一致。第三SPI从机的时序适配。有些小板的SPI硬件要求CS全程拉低期间保持时钟连续性有些则允许主机在每个字节之间停顿。如果主机MCU的SPI驱动传输期间每隔几个字节就被高优先级中断打断时钟会中间停顿。小板这边尽量用硬件SPIDMA容忍字节间暂停否则通信一紧张就出错。3.4 编译、烧录和端到端验证代码改完后编译烧录。烧录这块推荐先用JTAG/SWD调试器烧录方便控制复位和查看断点量产阶段再用串口或者以太网Bootloader刷写。烧录完成后先用逻辑分析仪或者示波器抓一下SPI信号确认SCK频率、CS时序、数据字节顺序都和设计一致——这一步不要省因为仅靠肉眼观察运行状态很难确认SPI物理链路是健康的。然后做端到端验证。用PLC或者电脑上的模拟扫描器连上设备建立IO连接把一些测试值写进去看伺服主控是否能收到并将状态返回。我的常用方法是先在PLC里造一个周期性递增的数据伺服主控收到后回传一个递增的计数器值比对两侧的递增序列值是否相等、有没有丢包。这个测试能同时检验SPI链路和EtherNet/IP连接的质量比直接空跑要直观得多。4. EtherNet/IP协议栈里的关键细节与易踩坑SPI链路稳了Demo程序也跑起来了剩下的就是EtherNet/IP协议栈这块“黑盒”里到底有什么、哪些地方最容易出问题。4.1 协议栈选型与“有没有源码”的问题很多同行第一次接触EtherNet/IP都会问“这个协议有源代码吗”答案是可以有。EtherNet/IP协议栈分商业版和开源版。开源方案里比较有名的是OpENer它在GitHub上有完整源码支持基本的Adapter功能包括CIP对象模型、隐式/显式报文、EDS生成等。但开源栈的问题也明显迭代较慢对新版本协议比如CIP Motion、CIP Sync支持不完整文档偏少出问题得自己啃代码。商业协议栈比如从芯片厂商或第三方协议栈公司购买的好处是稳定性好一致性测试ODVA Conformance Test过起来快技术支持有保障。Demo程序里如果带的协议栈是库文件比如lib文件或预编译的obj文件那就是商业授权方式——你能调用API能看到配置头文件但看不到内部实现。这种情况下你能改的就是应用层和配置层不必去碰协议栈核心。我的建议是如果做量产产品优先用带商业授权的Demo配套协议栈省时省心。如果你想学习原理、做预研或小批量项目开源栈足够用。选开源栈的话一定要自己先把协议栈的源码架构读一遍主要弄清楚连接管理器Connection Manager对象ID为0x06、Assembly对象、CIP路由表这三块是怎么实现的。4.2 EDS文件和AOI让PLC认识你的设备EtherNet/IP设备能不能被PLC正确识别很大程度上取决于EDS文件Electronic Data Sheet写得好不好。EDS文件是基于INI格式的文本描述了设备的厂商ID、设备类型、产品代码、通信参数、Assembly对象实例和各个参数的定义。一开始我懒得自己写EDS直接用Demo自带的EDS文件改了个名字和产品描述就上传到PLC结果PLC扫描器能建连但数据解析全错——原因就是EDS里定义的Assembly数据长度和实际固件里的不一致。EDS文件里的Assembly对象格式比如“32字节输入32字节输出”必须和固件中Assembly对象的实际配置完全一致一个字节都不能差。AOIAdd-On Instruction是Studio 5000里的一块封装好的逻辑块本质上是把你PLC侧的处理逻辑打包成一个可复用的模块。很多伺服客户会要求厂商提供AOI方便他们在PLC程序里直接调用“使能、复位、读写位置”这些功能。这个AOI的编写其实就是按照你制定的状态机规则把EDS里的参数映射成PLC的内存地址不复杂但很琐碎。AOI写好后一定要用PLC模拟器或者真实PLC验证一遍每个功能码。4.3 连接建立与CIP对象模型EtherNet/IP的连接建立过程本身是个很值钱的知识点。扫描器PLC会先通过TCP 44818端口发送显式报文请求向设备的Connection Manager对象发起Forward Open请求这个请求里包含了要建立的连接类型、数据包间隔RPIRequested Packet Interval、输入输出连接大小等。设备收到后如果同意会返回一个“连接ID”之后双方就通过UDP 2222端口或者配置的IO端口周期性交换隐式报文。这里最容易出的坑是RPI设置。伺服系统的RPI通常在4ms到16ms之间如果你的SPI链路或者协议栈处理速度跟不上就会频繁出现“连接超时”被PLC断开。遇到这种情况第一步不是怀疑协议栈而是检查小板的CPU占用率。协议栈建议开RTOS把以太网收发、SPI数据解析、协议栈主循环拆到不同任务里避免一个任务卡死导致所有连接都被断开。CIP对象这块你的固件至少要完整实现Identity对象0x01、TCP/IP对象0xF5、Ethernet Link对象0xF6、Assembly对象0x04、Connection Manager对象0x06。如果是伺服应用还要根据协议版本实现CIP Motion相关的Motion Device对象0x1E和Motion Axis对象0x1D。每加一个对象都要重新过一遍一致性测试ODVA对对象定义的合法性查得很严。5. 常见问题与排查实录最后整理一下我在这类项目里实际遇到过的典型问题每一条都值得拉出来给大伙提个醒。5.1 SPI偶发丢帧和数据错位症状伺服运行一段时间后出现报“通信故障”但重新上电恢复。通过串口日志看是主机读取到的从机状态数据CRC校验失败。排查过程一开始怀疑SPI速率太高降到4Mbps问题还是随机出现。后来用逻辑分析仪抓SPI波形发现主机在传输过程中确实有字节间停顿但停顿的时间长短不固定。经过对比发现停顿时间长的那些次刚好对应主机电流环中断处理时间较长。这不是SPI本身的问题而是小板的从机SPI在CS有效期间主机时钟停止时无法自我同步最终导致字节错位。解决调整了主机的SPI驱动在传输开始前先发一个固定的预置字节比如0x55从机通过捕获这个同步字节来确定字节边界再从缓冲数组偏移地址读取真正的数据。这样即使中间有停顿也不会错位。另外在从机端增加“SPI总线空闲超时”判断超时后自动复位SPI外设避免长时间卡死。5.2 PLC扫描器连不上从站症状把设备加入IO树后PLC一直显示“Connection Timeout”设备识别不到。排查过程先查IP确认IP在同一网段没问题再查连接参数EDS文件里的RPI设置用的是默认值也没毛病。最后把小板固件里的串口日志打开看发现系统在给PLC报文回“Connection Error”之前报了一个“CIP对象未创建成功”的异常。再往下查发现是小板启动时SPI链路等待主控数据超时导致Assembly对象初始化不完整对象数量不够PLC的Forward Open请求被拒绝。解决把Assembly对象的初始化逻辑从“等待主控数据”改成“上电即初始化数据在后台更新”不依赖SPI链路是否已经通信上。这样就算主控还没工作EtherNet/IP测也能先把基本连接建立起来。5.3 看门狗误复位导致掉线症状设备运行一段时间后突然离线串口日志显示“reset by watchdog”。排查过程看门狗喂狗的逻辑放在了协议栈主循环里理想情况下没问题。但协议栈在处理某些大报文如EDS文件全量读取时主循环会长时间阻塞超过看门狗超时时间芯片被强制复位。解决把喂狗逻辑拆到独立的高优先级定时器中断里或者使用独立的看门狗任务将任务挂起时间从看门狗时间中扣除。更稳妥的做法是看门狗超时时间放宽到500ms以上但协议栈主循环中又设置一个软件看门狗超时100ms就记录日志并手动复位协议栈相关任务做到“软保护硬保护”双保险。5.4 位置指令抖动与RPI不稳定症状用PLC发周期性位置指令时伺服实际轨迹有轻微抖动示波器看SPI数据帧间隔发现抖动幅度达到几百微秒。排查过程抖动的主要来源是PLC端的RPI不稳定和网络争用而小板和主控之间的SPI交换是同步于伺服周期的本身很稳定。问题是SPI帧里的指令值一直在小幅跳变。解决在主机侧SPI接收后加一个限幅滤波如果新帧的目标位置和上一帧的差值超过额定限制就丢弃当前值沿用上一帧。同时在PLC端建议客户提高RPI优先级或者将EtherNet/IP报文划分到一个独立的VLAN里减少广播报文干扰。6. 一些个人的经验与建议做了几个这样的项目之后我的体会是方案本身的架构并没有多玄乎真正的难度在于两点——第一SPI链路的数据一致性设计第二协议栈与伺服应用之间的映射关系。这两点做好了整个系统基本就稳定了。针对这两个点我再多说几句。SPI链路的设计一定要预留“通信状态观测”能力。哪怕不在正式产品里开发阶段的调试版本里也要加两个东西一个记录SPI帧错误的计数器一个可触发上翻的GPIO测试点。计数器用来判断问题严重程度GPIO测试点用示波器抓传输间隔能快速判断链路是否被阻塞。没有这两样排查通信问题时你只能靠猜效率会低很多。协议栈和伺服应用的映射关系记得做一张“映射表”挂在源码管理系统的文档里。哪些Address对应哪些寄存器控制字的每一位都有什么含义状态字的每个bit是哪个报警码这些都要写清楚。项目进行到后期PLC工程师和固件工程师会反复需要这张表它也是你之后的AOI编写和EDS维护的基准。不要只在代码注释里写单独的Markdown或Excel表格效果要好得多。最后再讲一个容易被忽略的小技巧Debug版本的固件里一定要保留一个“无条件清除所有CIP连接”的调试入口可以通过串口命令触发。实际调试时会经常遇到PLC端的连接状态和设备端不同步的问题如果固件不支持手动清理连接就只能每次重启设备或断电非常耽误事。多写一个命令函数的事情在日后现场调试和客户支持时能节省大量时间。如果你也在做类似的伺服总线适配项目希望这篇整理对你有点帮助。方案选型、SPI帧结构设计、Demo适配思路、协议栈调试方法这些思路其实不只适