ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

EtherCAT SDO从原理到实战:对象字典、CoE报文与主从站配置

2026/9/10 16:29:35 拓冰建站 浏览量
EtherCAT SDO从原理到实战:对象字典、CoE报文与主从站配置 既然能翻到第10篇说明你已经不是第一次碰EtherCAT了。主站也好、从站也罢前面几篇大概率已经把扫描、PDO映射、DC同步这些基础理顺了。但这会儿你会发现一个绕不开的问题程序跑起来之后想改个参数、读个状态PDO里没有、周期数据里也拿不到怎么办答案就是SDO。SDO全称Service Data Object服务数据对象。它和PDO最大的区别在于PDO是周期性的过程数据是主站和从站之间按固定周期交换的高速数据通道而SDO是非周期的服务数据本质上是主站对从站“对象字典”的随机读写访问。可以这样理解PDO是产线上的传送带是按节拍跑的东西SDO是临时叫来的快递员你需要什么才让他送什么。这篇博文我把SDO从原理到报文、从主站配置到从站固件再到实际踩坑完整拆开讲一遍。无论你是用TwinCAT、CODESYS做控制系统集成还是自己在做从站设备开发这篇文章都会对你有用。1. SDO在EtherCAT里的位置1.1 SDO和PDO的分工刚开始接触EtherCAT的人最容易犯的错就是把SDO和PDO搞混。简单说PDO跑的是过程数据是周期性的数据量小、实时性高。而SDO走的是邮箱通信通道Mailbox是非周期的数据量可以大很多实时性要求低主要用于参数配置、状态读取、固件升级这类操作。从EtherCAT数据链路层来看从站设备里有两类数据通道一类是过程数据通道由同步管理器SMSyncManager来管理比如SM2、SM3分别管输入输出过程数据另一类是邮箱通道通常占用SM0、SM1一个管主站到从站的邮箱写入Mailbox Out一个管从站到主站的邮箱读取Mailbox In。SDO就是挂在邮箱通道上的所以如果从站的XML里没有配置邮箱通道或者从站没有初始化对应SMSDO请求是根本发不到从站里去的。SDO本身不是EtherCAT独有概念它本来属于CANopen协议。EtherCAT里为了兼容CANopen庞大的设备行规和对象字典体系专门定义了一种映射方式叫CoECANopen over EtherCAT。也就是说你在EtherCAT里看到的SDO其实是CoE服务的一种。CoE里面还有其他服务比如紧急事件Emergency、时间戳Time Stamp、SDO请求响应等。所以我们在调试时抓包看到“CoE”开头的报文里面很可能就是SDO数据。1.2 一趟SDO请求要经过哪些环节一个完整的SDO请求从主站发起到从站返回响应中间要经过好几道关卡。我拿实际开发中最常见的一段流程来讲第一步主站软件比如TwinCAT或CODESYS会根据从站的ESI文件XML格式的设备描述文件知道从站的对象字典里有哪些对象以及它们的索引、子索引、数据类型和读写权限。你发出一个读请求其实就是指定索引和子索引让从站把它对象字典里对应位置的值返回来。第二步主站把SDO请求封装在邮箱报文里写入到从站的邮箱输入同步管理器SM0或对应通道。这个邮箱报文自带邮箱头Mailbox Header里面包含长度、从站地址、通道类型、消息类型这里就是CoE等信息。第三步从站的ESCEtherCAT Slave Controller芯片检测到邮箱数据到达会触发一个事件给你从站固件里的协议栈代码。你的固件里必须实现了CoE处理函数它会把邮箱里面的CoE头和数据解析出来判断是SDO Upload还是SDO Download请求然后根据索引和子索引去对象字典里查找对应的条目。第四步从站固件从对象字典里取出数据封装成SDO响应报文放回邮箱输出SM通知主站邮箱数据已准备好。主站下次扫描邮箱输出通道时就会把响应收回去。这个过程看起来不复杂但实际开发中每个环节都可能出问题邮箱通道没配好、CoE头解析错了、对象字典索引写错了、返回数据类型不匹配……后面我会逐个展开。1.3 为什么常用0x6060、0x6040这些“神秘数字”很多刚从PLC转过来做EtherCAT驱动的朋友第一次看到0x6040、0x6060、0x608F这些十六进制索引会觉得莫名其妙。其实没必要背但要理解规律这些索引来自CiA 402标准也就是“驱动器与运动控制设备行规”。只要你的从站设备声称支持CiA 402那么它必须把运行模式、控制字、状态字、目标位置、实际位置等信息放在标准规定的位置上。所以我们在项目中用SDO去读0x6060实际上就是在读“当前运行模式”比如0x01是位置模式、0x02是速度模式、0x03是转矩模式写0x6040子索引0是发控制字比如0x06是使能0x07是使能加运行读0x6041子索引0是状态字状态字里第几位对应“准备好”、第几位对应“使能中”CiA 402状态机里都有明确说法。掌握了这些标准对象之后你会发现SDO读写变成了一件非常“标准”的事——不管换什么牌子的伺服只要它支持CiA 4020x6040、0x6060这些对象永远在那里。2. 对象字典与CoE读SDO之前必须过关的基础2.1 对象字典到底是一张什么表对象字典英文Object Dictionary简称OD你可以把它理解成从站内部一张“寄存器总表”。这张表的一行就是一个对象。对象用16位索引Index来定位索引下面还可以挂子索引SubIndex子索引用8位表示范围从0到255。索引0x1000到0xFFFF子索引从0开始子索引0通常表示这个子索引列表的条目数量或者保留。对象字典里每个条目都有数据类型。常见的有UINT8、UINT16、UINT32、INT8、INT16、INT32、BOOLEAN、OCTET_STRING等等。从站固件在做SDO响应时必须严格按照对象字典里定义的数据类型来打包数据。比如你从站里把0x6060定义成UINT8主站用UINT16去读虽然很多情况下能读到值但严格来说这是一种错误部分主站软件甚至会直接报错。对象字典还有一个重要的属性叫访问权限Access。通常有只读ro、只写wo、可读写rw和常量const。比如设备名称、固件版本这类信息就是只读的而运行模式、目标位置这些就是可读写的。你在主站侧用SDO去写一个只读对象从站会返回Abort报文错误码一般是0x06010000或0x06020000。2.2 CoE的SDO服务究竟有哪几种在CoE协议里SDO服务并不是只有“读”和“写”两种。展开来看有以下几类SDO Download对应主站往从站写数据。这里又分两种快速下载Expedited数据少于等于4字节时用和分段下载Segmented数据超过4字节时用。快速下载时数据直接放在SDO请求报文的数据区里分段下载则要把数据拆成很多段每段都带一个序列号从站每收到一段会回一个“确认”响应拆包组包逻辑都在协议栈里完成。SDO Upload对应主站从从站读数据。和下载一样也分快速上传和分段上传。平时调试时读参数大多走快速上传。如果你要读一个长字符串或一段日志文件才会触发分段上传。SDO Info这是用来读从站对象字典“元信息”的服务。比如你想知道0x6000这个索引下面有几个子索引或者某个对象名称是什么可以通过SDO Info来查。TwinCAT在线对象字典里能列出从站XML里配置的所有对象它就是靠读取从站的SDO Info信息动态构建出来的。在实际开发中95%以上的场景你只需要关心SDO Download和SDO Upload但遇到长数据读写时明白Segmented机制能帮你少走很多弯路。2.3 从站XMLESI文件和对象字典的关系每次做从站配置必然要碰XML文件也就是ESIEtherCAT Slave Information文件。主站软件正是靠解析这个XML才知道从站长什么样。XML里除了包含厂商ID、产品码、版本号这些基础信息还会把整个对象字典写进去每个对象有索引、子索引、名称、数据类型、读写属性、PDO映射能力等。这里要特别提醒做从站开发的朋友从站固件里的对象字典和XML文件里描述的对象字典必须严格一致。如果XML里声明了0x6091是子索引0和1各一个UINT32但从站固件里实际定义的类型不对主站在SDO读写时就会出现数据类型对不上或返回值异常。我在做从站方案时习惯在修改协议栈固件后用SlaveInfo工具或主站软件重新扫描一遍从站对照XML逐项检查对象字典确认所有条目都能正常读出来再进入下一步。3. 主站侧的SDO实操TwinCAT和CODESYS两种常用姿势3.1 TwinCAT里用功能块读写SDO用TwinCAT做EtherCAT主站最常用的SDO读写方式就是调用TC2_System库里的FB_CoeWrite和FB_CoeRead。这两个功能块在开发调试里出场率极高。FB_CoeRead为例需要填写的输入参数有sNetId主站的AMS NetId、nSlaveAddr从站在总线上的地址、nIndex要读的索引、nSubIndex子索引、bExecute上升沿触发执行、tTimeout超时时间。输出参数里有bBusy、bError、nErrId、bValid以及读回来的数据存放在目标变量里。举个实际例子我想在程序运行中读取某台伺服当前的实际位置但PDO里没有映射这个字段就可以在PLC里这样写// 读取0x6064子索引0目标变量为LREAL类型的实际位置 fbCoeRead( sNetId : 192.168.0.10.1.1, nSlaveAddr : 1, nIndex : 16#6064, nSubIndex : 0, bExecute : bTrigger, tTimeout : T#2S, pDestLen : SIZEOF(rActualPos), pDestAddr : ADR(rActualPos) );这里特别提醒几个坑第一sNetId不是从站设备的IP而是TwinCAT主站的AMS NetId在系统托盘TwinCAT图标里能看到第二nSlaveAddr填的是从站在EtherCAT总线里的从站号不是EtherCAT从站的站地址那通常是主站自动分配的第三pDestLen和pDestAddr要跟对象类型匹配类型不匹配轻则读出来乱数重则直接内存错误蓝屏。读SDO还好写SDO建议你加一个“先切状态再写”的思路。很多从站对象虽然标记为可写但实际在运行状态下不允许写必须先把从站切到Pre-OP状态再写写完再切回OP。这一点和后面要说的“SDO写入时机”是关联的。3.2 CODESYS里怎么读写SDOCODESYS做EtherCAT主站时SDO的读写方式略有不同但思路一样。你可以在设备树里直接找到从站双击打开从站配置页面切换到“SDO”标签页手动添加一条SDO写入列表或者用功能块随时读写。CODESYS里最常用的功能块是EthcSdoWrite和EthcSdoRead它们存在于EtherCAT库中。以EthcSdoRead为例你需要提供从站句柄通常用EtherCATSlave输出从GetSlaveInfo获得、索引、子索引、目标缓冲区等参数。还有一种更常见的使用场景在CODESYS的从站配置界面里可以直接在“启动参数”StartUp Parameters里预先配置SDO写入项。也就是说主站在每次建立通信、从站刚进入Pre-OP状态时会自动把这一串SDO命令逐条写进从站。这个功能非常有用适合把一些“上电必须初始化”的参数放在这里比如电机额定电流、电子齿轮比、正反转方向等。这样即使从站断电重启主站重新连接后也能保证配置一致。需要注意启动参数里SDO写入的顺序是严格按照列表从上往下执行的。如果两个对象之间有依赖关系比如要先写电子齿轮比分子才能写分母那就必须把分子写在前面。否则从站可能会报错或写入失败。3.3 一个真实案例把步进电机的脉冲当量写进从站拿我做过的步进电机驱动器方案来说。步进电机的脉冲当量控制说白了就是“每一个脉冲对应工作台移动多少毫米”。脉冲当量不对整个定位精度全完。我们当时的运动机构是丝杠导轨丝杠导程是5mm步进电机步距角1.8度驱动器细分数设为16。那么电机转一圈需要的脉冲数 360 / 1.8 × 16 3200个脉冲。每个脉冲对应的直线位移 5mm / 3200 0.0015625mm。到这里脉冲当量就出来了。但很多步进驱动器不会让你直接写“脉冲当量”而是通过电子齿轮比来设置。电子齿轮比的分子和分母通常放在CiA 402的0x6091齿轮比和0x6090每转指令增量等对象里。我们项目里用的驱动器是支持CiA 402子协议所以我通过SDO写入电子齿轮比相关对象把“每圈脉冲数”设成3200把“每圈位移”设成5000单位0.001mm也就是5mm这样每写一个位置指令对应的实际位移刚好是0.001mm。算完参数之后用SDO写进从站再让驱动器重新上电生效。这里有个细节不同厂家的驱动器对电子齿轮比的定义不一定一样有的直接给比值分子/分母有的给两个独立寄存器。在写SDO之前一定先读一遍0x6090、0x6091和0x6092的当前值确认厂家对这几个对象的定义和位宽再动手计算。我遇到过某品牌驱动器把0x6091当作“电机每转对应的负载位移”它的子索引定义和标准CiA 402不一致如果盲目按通用逻辑写位置精度会差很多。4. 从站侧的SDO实现与常见踩坑4.1 从站固件里SDO请求是怎么处理的如果你是自己做EtherCAT从站不是直接用现成的伺服驱动器那么SDO处理的代码大概率是从SSCSlave Stack Code工具生成的。SSC会根据你勾选的功能生成一套基础协议栈其中包括CoE处理逻辑。它在收到主站SDO请求时会自动解析请求查对象字典生成响应报文。理论上你不需要改动SDO收发核心逻辑只需要把精力放在“对象字典怎么定义”和“对象变更时怎么同步到硬件”上。具体来说你在SSC工具里定义的对象会生成一大张变量表通常放在ObjectList.h和ObjectList.c里。每个对象映射到一个变量比如obj1000就是设备类型obj6060是运行模式。工具帮你做好了SDO读写和这些变量的绑定。当你需要真正控制硬件的行为时一般会在协议栈的“写入对象后回调”或“应用层”里加自己的逻辑。比如0x6040控制字变了你需要在应用层状态机里把它转换成伺服使能信号0x6060运行模式变了你需要在每个周期里改变控制算法分支。很多第一次做从站的朋友拿到SSC生成代码后直接编译烧录然后发现SDO读出来的全是默认值写什么也没反应。大概率就是没有弄清“对象字典里的变量”和“应用层逻辑代码”之间缺少同步处理只改了变量没告诉硬件该干什么。4.2 从站状态机为什么必须在Pre-OP后才能改参数EtherCAT从站有明确的状态机Init、Pre-OP、Safe-OP、OP。每个状态之间的切换都是主站通过写状态机控制字APWR来实现的。和SDO直接相关的是从站只有在进入Pre-OP状态后邮箱通信才会建立起来也就是说只有到了Pre-OPSDO请求才能被正常处理。在Init状态下邮箱通道还没配置好你发SDO是不会有任何响应的。这就回答了很多新手的问题从站在什么状态下可以改SM3同步类型答案是Pre-OP状态之后才有意义。SM3是输出过程数据的同步管理器它的同步类型比如0x0001表示SM-SYNC即SM在收到同步信号后把数据锁存到本地0x0000表示FreeRun即SM在收到数据后直接输出通常是在主站启动配置阶段从Init切换到Pre-OP时由主站下发的。如果你在运行到OP状态后想改SM3的同步类型从站本身也许能接收这个SDO写请求但同步配置已经生效了改了也不一定会重新应用通常需要你把从站切回Pre-OP甚至Init状态再重新激活到OP主站会按新的配置重新下发。所以遇到“改了没反应”时不要急着怀疑SDO有问题先检查当前从站状态。我的建议是涉及SM同步、DC配置、邮箱参数等底层配置的修改全部放在主站启动参数里或者切回Pre-OP后再写。涉及运行参数如位置、速度、转矩的修改则在OP状态用SDO实时写没有问题。4.3 SDO通信也讲究超时和长度SDO通讯虽然是“尽力而为”的非周期服务但它也有严格的时间约束。主站发出SDO请求后一般会等待从站响应等待时间通常由主站软件设定比如TwinCAT默认超时时间可能是3秒CODESYS可配置。如果从站一直没响应主站会报超时错误常见错误代码在TwinCAT是0x1802等待响应超时或类似含义。从站侧也容易出现一个隐蔽问题邮箱缓冲区大小不足。EtherCAT邮箱的最大数据长度一般是固定的比如ET1100从站协议栈默认邮箱大小可能是1500字节或更小。如果你要用SDO分段上传一个大对象比如固件升级数据从站必须支持分段传输。如果协议栈没有正确实现分段处理大对象读取到一半就会卡住或者返回错误。另外SDO的快速传输只能传最多4字节。如果你定义的对象是32位长度没问题如果是64位比如某些位置值是INT64快速传输就不够用必须走分段传输。从站协议栈如果只实现了Expedited没实现Segmented就会出现读取失败。4.4 一个典型的编译报错PIC32从站代码struct错误热词里有一条很典型的从站编译报错..\middlewares\ethercat\pic32 ethercat slave.c(197): error: #136: struct u (untyped)这种报错看起来像是编译器在197行遇到了一个结构体声明不完整或类型未定义。我自己在PIC32上做过从站这个报错的根源一般不是从站协议栈本身坏了而是某个结构体标签被宏展开搞乱了。最常见的原因是你在改成自己的对象字典时不小心把ObjectList里的联合体类型写错了。SSC生成的代码里每个对象都是一个union类型可能由u、i、p等成员构成如果ObjectList.h里有某个对象的数据类型宏没有正确展开编译器就不知道该按什么类型去解释这个联合体成员于是报出struct 这样的错误。排查思路很简单分三步走第一步直接定位到报错的第197行看它引用的是哪个对象或哪个结构体第二步检查ObjectList.h里对应对象的数据类型宏是否被正确定义比如检查是否有重复定义或遗漏include第三步开门见山地说如果之前用SSC工具生成过代码建议将ObjectList.h、ObjectList.c等文件重新生成一遍然后把自己的应用层代码复制回去别手改协议栈生成的头部。此外PIC32的工程路径里有middlewares这个词说明用的是MPLAB Harmony集成框架。Harmony的EtherCAT中间件是有版本要求的如果中间件版本和SSC生成的代码版本不一致也会出现各种奇怪的编译错误。建议把SSC工具版本和Harmony库版本统一能省很多事。5. 从报文角度理解SDO现场抓包分析实战5.1 用抓包工具看SDO请求和响应调SDO最有效的手段就是抓包。TwinCAT里自带Frame View帧视图在System Manager或 TwinCAT XAE里可以直接查看以太网帧分析EtherCAT报文结构。没装TwinCAT的也可以用Wireshark配合EtherCAT解析插件。抓包时有一个技巧先用主站软件在线扫描从站然后在从站参数列表里选择一个对象发起SDO读操作同时在Wireshark里过滤EtherCAT类型报文找到从主站发往从站的邮箱报文。仔细看就能发现SDO请求帧和响应帧的完整结构。5.2 SDO报文里每个字节代表什么含义一个典型的SDO请求报文快速读请求分为以下几个部分CoE Head通常2个字节包含CoE消息类型比如0x02表示SDO请求0x03表示SDO响应。你可以看到在Wireshark里解析出“SDO Request”字样。SDO Header1个字节这里面最关键的是CCS字段Client Command Specifier。请求读数据的命令字是0x40请求写数据的命令字是0x234字节快速写、0x2B2字节快速写、0x2F1字节快速写。响应里对应的SCS字段中读响应命令字是0x434字节快速读响应或0x4B2字节、0x4F1字节写响应则是0x60。然后是Index和SubIndex。Index占2个字节低位在前。SubIndex占1个字节。最后是Data区最多4个字节。比如你要读0x6040子索引0请求报文里SDO Header就是0x40Index是0x40 0x60SubIndex是0x00Data区补4个字节通常填0。从站返回的响应报文SDO Header是0x4F如果数据是1字节或0x4B2字节或0x434字节Data区放实际值。这里分享一个记忆方式十六进制0x40、0x60开头的都是有“读”或“写”动作的命令字而看到0x60开头的响应基本是写操作的成功确认。见到0x80开头的响应就是Abort说明这次SDO请求被拒绝了后面跟着的4字节错误码能告诉你具体原因。5.3 SDO错误码速查表SDO通信失败从站绝大多数情况下会回一个Abort报文错误码4个字节高字节通常是0x06开头表示“对象字典访问错误”类。这里整理一个实用速查表错误码含义处理建议0x06010000不支持对该对象的访问比如写只读对象检查对象访问属性0x06020000对象字典中不存在该对象检查索引子索引是否正确0x06040041对象不能映射到PDO检查PDO映射配置0x06070010数据类型不匹配长度错误检查数据类型和位宽0x06090030超出范围的值检查写入值是否在范围内0x08000000其他错误结合具体对象和应用逻辑排查0x08000020数据无法传输或存储到应用检查从站应用层是否就绪0x08000021由于本地原因导致无法传输检查从站状态和硬件资源排查SDO超时或Abort我的顺序是先看从站是否在Pre-OP及以上状态再看对象字典里索引子索引是否真实存在再看类型是否匹配最后看写入值范围。把这四步走完90%的SDO问题都能定位。6. 这些年在SDO上踩过的坑给你几条实在建议一路下来SDO的原理、配置、抓包、排错基本都覆盖了。最后我把自己这几年在SDO上积累的几个经验浓缩成几条实用建议希望你能少走弯路。第一任何SDO操作之前先确认从站处于正确状态。Pre-OP是分水岭在Pre-OP状态SDO才可用但过程数据还没跑起来调试阶段我习惯先把从站切到Pre-OP把要配置的参数全部写一遍再切回OP避免在运行状态下频繁改对象引发不可预期的问题。第二所有SDO写操作一定要有超时和错误处理。别写“发了就不管了”的代码真实总线上从站可能忙、可能掉线、可能返回Abort你没有错误码就只能两眼一抹黑。第三不要用SDO传高频或大数据量的数据。SDO本身是非周期的太频繁的SDO读写会挤占邮箱带宽从站CPU占用率也会飙升高频数据老老实实用PDO大批量数据用FoEFile Access over EtherCAT更合适。再给你一个最实用的建议调试SDO前先试着用主站软件的“在线对象字典”功能读取0x1000设备类型。这一步能通说明主站到从站的邮箱通道、从站CoE处理、对象字典基础访问都是正常的后面所有问题都能在更高层面去查这一步都不通就不要再纠结你写的某个对象为什么读不到先回去检查邮箱通信和状态机吧。