从零开发 EtherCAT 主站(十):SDO 参数读写 Demo——SOEM 源码调用链与对象字典 📅 发布时间:2026/9/4 5:34:13 👁 浏览次数: 从零开发 EtherCAT 主站十SDO 参数读写 Demo——SOEM 源码调用链与对象字典文章目录从零开发 EtherCAT 主站十SDO 参数读写 Demo——SOEM 源码调用链与对象字典〇、本篇要解决什么问题一、SDO 与 PDO 的分工30 秒回顾二、对象字典Object DictionarySDO 的寻址地图三、SOEM 源码调用链一个 SDO 读请求是怎么发出去的四、第一个 Demosdo_demo.c五、遇到 wkc ! 1 怎么办——SDO Abort Code 排查六、本篇 Demo 在整体项目中的位置〇、本篇要解决什么问题我们的目标是给电机模组做一个上位机其中一个刚需功能就是在线读写参数上电读取电机型号、固件版本、额定电流配置 PID 增益、加减速时间、电流限幅设置运行模式、目标速度、Home 方式读取报警状态、故障码这些参数不是周期性变化的过程数据而是按索引组织、偶尔才读写、需要明确确认的数据。它们走的通道就是SDO。本篇结束你将拿到一个能跑的sdo_demo实现连接主站 → 扫描从站 → SDO 读额定电流 → SDO 写目标速度 → 退出一、SDO 与 PDO 的分工30 秒回顾维度PDO过程数据SDO服务数据通信时机周期性自动发送非周期按需发起数据类型速度、位置、状态字增益、限位、厂商参数寻址逻辑地址 FMMU对象字典 Index SubIndex传输一个 Frame 遍历所有从站一对一问答请求→响应实时性微秒级毫秒级一句话PDO 是货车车队固定班次拉货SDO 是邮政快递按需寄件、带回执。二、对象字典Object DictionarySDO 的寻址地图SDO 读写的核心单位是对象字典条目用Index SubIndex唯一寻址。常见索引索引含义类型0x1000设备类型只读0x1008制造商设备名只读字符串0x1018标识对象厂商号/产品号/版本复合0x6040控制字 ControlwordR/W0x6041状态字 Statusword只读0x6060运行模式 Mode of OperationR/W0x6075额定电流 Rated CurrentR/W0x60FF目标速度 Target VelocityR/W0x607A目标位置 Target PositionR/W你电机模组的具体参数范围以它的ESI 文件XML或厂商手册为准。SOEM 扫描后会把 ESI 里的 PDO 信息读进ec_slave[].结构中。三、SOEM 源码调用链一个 SDO 读请求是怎么发出去的SOEM 提供了一组封装好的 API我们从最上层往下看ec_SDOread() / ec_SDOwrite() ← 应用层调用本篇用这个 │ ▼ ecx_SDOread() / ecx_SDOwrite() ← 上下文版实现 │ ├── ecx_mbxsend() ← 组 Mailbox 报文并发送 └── ecx_mbxreceive() ← 等待从站响应看一下ec_coe.c里的核心函数原型// SOEM: src/ec_coe.cintec_SDOread(uint16 slave,uint16 index,uint8 subindex,int*psize,void*pdata,inttimeout){returnecx_SDOread(ecx_context,slave,index,subindex,psize,pdata,timeout);}intec_SDOwrite(uint16 slave,uint16 index,uint8 subindex,intpsize,void*pdata,inttimeout){returnecx_SDOwrite(ecx_context,slave,index,subindex,psize,pdata,timeout);}参数含义slave从站号0 起index / subindex对象字典索引与子索引psize读时为「缓冲长度返回实际读到字节数」写时为「写入字节数」pdata数据缓冲timeout超时毫秒返回值为Working Counter为 1 表示主站和从站都成功处理了该请求0 表示从站未响应为 -1 或负数表示超时/错误。四、第一个 Demosdo_demo.c下面是一个完整可编译运行的 SDO 读写 Demo。核心思路初始化 → 扫描 → 读 0x6075额定电流→ 写 0x60FF目标速度。/** * sdo_demo.c —— SDO 参数读写 Demo * 编译: 链接 libsoem与之前 simple_ng 相同的工程配置 * 运行: sdo_demo 网卡名 例: sdo_demo \\Device\\NPF_{...} */#includeethercat.h#includestdio.h#defineEC_TIMEOUTRET1000/* 单次请求超时 ms */intmain(intargc,char*argv[]){uint32 rated_current0;/* 0x6075 额定电流, 单位 mA */int32 target_vel0;/* 0x60FF 目标速度, 单位 rpm */intrdl0,wkc0;if(argc2){printf(用法: sdo_demo 网卡名\n);return-1;}/* 1. 初始化主站网卡 */if(!ec_init(argv[1])){printf(主站初始化失败\n);return-1;}printf(主站初始化成功: %s\n,argv[1]);/* 2. 扫描从站 */if(ec_config_init(FALSE)0){printf(扫描从站失败\n);return-1;}printf(发现 %d 个从站\n,ec_slavecount);for(inti0;iec_slavecount;i){printf( 从站[%d] 厂商0x%04X 产品0x%04X 名字%s\n,i,ec_slave[i].eep_man,ec_slave[i].eep_id,ec_slave[i].name);}/* 3. SDO 读: 0x6075 额定电流 (uint32) */rdlsizeof(rated_current);wkcec_SDOread(0,0x6075,0x00,rdl,rated_current,EC_TIMEOUTRET);if(wkc1)printf(SDO 读成功: 额定电流 0x6075 %u mA (读到 %d 字节)\n,(unsigned)rated_current,rdl);else{printf(SDO 读失败 wkc%d\n,wkc);ec_SDOerror(0);/* 打印 Abort Code */}/* 4. SDO 写: 0x60FF 目标速度 (int32) 1000 rpm */target_vel1000;wkcec_SDOwrite(0,0x60FF,0x00,sizeof(target_vel),target_vel,EC_TIMEOUTRET);if(wkc1)printf(SDO 写成功: 目标速度 0x60FF %d rpm\n,(int)target_vel);else{printf(SDO 写失败 wkc%d\n,wkc);ec_SDOerror(0);}return0;} 小技巧读取字符串对象如 0x1008 设备名时psize先传缓冲大小读完rdl就是实际字符串长度记得给缓冲末尾补\0。五、遇到wkc ! 1怎么办——SDO Abort Code 排查当 SDO 请求被从站拒绝时SOEM 会记录一个Abort Code用ec_SDOerror()打印。常见错误码Abort Code含义处理0x06010000索引不存在查 ESI/手册确认索引0x06090011子索引不存在确认 SubIndex 范围0x06020000对象不支持读写该对象只读/只写0x06040041对象不可映射到 PDO用 SDO 而非 PDO0x06070012长度不匹配检查字节序/数据类型0x08000000一般错误查从站具体实现0x08000020数据无法传输/存储检查从站状态工程经验排错顺序永远是「索引 → 子索引 → 类型长度 → 从站状态」。先在slaveinfo例程里确认从站对象字典再动代码。六、本篇 Demo 在整体项目中的位置上位机 UI │ 参数浏览界面 ▼ 参数服务层 ← 封装 ec_SDOread/ec_SDOwrite │ ▼ SOEM 主站Mailbox 通道下一篇我们进入最核心的 PDO 实时控制 Demo让电机模组真正转起来。下一篇预告《从零开发 EtherCAT 主站十一PDO 实时控制 Demo——从 IOMap 到周期通信让电机模组转起来》