ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

STM32F103+LAN9252 EtherCAT从站迁移:5个硬件坑与解决

STM32F103+LAN9252 EtherCAT从站迁移:5个硬件坑与解决 去年接了一个活儿要把一套EtherCAT从站方案从STM32F407迁移到STM32F103上从站控制器用的是LAN9252。收到需求的时候我心里觉得这事不难——LAN9252本身就是EtherCAT从站控制器EtherCAT报文处理、FMMU、SyncManager这些都在芯片内部完成了F103只需要通过SPI读写寄存器处理过程数据算力完全够用。可真正动手之后代码层面的迁移反而没花多少时间真正折磨人的是一连串硬件坑。这篇文章把我在迁移中踩过的5个坑完整记录下来包括现象、排查过程和最终解决方案给准备用STM32F103LAN9252做从站开发的朋友做个参考希望能帮你少走几天弯路。1. 迁移前必须想清楚的一件事F103到底负责什么1.1 为什么F103LAN9252这个组合能成立先说清楚这个组合为什么可行。EtherCAT从站在硬件上分两层一层是ESCEtherCAT Slave Controller负责物理层和数据链路层另一层是MCU负责应用层。LAN9252就是Microchip的ESC芯片内置了EtherCAT数据帧处理、环回、FMMU、SyncManager、分布时钟和状态机MCU要做的其实是“站在旁边等结果”等LAN9252把过程数据从EtherCAT报文里剥离出来放到内部的DPRAM里再用SPI把数据搬走处理完之后把应用层数据写回去。对16路数字量IO这种简单从站来说F103完全够用这也是为什么很多工控板卡会用这个组合来降成本。F407和F103之间SPI读写流程、应用层代码迁移过去并不难难点在于两个芯片对外的电气特性、时钟树、中断资源、复位要求都不一样这些属于参考手册里写得比较散、又恰恰容易被迁移代码带过去的东西。1.2 迁移的边界软件可以搬硬件依赖不能想当然我当时划分的迁移范围是这样的SPI驱动直接迁移但需要重新核对GPIO配置和时钟分频LAN9252寄存器配置流程直接迁移ESC寄存器地址是芯片规定的与MCU无关应用层IO映射、状态机逻辑直接迁移中断处理重点检查F103的EXTI和F407有细节差异复位控制、时钟源、调试串口全部重做最典型的错误就是把LAN9252当普通SPI外设来用只改引脚不改依赖。实际上从站能不能起来和复位时序、中断信号、时钟源、调试串口都有关系。我在出发前列了一个五个维度的检查清单复位信号、中断信号、SPI信号、时钟源、调试串口。后来回头看这五个维度刚好对应了我踩进去的五个坑一个都没跑掉。1.3 我出发前列的检查清单五个硬件维度检查项重点关注SPI引脚模式、总线时钟分频、首次访问时序中断IRQ电气特性、EXTI编号冲突、中断服务函数负载复位复位信号持续时间、释放时序、GPIO默认电平时钟源LAN9252的25MHz晶振、F103的HSE范围、MCO/CLKOUT互供风险调试串口USART1与USART3所在总线时钟差异、中断向量号如果你也是从别的平台迁到F103建议先把这张表过一遍能避掉大部分问题。下面逐个说坑。2. 坑一SPI初始化“看起来一样”MISO却一直回0xFF2.1 现象TwinCAT扫描不到从站MISO像焊死了一样板子打样回来烧录迁移后的固件用TwinCAT扫描EtherCAT从站结果是扫描不到。LAN9252的供电3.3V量了正常25MHz晶振也起振了但是从站指示灯的状态明显不对。用万用表顺了一遍SPI引脚通断都正常没有虚焊也没有短路。当时第一反应是LAN9252没正常工作可能焊接问题换了一颗芯片还是一样。回头怀疑F103的SPI配置但代码是从F407上直接迁移过来的SPI1初始化几行代码看起来完全一致。这种“看起来没问题但就是不通”的状态往往说明问题不在逻辑而在物理层配置。2.2 排查链路从GPIO模式到首次访问时序用逻辑分析仪抓SPI三根线和片选信号SCK和MOSI上确实有正常的波形但MISO一直保持高电平读回来的数据全是0xFF。这说明F103的SPI外设虽然在发命令但根本没有把MISO的数据采样进来。开始逐行对比F407和F103的SPI初始化代码发现唯一的差异藏在GPIO配置里。原来的代码中SCK和MOSI配置成复用推挽输出MISO配置成浮空输入这是标准做法。但迁移时我从某个示例工程里复制了一份GPIO初始化代码那里面MISO被错误地配置成了复用推挽输出。F103的GPIO配置不像F4那么“宽容”MISO一旦被配成输出SPI外设就永远读不回数据表现出来就是MISO被拉高读回0xFF。把MISO改回浮空输入之后MISO引脚有波形了但读回来的数据还是不稳定的。继续查又发现第二个问题代码在SPI初始化之后立即就去读LAN9252的寄存器而LAN9252在上电复位之后还需要一段时间才能接受SPI访问。F103的启动速度比F407快这个问题在F407上可能被其他初始化流程掩盖了到了F103就暴露出来前几次SPI读写全是无效数据。2.3 根因与解决两条“看着不影响”的错误叠加两个问题叠加一个在GPIO模式一个在访问时序。解决起来并不复杂MISO必须配置成浮空输入或上拉输入不能是复用推挽输出SPI初始化完成后加至少10ms延时然后读LAN9252的BYTE_TEST寄存器地址0x0000确认通信正常再继续后续流程// 正确的SPI1 GPIO初始化F103 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; // SCK/MOSI复用推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // MISO浮空输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure);另外一个细节要注意LAN9252的SPI从站接口支持Mode 0和Mode 3迁移时最好固定使用和原平台一致的SPI模式不要中途切换。项目里见过有人从网上拼了两段代码分别是Mode 0和Mode 3导致SPI通信偶发错误表面看就像时序不稳定实际上只是CPOL和CPHA不一致。这类问题一旦锁定用示波器抓SCK空闲电平就能发现空闲低电平是Mode 0空闲高电平是Mode 3一眼判断。3. 坑二中断引脚选错位置EtherCAT状态机卡在INIT3.1 现象状态机卡在INITIRQ引脚电平不对劲SPI通信打通之后TwinCAT能扫描到LAN9252了但新的问题又冒出来从站状态机可以进INIT但切到PRE_OP之后一旦主站开始发过程数据帧从站就频繁掉线甚至卡死在INIT进不了SAFE_OP和OP。LAN9252是通过IRQ引脚通知MCU“过程数据准备好了”或者“有事件需要处理”的如果MCU收不到中断或者中断响应不对状态机就跑不起来。当时用示波器看IRQ引脚发现一个问题IRQ引脚的电平是浮空的高电平和低电平都不稳定中间还有一段电平在1V左右游荡。3.2 排查链路开漏输出、上拉电阻、EXTI线冲突查LAN9252数据手册IRQ引脚是开漏输出内部没有上拉电阻外部必须加上拉否则电平根本拉不上去。这是一个很经典的电气特性坑。原来的F407板子上IRQ引脚旁边正好有别的器件带了上拉所以一直没暴露换了F103的板子布局变了上拉电阻没画上去IRQ就变成了一根“悬浮线”。第二个问题更隐蔽。IRQ一开始接在F103的PB0上配了EXTI0和NVIC按理说是可以的。但看板子的完整原理图发现板上另一个传感器用到了PA0的外部中断。F103的EXTI0到EXTI15同一编号只能服务一个引脚PA0和PB0共用EXTI0线结果就是两个中断信号互相干扰中断行为完全错乱。有时候是另一个传感器频繁触发中断有时候是LAN9252的中断被冲掉表现就是从站状态机一直起不来。3.3 解决后的经验中断服务函数不能干重活解决方法是三板斧LAN9252的IRQ引脚外部加上拉电阻一般10k到3.3V把IRQ换到没有被占用的EXTI线比如PB1同时确认PB1对应的EXTI1没有被其他外设占用中断服务函数里只做快速处理先读LAN9252的IRQ状态寄存器清标志置一个事件标志后立即退出数据处理放到主循环void EXTI1_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line1) ! RESET) { // 置事件标志不在这里做SPI读写 g_lan9252_event 1; EXTI_ClearITPendingBit(EXTI_Line1); } }这里特别强调一下不要在中断服务函数里做阻塞式SPI读写。F103的SPI一次读写在几微秒到几十微秒看起来不长但如果LAN9252在通信过程中持续产生中断中断服务函数里做SPI传输很容易造成重入或者忙等最终表现为系统死机或状态机卡死。让中断服务函数只负责“通知”数据搬运放主循环实测稳定很多。顺带说一个相关经验如果板子上的LAN9252 IRQ接了PA11或者PA12同时你又想用USB调试就会遇到F103的PA11/PA12引脚复用冲突问题。PA11/PA12默认关联USB外设即使不用USB配置成普通IO时也要小心外设时钟是否被意外开启。如果板子同时有USB和LAN9252建议IRQ避开PA11/PA12。4. 坑三复位时序不对LAN9252“假死”4.1 现象十个模块总有那么两三个上电起不来通信和中断都调通之后做小批量测试时又发现一个更头疼的问题一批10个从站模块上电后总有那么两三个起不来需要手动断电再上电才有概率恢复正常。这种偶发性故障是最难排查的因为它不是每次都出现而且看起来和代码逻辑无关。用示波器抓了出问题模块的3.3V电源上电瞬间电源确实有跌落但跌落幅度还算正常不至于让芯片完全复位。继续抓LAN9252的nRESET引脚问题浮出水面复位引脚的波形非常凌乱不是干净的单次低电平脉冲而是在低电平和高电平之间反复抖动了几次最后才稳定在高电平。4.2 排查链路RC复位和GPIO默认电平的锅第一版原理图上LAN9252的nRESET用了简单的RC复位电路一颗电阻加一颗电容。这种复位电路在芯片功耗不大、电源上升沿理想的情况下问题不大但这块板子上LAN9252、F103和其他外设同时上电3.3V电源上升过程中存在波动RC复位电路很容易在阈值附近反复翻转导致nRESET信号出现多次抖动。LAN9252内部逻辑在复位信号抖动时处于不确定状态后续SPI访问也就不稳定。另一个问题出在代码里。控制LAN9252复位脚的GPIO在上电初始化时先被配置成了输出低电平这意味着F103一上电就把LAN9252按住复位。如果后面初始化流程有任何地方失败了跳过了拉高语句LAN9252就一直起不来。这个设计在逻辑上看似没问题但GPIO默认电平是不可控的如果初始化顺序不对复位脚就会一直处于“按住”状态。4.3 解决后的复位时序拉低、等待、拉高、再等待最终的解决方案是把RC复位改成F103的GPIO主动控制复位时序顺序固定为复位脚配置成推挽输出输出低电平延时20ms确保LAN9252电源和时钟稳定拉高复位脚再延时5ms然后才允许SPI访问LAN9252这里有个细节GPIO初始化的顺序要注意一定要先设置默认电平再配置模式。标准库的GPIO_Init通常会在配置过程中改变引脚状态如果先配置模式再设置电平中间可能会出现一个短暂的低电平窗口。对复位脚来说这个短暂的窗口就可能造成上电时LAN9252被意外复位。先调用GPIO_SetBits拉高再调用GPIO_Init配置成推挽输出能把这个窗口完全堵住。// 控制LAN9252复位脚的GPIO初始化F103 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_SetBits(GPIOC, GPIO_Pin_0); // 先默认拉高 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOC, GPIO_InitStructure); // 然后执行复位时序拉低20ms拉高等5ms GPIO_ResetBits(GPIOC, GPIO_Pin_0); DelayMs(20); GPIO_SetBits(GPIOC, GPIO_Pin_0); DelayMs(5);还有一个提示LAN9252的nRESET释放之后到芯片能够正常响应SPI中间还有一段时间。不要按数据手册的最小值去卡上限工程上要给足余量。我实际使用过程中20ms加5ms的组合非常可靠即使电源质量差一点的板子也能稳定启动。5. 坑四25MHz晶振的“省钱”方案把F103也坑了5.1 现象共享25MHz时钟的想法直接把系统搞崩这个坑说实话是自己给自己找的。当时画原理图阶段有同事提出一个“省料”方案LAN9252需要25MHz晶振F103外部也可以接晶振能不能让F103从MCO引脚输出一个25MHz时钟给LAN9252省掉一颗晶振听起来很诱人实际做的时候立刻翻车。第一轮尝试F103的主晶振还是8MHzMCO输出只能从8MHz、8MHz二分频、PLL时钟二分频等选项里选输出不了25MHzLAN9252根本没起振。第二轮更激进直接把F103上的8MHz晶振换成了25MHz结果F103连程序都跑不起来调试器连接都困难。5.2 为什么F103不能直接吃25MHzHSE范围那点事问题的根子在F103的HSE输入范围。STM32F103的HSE支持范围是4MHz到16MHz25MHz已经超出了规格。F103内部PLL的倍频系数是有限的一组值想从25MHz倍频到72MHz系统时钟根本找不到整数倍频组合。强行用25MHz晶振PLL无法锁定系统时钟也就不正常程序自然跑不动。还有一个反向操作同样不可行用LAN9252的CLKOUT引脚输出25MHz时钟给F103当系统时钟。LAN9252的CLKOUT确实能输出25MHz但F103吃不了25MHz的HSE这个方案在F103上从根上就不成立。如果是其他支持更宽HSE范围的MCU也许有戏但在F103上不要折腾。5.3 正确做法两颗晶振互不打扰最终方案就是老老实实用两颗晶振LAN9252配一颗25MHz晶振F103配一颗8MHz晶振相互独立。在做PCB布局时两个晶振都要靠近各自芯片走线尽量短负载电容按数据手册推荐值选。LAN9252的晶振两脚走线不要平行绕远不然会引入杂散电容导致起振困难。LAN9252的CLKOUT引脚如果不用悬空或者通过一个小电容接地都可以不要让它悬空太长走线避免对周围信号产生辐射干扰。有一点要提一下如果你以后还想做更复杂的EtherCAT从站比如需要分布时钟同步精度很高时钟源的质量会影响同步精度。LAN9252的晶振建议选择精度高一点的最好用有源晶振或者至少用无源晶振加合适负载电容不要为了省几毛钱影响整个系统的同步性能。F103这边8MHz晶振也尽量选低ESR的关系到串口波特率精度和USB的稳定性。6. 坑五调试串口1正常、串口3乱码问题出在总线时钟6.1 现象串口1正常、串口3乱码从站逻辑全部调通之后要把调试日志跑起来。代码里用USART1打印核心日志用USART3打印扩展调试信息。结果USART1输出完全正常USART3输出的日志全是乱码偶尔连数据都没有看起来就像串口3的引脚配置错了或者芯片坏了。一开始真以为USART3的引脚没配好检查GPIO复用没问题波特率配置看起来也对。后来用示波器抓USART3的TX引脚发现波形是有的方波很干净但时间间隔明显不对用115200解码全是乱码用230400解码反而能看出一点规律。6.2 排查链路示波器解码和APB1/APB2差异这就说明实际波特率大约是配置值的两倍。问题出在哪查F103的时钟树USART1挂载在APB2总线上APB2最高72MHzUSART2和USART3挂载在APB1总线上APB1最高36MHz。而原来的F407平台上APB1是42MHzAPB2是84MHz两者正好也是两倍关系但代码里波特率计算宏写的是同一个值。迁移时那段自定义的波特率计算代码是从F407工程里带过来的里面把USART3的时钟直接写成了42000000。到了F103上USART3实际挂在36MHz的APB1上用42MHz去计算USARTDIV算出来的分频值偏小实际波特率就变成了约230400串口当然乱码。USART1之所以正常是因为F103的APB2是72MHz而F407的APB2是84MHz波特率误差不算太离谱日志还能看。6.3 解决与迁移检查清单时钟宏、中断函数名、NVIC解决办法就是把两个串口的时钟来源分别定义USART1用APB2时钟USART3用APB1时钟// F103上 // USART1 - APB2 72MHz // USART3 - APB1 36MHz #define PCLK1 36000000UL // APB1: USART2/3 #define PCLK2 72000000UL // APB2: USART1如果是用标准库的USART_Init实际上它会自动调用RCC_GetClocksFreq获取PCLK1和PCLK2理论上不会出现这个问题。会踩这个坑的通常是直接从寄存器或者HAL层迁过来的自定义代码。如果你在迁移代码花几分钟确认一下波特率计算用的是哪个时钟值能省掉至少半天调试时间。串口迁移还有一个隐藏坑中断服务函数名和NVIC中断通道号。F103里USART3的中断服务函数是USART3_IRQHandlerNVIC通道号是USART3_IRQn这行本身不会因平台变化而改变但如果你的代码是手工写的启动文件或者用了不同的中断向量表就要检查函数名是不是被改成了别的名字。另一个容易忽略的是AFIO重映射如果USART3的TX/RX引脚用了重映射功能比如PB10/PB11或者PD8/PD9F103上需要额外使能AFIO时钟并调用GPIO_PinRemapConfigF407上没有这么麻烦很多人迁移时会漏掉这一行。7. 迁移完成后的验证流程与几个值得养成的习惯7.1 迁移后完整验证流程五个坑全部处理完之后我没有急着交付而是做了一轮完整的验证流程这里直接列出来供参考上电后读取LAN9252的BYTE_TEST寄存器地址0x0000连续读100次统计错误率确认SPI通信稳定用TwinCAT扫描EtherCAT从站确认ESC能正确识别检查SII信息是否完整状态机从INIT切换到PRE_OP检查SM和PDO映射是否正常切换到SAFE_OP确认输入数据能从数字量输入口读到切换到OP用主站做输出控制和输入采集测试跑PTO过程数据循环用示波器看IRQ引脚和SPI通信的时序关系确认中断响应没有丢连续运行24小时盯从站是否出现掉线或状态机自动复位这套流程走下来基本能覆盖从站代码迁移的大部分问题。尤其是第一项每次上电后先读回寄存器验证通信这是一个成本很低但收益很高的习惯能帮你把“SPI物理层问题”和“应用逻辑问题”快速分开。7.2 五个坑速查表坑典型现象根因快速判断方法SPI读回0xFFMISO一直高电平GPIO模式配置错误MISO配成输出示波器抓MISO确认是否有数据波形IRQ中断错乱状态机卡在INIT开漏输出缺上拉EXTI线冲突万用表量IRQ静态电平检查EXTI映射表上电偶发起不来断电重上电概率恢复复位时序不达标RC复位抖动示波器抓nRESET波形检查GPIO默认电平25MHz时钟互供失败LAN9252不工作F103无法启动F103 HSE不支持25MHzMCO输出频率不匹配查看F103数据手册HSE范围检查晶振连接串口3乱码USART1正常USART3乱码APB1与APB2时钟差异波特率计算时钟值错误示波器抓TX波形实测波特率和配置对比7.3 几个值得长期坚持的调试习惯这套折腾下来最大的收获不是那5个解决方案而是几个调试习惯先看硬件再看代码。SPI不通、中断不触发、串口乱码这些问题先用示波器和万用表排除物理层问题再去怀疑代码逻辑。很多时候代码没有错错的是引脚上的电平不对。迁移代码不要跨系列直接复制GPIO和时钟初始化。F407和F103虽然都是STM32但GPIO模式定义、总线时钟、重映射方式都有差异直接复制就是给自己埋雷。每次上电后第一步一定要做寄存器回读验证。对LAN9252来说就是读BYTE_TEST对普通SPI外设来说就是读设备ID用最简单的操作确认物理通道是通的再聊其他。另外还有两个F103特有的小习惯一是BOOT0引脚如果悬空DAP下载偶尔会失败建议用10k电阻下拉到地二是如果不使用USB外设不要在代码里开启USB时钟PA11/PA12在USB时钟开启时会表现出一些奇怪的引脚行为容易让人误判为外部硬件问题。个人体会是这类迁移工作表面上改的是代码实际上考验的是对芯片外部硬件依赖的理解。很多时候不是逻辑写得不够好而是复位时序、电气特性、总线差异这些“看不见的东西”在作祟。把每个坑背后的硬件原理弄明白以后再遇到类似平台切换心里就有底了。
返回列表