STM32F407+LAN9252 Ether CAT从站开发 新手指导

发布时间:2026/10/4 16:51:31
STM32F407+LAN9252 Ether CAT从站开发 新手指导 STM32F407LAN9252 Ether CAT从站开发目标实现使用STM32F407配合LAN9252以SPI通信制作Ether CAT从站无DCEOEFOESPO功能仅可做IO或特殊功能从站使用。本教程是本人通过AI指导整理的没有抄袭过别人的教程也不知道会不会倍别人抄袭我本着技术开放的心态给大家提供这份心得。希望这份资料会对需要的人提供帮助。第一次发布只会搞文字。此处提供相关代码链接:https://pan.baidu.coms15GHQmcQiV_hiSOv5Idas4g提取码:uspz/使用的软件介绍SSC Tool V5.11Ether CAT从站代码文件生成工具KeilSTM32F407代码编写软件TWin CAT用电脑模拟EtherCAT主站Excle配合ssc生成想要的过程数据过程介绍1SSC Tool加载LAN9252 SDK模板。2配置SSC工程参数。3通过Excle编辑过程数据项。4SSC生成相关代码。5修改SPIDriver库代码使其适配stm32f407。6修改9252_HW库代码使其适配修改后的SPIDriver库。7将SSC生成的代码和SPIDriver库添加到stm32f407标准工程中。8根据keil编译错误和警告提示排查问题直到消除所有错误。9根据实际情况编写APPL_InputMapping,APPL_OutputMapping,APPL_Application,和自己的代码.本教程重点讲解代码的修改,关于软件的操作不会特别细讲.教程开始:1SSC Tool加载LAN9252 SDK模板:将上文连接中文件下载,解压,找到LAN9252-PIC32-SDK-1.1.zip解压.打开SSC Tool软件,点击:FileNewImport在LAN9252-PIC32-SDK-1.1文件夹中找到Microchip_LAN9252_SSC_Config.xml打开Custom选择:Microchip-SPI-GPIOOK会有一个提示窗点击是再点击OK再LAN9252-PIC32-SDK-1.1\SSC\Common文件夹中找到9252_HW.c文件并打开.这一步完成后,LAN9252的模板加载完成,这一步很重要,因为SSC生成的代码需要以9252_HW.c文件中代码为原型,关系着后面的移植是否顺利.2配置SSC工程参数这一步主要是设置参数,目标设置:无DC,无EOE,无FOE,无SDO.和保证LAN9252运行的基本参数VENDOR_ID0x00009252;DC_SUPPORTED0(无DC关键);ECAT_TIMER_INT1;SDO_RES_INTERFACE0;EOE_SUPPORTED0;FOE_SUPPORTED0;到此,SSC工程配置基本完成,如果再配置是发现VOE,SOE,EOE,FOE中有1的,需要将其设置0.3通过Excle编辑过程数据项再SSC中ToolApplicationCreate new:开启SSC提供的excle配置模板用于配置PDO数据.在Excle中,找到0x6nnx项,这是主站输入数据PDO,地址范围0x6000~0x6fff,在0x6nnx项下添加新行,并按照要求添加新行:要求:第一行的Index列为0x6000,ObjectCode列为ARRAY.可以认为第一行为索引项.紧挨第一行的第二行开始:SI为序号从1开始,DataType为数据类型教程使用的是Byte,Name为数据名称可任意设置除中文外,Default设置为0x00,我也不清楚这是啥意思好像是初始值.rx/tx设置为tx.这样设置可以理解为只要是在0x6000范围内的都是0x6000的子索引.0x7000的设置与0x6000类似,只是Index列为0x7000,rx/tx设置为rx.excle设置好后,保存文件,关闭excle后稍等片刻等待ssc加载刚才的excle文件中内容.如果你有配置好的excle文件可以在ToolApplicationImport找打目标excle直接加载.当ssc加载完成后,会有一个弹窗,这是分析excle文件后的结果,如果结果中没有报红内容就正常点击OK即可.报红全是英文,我也不知道,可以找AI分析.4SSC生成相关代码这是最简单的一步.在SSC中:ProjectCreate new Slave files,在弹窗中点击start,开生成代码,等待生成完成即可.生成完成后会提示是否打开生成文件夹,如果找不到文件在哪里可以点击Open.点击Close关闭即可.5修改SPIDriver库代码使其适配stm32f407从这一步开始,就要修改代码了.但是又AI协助,需要修改的东西不多.在LAN9252-PIC32-SDK-1.1\SSC\PIC32-SPI\SPIDriver文件夹中找到SPIDriver.c.h文件.这是MCU的SPI硬件驱动库,里面有SPI外设初始化,SPI发送字节,SPI接受字节,还有ESC寄存器读写函数等.这都没关系,我们找到这两个函数文件,将其复制交给AI分析每个函数的作用,并要求它针对STM32F407标准库进行代码适配修改.我的资料包中有修改好的在:ECAT移植练习\HW中,其中带有官方的文件为SDK解压后的未修改源码,而SPIDriver.c.h文件是已经适配修改后的代码.在进行项目文件加载时千万不要搞错.6修改9252_HW库代码使其适配修改后的SPIDriver库与5同理但是这里有很多宏定义,DC中断,ESC地址,ESC命令码等还有SPIDriver的SPI引脚,如果SPI引脚不同可在9252_HW库中修改,或者将其中关于SPI的宏定义剪切到SPIDriver库中.很多人以为,移植Ether CAT协议栈最难的地方后面的代码,其实最难的地方时5和6.并且我推断:所有MCULAN9252模式的从站的难点都是5和6.并且更管任意品牌的MCU需要更改的也是5和6.7将SSC生成的代码和SPIDriver库添加到stm32f407标准工程中现在我们有SSC生成的代码文件夹和经过适配的SPIDriver库,9252_HW库.其中SSC生成的代码中也有9252_HW库,这个库时修改前的库,所以SSC生成的代码中除了9252_HW库外其余的库文件都要使用.在Keil中,我新建了两个文件夹,ECAT和HW,ECAT存放SSC生成的代码.HW存放SPIDriver库,9252_HW库,虽然舍弃了SSC生成的9252_HW库,但是这个库必须要用到,并且经过AI的分析,在无DC的场合中,SPIDriver库,9252_HW库几乎不用变,可以当成底层代码使用,为了避免后期移植时出错,所有将9252_HW库从ECAT文件夹中分离.但是不影响使用.8根据keil编译错误和警告提示排查问题直到消除所有错误我们将所有文件加载到Keil工程中后,编译会有报错,我们将报错信息交给AI指导我们解决.现在的代码编译后大概率会出现一下信息:..\ECAT\emcy.c(299):error:#167:argument of type__packed unsigned short *is incompatible with parameter of typevoid *restrict..\ECAT\sdoserv.c(277):error:#167:argument of type__packed unsigned short *is incompatible with parameter of typeconst void *restrict..\ECAT\sdoserv.c(433):error:#167:argument of type__packed unsigned short *is incompatible with parameter of typevoid *restrict..\ECAT\sdoserv.c(652):error:#513:a value of type__packed unsigned short *cannot be assigned to an entity of typeunsigned short *..\ECAT\sdoserv.c(667):error:#513:a value of type__packed unsigned short *cannot be assigned to an entity of typeunsigned short *..\ECAT\sdoserv.c(770):error:#167:argument of type__packed unsigned short *is incompatible with parameter of typevoid *restrict..\ECAT\sdoserv.c(798):error:#167:argument of type__packed unsigned short *is incompatible with parameter of typeconst void *restrict..\ECAT\sdoserv.c(936):error:#167:argument of type__packed unsigned short *is incompatible with parameter of typevoid *restrict..\ECAT\sdoserv.c(946):error:#167:argument of type__packed unsigned short *is incompatible with parameter of typevoid *restrict..\ECAT\sdoserv.c(955):error:#167:argument of type__packed unsigned short *is incompatible with parameter of typevoid *restrict..\OBJ\Template.axf-10Error(s),7Warning(s).我们把这些错误点交给AI,让它告诉我们该怎么修改.经过AI分析: 这 10 个错误其实是同一个根因只需改 2 处共 3 行就能全部消除。根因先看这段后面 10 条都是它的分支CAT/ecat_def.h:754#defineMBX_STRUCT_PACKED_END__attribute__((aligned(1),packed))//所有邮箱结构体TMBX、TEMCYMESSAGE、SDO 系列都用这个宏打包。在 ARM Compiler 5 下对 packed 结构体的成员取地址得到的类型是 __packed unsigned short *带非对齐限定而不是普通的 unsigned short *。//于是出现两类不兼容//• 传给 memcpy 的 void *restrict → error #167//• 赋给普通 UINT16 * → error #513//这不是逻辑错误是类型系统不兼容。错误1ECAT\emcy.c(299)— error #167原因psWriteMbx-Data 是 packed 结构体 TEMCYMESSAGE 的数组成员取地址...Data[1]得到 __packedunsignedshort*而 MBXMEMCPY 直接展开成 memcpy其形参是void*restrict类型不兼容。解决不用改这一行。改 ecat_def.h:549的宏定义即可见下方「统一改法1」一次覆盖 emcy.c 和 sdoserv.c 全部8处 #167。 错误2ECAT\sdoserv.c(277)— error #167原因pSdoInd-SdoHeader.Data 是 packed 成员取地址后类型不符。解决归宏修改本行不动。 错误3ECAT\sdoserv.c(433)— error #167原因同错误2pSdoSegRes-SdoHeader.Data 为 packed 成员。解决归宏修改本行不动 错误4ECAT\sdoserv.c(652)— error #513原因这是赋值不是 memcpy所以改 MBXMEMCPY 宏管不到它。-Data 是 __packedunsignedshort*而 pData 声明为unsignedshort*类型不兼容。解决必须单独改这一行加双重 cast 错误5ECAT\sdoserv.c(667)— error #513原因同错误4。解决单独改 错误6ECAT\sdoserv.c(770)— error #167原因packed 成员作为 memcpy 第一参数。解决归宏修改本行不动。 错误7ECAT\sdoserv.c(798)— error #167原因packed 成员作为 memcpy 第二参数所以报错信息里是constvoid*restrict。解决归宏修改本行不动。 错误8ECAT\sdoserv.c(936)— error #167原因同上。解决归宏修改本行不动。 错误9ECAT\sdoserv.c(946)— error #167原因同上。解决归宏修改本行不动。 错误10ECAT\sdoserv.c(955)— error #167原因同上。解决归宏修改本行不动。XX具体改法只有 2 处改法 1ECAT\ecat_def.h 第 549 行 —— 解决错误 1、2、3、6、7、8、9、10共 8 个 #167原文#define MBXMEMCPY memcpy 改为#defineMBXMEMCPY(dest,src,size)memcpy((void*)(dest),(constvoid*)(src),(size))//为什么这样改就够报错的 8 处用的全是 MBXMEMCPY宏里把形参显式转成 void * / const void *展开后类型就匹配了调用点一行都不用动。…改法 2ECAT\sdoserv.c 第 652 和 667 行 —— 解决错误 4、5共 2 个 #513这两处是赋值语句宏管不到必须单独加 cast。注意原行括号层级改完要保证括号配平652行原文:pData((TINITSDOUPLOADEXPRESMBX MBXMEM*)pSdoInd)-Data;改为:pData(UINT16*)(void*)(((TINITSDOUPLOADEXPRESMBX MBXMEM*)pSdoInd)-Data);667行原文pData((TINITSDOUPLOADNORMRESMBX MBXMEM*)pSdoInd)-Data;改为:pData(UINT16*)(void*)(((TINITSDOUPLOADNORMRESMBX MBXMEM*)pSdoInd)-Data);XX改完后,再编译,此时只有1条错误了..\OBJ\Template.axf:Error:L6915E:Library reports error:__use_no_semihosting was requested,but _ttywrch was referenced在 SYSTEM\usart\usart.c。这是正点原子模板的经典代码块只需要补 6 行。这是STM32F407模板的问题, 这个代码块只定义了 _sys_exit第 21-24 行而 ARM 的半主机 I/O 函数族包含 fputc、fgetc、_ttywrch 三个。你的代码里 fputc 已经重定义了第 26 行但 _ttywrch 漏了 —— 标准库的诊断/错误输出路径会引用它于是链接器把它抓出来了。解决办法 在 usart.c 第 24 行_sys_exit 的右花括号之后、第 25 行 //重定义fputc函数 之前插入//AC6/armclang 与部分 AC5 版本在错误输出路径上还会引用 _ttywrchvoid_ttywrch(intch){chch;}//部分编译器版本还会引用它一并补上避免二次链接失败char*_sys_command_string(char*cmd,intlen){(void)cmd;(void)len;returnNULL;}XX此时,再编译代码,就会发现错误数量为0接下来,就来解决警告现有的警告内容:1..\ECAT\objdef.c(729):warning:#111-D:statement is unreachablebreak;2..\ECAT\objdef.c(867):warning:#767-D:conversion from pointer to smaller integerif((((INT16)pVarPtr)0x1)0x1)3..\ECAT\SPB_SSC.c(266):warning:#1215-D:#warning directive:Implement input (Slave - Master) mapping4..\ECAT\SPB_SSC.c(282):warning:#1215-D:#warning directive:Implement output (Master - Slave) mapping5..\ECAT\SPB_SSC.c(296):warning:#1215-D:#warning directive:Implement the slave application6..\ECAT\SPB_SSC.c(312):warning:#1215-D:#warning directive:Implement explicit Device ID latching7..\ECAT\SPB_SSC.c(331):warning:#951-D:returntype of functionmainmust beint我们将这些内容交给AI,让它分析并帮助我们解决.AI分析: 这 7 个分三类,1:无害噪音 SPB_SSC.c:312、objdef.c:729/867 原厂代码/已实现功能2:建议改 SPB_SSC.c:331 void main 不规范3:真问题 SPB_SSC.c:266/282/296 三个函数是空壳从站数据恒 0修改1: objdef.c:729,return 之后的 break 永远执行不到。删掉这行即可逻辑完全不变。objdef.c:867指针截断, 把 32 位指针截断成 16 位再判最低位。当前逻辑是正确的低 16 位的 bit0 就等于完整地址的 bit0但改成 UINT32 更规范、也消除警告if((((UINT32)pVarPtr) 0x1) 0x1)SPB_SSC.c:312 APPL_GetDeviceID 这个其实已经实现了第 315 行 return 0x5;只是留了 #warning。删掉第 312 行那行 #warning 即可函数体不用动。修改2: SPB_SSC.c:331 — void main 改 int main 原因C 标准要求 main 返回 intKeil 报 #951-D。因为 ecat_def.h 里 _PIC24 0所以走 void main 分支。可以将void main改为int main,或者将 #if _PIC24和#endif注释掉,只保留1个int main.修改3: 这三个 #warning 对应的函数体全是空的只有一行 #warning。不填的话主站能扫到、能进 OP但过程数据全 0 且写不动。我们先忽略修改3,只要能在TWinCAT中让从站进入OP模式,就可以了.XXX现在就是见证奇迹的时刻,将SSC生成的XML文件,通过TWinCAT烧录到LAN9252的EEPROM中.将代码也烧录到MCU中.给模块断电重启,在twincat中也可以扫描到lan9252的从站,并且打开后看到的数据配置也是和我们设定的一样.这时启动ECAT通讯,并将主站切换到OP模式,你会发现,从站无法进入OP,还会报错.此时你会尝试,重新烧录,模块断电,twincat重启,甚至电脑重启,你发现还是不行,这时你无意中尝试按下MCU的Reset键,在启动主站,你会发现从站可以进入OP了,经过多测测试,你得出结论,当模块断电重启后无法自动进入OP,需要MCU重启一下才行.我们将这个问题告诉AI,AI分析得出结果: 上电时序问题 —— MCU 跑得比 LAN9252 快, LAN9252 上电后要完成几件事内部复位 → 从外部 EEPROM 加载 SII 配置身份信息、SM 配置、PDO 映射 → PHY 初始化/链路协商。这个过程需要几十到几百毫秒.AI说可以在MCU启动执行代码前,加一段延时,我们先加个1秒的延时.经过测试,发现可以正常的进入OP,不会再有刚才的情况了.我们将这个问题反馈给AI后,得到分析: MCU 比 LAN9252 跑得快在 ESC 还没从 EEPROM 加载完配置时就开始访问协议栈跑在一个未就绪的 ESC 上所以进不了 OP。按 MCU Reset 时 LAN9252 早已就绪所以正常。1 秒盲延时能跑通但不建议就这么定稿每次上电都白等 2 秒量产设备启动太慢而且万一 EEPROM 异常会死等。改成等标志位 超时兜底更好。这里我自己只用了延时.好的,到这里,我们的协议栈移植已经完成了,但是我们只完成了协议栈的移植,因为刚才的 3:真问题 SPB_SSC.c:266/282/296 三个函数是空壳从站数据恒 0,导致twincat主站得到的输入数据一直是0,输出的数据也没有任何响应.接下来我们要做的就是进行数据的打通.9根据实际情况编写APPL_InputMapping,APPL_OutputMapping,APPL_Application,和自己的代码.我的0x6000主站输入中是8个字节,0x7000主站输出也一样,那么我们要做的就是让模块对数据进行响应.我们先测试回环,是验证数据链路是否打通最经典的手段而且它有一个对你现在特别有价值的优点完全不需要任何外部硬件。解释一下APPL_OutputMapping APPL_InputMapping APPL_Application 函数干了什么,作用是什么APPL_OutputMapping把主站发来的货搬进车间APPL_InputMapping把做好的货搬出去给主站APPL_Application真正干活点灯、读传感器APPL_OutputMapping 协议栈的调用链ecatappl.c 主站把输出数据写进了 ESC 的 SM2 缓冲区ESC 产生 PROCESS_OUTPUT_EVENT 事件协议栈检测到后就调用它。UINT16* pData —— 指向 aPdOutputData 数组ecatappl.c:157 定义。协议栈已经帮你做完了最底层的活 —— HW_EscReadIsr 已经通过 SPI 把 ESC 里的 8 个字节读到了 aPdOutputData 里。你拿到的是现成的数据不需要再读 SPI。只做一件事把 pData 里的数据搬到你的应用对象对象字典。为什么要转 UINT8*pData 是 UINT16*因为底层按字访问效率高但你的 PDO 是 8 个 BYTE。所以必须转成 UINT8* 才能按字节准确取值。这是一个新手常踩的坑——不转的话 pData[0] 一次就吃掉两个字节。APPL_InputMapping 调用时机ecatappl.c:340 或 618在 APPL_Application 之后。把 Obj0x6000 搬到 pData然后协议栈自动帮你通过 SPI 写进 ESC 的 SM3 缓冲区。你不需要自己写 SPI —— 函数返回后协议栈的 HW_EscWriteIsr 会接管。APPL_Application 协议栈调 ECAT_Application()后者再调 APPL_Application()。在你当前的 FreeRun 模式下位置是 ecatappl.c:613。关键它紧跟在 OutputMapping 之后、InputMapping 之前。它不碰 pData只跟对象字典 Obj0x6000 / Obj0x7000 打交道。这是它和另外两个函数最大的区别。一个周期的执行顺序收货 → 加工 → 发货。严格的流水线不能乱。下面我把函数代码放进来:voidAPPL_InputMapping(UINT16*pData){UINT8*pDst(UINT8*)pData;pDst[0]Obj0x6000.INData1;pDst[1]Obj0x6000.INData2;pDst[2]Obj0x6000.INData3;pDst[3]Obj0x6000.INData4;pDst[4]Obj0x6000.INData5;pDst[5]Obj0x6000.INData6;pDst[6]Obj0x6000.INData7;pDst[7]Obj0x6000.INData8;}voidAPPL_OutputMapping(UINT16*pData){UINT8*pSrc(UINT8*)pData;Obj0x7000.OUTData1pSrc[0];Obj0x7000.OUTData2pSrc[1];Obj0x7000.OUTData3pSrc[2];Obj0x7000.OUTData4pSrc[3];Obj0x7000.OUTData5pSrc[4];Obj0x7000.OUTData6pSrc[5];Obj0x7000.OUTData7pSrc[6];Obj0x7000.OUTData8pSrc[7];}voidAPPL_Application(void){Obj0x6000.INData1Obj0x7000.OUTData1;Obj0x6000.INData2Obj0x7000.OUTData2;Obj0x6000.INData3Obj0x7000.OUTData3;Obj0x6000.INData4Obj0x7000.OUTData4;Obj0x6000.INData5Obj0x7000.OUTData5;Obj0x6000.INData6Obj0x7000.OUTData6;Obj0x6000.INData7Obj0x7000.OUTData7;Obj0x6000.INData8Obj0x7000.OUTData8;}将代码烧录到MCU中,In TwinCAT, write data to OutData and check InDatata中,数据相同,则成功.这也表示本次ethercat的从站协议栈移植成功,回环测试成功.这里再次表示AI是个好东西.本次移植教程中,几乎所有的代码问题都是AI解决的,我只是整理和反馈问题.