简介:面向嵌入式开发者的EtherCAT从站构建实践文档,聚焦STM32F103与LAN9252的软硬件协同设计,适合已具备一定单片机基础、希望打通工业以太网从站开发链路的工程师参考。内容围绕LAN9252四种工作模式的选型展开,重点讲解SPI串口模式下的数据交互,并覆盖XML设备描述文件构建、SSC工具生成协议栈、原理图与PCB设计、代码移植及TwinCAT2联调等环节,对HW_Init、MainInit、MainLoop骨架以及IRQ、SY0、SY1中断处理给出具体说明。资源包共1个docx文件,约1.75MB,以图文文档形式系统梳理开发流程与移植要点。目前已有2921人学习,可帮助读者理解EtherCAT从站通信机制、掌握XML配置与协议栈移植思路,并借鉴中断与SPI通信的排错经验。
1. 从一块 64 入 64 出的 IO 板说起:STM32F103 配 LAN9252 跑 EtherCAT 从站到底难在哪
手上有一台只认 TwinCAT2.0 的 ARM 架构主站,操作系统还是 Windows CE,项目要求周期交互 64 路输入、64 路输出,从站侧选型落在 STM32F103 加 LAN9252 这套组合上。这不是随便挑的:LAN9252 自带两颗以太网 PHY、支持内部网络交换,从站必须一进一出不能终结总线,它刚好满足;STM32F103 便宜、生态成熟,SPI 主模式跟 LAN9252 对接,硬件成本压得住。真正让人翻车的地方不在电路,而在三件事:XML 描述文件里那 14 字节 ConfigData 决定 LAN9252 走 SPI 还是并行口,中断使能时机错一步就连不上主站,协议栈生成的代码几乎没法直接读只能按骨架剥。这篇笔记把从 SSC 建工程、改 XML、CubeMX 配 SPI 到中断对接的完整链路拆开,适合正在做 EtherCAT 从站、手里已经有学习板或协议栈的嵌入式工程师照着复现。
2. 选型与协议栈骨架:为什么是 SPI 模式而不是并行 HBI
2.1 四种工作模式的取舍逻辑
LAN9252 手册里列了四种模式:数字 IO 模式只有 8 进 8 出,项目要 64 进 64 出直接排除;并行 HBI 模式 16/8 位数据线,速度快但占引脚多,STM32F103 的 FSMC 虽然能接,可 PCB 走线数量和布线难度都上去了;扩展模式是 SPI 加一路 MII 再挂 PHY,多一个 RJ45,本项目从站只需要两个网口做一拖一,用不上第三口;SPI 模式四根线搞定,STM32 的 SPI1 标准四线接法,30MHz 时钟够用,LAN9252 高速 SQI 能到 80MHz 但没必要一上来就上。选 SPI 的核心理由是引脚省、布线简单、协议栈里 SPI 适配层最成熟,代价是带宽比并行低,不过 64+64 位的过程数据量很小,周期通信完全撑得住。
这里有个容易忽略的点:模式不是焊完板子就定死的,而是由 EEPROM 里那 14 字节 ConfigData 的第一个字节决定的。第一个字节写 0x80 对应寄存器 0x0140h(Process Data Interface),就是 SPI 模式。所以硬件上 SPI 四线接好只是前提,软件上 XML 里 ConfigData 没改对,芯片上电照样按别的接口初始化,现象就是 SPI 读回全 0xFF 或者 0x00,很多人卡在这一步以为是焊接问题。
2.2 协议栈的三个函数骨架
SSC 生成的协议栈代码量很大,直接从头读基本读不下去。我的做法是从 main 入手,剥出来就三块:
HW_Init(); // 硬件初始化:SPI、GPIO、定时器、中断入口注册 MainInit(); // 协议栈初始化:状态机、邮箱、PDO 配置 while (1) { MainLoop(); // 主循环:状态机推进、邮箱处理、看门狗喂狗 }HW_Init()里做的事就是把你 MCU 的外设和协议栈内部期望的接口对上,SPI 读写函数、定时器启动、外部中断使能都从这里挂进去。MainInit()负责把从站状态机拉到 INIT 再到 PREOP,这中间会读 EEPROM、校验 ConfigData。MainLoop()是死循环里的常驻逻辑,1ms 定时器中断里调用的ECAT_CheckTimer()负责看门狗和周期任务,主循环和定时器中断配合推进状态机。
理解这个骨架之后,移植工作就变成填空题:协议栈期望一个HW_EepromRead(),你就用 SPI 去读 LAN9252 挂的 EEPROM;期望PDI_Isr()在外部中断里被调用,你就在HAL_GPIO_EXTI_Callback()里分发。不需要读懂整个协议栈,只需要把接口对上。
2.3 用 SSC 生成工程和 XML 的实操步骤
SSC 是 EtherCAT 协会给会员的从站协议栈代码工具,装好之后 File→New,模板别用默认的,选 Custom 下拉里的EL9800 | 8Bit Digital I/O, 16Bit Analog Input,这个模板的 SPI 配置和数字 IO 结构最接近本项目。建完工程先 Tool→Application→Create New,会弹出一个 Excel 模板,这就是配 IO 点位的入口。
Excel 里对//0x6nnx(输入)和//0x7nnx(输出)两个区段批量填 64 个位,熟练用拖拽填充很快。最后一个Maximum number of modules改成 3。保存后 SSC 自动弹导入对话框,点 OK 导入,再 Project→Create new Slave Files 生成协议栈和 XML。
Excel 配置要点: - //0x6nnx 区段:64 个 BIT 输入,对应键盘/开关量 - //0x7nnx 区段:64 个 BIT 输出,对应 LED - Maximum number of modules:3 - 保存后由 SSC 自动导入,不要手工改 XML 的 PDO 段生成出来的 XML 里 PDO Entry 有 128 条,手工写基本不可能不出错,这也是必须用 SSC 的原因。SSC 同时生成的.c文件里,对象字典相关的那个文件最有价值,后面移植逻辑时直接参考它填自己的数据结构。
提示:SSC 安装包受协会会员条款约束,不在公开渠道扩散,拿不到的话先确认自己或所在团队有没有会员资格,别在来路不明的包里浪费时间。
3. 改 XML 的四个关键节点:ConfigData、EEPROM 与对象字典
3.1 设备基础信息与唯一性字段
XML 顶层分 Vendor 和 Descriptions 两块。Vendor 下的 id 和 name 来自会员信息,ImageData16x14 是 16 色深 bmp 的十六进制数据,用画图做一个 16x14 的图标,再用十六进制查看器把字节贴进去就行。Descriptions→Devices→Device 下面几个字段要留意:
| 节点 | 作用 | 注意点 |
|---|---|---|
| Physics | 网口类型 | YY 表示两个 RJ45 一进一出,别改成 K(EBUS 专用口) |
| Type/ProductCode | 产品编号 | TwinCAT 靠它和 RevisionNo 联合识别设备唯一性 |
| Type/RevisionNo | 版本号 | 硬件迭代必须同步升,两份 XML 这两个字段相同只会认一个 |
| Name | 设备名 | 任意取,但和对象字典里 0x1008 的字符串要对上 |
| Info | 状态切换超时 | 无特殊情况不动,TwinCAT 界面能看到这些超时值 |
ProductCode 和 RevisionNo 这一对是血泪经验:调试阶段改过硬件没改版本号,TwinCAT 里扫出来的还是旧设备描述,PDO 映射对不上,查了半天以为是协议栈问题。后来养成习惯,每次改 XML 里任何跟 IO 相关的定义,RevisionNo 加一。
3.2 对象字典与 PDO 的对应关系
Profile 节点下分 DataType 和 Objects,前者像 typedef,后者像变量定义。比如 0x1008 的 Device name,type 是 STRING(10),去 DataType 里找 STRING(10) 的 BitSize 是 80 bit,正好 10 字节,内容就是设备基础信息里定义的设备名。这套关系和 SSC 生成的SSC-DeviceObjects.h一一对应,移植时对着看能省很多猜的时间。
Sm 节点有四个通道:MBoxOut、MBoxIn 管邮箱通信,Inputs、Outputs 管过程数据。Inputs 指向 RxPdo,Outputs 指向 TxPdo,每个 Entry 就是 Excel 里配的一个点位。SM 通道的 size 要求 16 位对齐,Entry 的 bitlen 和 subindex 属性别乱动,这部分 SSC 已经算好了。
3.3 ConfigData 那 14 个字节怎么改
这是整份 XML 里最需要动手改的地方。SSC 默认按 ET1100 生成,ConfigData 是050E03440A0000000000,换成 LAN9252 要按迁移手册改成800E00CC8813F0000000800000。第一个字节 0x80 对应 0x0140h 寄存器配成 SPI 模式,后面字节涉及 PHY 配置和保留位,手册没让改的就保持默认。
ConfigData 修改对照: 默认(ET1100):050E03440A0000000000 LAN9252 SPI :800E00CC8813F0000000800000 关键位:首字节 0x80 → 0x0140h = SPI 接口EEPROM 节点里 ByteSize 一般写 2048,物理芯片建议用 24FC512(512Kbit),留足余量。这里有个玄学问题:EEPROM 芯片型号不对,EtherCAT 连接会时好时坏,换回 24FC512 就稳了,具体原因跟上电时序和页写周期有关,踩过一次就记住了。
3.4 XML 的落地位置
改完的 XML 放到 TwinCAT 安装目录下的/Io/EtherCAT文件夹,TwinCAT 扫描到设备后把关键数据烧进 LAN9252 挂的 EEPROM,之后设备上电自己按 EEPROM 配置初始化。所以 XML 改错一次,烧进去之后要重新烧,调试阶段建议把烧录步骤单独记一笔,别和 PLC 下载混在一起。
4. CubeMX 配 SPI 与中断:使能时机是连不上的头号原因
4.1 外设清单与参数
新建 STM32F103ZExx 工程,需要配的外设就四类:SPI1 四线标准模式、1ms 定时器、三个外部中断(IRQ、SYNC0、SYNC1)。SPI 时钟分频后控制在 30MHz 以内,CPOL/CPHA 按 LAN9252 手册的 SPI 时序来,一般模式 0。定时器配 1ms 周期中断,命名ECAT_CheckTimer。三个 EXTI 分别对应 LAN9252 的 IRQ、SYNC0、SYNC1 引脚,下降沿触发。
NVIC 优先级分配我一般这么给:ECAT_CheckTimer1 抢先 0 响应,PDI_Isr1 抢先 0 响应,Sync0_Isr和Sync1_Isr2 抢先 0 响应。SPI 不配中断,因为 SPI 是全双工,读的同时在写,没有中断读写的必要。
4.2 生成代码后必须注释掉的那几行
CubeMX 里三个外部中断和定时器都勾了 enable,生成的初始化函数最后会有一行使能语句。这行必须注释掉:
/* CubeMX 生成,需注释掉,由协议栈决定使能时机 */ // HAL_NVIC_EnableIRQ(EXTI0_IRQn); // HAL_NVIC_EnableIRQ(EXTI1_IRQn); // HAL_NVIC_EnableIRQ(EXTI2_IRQn); // HAL_TIM_Base_Start_IT(&htim3);原因很直接:协议栈在HW_Init()里会根据接口定义决定什么时候开中断,上电就使能外部中断,协议栈还没初始化完就进中断,状态机直接乱掉,现象就是 TwinCAT 扫描不到设备或者扫到就掉线。这个坑几乎每个新手都会踩一次,记住一句话——中断使能权交给协议栈,CubeMX 只负责生成配置。
4.3 中断回调的分发写法
stm32f1xx_it.c里生成的 EXTI 中断服务函数最终会调到HAL_GPIO_EXTI_Callback(),把分发逻辑写在这里:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == LAN9252_IRQ_Pin) { PDI_Isr(); // 协议栈主中断,必须实现 return; } if (GPIO_Pin == LAN9252_SYC0_Pin) { DISABLE_ESC_INT(); // 同步中断期间屏蔽主中断 Sync0_Isr(); ENABLE_ESC_INT(); return; } if (GPIO_Pin == LAN9252_SYC1_Pin) { DISABLE_ESC_INT(); Sync1_Isr(); ENABLE_ESC_INT(); return; } }PDI_Isr()是必须实现的,SYNC0/SYNC1 用不用分布式时钟决定要不要接。同步中断前后包一层DISABLE_ESC_INT()/ENABLE_ESC_INT()是为了防止同步处理期间被主中断打断导致时序错乱,这两个宏在协议栈里有定义,直接调。
4.4 SPI 适配层的封装
协议栈期望的 SPI 接口一般是读写一体的函数,封装成它要的名字:
uint8_t HW_SPI_ReadByte(void) { uint8_t tx = 0xFF, rx = 0; HAL_SPI_TransmitReceive(&hspi1, &tx, &rx, 1, 100); return rx; } void HW_SPI_WriteByte(uint8_t data) { uint8_t rx; HAL_SPI_TransmitReceive(&hspi1, &data, &rx, 1, 100); }超时参数 100ms 是保守值,实际单字节传输微秒级,给大一点防止偶发调度延迟误判超时。SPI 句柄hspi1来自 CubeMX 生成的spi.c,别在协议栈文件里重复定义。读写一体的本质是 SPI 全双工特性,发一个字节同时收一个字节,LAN9252 的 PDI 接口就是按这个时序设计的。
5. 避坑排查:连不上、掉线、PDO 对不上分别查什么
5.1 扫描不到设备,SPI 读回全 0xFF
现象:TwinCAT 扫描网络没有任何从站,MCU 侧读 LAN9252 寄存器全是 0xFF。原因大概率是 ConfigData 没改成 SPI 模式,芯片上电按并行口初始化,SPI 接口不响应。解决:确认 XML 里 ConfigData 首字节是 0x80,重新烧 EEPROM,再上电。顺带查一下 SPI 的 CPOL/CPHA 和片选时序,LAN9252 对片选建立时间有要求。
5.2 能扫到但一切换状态就掉线
现象:设备能识别,INIT 到 PREOP 正常,一到 SAFEOP 或 OP 就断。原因通常是中断使能时机不对,或者 1ms 定时器中断里ECAT_CheckTimer()没被正确调用导致看门狗超时。解决:检查HW_Init()里定时器和外部中断的使能入口是否由协议栈调用,CubeMX 生成的那几行使能语句是否注释干净;再看定时器中断服务函数里是不是真的调了ECAT_CheckTimer()。
5.3 PDO 数据错位或数量不对
现象:TwinCAT 里看到的输入输出点位数量不是 64+64,或者数据错位。原因一般是 XML 里 PDO Entry 和 Excel 配置不一致,或者 ProductCode/RevisionNo 没升导致 TwinCAT 用了缓存的旧描述。解决:清掉 TwinCAT 的缓存设备描述,确认/Io/EtherCAT下只有一份对应版本的 XML,RevisionNo 加一后重新扫描。
5.4 EEPROM 写入失败或偶发连接困难
现象:烧录 EEPROM 时报错,或者连接时好时坏。原因可能是 EEPROM 型号不对(非 24FC512)、I2C 上拉电阻偏大、页写周期没等够。解决:换 24FC512,上拉改 2.2k 到 4.7k,烧录后延时再读校验。这个属于硬件层的坑,软件怎么调都没用。
5.5 分布式时钟同步偏差大
现象:用了 SYNC0/SYNC1 但同步精度差。原因多是同步中断优先级低于主中断,或者DISABLE_ESC_INT()包的范围不对。解决:按 4.1 的优先级分配,同步中断抢先级设 2,主中断设 1,确保同步处理不被主中断打断。
6. 从能跑到跑稳:用对象字典反查 PDO 映射的验证手法
设备能进 OP 只是及格线,真正要交付得确认 64 进 64 出的每一位都对得上。我的验证习惯是从对象字典反查:TwinCAT 里进 OP 后,用在线写变量功能逐个翻转输出位,同时用示波器或逻辑分析仪看对应 LED 引脚;输入侧拨动键盘开关,在 TwinCAT 里看对应位变化。这一步能抓出 PDO 映射错位、字节序反了、位偏移一位这类问题,比盯着 XML 看快得多。
更系统的做法是对着SSC-DeviceObjects.h里的对象定义,把 0x6000 段(输入)和 0x7000 段(输出)的每个子索引和实际引脚列一张对照表,调试时按表打勾。下面是我常用的验证顺序:
| 步骤 | 操作 | 通过标准 |
|---|---|---|
| 1 | 扫描并烧录 EEPROM | 设备出现在 TwinCAT 树中 |
| 2 | 状态机 INIT→PREOP→SAFEOP→OP | 无掉线,超时在 Info 设定范围内 |
| 3 | 输出位逐位翻转 | 对应 LED 亮灭一致,无相邻位串扰 |
| 4 | 输入位逐位拨动 | TwinCAT 变量表对应位变化 |
| 5 | 周期通信压测 | 连续运行无看门狗复位 |
压测这一步别省,我见过调试时一切正常、跑半小时掉一次的情况,最后查出来是 1ms 定时器里某个分支执行时间偶尔超限,看门狗被喂晚了一拍。把ECAT_CheckTimer()里的耗时用 GPIO 翻转加示波器量一下,心里就有数了。
还有一个进阶技巧:XML 里 Info 节点的状态切换超时值可以适当放宽用于调试,但交付前要改回保守值,否则现场干扰大时状态机切换会卡在超时边界。改完记得升 RevisionNo,重新烧 EEPROM。
从那以后我每次改完 XML 或中断配置,都强制走一遍「清缓存描述→升版本号→重烧 EEPROM→全状态机切换→逐位 IO 验证」这套流程,不再相信「上次能跑这次应该也行」。EtherCAT 从站这活儿,玄学都藏在使能时机和版本号里,流程走全了反而最省时间。希望帮到你。
本文还有配套的精品资源,点击获取