EtherCAT从站开发这几年热度一直不减,但真正动手做的人往往卡在第一步:协议栈代码从哪来、怎么配、怎么和硬件对接。市面上关于SSC工具的使用教程少之又少,官方文档又写得比较晦涩,新手很容易绕弯路。这篇文章就基于我实际做的一个项目——用STM32F407配合AX58100从站控制器芯片,完整跑通SSC V5.11生成从站代码的流程,把每一步怎么操作、哪些地方容易踩坑都讲清楚。整个过程涉及工具配置、代码生成、SPI底层驱动、对象字典设置、主站联调,内容比较长但都是实操记录,适合正在做EtherCAT从站或者准备入门的工程师参考。
1. 方案选型与系统架构:为什么选AX58100搭配STM32
1.1 三种主流EtherCAT从站实现方案对比
做EtherCAT从站,硬件架构上大概有三条路可以走。第一条是用带内部ESC(EtherCAT Slave Controller)的集成MCU,比如瑞萨的RA系列、英飞凌的XMC4300这类芯片,ESC单元直接集成在单片机内部,外围电路最简洁,但缺点是开发门槛高、芯片可选范围窄,而且示例代码相对封闭。第二条是独立ESC芯片外加一颗通用MCU,互连方式可以是SPI、并行总线或者数字IO,最常见的组合就是ET1100加STM32、LAN9252加DSP,以及本文要讲的AX58100加STM32。第三条是纯软件方案,用普通MCU的以太网MAC加外部PHY去实现EtherCAT从站,这种方案实时性和协议栈合法性都有隐患,正经产品基本不会这么干。
我从一开始基本就锁定了第二种方案,原因很直接:EtherCAT最难啃的实时性部分(EtherCAT数据帧收发、FMMU映射、分布式时钟、SYNC信号生成)全部由ESC芯片硬件完成,MCU只负责应用层逻辑和对象字典管理。这样MCU的压力非常小,即使跑在72MHz的F103上也绰绰有余,而且开发思路清晰,出了问题排查范围也很明确。至于为什么放弃ET1100和LAN9252,ET1100是老牌经典但价格偏高、还容易缺货,LAN9252的SPI接口在高速下对时序要求更苛刻。AX58100的优势是内置两路EtherCAT PHY(也就是说不需要额外外接PHY芯片就能直接接变压器联网),LQFP100封装对焊接和Layout都很友好,内部还集成了PWM、QEI等外设,后续想做运动控制类从站也不需要额外挂芯片。价格方面虽然不能写死,但综合成本明显比ET1100方案低一截。
1.2 整机数据链路与STM32/ESC分工
明确了用AX58100+STM32后,先看整个数据流向。EtherCAT主站(比如倍福TwinCAT或汇川PLC)通过网线发出数据帧,AX58100内部的两路PHY和ESC核收到帧后,根据FMMU映射和SM通道配置,把属于自己的过程数据剥离出来,转存到AX58100内部的PDO缓冲区中。此时AX58100会拉高IRQ中断引脚通知STM32"有新数据到了",STM32在中断或轮询中通过SPI接口把过程数据读出来;反过来STM32要把输入数据发给主站时,也是通过SPI写入AX58100的输入PDO缓冲区,ESC核会把它装配进下一个EtherCAT周期的数据帧回传给主站。
这套分工里最关键的一点是:实时性敏感的环节永远不依赖MCU。主站和从站之间的握手、状态机跳转、数据帧的转发延时全部由AX58100硬件完成,不管STM32这边在做什么(哪怕跑FreeRTOS被高优先级任务卡了几毫秒),EtherCAT链路都不会断。真正考验STM32响应速度的只有两件事——读取PDO数据并更新应用输出,以及处理同步中断。前者决定了从站对控制命令的响应延迟,后者在DC同步模式下会影响SYNC信号的抖动。所以做这类项目,STM32的中断处理和SPI访问效率要尽量优化,但不需要像纯软件实现EtherCAT那样把整个协议栈跑在MCU上,开发难度和风险完全不是一个量级。
2. 硬件连接与底层驱动准备:动手配置前先搞定这些
2.1 AX58100核心电路与STM32引脚对照
AX58100的工作电压是3.3V,但物理层PHY部分需要完整的差分信号线接到网络变压器(比如HR911105A或者单独变压器+RJ45座),这部分硬件设计手册里写得很清楚,几百欧的共模电阻和终端电阻该怎么放都有参考电路。这里只列与STM32交互的关键引脚:
| AX58100引脚 | 功能说明 | STM32对应引脚(示例) |
|---|---|---|
| SCS_N | SPI片选,低有效 | PA4/任意GPIO |
| SCLK | SPI时钟 | PA5/SPI1_SCK |
| SDI | SPI主出从入 | PA7/SPI1_MOSI |
| SDO | SPI主入从出 | PA6/SPI1_MISO |
| IRQ | ESC中断请求,事件触发拉低 | PB0/EXTI0 |
| SYNC0 | 分布式时钟同步信号 | PB1/EXTI1或定时器输入 |
| SYNC1 | DC同步信号(可选) | PB2 |
| nRESET | 复位输入,低有效 | PB3/GPIO |
| EEPROM_SCS/SCLK/MOSI/MISO | SII EEPROM接口,接SPI EEPROM | 不需要接STM32 |
AX58100必须外接一颗SPI EEPROM用于保存SII(Slave Information Interface)配置数据,类似从站的"身份证"。这颗EEPROM里包含Vendor ID、Product Code、从站名称、PDO默认配置等信息,主站扫描从站时首先读取的就是这些数据。推荐选择128Kbit以上的SPI EEPROM,比如Microchip的AT25128B或者AT25HP512这类,注意选SPI接口的,不要买成I2C接口的24C系列。EEPROM挂在AX58100自己的SPI接口上,和STM32没有直接关系,但后续用SSC工具生成SII配置后,要把它烧录进去。
2.2 STM32 SPI主机配置要点
STM32和AX58100之间的数据交互主要靠SPI,这部分底层驱动直接决定了通信的稳定性。工程里我用的是STM32F407的SPI1,配置成主机模式,8位数据帧,MSB先行,时钟极性CPOL=0、相位CPHA=0,也就是SPI Mode 0。AX58100数据手册里SPI从站接口在上升沿采样数据,对应就是Mode 0。初始化代码大致是这样:
/* SPI1 GPIO和硬件初始化 */ void AX58100_SPI_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; SPI_InitTypeDef SPI_InitStruct = {0}; __HAL_RCC_SPI1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /* SCK, MOSI, MISO */ GPIO_InitStruct.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF5_SPI1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* CS引脚,普通推挽输出 */ GPIO_InitStruct.Pin = GPIO_PIN_4; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); /* CS默认拉高 */ SPI_InitStruct.Mode = SPI_MODE_MASTER; SPI_InitStruct.Direction = SPI_DIRECTION_2LINES; SPI_InitStruct.DataSize = SPI_DATASIZE_8BIT; SPI_InitStruct.CLKPolarity = SPI_POLARITY_LOW; SPI_InitStruct.CLKPhase = SPI_PHASE_1EDGE; SPI_InitStruct.NSS = SPI_NSS_SOFT; SPI_InitStruct.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; /* 84MHz/8 = 10.5MHz */ SPI_InitStruct.FirstBit = SPI_FIRSTBIT_MSB; HAL_SPI_Init(&hspi1); }这里特别强调一下SPI时钟频率。AX58100理论上可以支持较高的SPI时钟,但实际Layout布线、杜邦线长度、芯片批次都会影响极限速率。我调试期间从1MHz起步,确认通信无误后逐步升到10.5MHz(84MHz主频8分频),稳定运行没有丢包。如果你的板子上SPI走线比较长或者没有包地,建议保守一点,控制在5MHz以内,丢包率会明显下降。另一个容易被忽略的细节是SPI的NSS必须配置为软件管理,让CS引脚完全由GPIO控制,否则硬件NSS在传输过程中可能产生多余的片选脉冲,导致AX58100误判帧边界。
2.3 IRQ中断和SYNC信号的接入方式
EtherCAT从站不能只靠轮询,否则主站状态机跳转、新数据到达等事件都可能错过。AX58100的IRQ引脚在发生本从站相关事件(比如收到新的过程数据、邮箱有消息、AL状态机请求变更)时会拉低,STM32需要对应配置一个外部中断引脚,将IRQ接入EXTI线。中断服务函数里要做的事情是读取AX58100的事件请求寄存器(地址0x0200-0x020B),逐个判断是AL事件、SM事件还是DC事件,再分别处理。注意中断服务函数里只做"读事件+标记变量"的快速处理,真正的业务逻辑放到主循环,避免中断嵌套导致实时任务被拖垮。
SYNC0/SYNC1信号主要用于DC同步模式,AX58100根据主站下发的分布式时钟配置,在自己的同步单元周期里输出脉冲。如果从站只是低速IO类设备,跑FreeRun模式不接SYNC也能工作;但如果要做伺服驱动或者高速数据采集,SYNC0就是整个应用的时间基准,建议接到STM32的定时器输入捕获引脚或者另一个EXTI线,在SYNC脉冲中断里完成ADC采样、数据锁存等操作。这种设计下,应用的控制周期就和EtherCAT主站完全同步,抖动可以控制在几百纳秒以内,这是靠MCU自身定时器做不到的。
3. SSC V5.11工具实操:生成EtherCAT从站代码的完整步骤
3.1 工具安装与准备工作
SSC(Slave Stack Code)工具是EtherCAT Technology Group(ETG)提供的免费代码生成工具,准确说它是个Windows环境下运行的向导程序,自动生成从站协议栈的C源码工程骨架。首先要去ETG官网注册账号并同意协议,在下载中心找到SSC工具,注意区分BETA版本和正式稳定版本,开发建议用正式版,我这次用的就是V5.11。下载后解压,目录下有SSC.exe(图形界面主程序)、SSC.pdf等文档,还有一些自动生成的演示从站工程模板。V5.11版本相比老版本在配置界面上有较大调整,对象字典和PDO映射的配置逻辑更贴近XML(ESI文件)的层次。
安装本身没什么坑,但有一点要提醒:SSC生成的代码默认是针对特定ESC类型和特定头文件的,工具内部自带了一些ESC型号模板,其中包含AX58100的选项。如果你用的SSC版本比较老,列表里找不到AX58100,最保险的做法是选择"Generic ESC"或"AX58100"出现过的分支版本,再在后续配置里手动改ESC型号宏。当然最好还是用新版本,直接选AX58100,避免后续手动改导致寄存器初始化时序不一致。
3.2 新建工程与关键配置项逐项讲解
打开SSC工具后,点击File -> New Configuration,会进入一个分步配置向导。每一步我都记一下当时的填写内容和我踩过的坑。
第一步是设置从站基本标识信息:Vendor ID、Product Code和Revision Number。Vendor ID是ETG分配给厂商的唯一ID,正式产品必须用自己公司的ID。个人学习或者项目预研阶段,可以先填0x00000002(ETG留作测试用的ID)或者自定义的数值,不会影响功能测试,但后续一旦要连接第三方主站做正式场合验证,就需要在ETG官网申请正式Vendor ID。Product Code自己定义就行,用来区分同一厂商的不同产品型号。
第二步选择ESC型号和PDI(Process Data Interface)。ESC型号这里我选了AX58100,PDI接口选择SPI Slave。注意这里有两个SPI相关选项,一个是"SPI Slave"(AX58100作为SPI从设备,STM32作为主设备访问它),另一个类似"SPI Master"的配置是给AX58100内部功能用的(比如访问外部EEPROM),不要选错。PDI选错的话,后面生成的代码里SPI读写接口完全不同,编译肯定过不了。
第三步配置Mailbox邮箱协议。EtherCAT从站的邮箱通道用于非周期数据,比如SDO参数读写、FoE固件升级。我勾选了CoE(CANopen over EtherCAT)协议,这在绝大部分工业伺服、IO模块里都是标配,后续使用TwinCAT或者汇川PLC做参数配置就靠它。FoE要不要勾取决于你的产品是否有在线升级需求,建议勾上,整个协议栈代码也就多几十KB Flash,但带来的是后续远程升级能力。邮箱数据大小在V5.11里可以直接填1536字节(ETG推荐值)或者更小的512字节,如果从站是简单的IO模块,512字节足够。
第四步配置同步模式。V5.11工具里会让你勾选从站支持的同步类型:FreeRun、SM-Synchronous、DC-Synchronous。这里我的做法是全部勾选,由主站通过CoE配置来最终决定运行时采用哪种模式。这样兼容性最好,主站可以根据自己的配置灵活选择。但要注意,代码里关于DC同步的宏如果没被正确使能,会出现主站使能DC模式后从站仍然按FreeRun运行的问题,症状就是TwinCAT里报DC同步丢失。
第五步对应过程数据PDO的初始定义。这一步比较关键,在V5.11里有个独立的"Process Data"标签页,可以手动添加RxPDO(主站到从站,即输出,比如DO的开关值)和TxPDO(从站到主站,即输入,比如DI的采集值和AI的采样值)。我新建的示例从站配置了:一个RxPDO包含4字节的DO输出数据,一个TxPDO包含8字节的DI输入数据和8字节的AI模拟量采样数据。PDO项与对象字典的映射关系(例如RxPDO里每个子项对应0x6200对象下的哪个子索引)也在这一页里完成,填错了后面主站看到的PDO内容就会错位。
第六步是选择应用层类型。V5.11提供"Custom Application"、"CiA402 Drive Profile"等选项。我做的通用IO从站,选了Custom Application,生成的代码里应用层是一个空壳,你可以在里面添加自己的业务逻辑。如果你要做的是伺服驱动器/变频器这一类运动控制从站,就应该选CiA402,工具会自动生成符合CiA402规范的对象字典雏形(0x6040控制字、0x6060运行模式等都会预设好),能省很多造轮子的时间。
完成以上配置后,最后一步点击Generate生成代码,工具会提示选择输出目录,并把配置好的从站代码工程写出来。务必注意保存配置工程文件(.ssc文件),因为后面改PDO映射或者对象字典,重新生成代码时还要用这份配置,否则又要从头填写几十页配置项。
3.3 代码结构速览与STM32工程集成
生成出来的代码是一个标准的C工程骨架,目录大致是:
src/:协议栈核心代码,包括EtherCAT状态机、CoE处理、SII读写等src/app/:应用层模板,app.c、app.h,主要业务逻辑写在这里src/esc/:ESC硬件访问层,SPI读写、中断处理入口src/port/:与MCU相关的移植层,比如GPIO、中断配置objdef.h、ssc_config.h等:配置头文件,由SSC工具自动生成,不要手动改
在把这些代码并入STM32工程之前,我的习惯是先在SSC生成的原始目录里看一眼ssc_config.h,确认几个核心宏是否和预期一致——SSC_USE_ESC必须为1(表示外部ESC芯片),SSC_USE_MAILBOX、SSC_USE_COE、SSC_USE_DC这些按需求打开,SSC_PDI_CTRL相关的宏要和SPI从站模式对应。由于SSC生成的代码并没有绑定具体的MCU型号,SPI底层函数(比如ESC_Read()、ESC_Write())默认只是空函数或者针对示例硬件做了占位,需要你根据STM32的SPI外设重新实现。
我把SSC生成的sSC_...目录下的.c和.h文件全部拷贝到Keil MDK工程里,然后在app.c中看到SSC预留的几个关键函数:APPL_InputUpdate()(应用输入更新,从PDO缓冲区读出主站指令)、APPL_OutputUpdate()(应用输出更新,把采集数据写入PDO缓冲区)、MAIN_Process()(应用层周期任务入口)。这几个函数就是你在STM32工程里发挥的地方。强烈建议先保持SSC生成的代码“原样不变”编过一次,确认协议栈本身没有编译问题,再动应用层。SSC生成的代码对C99特性有依赖,Keil MDK里需要在Options for Target -> C/C++ -> C99 Mode打勾,否则会报"declaration may not appear after executable statement"之类的错误。
3.4 对象字典和PDO映射的前后台逻辑
对象字典(Object Dictionary)是EtherCAT从站参数体系的核心,主站通过CoE访问对象字典来读写从站参数。在SSC V5.11里,对象字典的配置和PDO映射的配置是相互关联的,但理解的时候要分开看。
对象字典本身是逻辑上的一个索引表,以0x6000开头的区域就是厂商自定义应用对象。我在示例工程中新增了0x6200(DI输入对象)、0x6401(DO输出对象)、0x6402(AI模拟量输入对象),每个对象下又分了若干子索引。定义方式是编辑objdef.h文件,添加一个对应的条目并关联到PDO映射。SSC工具里填写时它会同步生成对应代码,但如果后续手动修改了对象字典(比如加了新对象),一定要回到SSC工具中重新配置一次并再生成一遍,否则容易造成objdef.h和配置不一致,编译出来跑起来行为怪怪的。
PDO映射解决的是"过程数据帧里的每个字节对应哪个对象"的问题。主站往从站发的过程数据包,默认情况下按字节排列在SM2缓冲区里;从站往主站发的数据按字节排列在SM3缓冲区里。SSC工具在配置PDO映射时会生成一段映射关系,把0x6200.01这个子索引对应的数据放到TxPDO的第一个字节位置,0x6401.01放到RxPDO的第一、第二个字节。这个映射关系在生成代码后被主站读取(ESI文件里也有),两边必须完全一致。如果主站侧和从站侧PDO映射不一致,典型现象就是TwinCAT里过OP时能正常进入,但过程数据里的值全部错位或者出现InvalidMapping错误。遇到这类问题,先别怀疑硬件,拿Wireshark抓包对比PDO头条和值,或者直接在TwinCAT里看PDO映射表,基本一眼就能找出错位。
4. 应用层编写与整机联调:从站代码的落地与验证
4.1 填补HAL层:让SPI读写和中断真正工作
SSC生成的代码框架默认假设ESC的SPI读写函数已经存在,我在工程里主要实现了以下三个关键函数。
第一个是ESC_Read(),负责从AX58100指定寄存器地址读取指定长度的数据。AX58100的SPI访问机制是:先由STM32发16位寄存器地址(高位在前),再按字节连续读写。我对照数据手册的PDI章节时序,用HAL库封装了单字节、多字节两种变体,多字节场景下直接连续调用HAL_SPI_TransmitReceive,避免频繁翻转CS引脚导致效率下降。
static void AX58100_SPI_Read(uint16_t reg_addr, uint8_t *data, uint16_t len) { uint8_t addr_h = (uint8_t)((reg_addr >> 8) & 0xFF); uint8_t addr_l = (uint8_t)(reg_addr & 0xFF); HAL_GPIO_WritePin(ESC_CS_GPIO_Port, ESC_CS_Pin, GPIO_PIN_RESET); /* 发送16位寄存器地址,读写标志位在地址字节中由AX58100识别 */ HAL_SPI_Transmit(&hspi1, &addr_h, 1, 10); HAL_SPI_Transmit(&hspi1, &addr_l, 1, 10); /* 之后全部是读数据 */ HAL_SPI_Receive(&hspi1, data, len, 100); HAL_GPIO_WritePin(ESC_CS_GPIO_Port, ESC_CS_Pin, GPIO_PIN_SET); }第二个是ESC_Write(),写寄存器场景。第三个是ESC_IRQHandler(),它是中断服务函数的框架。IRQ进来后读取0x0200-0x0205这几个事件寄存器,判断是否为AL状态机事件,如果是AL事件,则调用状态机处理函数AL_Event();如果是SM事件且有新数据,则设置一个全局标志g_new_pdo_flag = 1,主循环看到这个标志后执行PDO读写操作。
SPI读写的代码稳定后,我用一个最简单的自测方法验证:读写AX58100的ESC类型寄存器(地址0x0000),正常应该读出0x0C(AX58100的ESC类型标识),同时复位读取DL状态寄存器(0x0110)查看link状态是否正常。如果读到0xFF或者超时,说明SPI时序、引脚连接或者复位电路有问题,先用逻辑分析仪查波形,再排查硬件。
4.2 主循环任务划分与应用层逻辑编写
从站代码跑在STM32上,整体是一个"中断驱动+主循环轮询"的结构。main函数里初始化完硬件后进入死循环,循环里做四件事:调用EtherCAT_StateMachine()让协议栈状态机按需流转;检查g_new_pdo_flag,为真则调用APPL_InputUpdate()读取主站输出并更新GPIO输出;周期调用APPL_OutputUpdate()把DI和AI数据写入PDO缓冲区;最后调用一个LinkCheck()定时监控网线状态和AL状态寄存器,异常时点亮错误LED。这个结构不需要RTOS,裸机完全够用。
以DI/DO/AI逻辑为例,我实现的代码片段是:
void APPL_OutputUpdate(void) { /* 把PDO输入缓冲区里、主站下发的数据映射到硬件输出 */ if (g_new_pdo_flag) { uint8_t do_data = App_OUT_data[0]; /* 来自RxPDO的1字节DO */ HAL_GPIO_WritePin(DO_GPIO_Port, DO_0_Pin, (do_data & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(DO_GPIO_Port, DO_1_Pin, (do_data & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET); /* 后续引脚依此类推 */ g_new_pdo_flag = 0; } } void APPL_InputUpdate(void) { uint8_t di_data = 0; if (HAL_GPIO_ReadPin(DI_0_GPIO_Port, DI_0_Pin) == GPIO_PIN_SET) di_data |= 0x01; /* 采集其他DI引脚... */ App_IN_data[0] = di_data; /* 写入TxPDO缓冲区 */ /* AI模拟量通过ADC采集写入 App_IN_data[1..4],略 */ }这里有个我一开始忽略的细节:SSC生成的代码里,PDO缓冲区变量名可能是App_OUT_data、App_IN_data这么宏定义的,也可能在app.h里重命名,具体要看生成代码里定义的变量名和类型。建议先编译通过后,在MAIN_Process()里用调试器单步查看PDO缓冲区地址和变量名,再写业务代码。另外要注意APPL_OutputUpdate()是由主站周期指令驱动的,如果主站没进入OP状态,这个函数不会被周期调用,所以不能把开机自检之类的逻辑放在这个函数里,否则主站未连接时设备就一直不工作。
4.3 用TwinCAT做主站联调的正确姿势
从站代码烧录进STM32后,接下来就是和EtherCAT主站联调。我在电脑上装了TwinCAT 3 XAE(倍福的免费EtherCAT主站软件,实时模式下功能完全够用),准备一根网线直连到AX58100的EtherCAT端口。如果电脑网卡不支持,可能需要额外购买支持EtherCAT的网卡或者用USB转EtherCAT适配器。打开TwinCAT XAE后,新建一个工程,在I/O视图下右键Device -> Scan,扫描到本从站后,正常会弹出提示发现新设备,然后自动导入从站描述文件。
如果扫描不出来,先看AX58100的link指示灯有没有亮,不亮说明物理层有问题;灯亮但扫不到,去TwinCAT里尝试Manual添加设备,手动输入Vendor ID和Product Code,看能不能创建出从站。创建成功后,把从站切到OP状态:先设置状态机为PREOP,再SAFEOP,最后OP。每一步都可以在Control Panel对话框里观察状态是否切换成功,失败时看错误码和AL Status Code。
第一次切OP时我遇到过最典型的问题是AL Status Code 0x0019(Invalid Output Mapping)或者0x0011(Invalid SM Configuration),定位方法很简单:在TwinCAT的Process Image窗口里检查Sm2和Sm3的长度、同步模式是否和从站实际配置一致;再检查CoE对象字典中0x1C12/0x1C13(同步管理器PDO分配参数)是否正确。一般情况下,重新回到SSC工具,检查PDO映射的SM分配,把对应项修正并重新生成再烧录就能解决。整个联调过程里,我的经验是把状态机切换一步一步来,不要直接从INIT跳到OP,这样出问题能精准定位到是哪一级配置不满足。
5. 常见问题与排障技巧实录:SSC V5.11运行期踩坑汇总
5.1 从站扫描阶段的问题
扫描不到从站大概是我做EtherCAT开发以来遇到最频繁的问题,表现形式也多种多样:Vendor ID读出来是0xFFFFFFFF、TwinCAT显示"Device not responding"、主站扫描直接卡死。排查顺序我固定为:先看AX58100的Link灯(对应PHY链路是否建立),再测SPI(读ESC类型寄存器确认0x0000读出来正确),然后读DL状态寄存器确认链路状态,最后看SII EEPROM里烧录的Vendor ID和Product Code是否与预期一致。大多数情况下,最后一步才是罪魁祸首——EEPROM没有烧录或者烧录数据不完整,主站读不到厂商信息自然就无法识别从站。SSC生成的代码里,SII数据保存在SII_Data[]数组里,编译时会以固件形式加载到AX58100的EEPROM操作接口中。如果希望从站主控能在线写SII,需要额外写一个SII下载函数把数组合成EEPROM格式写入。但最省事的还是用AX58100配套的上位机工具,通过AX58100的一个特制端口直接把SII配置烧进EEPROM,烧录后重新上电扫描。
5.2 状态机切换和通信运行期问题
进入OP状态后,过程数据不动、输出不刷新、偶发掉线这类问题和前面扫描问题又不一样,往往需要结合寄存器和事件标志来排查。我在这边做了一个现象到原因的对照表,实际排查时按优先级从上往下看:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 从站扫描正常,但OP切换失败,AL状态码0x0011 | SM设置错误,比如SM长度小于PDO映射长度 | 检查0x0800区域SM0/1/2/3的配置,重点核对长度寄存器 |
| OP状态下写DO值无反应 | RxPDO映射位置和应用层读取位置不一致 | 在TwinCAT在线监控Process Image,确认DO字节在数组的第几位;在代码中把整个PDO缓冲区print出来对比 |
| 主站报Watchdog超时/从站掉线 | SPI通信不稳定、中断响应不及时 | 降低SPI时钟,检查IRQ引脚是否存在长毛刺,评估中断服务函数耗时 |
| 读取AI数据跳动大 | TxPDO更新周期和ADC采样周期不同步 | 把ADC采样放到SYNC0中断中,保证数据锁存周期和EtherCAT周期一致 |
| CoE读写对象字典超时 | 邮箱缓冲区设置过小,或者邮箱处理流程未正确调用 | 检查SM0/SM1长度是否与CoE传输缓冲区匹配,日志里看邮箱状态寄存器是否一直挂在"处理中" |
这里展开讲一下Watchdog超时这个最让人头疼的问题。EtherCAT主站从站侧都有一个看门狗机制,主站期望在一定时间内收到从站送出的帧,超过时间就视为从站掉线。一开始我的SPI时钟跑到10.5MHz,短物理连接下正常,但把从站板子用一根长网线引出、同时主站CPU负载拉满时,偶发掉线就出现了。用逻辑分析仪看SPI波形,发现倒不是SPI本身发错,而是MCU中断处理被其他高优先级任务抢占,导致IRQ事件响应时间抖动,从站侧更新PDO的间隔变得不固定,触发了看门狗超时。解决办法是:把SPI中断优先级调到最高(抢占优先级0,子优先级0);IRQ中断服务函数里只做标志置位,数据处理全部放主循环;同时主循环里给EtherCAT协议栈处理预留一个固定的时间片,不让其他逻辑抢占太多。调整后连续跑了72小时,没有再出现掉线。
5.3 SSC工具本身的几个坑和规避方法
SSC V5.11以及相关版本在安装与使用上也有一些细节值得记录。一是工具安装路径不能有中文和空格,否则生成的工程里某些源文件的相对路径包含非ASCII字符,Keil打开后会出现莫名其妙的头文件找不到错误。二是SSC工具每次打开配置工程时,会默认加载上一次的缓存设置,如果你在多个项目间切换,最好每次File -> New Configuration后重新手动确认ESC型号和PDI选项,我吃过一次亏——从做一个并行接口项目切换到SPI项目,却忘了改PDI,生成出来的代码里SSC_PDI_CTRL宏还是并行接口模式,烧进去SPI完全不通。三是SSC生成的代码中,ssc_config.h里的某些宏(比如SSC_POPULATE_OD)在不同版本默认值不同,升级工具版本后对比一下生成的配置文件和旧版本差异,能避免很多莫名其妙的行为变化。
另外,在修改对象字典和PDO映射之后,一定要重新生成代码并彻底重新编译。SSC工具并不会自动增量更新你的工程文件,它只是覆盖生成目标目录下的全部代码。如果你在旧代码基础上手改过objdef.h,重新生成后这些改动会被抹掉,所以我强烈建议:业务逻辑代码(app.c里那部分)尽量独立,不要改动SSC生成的协议栈本体文件,这样每次重新生成代码时只需要手动合并自己业务逻辑文件即可,省去反复编辑的痛苦。我自己维护一个user_app.c,单独添加进工程,SSC重新生成后只需要把app.c里几个回调函数填充到user_app.c,工作量很小。
最后,简单说说这个项目做到这个阶段的体会。现在这套IO从站已经在实验室跑通了,主站用TwinCAT和汇川的H5U系列PLC都验证过,扫描、配置PDO映射、读写DI/DO正常,CoE读写参数也没有问题。接下来如果想扩展,可以把AX58100内部集成的QEI接口和PWM接口用起来,把通用IO从站升级成一个带编码器反馈、带PWM输出的简易伺服控制从站,做样机验证和内部项目预研完全够用。核心流程和代码生成路径是一样的,只是对象字典需要按CiA402规范去定义。总的来说,AX58100加STM32这套方案把EtherCAT从站开发门槛降了一大截,再配合SSC工具规范化的代码生成流程,从选型到跑通一个最小系统,两三天时间比较现实。希望这篇记录能帮正在做同样方案的朋友少走几个弯路。