ARTICLE DETAIL

资讯详情

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

EtherCAT PDO映射配置实战:从ESI静态配置到SDO动态修改

EtherCAT PDO映射配置实战:从ESI静态配置到SDO动态修改 调试EtherCAT主站和从站时绕不开的一个环节就是PDO映射。前几天调一套多轴设备主站用IGH跑在工控机上从站是一台带增量编码器的伺服驱动器。把从站拉到OP状态后速度实际值怎么读都是0位置数据能读回来但完全对不上。查了接线、查了同步、查了伺服参数最后发现根因特别基础——PDO映射里压根没把0x606C速度实际值和0x6041状态字放进去。出厂默认的TxPDO只映射了位置相关对象目标数据根本没进过程数据区。EtherCAT再快PDO里没有这个对象数据也“搬”不过来。PDO映射配置这事说大不大说小不小。不懂原理的人靠试错懂原理的人靠一张映射表。这篇文章把我实际调试中用到的方法完整拆一遍第一种是在从站XML/ESI文件里静态配置映射适合量产和固定方案第二种是通过主站代码在运行时动态修改映射适合在线调试和兼容多种从站。两种方法都会给代码和XML示例最后把容易踩的坑和排查经验一并整理出来。无论是正在做EtherCAT从站开发还是用IGH、TwinCAT等主站调伺服或IO站这篇文章都值得先收藏。1. PDO映射的基础认知从对象字典到过程数据1.1 对象字典每个从站都有一本“货架台账”理解PDO映射之前先得说清楚对象字典Object Dictionary。EtherCAT从站内部不是一个“黑盒”它所有可以被外部访问的参数、状态量、控制量都挂在对象字典里。每个对象用一个16位的索引表示比如0x6040是控制字0x6041是状态字0x6064是位置实际值0x606C是速度实际值如果对象下面还有子条目就再加一个8位的子索引。可以把对象字典想象成超市的货架台账。每一种货物都固定摆在某个货架号的某个层板上管理员通过“货架号-层板号”就能找到它。但是在实际配送时不可能每送一件货就跑一趟仓库那样效率太低。SDO通信就是这种“点对点、跑一趟取一件”的方式适合配置参数、读写低频数据PDO通信则是“定时班车”按固定的座位表把指定的货物批量装车送走适合周期性的实时控制数据。PDO映射要解决的就是“班车座位表”怎么排的问题。1.2 TxPDO和RxPDO数据流向别搞反PDO分为两大类RxPDO主站发给从站的数据方向是“主站 - 从站”映射参数索引范围是0x1600到0x17FF。TxPDO从站发给主站的数据方向是“从站 - 主站”映射参数索引范围是0x1A00到0x1BFF。记忆方法很简单T可以理解成“To Master”x表示transmit也就是从站发出去的那一路。很多人在配置时把方向搞反导致控制字写不进去或者状态字读不到先把这个方向感建立起来后面所有配置都不会乱。1.3 映射条目一条映射由三个字段构成每一路PDO映射表里包含若干个映射条目每个条目是一个32位的值结构非常固定高16位对象索引表示要映射的是哪个对象。中间8位子索引表示该对象下的第几个子条目。低8位位长度表示这个对象在过程数据里占多少位。举例来说0x60400010这个映射条目高16位是0x6040中间8位是0x00低8位是0x10即16位含义是“把控制字对象0x6040的子索引0占16位放进过程数据”。0x60640020则表示0x6064位置实际值子索引0占32位。映射表本身的存放规则是子索引0保存映射条目的数量子索引1、2、3……依次保存每个映射条目。比如0x1A00的子索引0等于2子索引1等于0x60640020子索引2等于0x60410010含义是“我的TxPDO包含两个对象32位的位置实际值和16位的状态字”。有个细节容易被忽略位长度是“位”的单位不是字节。比如32位加16位加16位总长是64位正好8字节对齐但如果是32位加16位加8位总长只有56位放到DPRAM里实际上要占7个字节存的时候是按位连续排列的最后再按字节边界看。这个“位”和“字节”的差别在静态配置计算DPRAM偏移时经常出问题。2. 方法一通过XML/ESI文件静态配置PDO映射2.1 静态配置适合什么场景如果从站是量产设备或者项目的工艺方案很固定不需要在运行中频繁改数据内容那么在从站的ESI文件里把PDO映射固定下来是最省事的方式。主站加载ESI文件后就能提前知道这个从站支持哪些PDO、每个PDO里装着什么对象、各占多少位。对主站来说相当于拿到了一张“完整座位表”不需要再通过额外的逻辑去动态猜。静态配置的缺点是改起来麻烦。一旦现场需求变了比如原来只读位置现在还要读速度就得重新改XML、重新生成固件、重新更新EEPROM。如果现场有几十种不同型号的从站维护一堆XML文件本身也是一件烦心事。2.2 ESI文件中与PDO映射相关的节点ESI文件本质上是一个XML文件里面记录了从站的厂商信息、对象字典、PDO映射、同步管理器等。直接修改PDO映射时核心关注的是EtherCATInfo节点下与RxPDO和TxPDO相关的部分。一个典型的TxPDO节点结构如下RxPdo Fixed0 Sm2 Index0x1600/Index NameRxPDO 1/Name Entry Index0x6040/Index SubIndex0x00/SubIndex BitLen16/BitLen NameControlword/Name DataTypeUNSIGNED16/DataType /Entry /RxPdo上面的例子定义了一个RxPDO索引是0x1600挂在同步管理器SM2下面里面映射了控制字0x6040占16位。如果是TxPDO节点写成TxPdoSm通常对应SM3。注意Sm这个属性它决定了这个PDO走的是哪条同步管理器通道后面分配PDO时还要用到。2.3 实操案例给GD32F527从站追加速度反馈映射我手头有个基于GD32F527的EtherCAT从站工程默认的TxPDO0x1A00只映射了0x6064位置实际值32位。现在要把0x606C速度实际值和0x6041状态字也加进去目标是让主站每个周期都能拿到位置、速度、状态三样数据。修改后的TxPDO节点大概长这样TxPdo Fixed0 Sm3 Index0x1A00/Index NameTxPDO 1/Name Entry Index0x6064/Index SubIndex0x00/SubIndex BitLen32/BitLen NamePosition Actual Value/Name DataTypeUNSIGNED32/DataType /Entry Entry Index0x606C/Index SubIndex0x00/SubIndex BitLen32/BitLen NameVelocity Actual Value/Name DataTypeUNSIGNED32/DataType /Entry Entry Index0x6041/Index SubIndex0x00/SubIndex BitLen16/BitLen NameStatusword/Name DataTypeUNSIGNED16/DataType /Entry /TxPdo这里有几个地方要特别提醒Entry的排列顺序就是过程数据里的排列顺序排第一的放在偏移0的位置排第二的紧跟其后。BitLen必须是对象实际长度写错会导致后续所有条目错位。有些从站的XML里还会带一个SmOffset或者偏向于DPRAM地址偏移的字段如果没有就按映射表排列顺序连续存放。2.4 DPRAM偏移怎么算按位累加按字节对齐EtherCAT从站控制器ESC内部有一块DPRAMSM2和SM3通道的数据就存在这块区域里。PDO映射表决定了哪个对象的哪一位数据放在DPRAM的哪个偏移位置。偏移的计算规则很简单按位累加。以刚才的TxPDO 0x1A00为例第一条0x6064位长度32位起始偏移0位。第二条0x606C位长度32位起始偏移32位。第三条0x6041位长度16位起始偏移64位。整个TxPDO总长度 32 32 16 80位即10字节。如果最后一个对象的长度不是字节对齐比如32位加16位加8位总长56位但DPRAM实际占用是按字节来的会占8个字节56位向上对齐到8字节多出来的高位部分补0即可。计算偏移时最好统一用“位”来算最后再换算成字节长度配置到同步管理器里。很多数据错位问题根源就是有人把位长度当字节长度用32位当32字节用一错错一年。2.5 改完XML必须同步更新EEPROM/SII这是静态配置里最容易踩的坑。有的工程师改完XML主站重新加载ESI发现过程数据还是老样子就开始怀疑主站或者从站有问题。其实很多时候是因为从站EEPROM也叫SIISlave Information Interface里保存的PDO映射信息和XML不一致。从站上电后主站加载XML描述文件但很多从站的启动流程还会从EEPROM里读取对象字典初始值其中就包括0x1600、0x1A00这些映射参数。如果EEPROM里存的是旧映射XML里写的是新映射两边对不上轻则数据错乱重则从站直接报错。如果你用的是SSC工具生成从站代码改完XML后记得做一次EEPROM更新操作把新的映射表烧进从站。如果你和我一样习惯手改XML改完之后也要重新生成SII固件并更新EEPROM。反过来也要注意有些从站设计成“启动时EEPROM覆盖XML”这种情况你怎么改XML都不生效必须直接改EEPROM。调试前先确认你的从站属于哪种行为能省大量时间。3. 方法二通过主站代码在运行时动态配置PDO映射3.1 动态配置能解决什么在线调试的时候最烦的就是改一次映射就要重新烧一次从站固件。尤其是手头有多种不同厂商的从站每种从站支持的PDO内容还不一样主站代码不可能每换一种设备就重新编译一次。动态配置PDO映射的价值就是从站算法和硬件不用动主站在运行时通过SDO修改映射表让从站按新的“座位表”装载数据。IGH主站里最常用的两种做法是一种是在初始化阶段用ecrt_slave_config_pdos直接指定PDO配置这种方式代码可读性好但本质上是编译期固定适合项目定型后使用另一种是在运行时用SDO去写0x1600、0x1A00映射表和0x1C12、0x1C13分配表真正实现“运行中动态切换”。下面重点讲第二种。3.2 动态配置的标准流程无论从站是STM32、GD32还是专门的EtherCAT从站芯片动态配置的流程都是一样的分六步将从站切换到PreOP状态。OP状态下改映射是绝对禁止的很多从站直接返回SDO错误。通过SDO把映射表的子索引0清零也就是先把旧的映射条目清空避免残留数据干扰。依次通过SDO写入映射表的子索引1、2、3……每一条写入一个32位的映射条目值。把映射表的子索引0改写成实际映射条目数量。如果需要调整PDO分配再通过SDO写0x1C12RxPDO分配和0x1C13TxPDO分配把0x1600/0x1A00挂到对应的同步管理器下。重新将从站切到OP状态新映射生效。这里的第5步特别容易被忽略。很多人以为写了0x1600映射表就完事了其实0x1600只是定义了“PDO里装哪些对象”0x1C12/0x1C13才是决定“这个PDO是否参与本轮过程数据通信”的分配表。映射表和分配表的关系可以理解为“座位表”和“这辆班车是否发车”的关系。座位排好了但班车没排进发车表数据照样不会走。3.3 IGH主站代码示例一结构体静态配置在IGH中最典型的基础配置是先用结构体定义PDO条目再配置到从站上。这种方式适合项目方案固定的场景ec_pdo_entry_info_t servo_pdo_entries[] { {0x6040, 0x00, 16}, // 控制字 {0x6060, 0x00, 8}, // 运行模式 {0x60FF, 0x00, 32}, // 目标速度 }; ec_pdo_info_t servo_pdos[] { {0x1600, 3, servo_pdo_entries}, }; ec_sync_info_t servo_syncs[] { {0, EC_DIR_OUTPUT, 0, NULL}, {1, EC_DIR_INPUT, 0, NULL}, {2, EC_DIR_OUTPUT, 1, servo_pdos}, {3, EC_DIR_INPUT, 0, NULL}, {0xff} };然后在主从站配置时调用ec_slave_config_t *sc ecrt_slave_config_new(slave); if (!sc) return -1; if (ecrt_slave_config_pdos(sc, EC_END, servo_syncs)) { // 配置失败处理 }这种写法清晰直观但有一个隐含问题PDO映射在编译期就固定了运行时想改只能改代码重新编译。如果从站类型很杂代码里会出现一堆结构体维护成本随之上升。3.4 IGH主站代码示例二SDO运行时修改映射真正能“运行时动态配置”的写法是用SDO直接操作映射对象。下面的例子演示了如何把一个“控制字运行模式目标速度”的RxPDO映射动态写入从站// 假设变量sc是已经创建好的从站配置句柄 int ret; // 第1步清空旧映射 ret ecrt_slave_config_sdo32(sc, 0x1600, 0, 0, 500); if (ret) return ret; // 第2步写入映射条目 ret ecrt_slave_config_sdo32(sc, 0x1600, 1, 0x60400010, 500); if (ret) return ret; ret ecrt_slave_config_sdo32(sc, 0x1600, 2, 0x60600008, 500); if (ret) return ret; ret ecrt_slave_config_sdo32(sc, 0x1600, 3, 0x60FF0020, 500); if (ret) return ret; // 第3步设置映射数量 ret ecrt_slave_config_sdo32(sc, 0x1600, 0, 3, 500); if (ret) return ret; // 第4步把0x1600挂到RxPDO分配表 ret ecrt_slave_config_sdo32(sc, 0x1C12, 1, 0x1600, 500); if (ret) return ret; ret ecrt_slave_config_sdo32(sc, 0x1C12, 0, 1, 500); if (ret) return ret;写入的数值含义要再强调一遍0x60400010就是“对象0x6040子索引0位长度0x10即16位”。0x60FF0020表示“对象0x60FF子索引0位长度0x20即32位”。如果写错一个位的长度从站不会报错但数据会错位。写这个代码时建议在关键SDO调用之间加日志输出把每条写入的返回码打出来这样能快速定位是哪一步失败。TxPDO的配置完全同理把0x1600换成0x1A00把0x1C12换成0x1C13即可。例如往0x1A00里写0x60640020位置实际值32位和0x60410010状态字16位就是主站能周期性读到这两个数据。3.5 动态配置的几个重要限制动态配置虽然方便但也有几个限制必须说清楚。第一必须在PreOP状态修改。从站在OP状态下会按固定周期发过程数据此时改映射表会导致报文内容混乱大部分从站会直接拒绝修改。IGH里触发状态切换时要保证这个流程是“OP - PreOP - 修改映射 - 重新OP”。第二同步管理器长度要匹配。这里有个容易被忽视的问题0x1600映射表改了SM2通道的长度不会自动跟着变。如果主站初始化时已经按旧映射锁定了SM长度而新映射的数据长度更大就可能出现过程数据被截断。某些从站允许映射后重新初始化同步管理器有的则严格要求XML里声明最大长度。我踩过这个坑当时压入一个目标速度后控制字和模式都对的但速度值始终传不过去排查半天才发现是SM2接收长度没更新。第三运行中频繁切换映射并不可取。每次动态切换都需要从站退出OP再重新进入周期要花几十毫秒甚至更多。如果是伺服控制这类对连续性要求很高的场景不建议在运行中来回切映射尽量在启动阶段完成配置。4. 静态配置和动态配置怎么选4.1 两种方式对比表为了方便选型我把两种配置方式的差异整理成一张表对比项XML静态配置代码动态配置修改时机编译/出厂前固定运行时在线修改主站代码复杂度低加载XML即可中高需要写SDO配置逻辑从站固件影响需要改XML并更新EEPROM不需要动固件适合阶段量产设备、方案固定项目调试、多从站兼容维护成本XML多时管理麻烦逻辑统一新增从站一般改表即可风险等级改错XML容易数据错乱映射表写错同样数据错乱灵活性低高没有绝对的好坏只看场景。量产设备建议用XML静态配置把映射在出厂前就定好现场少折腾做设备集成或项目调试强烈建议用代码动态配置因为现场五花八门的从站你根本猜不到它内部默认映射是什么统一用SDO写一遍最稳妥。4.2 常见误区PDO映射和PDO分配别搞混这里必须单独拉出来讲一下因为太多人把PDO映射和PDO分配当成一回事。PDO映射0x1600/0x1A00定义的是“这个PDO里装哪些对象”回答的是“车上坐谁”的问题。PDO分配0x1C12/0x1C13定义的是“这个PDO是否被分配到某个同步管理器通道下参与过程数据交换”回答的是“这辆车是否在今天的发车班次里”。两者关系是先有映射再有分配。只改映射不分配过程数据里根本看不到这个PDO只分配不映射等于发了一趟空车里面什么都没有。调试时如果发现过程数据长度对不上第一步就检查0x1C12/0x1C13的分配表这比去查映射表更快。4.3 我个人的选型习惯我现在的做法是“静态打底动态兜底”。从站的XML里把最常用的一组映射配好保证最坏情况下主站不写任何SDO也能正常通信。然后再在主站代码里留一个动态配置接口用于现场需要扩展数据时覆盖默认映射。这样量产时无缝衔接调试时又能灵活调整。代码里统一用一个映射表结构体来管理新增从站只改表不改逻辑维护起来轻松很多。5. 实际调试中的坑与排查思路5.1 高频问题速查表下面这些问题是EtherCAT调试群里问得最多的我直接整理成速查表现象可能原因排查方向进入OP后状态字全为0控制字或状态字没映射进PDO检查0x1600/0x1A00映射表数据错位读到的位置值实际是速度值映射条目位长度错误或顺序错误逐条核对映射条目的位长度改了XML但过程数据不变EEPROM里的映射没有同步更新重新生成并烧录EEPROM过程数据长度比预期短SM通道长度小于映射总长度检查SM2/SM3配置长度运行中切映射后报Sync Error在OP状态修改了映射切回PreOP再改0x1C12分配表写入失败从站不支持动态PDO分配查从站手册确认是否支持可变映射5.2 排查顺序建议面对“数据能通但内容不对”这类问题我一般按这个顺序排查基本百发百中先看同步管理器配置确认SM2/SM3的方向、长度是否和映射表匹配再看映射表0x1600/0x1A00里的映射数量和映射条目逐条解析成“对象索引子索引位长度”打印出来人工核对然后看0x1C12/0x1C13分配表确认0x1600/0x1A00确实被分配到了对应通道最后用EtherCAT抓包工具或者IGH主站日志抓一两帧过程数据和从站实际发送的数据对比看看错位发生在哪个字节。很多问题查到第三步就已经水落石出了根本不需要抓包。真正需要抓包的往往是SM长度和映射长度都对不上或者有第三方设备在总线上捣乱。5.3 一个能救命的小工具映射表回读函数最后分享一个我一直在用的调试技巧。写主站代码时我都会加一个“映射表回读”函数把总线上所有从站的0x1600、0x1A00、0x1C12、0x1C13全部读出来然后以“索引:子索引 值”的格式打印。配置完成后先回读一次确认和预期一致再进OP。这个习惯帮我省过太多时间了。很多时候你以为自己写进去的映射是对的实际上某个SDO调用超时了、被拒绝了或者从站把子索引0清成了0你还在傻傻等数据。回读一次一眼就能看出问题在哪。PDO映射这种事查错不如防错代码里多写一个回读函数比任何调试工具都实在。
返回列表