简介这是一份EtherCAT IGH主站控制汇川SV660N伺服电机的实现代码面向工业自动化领域的嵌入式工程师、运动控制开发人员及PLC集成商解决IGH主站与SV660N驱动器通信协议对接、位置控制模式(PP)下的指令下发与实时反馈等实际问题。压缩包内包含1个C语言源文件sv660n_igh_ecat.c整体仅4KB代码结构紧凑覆盖EtherCAT通信接口初始化、主站参数配置、从站连接建立、发送接收函数封装以及电机控制指令解码等关键环节适合正在搭建EtherCAT主从站项目或调试SV660N伺服驱动的开发者参考。目前已有489人学习下载作为轻量级示例具有较高借鉴价值。通过研读该源码可以快速掌握IGH主站与汇川伺服通信的基本流程了解PP模式下目标位置设定与位置反馈的交互逻辑并从中借鉴网络中断、指令错误、电机故障等异常处理机制为自身项目提供一套可复用、可裁剪的最小实现框架。1. 为什么用 IGH 做 SV660N 控制省掉专用卡后的三个代价当手头只有一台装 Linux 的工控机和一台带 EtherCAT 口的 SV660N 伺服驱动器而没有专用运动控制卡时第一反应往往是去买卡。实际上用 IGH 开源主站加一块普通网卡就能把 SV660N 拖起来以 1ms 周期跑 CSP 位置模式还能让多台 SV660N 共享同一时钟源做同步运动。省钱的代价是要自己啃三块硬骨头内核实时性配置、CiA 402 状态机推进、DC 分布式时钟同步。这篇笔记围绕 IGH 与 SV660N 的组合按环境准备、核心代码、踩坑记录和二次开发四段展开适合在产线设备或实验室平台上自己搭运动控制系统的开发者。目标是让你照着跑通单轴点动再改造成多轴同步。2. 技术底座IGH 主站与 SV660N 的 CoE 协作链路2.1 报文循环EtherCAT 为什么能跑到 1ms 周期EtherCAT 不是传统的以太网请求响应模型它把主站网卡发出的帧做成一个大报文沿途每个从站在帧经过自己时用硬件读取属于自己的子报文数据并插入反馈数据然后帧继续往后传。IGH 主站正是基于这个模型设计的它作为 Linux 内核模块存在负责网卡收发、报文寻址、域管理和分布式时钟计算同时向用户空间暴露一套ecrt_开头的 API。SV660N 在这个链路里是纯从站角色。它不做协议栈决策只在帧到达时把自己的输入 PDO 填进报文、把输出 PDO 从报文里取出来。所谓控制周期就是主站每 1ms 发送一帧SV660N 在同一个周期里完成控制字、目标值的接收和实际速度、实际位置的反馈。IGH 的默认周期取决于你应用层定时循环的间隔通常用 1ms 或 500us这取决于伺服驱动器和负载的响应需求。这里有个容易被新手忽略的事实IGH 主站本身并不强制周期时间真正决定周期的是你在应用层使用定时器或实时线程的频率。主站只负责把你在两次调用之间准备的数据发送出去。所以控制质量的上限取决于你能不能稳定地在每个周期内完成位置运算并调用发送接口。这也是为什么后面要强调实时线程和内核实时性。2.2 对象字典、SDO 与 PDOSV660N 的 CoE 三件套SV660N 通过 CoECANopen over EtherCAT暴露全部控制参数三个最常打交道的机制是对象字典、SDO 与 PDO。对象字典是驱动器的内存表里面有旋转方向、电子齿轮比、加减速时间、当前位置、目标位置等参数每个对象用索引加子索引寻址。SDO 用于低速访问适合配置阶段修改参数或读取诊断信息PDO 则用于周期数据交换把需要每个周期刷新的对象打包进 EtherCAT 帧。以位置控制为例最关键的几个对象是索引子索引意义数据类型0x60400控制字状态机推进用uint160x60410状态字读取伺服当前状态uint160x60600运行模式写 7 表示 CSPint80x607A0目标位置CSP 模式下每周期更新int320x60640实际位置反馈int320x606C0实际速度反馈int32SDO 在 IGH 里既可以用命令行工具ethercat sdo操作也可以在应用代码里调用ecrt_slave_config_sdo预先写值。PDO 则需要提前在从站配置阶段声明映射关系。SV660N 的 ESI 文件里通常会给出默认 PDO 映射常见的是 RxPDO 里放控制字和目标位置TxPDO 里放状态字和实际位置。如果你的项目只需要做最基础的位置控制这套默认映射足够后面章节的代码也基于这套映射展开。2.3 CSP 模式与 CiA 402 状态机控制字怎么一步步把电机转起来SV660N 支持多种运行模式位置控制常用 CSP周期同步位置模式对应 CiA 402 行规里的模式码 7。在 CSP 模式下主站每个周期直接写目标位置 0x607A伺服内部的驱动器按周期插值执行位置环。相比传统的 Profile Position 模式CSP 的优势是位置轨迹完全由主站规划多轴联动时同步性更容易保证。但要让电机真正转起来光写目标位置不够必须先让伺服进入操作使能状态。整个过程依赖 CiA 402 状态机状态转移由控制字 0x6040 驱动状态读取靠状态字 0x6041。常见使能序列是先写 0x0006 进入 Ready to Switch On再写 0x0007 进入 Switched On最后写 0x000F 进入 Operation Enabled。这个序列不能跳步。很多第一次用 SV660N 的开发者直接写 0x000F发现状态字停留在 Switched On 不动原因就是没有先完成前置状态。写每个控制字之后都要读状态字确认到位再推下一步。后面避坑章节会专门说这个翻车现场。3. 环境搭建内核编译、网卡绑定与第一个从站识别3.1 内核准备实时性该做到哪一档IGH 主站以内核模块方式运行它对内核版本有要求但更关键的是实时性。如果只是手动点动普通内核也能跑但控制周期会频繁抖动位置曲线会出现毛刺。做多轴同步时周期抖动直接表现为同步误差所以实时内核几乎是必选项。常见的做法是给内核打 RT-Preempt 补丁或者直接用带PREEMPT_RT特性的发行版内核。我一般会先确认当前内核头文件齐全因为 IGH 编译模块时必须用到与运行内核完全匹配的头文件和生成文件。可以用以下命令准备基础环境sudo apt-get install build-essential sudo apt-get install linux-headers-$(uname -r)这里linux-headers-$(uname -r)由系统自动匹配当前内核版本避免手动查找版本号。装好后可以编译一个简单内核模块验证工具链也可以直接跳到 IGH 编译如果工具链有问题IGH 编译会报错。实时内核改造建议放在 IGH 跑通之后再腾挪否则同时排查两个问题会非常痛苦。3.2 编译安装 IGHconfigure 参数的作用IGH 源码解压后进入主目录打开 README 确认依赖关系。核心命令是 configure、make modules、make install。常见安装前缀是/opt/etherlab它会把ethercat命令行工具和脚本放到独立目录避免和系统工具混在一起。我这里下载的是 1.5.x 系列以你实际拿到的源码目录为准。./configure --prefix/opt/etherlab \ --with-linux-dir/usr/src/linux-headers-$(uname -r) \ --enable-e1000eyes \ --enable-genericyes make modules sudo make modules_install sudo make install参数说明--with-linux-dir指向内核头文件树IGH 编译内核模块时用它来解析内核符号--enable-e1000eyes启用 Intel 千兆网卡驱动支持多数工控机板载网卡属于这个系列--enable-genericyes启用通用网卡驱动它是最后的兜底方案当你手头网卡不在 IGH 内建驱动表里时generic 驱动能通过通用框架收发报文。编译完成后检查/opt/etherlab/lib/modules/$(uname -r)下是否生成了ec_master.ko等模块文件。这一步容易出问题的是make modules_install安装路径与 configure 时不一致导致模块扇出到系统内核目录而不是你预期的位置。用make modules_install并配合--prefix前缀通常会把模块数组放在内核模块树的 extra 目录实际加载时都能找到。3.3 网卡绑定与主站启动IGH 主站模块加载时通过main_devices参数指定使用哪张网卡。如果你的机器有多张网卡这里容易选错建议先用ip link确认网卡名再写。主站启动后该网卡就不再作为普通网络接口使用IP 配置会失效这是正常现象不是故障。sudo /opt/etherlab/sbin/ec_master.sh start sudo modprobe ec_master main_deviceseth0我一般先执行ec_master.sh start它读取配置后加载模块如果没走脚本也可以直接modprobe但要注意对应参数格式。main_devices支持逗号分隔多张网卡比如eth0,eth1用在一条 EtherCAT 总线上还要额外配置--enable-chain之类的拓扑选项初学者先只用一张网卡把 SV660N 直接插在这张网卡上不要接交换机。主站启动后立即打开命令行调试开关这对于找从站问题很有帮助sudo /opt/etherlab/bin/ethercat debug 1调试级别 1 只输出简要错误级别越高报文细节越多。正常情况下先看到主站注册成功再接上 SV660N 的 EtherCAT 线观察是否有报错。3.4 第一个从站识别用 ethercat 命令验证 SV660N接线完成后用命令行工具扫描总线确认 SV660N 是否被主站识别ethercat slave如果返回一行类似0 0:0 PreOp SV660N的信息说明从站已经进入 PreOp 状态等待配置。接下来查 SV660N 的 PDO 映射确认 RDxPDO 配置是否符合预期ethercat pdos -p 0参数-p 0表示从站位置 0。输出会列出 0x1600 和 0x1A00 两个 PDO 下挂的对象。如果这里看不到 0x607A说明 SV660N 当前同步管理器配置不是默认位置控制映射后面写代码时要按实际映射来注册偏移。还可以用 SDO 命令确认 SV660N 的运行模式和厂商信息ethercat sdo -p 0 read 0x6060 0 ethercat sdo -p 0 read 0x1018 10x6060 读出来是当前运行模式码0x1018 子索引 1 是厂商代码。这些信息用于核对后面 C 代码里配置从站时需要填的 vendor ID 和 product code。识别成功环境部分就算完成接下来进入代码拆解。4. 核心代码拆解CSP 模式下位置命令从写入到生效4.1 初始化请求主站、创建 domain、配置 SV660NIGH 提供给用户空间的接口核心对象有三个主站master、域domain、从站配置slave_config。主站指代整个 EtherCAT 总线实例域是一组从站 PDO 数据的集合你把关心的输入输出数据划进同一个域主站会在每个周期用一封报文同时读写这些数据从站配置则声明你要连接的 SV660N 和它的 PDO 布局。初始化代码先从请求主站开始。需要包含 IGH 的头文件并且以 root 权限运行程序因为主站设备节点属于系统级资源#include ecrt.h #include stdio.h #include string.h #include unistd.h #include signal.h #define PERIOD_NS 1000000 /* 1ms 周期 */ #define SV_VENDOR_ID 0x000000be /* 替换成 SV660N 实际 vendor id */ #define SV_PRODUCT_CODE 0x06666000 /* 替换成 SV660N 实际 product code */ static ec_master_t *master NULL; static ec_domain_t *domain NULL; static ec_slave_config_t *sc_sv NULL; int main(void) { master ecrt_request_master(0); if (!master) { perror(ecrt_request_master); return -1; } domain ecrt_master_create_domain(master); if (!domain) { perror(ecrt_master_create_domain); return -1; } sc_sv ecrt_master_slave_config(master, 0, 0, SV_VENDOR_ID, SV_PRODUCT_CODE); if (!sc_sv) { perror(ecrt_master_slave_config); return -1; } return 0; }ecrt_request_master(0)请求编号为 0 的主站对应modprobe时创建的第一个主站。ecrt_master_create_domain创建一个域后续所有 PDO 条目都挂到它下面。ecrt_master_slave_config的参数是别名、从站位置、vendor id、product code前两个用于定位从站后两个用于匹配 SV660N 的 ESI 身份信息。注意 vendor id 和 product code 必须与从站 EEPROM 里一致这个值可以在上一步用ethercat sdo read 0x1018读取核对直接猜会配置失败。4.2 PDO 注册与数据偏移让 0x6040 落进一块内存从站配置完成后要声明 PDO 映射。SV660N 的位置控制基础映射用 0x1600 做输出 PDO、0x1A00 做输入 PDO。输出指主站发给从站的数据输入是从站返回主站的数据。下面的数组用 IGH 提供的宏把对象字典条目挂到 PDO 上static ec_pdo_entry_info_t sv_rx_pdos[] { {0x6040, 0, 16}, /* 控制字 */ {0x607A, 0, 32} /* 目标位置 */ }; static ec_pdo_entry_info_t sv_tx_pdos[] { {0x6041, 0, 16}, /* 状态字 */ {0x6064, 0, 32} /* 实际位置 */ }; static ec_pdo_info_t sv_pdos[] { {0x1600, 2, sv_rx_pdos}, /* sync0 输出 */ {0x1A00, 2, sv_tx_pdos} /* sync1 输入 */ }; static ec_sync_info_t sv_syncs[] { {EC_DIR_OUTPUT, 0, 1, sv_pdos 0}, {EC_DIR_INPUT, 0, 1, sv_pdos 1}, {0xff, 0, 0, NULL} };ec_pdo_entry_info_t的三个字段分别表示索引、子索引、位宽。0x6040 子索引 0 是 16 位控制字0x607A 子索引 0 是 32 位目标位置。ec_pdo_info_t的第一个字段是 PDO 索引第二个是包含的条目数第三个是指向条目数组的指针。ec_sync_info_t用来把 PDO 绑定到同步管理器单元这里的EC_DIR_OUTPUT和EC_DIR_INPUT决定了数据流向。接下来把这张映射表交给主站并把需要访问的对象注册到域里。注册后 IGH 会为每个条目分配一个数据偏移量之后操作 PDO 数据就是操作一块大内存里的对应位置static ec_pdo_entry_reg_t sv_regs[] { {0, 0, SV_VENDOR_ID, SV_PRODUCT_CODE, 0x6040, 0, off_ctrl}, {0, 0, SV_VENDOR_ID, SV_PRODUCT_CODE, 0x607A, 0, off_target}, {0, 0, SV_VENDOR_ID, SV_PRODUCT_CODE, 0x6041, 0, off_status}, {0, 0, SV_VENDOR_ID, SV_PRODUCT_CODE, 0x6064, 0, off_actual}, {} }; ecrt_slave_config_pdos(sc_sv, EC_END, sv_syncs); ecrt_domain_reg_pdo_entry_list(domain, sv_regs);ecrt_slave_config_pdos把前面声明的同步映射写进从站配置。ecrt_domain_reg_pdo_entry_list把我们需要读写的具体对象逐个注册到域中同时把每条目的偏移地址回填给对应变量。这里的off_ctrl、off_target是 unsigned int 类型当整个域的数据块在每次周期交换后刷新时这些偏移就是访问数据的钥匙。偏移量在激活后才是最终值使用时不能提前解引用。4.3 周期循环send、receive 与 DC 同步初始化完成后先激活主站同时启动分布式时钟同步。SV660N 支持 DC 同步激活后从站会同步到主站的参考时钟这样才能保证多轴联动时的相位一致。ecrt_master_activate(master); if (ecrt_master_sync_reference_clock(master)) return -1; if (ecrt_master_sync_slave_clocks(master)) return -1;ecrt_master_activate让主站进入操作状态从站的状态也会被推向 SafeOP 或 OP。sync_reference_clock把主站所在网卡时钟设为参考时钟sync_slave_clocks命令各从站根据测量出的传输延迟修正自己的时钟偏移。这两步缺一不可缺了 DC 的从站会以自身周期运行多轴联动一定会出现累积漂移。周期循环里最关键的是顺序。先发后收还是先收后发取决于你如何管理数据缓冲区。常见做法是提前准备输出数据然后 send再 receive 并处理static uint8_t *pd NULL; pd ecrt_domain_data(domain); while (run) { /* 写入目标位置和控制字到 pd off_xxx */ ecrt_domain_queue(domain); ecrt_master_send(master); ecrt_domain_process(domain); ecrt_master_receive(master); /* 读取状态字和实际位置 */ uint16_t sw EC_READ_U16(pd off_status); int32_t act EC_READ_U32(pd off_actual); clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, next, NULL); next.tv_nsec PERIOD_NS; }ecrt_domain_queue把域数据排到待发送队列ecrt_master_send真正把报文发出去。ecrt_master_receive收帧后ecrt_domain_process把新收到的数据放进域数据块。IGH 的命名容易让人搞混send 之后必须 receivereceive 之后必须 process顺序颠倒会导致数据一直是上一次的值。周期定时用clock_nanosleep定到绝对时间点比nanosleep的 relative 模式漂移小得多。4.4 让电机转0x6060 模式、0x6040 状态机与目标位置写入位置控制完整流程分三步设置模式、推进状态机、周期性写目标位置。模式设置放在初始化阶段用一条 SDO 就行因为运行中不需要频繁切换ecrt_slave_config_sdo8(sc_sv, 0x6060, 0, 7); /* 7 CSP */ecrt_slave_config_sdo8会把 0x6060 的 8 位值在从站配置阶段写进去生效时间在从站切换到 OP 之前。这个接口在 IGH 里属于配置期 SDO 写入放在激活前调用避免在周期循环里做慢速 SDO 通信。状态机推进在进入周期循环后完成标准顺序是先 0x06 再 0x07 最后 0x0F每步之间用状态字确认uint16_t sw EC_READ_U16(pd off_status); if ((sw 0x004F) 0x0000) { EC_WRITE_U16(pd off_ctrl, 0x0006); /* Shutdown */ } else if ((sw 0x004F) 0x0021) { EC_WRITE_U16(pd off_ctrl, 0x0007); /* Switch On */ } else if ((sw 0x004F) 0x0023) { EC_WRITE_U16(pd off_ctrl, 0x000F); /* Enable Operation */ }状态字 0x6041 的低位集合表示伺服的位置状态。0x004F 是状态字最低 6 位中的关键掩码用来识别当前处于哪个状态。每次写入控制字后要等至少一个周期让从站处理再从状态字读取最新的值判断下一步不能连续写多个值而不读取。顺序写错或者跳步SV660N 会直接忽略或者面板报错。状态机进入 Operation Enabled 后每周期直接写目标位置EC_WRITE_U32(pd off_target, target_pos);target_pos 是 int32单位由 SV660N 的位置单位和电子齿轮比决定。比如设定每圈 10000 个脉冲target_pos 每增加 10000 就是一整圈。CSP 模式下主站每个周期都必须更新目标位置而且更新要足够平滑突然跳变会触发驱动器跟随误差报警。5. 避坑与排查SV660N 使能失败、丢帧与不同步5.1 现象 Q1写 0x0F 不使能状态字卡在 Switched On现象应用启动后按顺序写控制字 0x0006、0x0007、0x000F但状态字始终停留在 Switched On 位置电机没有使能声音驱动面板也没有报错。原因SV660N 在操作使能前还需要确认运行模式已设置为 CSP并且部分固件要求控制字写入后等待至少一个同步周期完成状态切换。如果你在初始化阶段用 SDO 写 0x6060 但写得不成功从站会保持之前的模式状态机虽然跑到操作使能但位置模式下电机不会真正通电。解决先用ethercat sdo -p 0 read 0x6060 0确认当前模式值等于 7。再确认控制字的每一步都读取状态字确认后再写下一步。另外 SV660N 对 0x6040 的 bit4 有特殊用法CSP 模式下部分固件要求先置 bit4 再清除一次才能让新目标位置生效这个行为要看你的固件版本建议在文档备注里留一个开关。5.2 现象 Q2ethercat slave 扫不到 SV660N现象网卡已经绑定到 IGHethercat slave只显示主站信息或者直接提示无从站EtherCAT 线序和供电都正常。原因最常见的锅是网卡选错。IGH 的main_deviceseth0绑定了 A 网卡但 SV660N 插在 B 网卡上从站自然扫不到。其次是网卡驱动不在 IGH 编译支持的驱动表里IGH 不会自动fallback到任意网卡它会报出链路层错误。解决先用ethtool -i eth0查看当前驱动名确认在 IGH 的 enabled 列表里。如果不在就重新 configure加上--enable-genericyes再编译。确认绑定关系后用/opt/etherlab/sbin/ec_master.sh start重新启动主站。还有一个隐蔽问题SV660N 通过交换机接入 EtherCAT 会导致非常高的延迟抖动IGH 官方就不建议在网卡和第一个从站之间接任何交换设备直连是最稳的。5.3 现象 Q3多轴不同步位置误差呈现周期性锯齿现象两台 SV660N 同时跟随同一条位置斜坡实测位置误差曲线上有周期性的锯齿波动误差幅值随速度上升而增大。原因DC 同步没有生效。从站没有收到有效的 DC 同步信号时会退化到各自本地时钟周期运行本地晶振之间的微小偏差在每次周期中累积成相位漂移表现出来就是周期性锯齿。IGH 里还会有一个更隐蔽的原因调用了ecrt_master_activate却没有调用ecrt_master_sync_slave_clocks从站虽然处于 OP但时钟从未校准。解决确认在 activate 之后立即执行sync_reference_clock和sync_slave_clocks。再用ethercat dc -p 0查看 SV660N 的 DC 状态重点看时钟漂移是否在微秒级以内。若是漂移仍然很大回编译阶段检查 configure 是否启用了网卡硬件时间戳支持。IGH 对某些网卡驱动使用软件时间戳同步精度会明显下降这种就只能换网卡或者换网卡驱动没有别的后悔药。5.4 现象 Q4PDO 映射报警驱动器拒收目标位置现象程序能进入周期循环但 SV660N 面板报警或者ethercat pdos -p 0显示的实际映射和代码里声明的不一致。原因SV660N 的默认映射和你代码里改写的映射不一致。IGH 在从站进入 SafeOP 时会把你的 PDO 配置下发到从站从站重新映射同步管理器。如果 ESI 文件里的对象字典约束比你的映射更严格例如 0x607A 不允许在 sync0 下从站就会拒绝配置。解决先核对代码里的 RxPDO/TxPDO 与 SV660N 的 ESI 描述是否一致。最简单的方式是用ethercat pdos -p 0在调试阶段读出默认映射然后原样照抄到数组里。如果确认映射没问题还报警就用 SDO 把从站恢复默认参数再重试ethercat sdo -p 0 write 0x1011 1 0x657661730x1011 子索引 1 写特定键值可以触发从站恢复出厂默认重新上电后再加载代码。注意执行这条命令后 SV660N 的节点地址和 PDO 配置都会重置后续还要重新配置不要在生产运行时乱用。6. 二次开发把单轴控制改造为多轴 CSP 同步群控6.1 从单轴到多轴把 PDO 配置抽成数组单轴代码跑通后扩展多轴的第一件事不是复制粘贴整段代码而是把从站配置和偏移管理抽象成数组。多轴场景下每个 SV660N 的 vendor id 相同但 product code 可能由于固件版本不同而有差异所以不能硬编码一个从站配置。我一般按从站位置索引来组织数据用零初始化结构体数组保存每台电机的控制字偏移、目标位置偏移、状态字偏移、实际位置偏移然后在一个循环里完成配置和注册。这样新增一台电机只需要调整总线上的从站数量代码主体不用改。IGH 允许ecrt_domain_reg_pdo_entry_list一次性注册多条从站的条目这是多轴扩展的基础。6.2 多轴周期循环与同步验证周期循环里写多轴目标值时用一个 for 循环遍历轴数组每轴写入各自的 0x607A 和 0x6040。这里要注意的一点是IGH 的域数据块会在ecrt_domain_process后被刷新所以同一次循环里读写所有轴的数据不会存在互相覆盖的问题。真正需要花心思的是目标值生成多轴同步的核心在于所有轴在同一个周期拿到的是同一时刻规划出来的位置而不是各自独立生成的斜坡。同步验证方法将所有轴回零后向每个轴下发完全相同的目标位置斜坡周期记录各自的实际位置 0x6064。两个轴的实际位置差值应当稳定在几个脉冲以内。如果差值随时间缓慢增长说明两轴之间的时钟同步仍有漂移回到 DC 同步配置排查。如果差值呈阶跃式变化大概率是某个从站的 PDO 映射在总线上出现了重新配置。从那以后我每次上电跑多轴程序前都会强制用ethercat dc和ethercat pdos走一遍先确认从站时钟和映射状态再启动应用。这套检查习惯帮我省掉了大量半夜排查同步问题的麻烦也希望帮到你。本文还有配套的精品资源点击获取