资讯详情

资讯详情

伺服内嵌EtherNet/IP:SPI通讯固件适配改造实战指南

做伺服驱动器的朋友应该都遇过这种需求客户整线用AB PLC或者欧姆龙、罗克韦尔这一系的控制器点名要EtherNet/IP通讯。伺服主控本身算力不差但要让它再扛一套完整的EtherNet/IP从站协议栈尤其还要做CIP Sync、毫秒级RPI刷新基本不现实。于是方案几乎都走向同一条路——主控旁边挂一颗嵌入式通讯小板小板上跑协议栈主控和它通过SPI完成数据交换。我这次做的就是把这颗小板的固件Demo程序从能通信改造成能上伺服产线稳定跑。下面把这套伺服内嵌EtherNet/IP 嵌入式小板SPI通讯固件Demo适配改造的完整过程拆开讲。涉及SPI协议、固件结构、寄存器映射、周期同步、排查经验内容偏实战适合正在做总线集成、运动控制、伺服驱动和嵌入式通讯开发的工程师参考。1. 项目背景为什么非得外挂一块嵌入式小板1.1 伺服主控自己跑EtherNet/IP协议栈的难处EtherNet/IP从站看起来只是标准以太网上跑CIPCommon Industrial Protocol但真正落地的时候协议栈要处理的比想象中多得多。CIP对象、Assembly对象、Explicit Message、Implicit IO、RPI管理、EtherNet/IP一致性测试光是把这些跑起来就要吃掉几十K代码空间和一大块RAM。伺服主控大部分算力都花在电流环、位置环、速度环上如果主控还是老一代DSP或者ARM M4级别的芯片直接在它里面移植协议栈一轮编译下来代码空间先吃紧实时任务调度也会被打乱。外挂方案把协议栈隔到独立MCU上以太网PHY也在小板上主控只面对SPI压力小很多。这个思路在工业总线里很常见EtherCAT、PROFINET、Powerlink都有类似外挂模块的做法。选外挂本质上是把复杂度卖给通讯小板把稳定性留给伺服主控。代价是主控和小板之间多了一条SPI链路链路可靠性、时序、握手都得自己做这就是这次改造的核心工作。1.2 为什么通讯口偏偏选SPI而不是UART/CAN伺服主控与外接通讯板可选的接口无非SPI、UART、CAN、并口。UART简单但波特率做到921600有效吞吐也就百KB级别跑1ms周期的伺服IO交换非常紧张。CAN速度不错但要加收发器、要考虑仲裁和总线负载协议栈侧也不一定方便。SPI是高速全双工可以同时收发时钟跑到十几二十MHz很平常硬件结构极简几乎每颗MCU都自带SPI外设。更重要的是Demo程序里协议栈和小板固件已经预留了SPI从机侧的驱动接口主控侧移植成本最低。但SPI的缺点也明显没有应答机制和流控没有帧边界。主机发一堆字节从机回一堆字节谁对谁、哪一包是哪一包完全靠应用层协议约定。所以SPI只能保证字节能过去数据有意义得靠帧格式、长度、校验和应用层状态机来保证。这是整个适配改造里最容易翻车的地方。2. 拿到手上的固件Demo程序到底是个什么东西2.1 Demo程序不是给产线用的是给你改的方案商或芯片原厂交付的SPI通讯固件Demo常见形式是一个嵌入式工程可能是基于某颗MCU的HAL库工程也可能是裸机MDK工程里面包含协议栈内核一般是加密库或目标文件、SPI从机驱动、一块模拟的双端口寄存器区、以及简单的读写示例main函数。Demo的定位是证明协议栈能跑起来不是给你直接上产线。默认引脚、SPI模式、帧格式、中断配置都是原厂评估板上的换成实际伺服控制板引脚要重映射、时钟要重新分频通信速率和帧间隔也得按伺服控制周期来调整。很多人拿到Demo第一件事就是编译下载结果板子接上去完全没反应原因多半是引脚和协议参数根本没改。我见过委托方的人拿着小板和主控的飞线板过来查问题查了半天发现主控用的是SPI2口Demo工程里初始化的是SPI1口时钟没打开一路全是高电平数据自然进不去。Demo程序里最值钱的部分是那个寄存器区。它对应EtherNet/IP的对象模型伺服要映射的控制字、状态字、速度给定、位置反馈、报警码都会被协议栈放到Assembly对象里再映射到寄存器区的固定偏移地址。主控侧通过SPI读写这些地址等效于读写EtherNet/IP对象。掌握这张寄存器映射表是整个项目里最关键的事。2.2 适配改造前必须确认的七个重点项拿到Demo工程后先别急着编译建议按下面这张清单逐一确认。每个项目我都标了适配时最容易翻车的点。适配项Demo默认状态实际需要改成什么注意事项SPI引脚映射原厂评估板引脚伺服主控实际使用的SPI引脚引脚冲突和复用是排查第一站SPI工作模式原厂默认CPOL/CPHA与从机小板完全一致模式不一致时钟对也不出数据数据位宽8位按协议栈要求保持8/16/32统一两边位宽不一致会产生错位SPI时钟频率偏低按线长、信号质量、报文长度调整时钟过高伴随信号质量下降字节序可能是小端按主控与协议栈约定翻转大小端不一致寄存器数据全错中断握手轮询或电平触发建议边沿触发超时处理丢一次中断整帧状态机就要还原缓冲区保护无临界区加临界区或DMA缓冲区互斥主控读写和小板固件更新数据可能冲突这张表看起来简单但每一项都能单独写一篇排查。第3部分我挑几个最关键的展开讲。3. SPI适配改造的核心步骤与原理3.1 硬件资源映射从原厂板到伺服主控板我这次项目里伺服主控是TI C2000系列DSP小板上跑协议栈的是STM32F4系列。双方约定的连接很简单SCLK、MISO、MOSI、SS四根线外加一根小板→主控的INT输出脚用于通知主控PLC那边有新数据了。C2000的SPIB口做主机小板做从机主控侧GPIO分配如下SCLK接到SPIB_CLKSPI时钟输出频率由主控SPIB寄存器配置。MOSI主出从入接SPIB_SIMO主控发送数据到小板固件。MISO主入从出接SPIB_SOMI小板固件回数据给主控。CS片选接GPIO低电平有效主控主动拉低启动一次传输。INT接DSP的GPIO中断脚小板收到来自PLC的新数据包后拉高主控中断里读寄存器。这里有个经验片选尽量用硬件片选不要在中断服务函数里软件拉片选。软件拉片选最大的问题是时序抖动中断一挤片选拉低和时钟起始之间的延时就不稳定从机端可能误判起始条件。C2000的SPI如果片选引脚分配紧张当然也可以退而求其次用IO模拟但必须保证片选拉低到SCLK首沿之间留足建立时间一般至少空一个SPI时钟周期。INT脚是这套方案的生命线。没有它主控只能靠轮询小板的数据变化标志RPI是1ms还是4ms都没法精确对齐。有INT脚以后PLC数据一到达小板固件立刻把数据填入寄存器区然后拉高INT主控在中断上下文里或主循环查询里读取把通讯时延压缩到可控范围。3.2 SPI模式、速率、片选怎么定才稳SPI有四种模式由CPOL时钟极性和CPHA时钟相位决定。Demo程序里的协议栈从机默认可能是Mode 0也可能Mode 3这不能靠猜必须查固件手册或用逻辑分析仪看。我一般直接在小板的示例工程里找SPI初始化代码看它设置的CPOL和CPHA然后让主控侧跟随。模式不对的典型现象是SCLK和CS都有波形但MISO上一帧数据全是FF或00偶尔蹦出几个乱码。速率方面我建议别把SPI时钟拉满。伺服主控和小板之间是板级连接线长一般不超过10cm但高速SPI容易受到主板上IGBT开关噪声、电源纹波干扰。C2000的SPIB时钟源来自系统时钟分频我当时把SPI时钟稳定在12MHz实测一个1ms伺服周期里足够完成两轮各20字节的寄存器读写。算一下假设一次读写要传输的数据长度是发送20字节接收20字节总共传送40字节每个字节8位SPI时钟12MHz那么一次交换需要的时间约等于 40×8 / 12MHz ≈ 26.7微秒。相比1ms的伺服周期算力占用只有2.7%非常充裕。即便考虑帧间隔、校验开销也不会成为瓶颈。片选极性也要注意。多数SPI从机要求CS低有效但有些从机在CS高电平期间做数据同步。我遇到过Demo的从机驱动是CS高有效的情况主控按低有效去操作结果通信完全不工作。确定极性的方法很简单看小板固件里SPI配置寄存器或者直接用示波器拉起CS看从机有没有反应。3.3 应用层帧协议设计SPI之上必须有一套“语言”SPI本身没有帧概念主控发一个字节从机同时回一个字节两边都必须知道当前这个字节是什么含义。Demo程序通常会定义一套简单的HOST接口协议结构类似字段长度说明帧头2字节固定值比如0xAA 0x55用于帧同步命令字1字节0x01读寄存器0x02写寄存器0x03握手目标地址2字节寄存器区偏移地址数据长度2字节后续有效数据字节数数据域N字节读命令时为空写命令时为要写入的数据CRC2字节对帧头到数据域做CRC16校验帧尾1字节固定0x7E这套协议看着简单但实际项目里是可靠的基石。帧头用于对齐CRC用于校验帧尾用于验证一帧是否完整。主控侧每次拉低CS前先把整帧按这个结构填充到发送缓冲区然后一次SPI传输发出去。从机侧逐字节收完解析出命令、地址和数据执行对应操作再把应答帧从MISO线上吐回来。设计帧格式时有几个经验不要省CRC。SPI在强电环境里受到电磁干扰的概率不低没有校验寄存器里偶尔翻转一个bit都够你查一整天。长度字段一定要有。协议栈版本升级后寄存器区可能会扩大固定长度帧兼容性差。应答帧里带上原命令字方便主控侧校验我问的和你答的是不是同一件事。不要把帧头和普通数据设成同一个值否则容易误同步。我常见的是0xAA 0x55但寄存器数据里也可能出现0xAA 0x55所以帧尾和CRC必须双保险。3.4 寄存器映射表与EtherNet/IP对象字典的对应这是整个适配改造里和伺服业务最相关的部分。EtherNet/IP从站里PLC侧配置的Input/Output Assembly是有固定字节布局的。伺服场景典型的映射如下PLC→伺服主控需要写入的Input Assembly控制字Control Word2字节包含启停、使能、复位、模式切换位。模式选择 1字节位置模式、速度模式、转矩模式。速度给定 4字节有符号整数或浮点单位是用户自定义的单位。转矩限制 2字节。附加命令 2字节。合计约11字节。伺服→PLC主控需要读取的Output Assembly状态字Status Word2字节包含就绪、运行、报警、模式确认位。实际速度 4字节。实际位置 4字节。报警码 2字节。诊断数据 4字节。合计约16字节。改造成熟后我会做一张Excel表把Assembly字段和寄存器偏移一笔一笔对应出来。这张表既要给嵌入式工程师看也要给PLC调试工程师看。PLC侧组态软件里配置的Assembly大小和字节序必须和这张表严格一致否则伺服收到控制字和PLC实际发出来的相差十万八千里。寄存器偏移建议从0x0000开始顺序排读区和写区分开。比如0x0000~0x000F是伺服→PLC的数据0x0020~0x002F是PLC→伺服的数据主控按固定周期读0x0000开始的一段连续区域和写0x0020开始的一段连续区域。这样SPI传输可以用固定长度报文明文传省去每次传地址的额外开销逻辑也更清晰。3.5 周期同步与INT握手机制伺服控制周期通常是1ms、2ms或4ms。EtherNet/IP的RPI是这个周期的整数倍。主控侧的控制中断每1ms触发一次在中断里做电流环、速度环、位置环同时需要把最新的给定和反馈与PLC侧同步。我的做法是主控控制中断里定义一个小任务每次进入中断先读INT引脚状态。如果INT为高表示小板固件新收到PLC数据主控就发起一次SPI读操作从寄存器区0x0000处读回伺服状态和PLC命令如果没有INT信号说明数据没有更新可以直接跳过减少SPI占用。写操作同理主控计算好给定值以后把数据写入两个DSP缓冲区再发起SPI写操作把数据从0x0020地址写入小板的寄存器区协议栈会在下一个RPI周期自动把它发出给PLC。注意INT是电平触发还是边沿触发。我建议在DSP里配置成上升沿中断中断服务函数里只置一个标志位不处理SPI数据实际读写放到控制周期的低优先级别代码里做。这样能避免因为SPI传输耗时导致控制中断优先级被阻塞。踩过最大的坑是一开始在INT中断里直接做SPI收发传输时间又长把1ms控制周期拖到1.3ms电流环直接啸叫。4. 实操过程与核心状态机实现4.1 Demo程序工程迁移顺序拿到Demo工程以后我建议按下面这个顺序做迁移每步都验证不要一口吃成胖子。第一步先看原理图。把小板上所有SPI相关引脚、中断脚、电源脚整理成pin表和伺服主控板的pin表对照确认没有用电平不匹配3.3V与5V的情况。EtherNet/IP通讯小板一般是3.3V逻辑但不少伺服主控板上还有5V的IO区域两边直连前要确认主控侧SPI引脚的IO电平状态该做电平转换做电平转换。第二步先把Demo工程编译通过下载到小板用一根逻辑分析仪抓从机侧的CS、CLK、MISO、MOSI。这时候不接主控直接靠Demo自带的回环测试或串口调试命令确认从机SPI驱动活着、寄存器区可读可写。这样后面接主控时收不到数据就知道问题在主控侧。第三步主控侧写一个最小SPI测试函数先不跑状态机只做一件事固定向0x0020写一个0xAA55然后立刻读0x0000看能不能读回预期数据。这一步过了SPI链路基本没问题。第四步跑完整的主控状态机接入PLC侧用组态工具做IO扫描看数据是否按预期流动。4.2 SPI传输状态机设计主控侧SPI通讯逻辑建议实现成一个有限状态机方便把发送、接收、校验、超时处理都收敛到一个模块里。核心状态如下typedef enum { SPI_STATE_IDLE, SPI_STATE_PREPARE, SPI_STATE_TRANSFER, SPI_STATE_PARSE, SPI_STATE_ERROR, SPI_STATE_RESET } SpiCommState_t;各状态逻辑IDLE等待1ms周期计时或INT事件触发一次传输。PREPARE把当前待发送帧填入SPI发送缓冲区清空接收缓冲区组装CRC。TRANSFER使能SPI主机传输等待发送完成中断或DMA回调。这期间不阻塞其他任务。PARSE传输完成后解析接收帧校验帧头、CRC、帧尾。通过后把数据域写入相应寄存器结构体。ERROR校验失败、超时或接收长度不符累计错误计数如果连续错误超过设定阈值则切换ERROR状态。RESET错误恢复重新初始化SPI外设等待下一次触发。状态机主体代码风格大致是void SpiComm_StateMachine(void) { switch (state) { case SPI_STATE_IDLE: if (new_data_flag || period_tick) { SpiComm_PrepareFrame(tx_buf, tx_len); state SPI_STATE_PREPARE; } break; case SPI_STATE_PREPARE: SpiComm_CalcCrc(tx_buf, tx_len); SpiComm_ClearRxBuffer(rx_buf, rx_len); state SPI_STATE_TRANSFER; break; case SPI_STATE_TRANSFER: if (SpiComm_TransferDone()) { state SPI_STATE_PARSE; } else if (SpiComm_Timeout()) { comm_error_count; state SPI_STATE_ERROR; } break; case SPI_STATE_PARSE: if (SpiComm_ValidateFrame(rx_buf, rx_len)) { SpiComm_UpdateRegisters(rx_buf); comm_error_count 0; new_data_flag 0; state SPI_STATE_IDLE; } else { comm_error_count; state SPI_STATE_ERROR; } break; case SPI_STATE_ERROR: SpiComm_ResetExternal(); state SPI_STATE_RESET; break; case SPI_STATE_RESET: state SPI_STATE_IDLE; break; default: state SPI_STATE_IDLE; break; } }这个状态机看起来简单实际运行稳定。重点是超时判断。SPI传输开始后如果从机没有在预定时间拉低CS或返回数据主机不能傻等必须有一个超时计数器把状态机踢回IDLE。否则一次异常就可能导致后续所有通讯全部卡死。4.3 寄存器读写函数怎么封装主控侧读寄存器封装成带重试的接口int SpiComm_ReadReg(uint16_t addr, uint16_t *value) { SpiFrame_t frame; frame.header 0xAA55; frame.cmd 0x01; frame.addr addr; frame.len 0; frame.data[0] 0; frame.crc SpiComm_CalcFrameCrc(frame); SpiComm_SendFrame(frame); // 等待从机应答带超时 if (SpiComm_WaitResponse(10) ! 0) { return -1; } // 校验应答 if (SpiComm_ValidateFrame(rx_frame) ! 0) { return -2; } *value (uint16_t)(rx_frame.data[0] | (rx_frame.data[1] 8)); return 0; }写寄存器类似命令字改成0x02数据域里带写入内容。为了提高性能周期传输时建议直接使用批量读写接口一次性把整块寄存器区读回主控的伺服接口结构体里而不是逐字读。因为逐字SPI传输的帧开销很大批量传输只做一次帧头、地址、CRC就能带走几十字节数据。以下是批量读的简化逻辑int SpiComm_ReadBlock(uint16_t start_addr, uint16_t *data, uint16_t len) { // 构造批量读帧地址start_addr长度len // 等待应答帧 // 校验 // 把应答帧数据域拷贝到data指向的连续内存 }伺服控制周期里调用批量读/批量写每次固定读16字节、写16字节实测1ms周期内占用时间约30us完全可行。4.4 控制周期与通讯周期的配合伺服控制中断是最高优先级通讯由周期任务或状态机驱动不要让通讯打断控制。我在DSP里把这个逻辑安排成三层第一层控制中断1ms周期执行电流环、速度环、位置环读INT标志写给定输出。第二层通讯状态机在控制中断结束后由主循环调用执行SPI状态迁移、帧解析、错误恢复。第三层PLC组态通信如果有和上位机调试接口优先级最低。这里有一个细节控制中断里更新的给定值是放在共享缓冲区里的需要在SPI发送帧组装前用临时变量拷贝一份。不能在SPI正在传输的时候由控制中断修改发送缓冲区的内容否则会产生半新半旧的帧。我用的做法是双缓冲区控制中断写入A缓冲区通讯状态机读的是B缓冲区准备发送前执行一次memcpy批量拷贝并关闭一次中断保证拷贝原子性。5. 常见问题与排查技巧实录5.1 一点通讯都没有从哪查起最常遇到的故障是主控和小板之间完全不通。按下面顺序排查通常能快速定位先量电压。小板5V或3.3V是否正常MCU是否处于复位状态。有次是Demo工程的看门狗默认开启但芯片没喂狗导致小板反复复位SPI自然没反应。再量CS。主控发起传输时用示波器看CS有没有拉低。没有拉低检查GPIO配置和片选极性。然后看SCLK有没有时钟。没有时钟查SPI外设时钟使能是否打开、分频系数是否设置正确。最后观察MISO。主机发0xAA 0x55这种固定pattern观察从机应答帧是否回正常的0x55 0xAA。如果MISO全是高或全是低基本是从机没有上电、从机SPI没初始化或者MISO线序错了。有一次我排查了很久最后发现是小板协议栈所在的MCU有BOOT引脚被拉到了启动模式代码没跑起来。所以收到一直没反应的板子先确认从机能正常运行Demo自带的跑马灯或串口打印别过度自信。5.2 偶尔错帧、数据跳变是速率还是干扰跑了一段时间后出现偶发错帧这是最头疼的。排查方向分三块速率、干扰、时序。先说速率。SPI信号完整性在高速下会劣化尤其是飞线连接时长线电容、串扰、反射都会造成采样点错误。如果你的SPI时钟已经超过10MHz建议先降到8MHz或6MHz试试。如果错帧率明显下降说明主要是速率问题。解决方法是缩短线长、减少过孔、加33Ω串联电阻、改PCB布线或者继续保持较低速率。再说干扰。伺服控制系统里有IGBT、PWM、电机母线电磁环境恶劣。SPI线上出现毛刺或脉冲会被当成时钟或数据。建议SPI四根线单独走线远离功率线必要时在CS、SCLK线上加RC滤波。我遇到过一种间歇性错帧查到最后是主控板的地平面被功率地挤占了SPI回路地和功率地共用了一段走线IGBT开关噪声从地回路耦合进来导致采样误判。后来把控制地和功率地单点连接、SPI区域单独铺铜问题消失。最后是时序。SPI主机侧采样时刻和从机数据切换时刻太接近也会偶发错帧。这种情况通过调整CPHA可以得到改善或者调低SPI时钟给从机留足数据建立时间。如果用的还是软件拉CS检查拉低CS到首个CLK的间隔是否足够建议至少空出1~2个时钟周期。5.3 PLC侧数据不对先查字节序再查映射PLC侧组态后伺服状态字读出来值怪怪的比如PLC里显示速度给定是负数但伺服实际运行方向正常或者控制字位对不上。这类问题大概率不是通讯链路故障而是字节序或寄存器偏移不对。EtherNet/IP报文里数据通常按大端方式排列而不少主控DSP和Demo寄存器区内部用的小端。把32位速度值塞进寄存器后PLC侧如果按大端去解析看到的就是字节打乱的数。解决办法是在主控侧组装数据域时主动做字节序转换统一成EtherNet/IP协议要求的大端字节序。一般原厂Demo程序的数据解析函数里会有转换示例照着改就行。寄存器映射表也要反复核对。我习惯做一张带偏移地址的Checklist每一项字段长度、单位、类型浮点还是整数、字节序都列清楚。PLC调试时用RTU报文或组态软件的监视界面逐个字段校验至少让PLC和伺服先对上一两个点确认整条链路走通了再讨论优化。5.4 运行一段时间后通讯卡死怎么处理通讯卡死通常伴随INT持续为高或持续为低。原因可能是主控或从机状态机死锁、缓冲区溢出、协议栈异常恢复机制没生效。主控侧状态机必须有超时和错误恢复机制。我已经在4.2里实现了ERROR状态这个状态不是摆设要真正做到连续多次错误后复位外设。复位不是直接重启小板而是把主控侧SPI外设重新初始化然后再通过叀手命令让小板固件重置SPI接口状态。有些Demo程序支持软复位寄存器主控往0x7FF0写入固定值小板固件就会重新初始化通信接口这个方法很好用。从机侧如果固件里协议栈是加密库偶发的内部死锁比较难定位。常见有效措施是让主控侧定时比如每100ms发送一个握手命令如果连续几次得不到应答就强制拉低小板的复位引脚软复位整个小板和协议栈。伺服不允许长时间通讯中断宁可花几十ms复位一次也不能让产线一直停在报警状态。5.5 排查工具与经验速查表这次项目我用的主要工具逻辑分析仪抓SPI四线时序看帧格式是否正常。示波器看信号质量、毛刺、上升下降沿、地噪声。串口调试主控侧和小板侧都留一个调试串口打状态机切换、错误码、寄存器快照。PLC组态软件直接在线监视Input/Output Assembly验证真实数据流。最后把几个高频问题整理成速查表方便现场直接对。现象可能原因快速排查手段完全不通引脚/时钟/电源/从机没启动先量电源和CS再量CLK最后看MISO偶发错帧速率过高/干扰/地线噪声降速率、查地、看波形数据全FFSPI模式不对/CS极性错/线序错查CPOL/CPHA量CS有效电平字节对不上大小端、寄存器偏移对照映射表抓一帧解析字节通信一段时间卡死状态机死锁/从机异常加超时复位机制握手命令检测控制周期被拖垮通讯占用中断时间过长把SPI从高速中断里移出去这套东西做完以后我最大的感触是EtherNet/IP协议栈本身的移植反而是小事方案商Demo给的代码足够你跑通真正考验人的是SPI链路可靠性、寄存器映射的缜密性和状态机恢复能力。伺服这种强实时系统里通讯链路上任何一个不确定性都会被控制算法放大成噪声和抖动所以宁可把应用层协议做重一点CRC、超时、重试、复位都加满也不能图省事只做裸数据传输。至少在我做过的这几块板子上这套SPI帧协议双寄存器区INT握手状态机错误恢复的组合稳定跑了很长时间没再出现过PLC和伺服之间的通讯失联问题。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →