简介:这份资源面向嵌入式工程师、运动控制开发者与高校自动化方向学生,提供将开源SOEM协议栈移植到STM32平台、构建EtherCAT主站的完整工程源码,可用于与伺服电机等从站设备通信、验证PDO与SDO数据交互,并在此基础上开发电机控制等上层应用。压缩包共267个文件,约1.63MB,以139个.h头文件与119个.c源文件为主体,涵盖STM32F7系列HAL库驱动、以太网MAC与PHY配置、SOEM协议栈适配及RTOS集成代码,另含uvprojx与uvoptx工程文件、hex烧录固件、s汇编启动文件及bat编译脚本,可直接用Keil打开编译。资源已有18343人学习下载,热度较高。读者可从中获得主站移植的目录组织与初始化流程、实时性优化与中断优先级配置思路、通信测试与问题调试方法,以及一份可复用的工程模板,降低EtherCAT主站开发门槛。
1. 从站买得起、主站卡脖子:为什么我选 STM32 + SOEM 做 EtherCAT 主站
做运动控制项目的人多半遇到过这个局面:伺服驱动器、IO 模块、编码器从站一抓一大把,价格也压得下来,真正难搞的是主站。商用主站授权按节点收费,一套下来比整套从站还贵;用 PC 加实时补丁跑,抖动一上来位置环就抖,现场调试时那种玄学丢帧能耗掉一整周。这个方案要解决的就是这件事——用一颗 STM32 加开源 SOEM 协议栈,把 EtherCAT 主站做出来,跑通周期通信、过程数据映射和伺服使能流程,成本压到一块主控板,适合做毕设、课设,也适合小批量设备自研主站的团队直接改。
选型上有个反直觉的点:很多人以为主站必须上 Linux 加网卡,其实 EtherCAT 主站的核心是「按周期收发以太网帧 + 维护状态机」,STM32 的 ETH 外设加 DMA 描述符完全扛得住 1ms 甚至更短的周期,前提是把 SOEM 的裸机移植做干净。下面按需求、架构、移植、伺服对接、避坑、验收的顺序,把整套方案摊开讲。
2. 需求拆解与技术选型:主站到底要满足哪几个硬指标
2.1 先定周期和节点数,再谈芯片
EtherCAT 主站的性能不是看主频,是看三个数:通信周期、从站节点数、过程数据量。我一般先跟需求方确认这三项,再倒推硬件。典型的中小型设备场景是 1ms 周期、8 到 16 个从站、每个从站几十字节 PDO,这个量级 STM32F407 或 F767 的 ETH 外设配合 DMA 足够,帧在物理层收发由硬件完成,CPU 只处理协议栈和状态机。
如果周期要压到 250us 以下,或者节点超过 32 个,就得考虑 F7/H7 系列,或者把 PDO 处理拆到独立任务里。选型时容易翻车的是只看主频不看 ETH 外设:部分 STM32 型号的 ETH 只有 MII 没有 RMII,或者 DMA 描述符数量受限,这些在数据手册里要逐条核对,别等板子打回来才发现。
2.2 SOEM 为什么适合裸机移植
SOEM 是开源 EtherCAT 主站协议栈,C 语言写的,不依赖操作系统,核心是 ecx_ 系列接口。它的分层很清楚:底层是网卡收发回调,中间是 EtherCAT 状态机和邮箱协议,上层是过程数据读写。移植到 STM32 只需要实现网卡收发和定时器两个钩子,其余逻辑原样编译。
相比自己从零写 EtherCAT 帧解析,SOEM 省掉的是最耗时的部分——状态机转换、邮箱通信、CoE 对象字典访问这些都有现成实现。代价是要理解它的调用时序,尤其是 ecx_config_init 和 ecx_config_map_group 这两步,顺序错了从站进不了 OP 状态。
2.3 硬件与工具链清单
| 项目 | 选型 | 说明 |
|---|---|---|
| 主控 | STM32F407/F767 | 带 ETH MAC,RMII 接口 |
| PHY | LAN8720 或 DP83848 | RMII 模式,25MHz 时钟 |
| 协议栈 | SOEM | 裸机移植,去掉 OS 相关层 |
| 开发环境 | Keil / STM32CubeIDE | 生成 ETH 初始化代码 |
| 调试 | 示波器 + Wireshark | 抓 EtherCAT 帧看时序 |
工具链上,CubeMX 负责生成 ETH 和时钟初始化,SOEM 源码手动加入工程。Wireshark 装 EtherCAT 解析插件后能直接看帧结构,调试阶段比串口打印管用得多。
3. 总体架构设计:三层结构与四个核心模块的职责划分
3.1 分层结构
整套主站分三层。最底层是硬件抽象层,负责 ETH 外设初始化、DMA 描述符管理、收发中断;中间是 SOEM 协议栈层,处理 EtherCAT 帧组装解析、状态机、邮箱通信;最上层是应用层,做 PDO 映射、伺服使能、位置速度指令下发和状态回读。
三层之间靠两个接口连接:硬件层给协议栈提供 ecx_recv 和 ecx_send 回调,协议栈给应用层提供 ec_slave 数组和 IOmap 指针。这个划分的好处是换 PHY 或换 STM32 型号时只动硬件层,应用逻辑不用改。
3.2 四个核心模块
第一个是网卡收发模块,用 ETH DMA 做零拷贝收发,收到帧后直接交给 SOEM 解析。第二个是定时器模块,用一个硬件定时器产生周期中断,每个中断触发一次主站循环。第三个是状态机管理模块,负责 INITCHECK 到 OP 的转换和异常回退。第四个是 PDO 映射模块,把从站的过程数据映射到本地缓冲区,应用层读写这个缓冲区就等于读写从站。
3.3 主循环时序
主站循环跑在定时器中断或主循环里,顺序是:收帧、解析、执行状态机、组帧、发帧。这个顺序不能乱,先收后发保证响应及时。周期时间由定时器决定,SOEM 本身不管周期,周期是应用层给的。
/* 主站周期任务,由 1ms 定时器中断触发 */ void ecat_cycle_task(void) { /* 1. 接收从站返回的帧 */ ecx_receive_processdata(&ctx, EC_TIMEOUTRET); /* 2. 应用层读写 IOmap,即过程数据 */ app_pdo_exchange(); /* 3. 发送下一周期帧 */ ecx_send_processdata(&ctx); }这段是主站循环的骨架。ecx_receive_processdata 把从站数据搬进 IOmap,app_pdo_exchange 做业务处理,ecx_send_processdata 把输出数据发出去。EC_TIMEOUTRET 是接收超时,一般给 2000us,太小会误判丢帧,太大会拖慢周期。
4. SOEM 裸机移植:网卡驱动与定时器的两个关键钩子
4.1 网卡收发回调实现
SOEM 移植的核心是实现两个函数:一个负责把帧发出去,一个负责把收到的帧取回来。STM32 这边用 ETH DMA 的发送描述符和接收描述符,发送时把 SOEM 给的缓冲区拷进发送描述符,接收时从接收描述符取帧。
/* SOEM 发送回调:把帧写入 ETH 发送描述符 */ int stm32_send_frame(void *ptr, int len) { /* 等待发送描述符可用 */ while (ETH->DMASR & ETH_DMASR_TBUSY); /* 拷贝数据到发送缓冲区 */ memcpy(tx_buffer, ptr, len); /* 设置描述符长度并触发发送 */ tx_desc->ControlBufferSize = len; tx_desc->Status |= ETH_DMATXDESC_OWN; /* 启动发送 */ ETH->DMATPDR = 0; return len; }发送回调里关键是等 TBUSY 标志,描述符没空就发会丢帧。接收回调类似,从接收描述符读帧,判断是不是 EtherCAT 帧再交给 SOEM。这里有个细节:EtherCAT 帧的以太网类型是 0x88A4,非这个类型的帧直接丢弃,别往协议栈里塞。
4.2 定时器与周期控制
周期控制用一个硬件定时器,1ms 中断一次,中断里调 ecat_cycle_task。定时器优先级要高于其他中断,否则周期会被打断。我一般把定时器中断优先级设成最高,ETH 收发中断次之,串口等调试中断最低。
/* 定时器中断服务函数,1ms 触发一次主站循环 */ void TIM3_IRQHandler(void) { if (TIM3->SR & TIM_SR_UIF) { TIM3->SR &= ~TIM_SR_UIF; /* 触发主站周期任务 */ ecat_cycle_task(); } }周期抖动主要来自中断延迟和帧处理时间。如果发现周期不稳,先看定时器中断有没有被高优先级中断抢占,再看 ecx_receive_processdata 的耗时。帧处理时间超过周期一半就要考虑优化或降节点数。
4.3 状态机启动流程
上电后主站要按顺序走:ecx_init 初始化、ecx_config_init 扫描从站、ecx_config_map_group 映射 PDO、然后逐级把从站状态推到 OP。每一步都有超时,超时就回退重试。
/* 主站启动流程 */ void ecat_startup(void) { /* 初始化 SOEM 上下文 */ ecx_init(&ctx, ifname); /* 扫描从站,返回从站数量 */ if (ecx_config_init(&ctx) <= 0) { /* 没扫到从站,检查接线和 PHY */ return; } /* 映射过程数据到 IOmap */ ecx_config_map_group(&ctx, &iomap, 0); /* 逐级推进从站状态到 OP */ ecx_statecheck(&ctx, 0, EC_STATE_OP, EC_TIMEOUTSTATE); }ecx_config_init 返回从站数量,返回 0 说明物理层没通,先查网线和 PHY 时钟。ecx_config_map_group 把 PDO 映射到 iomap,应用层通过 iomap 偏移访问各从站数据。状态推进到 OP 后从站才开始周期通信。
5. 伺服电机对接:PDO 映射与使能流程的实操细节
5.1 PDO 映射怎么配
伺服从站的 PDO 映射由从站 ESI 文件定义,主站这边要做的是把映射关系对上。通常控制字 0x6040、状态字 0x6041、目标位置 0x607A、实际位置 0x6064 这几个对象是必配的。映射时注意字节对齐,SOEM 的 IOmap 是按从站顺序排的,每个从站的输出和输入各占一段。
/* 伺服 PDO 偏移定义,按从站顺序排列 */ #define SLAVE0_OUT_OFFSET 0 /* 控制字 + 目标位置 */ #define SLAVE0_IN_OFFSET 8 /* 状态字 + 实际位置 */ /* 下发控制字和位置指令 */ void servo_set_position(uint16_t ctrl, int32_t pos) { /* 控制字写入输出区 */ *(uint16_t *)(iomap + SLAVE0_OUT_OFFSET) = ctrl; /* 目标位置写入输出区 */ *(int32_t *)(iomap + SLAVE0_OUT_OFFSET + 2) = pos; }偏移量要根据实际映射算,别硬编码。从站数量或 PDO 配置变了,偏移就变。我一般写个映射表,启动时打印每个从站的偏移,调试时对着看。
5.2 使能流程与状态字判断
伺服使能不是发个命令就行,要按状态字一步步走:先发 Shutdown 等状态字变成 Ready to Switch On,再发 Switch On 等 Switched On,最后发 Enable Operation。每一步都要读状态字确认,超时就报错。
/* 伺服使能状态机 */ void servo_enable(void) { uint16_t status; /* 第一步:Shutdown */ servo_set_position(0x0006, 0); do { ecx_receive_processdata(&ctx, EC_TIMEOUTRET); status = *(uint16_t *)(iomap + SLAVE0_IN_OFFSET); } while ((status & 0x006F) != 0x0021); /* 第二步:Switch On */ servo_set_position(0x0007, 0); /* 第三步:Enable Operation */ servo_set_position(0x000F, 0); }状态字判断用掩码,0x006F 是状态位组合,0x0021 是 Ready to Switch On 的期望值。不同品牌伺服状态字定义略有差异,对着手册改掩码。
5.3 位置模式下的周期同步
使能完成后进入周期同步位置模式,每个周期下发目标位置,从站返回实际位置。目标位置一般做插补,别直接跳变,否则伺服会报警。实际位置回读后做闭环校正,偏差大就查编码器分辨率和齿轮比。
6. 避坑与常见问题排查:调试现场最容易翻车的五个点
6.1 从站扫不到,ecx_config_init 返回 0
现象是上电后主站扫不到任何从站。原因通常是物理层没通:网线接反、PHY 时钟没起振、RMII 引脚配置错。解决方法是先用示波器量 PHY 的 25MHz 时钟,再用 Wireshark 抓包看有没有 EtherCAT 帧发出去。如果帧发出去了但从站不回,查从站供电和接线顺序。
6.2 状态卡在 PREOP 进不了 OP
现象是状态机能推到 PREOP,但进 OP 超时。原因是 PDO 映射没对上,或者从站配置和 ESI 不一致。解决方法是打印每个从站的 SM 配置和 PDO 映射,对着 ESI 文件核对。常见的是输出 PDO 长度算错,导致 IOmap 偏移错位。
6.3 周期通信丢帧,位置环抖动
现象是运行中偶尔丢帧,伺服位置抖动。原因是周期任务被高优先级中断打断,或者帧处理时间超过周期。解决方法是把定时器中断优先级提到最高,测量 ecx_receive_processdata 的耗时,必要时减少节点数或加长周期。
6.4 伺服使能报错,状态字不按预期跳转
现象是使能流程走到一半卡住,状态字停在某个值。原因是控制字序列不对,或者从站有报警未清除。解决方法是按手册的控制字时序重发,先发故障复位 0x0080 清报警,再走使能流程。
6.5 IOmap 数据错位,读写到别的从站
现象是读写的 PDO 数据对不上,控制字发到了别的位置。原因是 IOmap 偏移计算错,或者从站顺序和映射时不一致。解决方法是启动时打印每个从站的偏移和长度,运行时对着映射表核对。
7. 验收与答辩:怎么证明这套主站真的跑通了
验收这套主站,我一般分四步走,每步都有可量化的判据,答辩时直接拿数据说话。
第一步是物理层验收。用 Wireshark 抓包,确认主站发出的帧以太网类型是 0x88A4,从站有回应帧。这一步证明硬件和 PHY 通了。判据是抓包能看到完整的 EtherCAT 帧交互,没有 CRC 错误。
第二步是状态机验收。上电后主站能自动把从站从 INIT 推到 OP,全程无超时。判据是串口打印的状态转换日志,每个从站的状态跳转时间和预期一致。这一步证明协议栈移植正确。
第三步是周期通信验收。用示波器量定时器中断和 ETH 发送信号,确认周期稳定在 1ms,抖动小于 50us。同时跑一小时连续通信,统计丢帧率,要求为零。判据是示波器波形和丢帧计数。
第四步是伺服功能验收。下发位置指令,读回实际位置,确认跟随误差在允许范围内。做一次阶跃响应,看超调和稳定时间。判据是位置曲线和误差数据。
| 验收项 | 判据 | 工具 |
|---|---|---|
| 物理层 | 抓包有完整帧交互 | Wireshark |
| 状态机 | 自动推进到 OP 无超时 | 串口日志 |
| 周期通信 | 1ms 周期,抖动 < 50us,零丢帧 | 示波器 |
| 伺服功能 | 跟随误差在允许范围 | 位置曲线 |
答辩时容易被问的是「为什么不用商用主站」和「周期能做到多小」。第一个问题从成本和自主可控答,第二个问题拿实测数据答,别拍脑袋说理论值。还有个小技巧:把 Wireshark 抓的帧和示波器波形截图放进 PPT,比讲一堆原理有说服力。
从那以后我每次做 EtherCAT 主站项目,上电第一件事都是先抓包确认物理层,再走状态机,最后才调伺服。这个顺序能省掉大量返工。希望帮到你。
本文还有配套的精品资源,点击获取