ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32CubeMX配置LAN8720以太网:五个关键细节与避坑指南

STM32CubeMX配置LAN8720以太网:五个关键细节与避坑指南 1. 为什么LAN8720在CubeMX里总有人翻车搞STM32以太网的朋友十个里有八个用过LAN8720这颗PHY芯片。便宜、好买、资料多关键是它支持RMII接口引脚少布线相对省心。但你要是去论坛里翻一翻会发现一个很有意思的现象用LAN8720的人多踩坑的人更多。而且这些坑往往不是硬件设计的问题而是出在STM32CubeMX的配置环节。我自己前前后后做过好几个带以太网的板子从F407到F429再到H7系列LAN8720这颗PHY用了不下十次。说实话每次重新建工程的时候还是会在某些细节上卡一下。不是不会而是CubeMX这个工具在某些地方的设计逻辑跟LAN8720的实际工作方式之间存在一些“微妙的错位”。你如果完全照着默认配置走大概率能ping通但一旦跑高负载或者长时间运行问题就冒出来了。这篇文章我打算把这些年踩过的坑系统性地梳理一遍聚焦在五个最容易被忽略的细节上RMII模式下的REF_CLK方向配置、PHY地址的确认与设置、MDIO时钟分频、复位引脚与时序、以及时钟树与RMII时钟源的匹配。每一个点我都会说清楚“为什么容易错”以及“怎么确认自己配对了”。适合谁看如果你正在用STM32CubeMX配置LAN8720或者已经配好了但网络时通时不通、ping丢包、初始化偶尔失败那这篇内容应该能帮你省下不少调试时间。如果你还没开始那更好提前知道这些坑在哪比事后拿示波器一根根查线要轻松得多。2. 五个关键细节的深度拆解2.1 RMII模式选择与REF_CLK方向第一个大坑在CubeMX里配置以太网外设的时候第一个要做的选择就是PHY接口模式。CubeMX给了三个选项MII、RMII、以及RMII with REF_CLK output。很多人看到LAN8720支持RMII就直接选了RMII然后生成代码发现网络死活不通。问题出在哪出在REF_CLK这个50MHz参考时钟的方向上。RMII接口相比MII最大的简化就是把数据线从4位降到2位同时把收发时钟合并成一个50MHz的REF_CLK。这个REF_CLK谁来提供有两种方案一种是PHY提供STM32接收另一种是STM32提供PHY接收。LAN8720这颗芯片默认情况下是需要外部给它提供一个50MHz的时钟源它自己并不产生这个时钟。也就是说LAN8720通常工作在“外部时钟输入”模式这个50MHz要么来自一个有源晶振要么来自STM32的MCO引脚输出。所以你在CubeMX里如果选了“RMII”而没有选“RMII with REF_CLK output”CubeMX会认为REF_CLK是从外部也就是PHY侧输入的。但实际上你的LAN8720并没有输出50MHz的能力它需要你给它送时钟。这就导致STM32在等一个永远不会来的时钟信号ETH外设初始化直接卡死或者超时。正确的做法是如果你用的是LAN8720并且板上没有独立的有源晶振给PHY供50MHz那你就应该在CubeMX里选择RMII with REF_CLK output模式。这样STM32会通过PA8引脚MCO1输出50MHz时钟给LAN8720的XTAL1/REF_CLK引脚。但这里还有一个隐藏细节PA8作为MCO输出50MHz它的时钟源必须是PLL输出的某个分频。在CubeMX的时钟树配置里你需要确保MCO1的时钟源选的是PLLCLK并且分频系数设置正确使得MCO输出恰好是50MHz。这个计算后面在时钟树部分会详细说。注意有些板子设计的时候用了25MHz晶振给LAN8720然后LAN8720内部PLL倍频到50MHz输出给STM32。这种方案下CubeMX里就应该选普通的RMII模式REF_CLK方向是PHY到STM32。所以关键是看你板子上50MHz时钟的实际来源不能一概而论。我个人的经验是如果你是自己画板子尽量用STM32输出50MHz给PHY的方案这样可以省掉一个晶振而且时钟同步性更好。但前提是你要把MCO配置对。2.2 PHY地址确认不是你想的那么简单PHY地址这个问题看起来很简单——LAN8720的PHY地址由PHYAD0引脚决定接下拉就是地址0接上拉就是地址1。CubeMX里默认填的是0。很多人就直接默认了也不去确认硬件上PHYAD0到底接了什么。但实际情况是很多开发板的原理图上PHYAD0引脚是通过一个电阻分压或者直接连到某个GPIO上的。如果你不去翻原理图确认很可能硬件上PHYAD0被拉高了地址是1而CubeMX里配的是0结果就是ETH外设一直在发MDIO命令但收不到PHY的回应初始化超时。更隐蔽的一种情况是有些板子把PHYAD0和某个LED引脚复用在一起或者通过跳线帽选择。这时候你光看原理图还不够得实际测量或者查跳线帽的位置。怎么确认PHY地址最靠谱的方法是在初始化代码里加一段扫描逻辑从地址0到31逐个尝试读取PHY的ID寄存器寄存器2和3。LAN8720的ID应该是0x0007和0xC0F1。如果你读到的值不对说明地址不对或者MDIO通信有问题。for (uint8_t addr 0; addr 32; addr) { uint32_t id 0; HAL_ETH_ReadPHYRegister(heth, addr, 2, id); if ((id 0xFFFF) 0x0007) { // 找到LAN8720了addr就是正确的PHY地址 break; } }这段代码在实际调试中非常有用建议你在第一次调试以太网的时候就加上确认地址后再改成固定值。提示CubeMX里PHY地址的配置项在Ethernet Parameters里叫PHY Address。默认值是0但一定要根据硬件实际情况修改。2.3 MDIO时钟分频太快太慢都不行MDIO接口是用来读写PHY寄存器的它有一个时钟线MDC。这个MDC时钟是由STM32的ETH外设分频产生的分频系数在CubeMX里可以配置。问题在于很多人不知道这个分频系数该怎么算。CubeMX里有一个参数叫“MDC Clock Range”它不是一个直接的分频值而是一个范围选择。你需要根据你的HCLK频率来选一个合适的分频使得MDC时钟落在IEEE 802.3规定的范围内通常1MHz到2.5MHz之间。举个例子如果你的HCLK是168MHz你选了分频系数为42那MDC就是4MHz超过了PHY能接受的范围。LAN8720的MDC最高频率是25MHz看起来4MHz没问题但实际调试中发现MDC太快会导致某些寄存器读写不稳定尤其是在长走线或者干扰环境下。我一般会把MDC设置在1MHz到2MHz之间。以168MHz HCLK为例分频系数选84到168之间比较合适。CubeMX里对应的“MDC Clock Range”选项你需要根据HCLK频率查表选择。具体对应关系在参考手册的ETH章节里有详细表格。HCLK频率推荐分频值实际MDC频率168MHz1241.35MHz168MHz1021.65MHz120MHz881.36MHz100MHz741.35MHz这个表格是我在实际项目中总结的不一定跟手册完全一致但实测下来很稳。你可以根据自己的HCLK频率用公式MDC HCLK / (分频值 × 2)来反推。注意不同系列的STM32ETH外设的分频公式可能略有差异F4系列和H7系列就不太一样具体以参考手册为准。2.4 复位引脚与时序硬件复位不能省LAN8720有一个nRST引脚低电平复位。很多人在画板子的时候把这个引脚直接接到STM32的某个GPIO上然后在代码里拉低再拉高完成复位。这个思路是对的但时序上容易出问题。CubeMX生成的ETH初始化代码里默认是没有PHY硬件复位操作的。它只做了软件复位通过写PHY的BCR寄存器。但LAN8720在上电后如果nRST引脚没有被正确拉低再拉高内部状态机可能处于不确定状态导致后续的软件复位也失败。我遇到过好几次这样的情况板子冷启动的时候网络正常热复位按复位键之后网络就挂了。查了半天发现是PHY的复位时序问题。STM32复位后GPIO默认是浮空输入状态如果LAN8720的nRST引脚没有外部上拉它可能处于一个中间电平PHY工作不正常。解决方案有两个一是在硬件上给nRST加一个上拉电阻确保STM32复位期间PHY处于正常工作状态二是在软件初始化ETH之前先手动控制nRST引脚拉低至少100微秒再拉高然后延时至少100毫秒等PHY内部稳定再进行ETH初始化。// PHY硬件复位 HAL_GPIO_WritePin(ETH_RST_GPIO_Port, ETH_RST_Pin, GPIO_PIN_RESET); HAL_Delay(100); // 拉低至少100us这里用100ms更保险 HAL_GPIO_WritePin(ETH_RST_GPIO_Port, ETH_RST_Pin, GPIO_PIN_SET); HAL_Delay(100); // 等待PHY内部PLL锁定 // 然后再调用MX_ETH_Init() MX_ETH_Init();这段代码要放在MX_ETH_Init()之前。CubeMX生成的代码里MX_ETH_Init()是在main函数里自动调用的你需要手动在它前面插入PHY复位逻辑。或者你可以修改MX_ETH_Init()函数在里面加上复位操作。注意LAN8720的数据手册里写的复位时间是“至少100微秒”但实际使用中建议拉低100毫秒以上给内部PLL足够的锁定时间。尤其是冷启动的时候电源上升斜率慢的话PHY内部状态建立需要更长时间。2.5 时钟树与RMII时钟源匹配最后一道关卡前面提到了REF_CLK的50MHz来源问题这里展开说时钟树的具体配置。假设你选择了“RMII with REF_CLK output”模式STM32需要通过MCO1PA8输出50MHz给LAN8720。MCO1的时钟源在CubeMX的时钟树配置界面里选择通常选PLLCLK。然后你需要设置MCO1的分频系数使得输出恰好是50MHz。以STM32F407为例假设系统时钟配置为168MHzPLLCLK是168MHz。MCO1的分频系数可以是1到5。168除以50不是整数所以不能直接分频得到50MHz。这时候你有两个选择一是调整PLL参数让PLLCLK变成一个能被50整除的频率二是用MCO1的另一个时钟源比如PLLI2SCLK。实际上STM32F4的MCO1时钟源选择里有一个PLLI2SCLK这个时钟是独立于主PLL的可以单独配置。你可以把PLLI2S配置成输出50MHz然后MCO1选择PLLI2SCLK作为源分频系数设为1。这样就能精确输出50MHz。具体配置步骤在CubeMX的时钟树界面找到PLLI2S的配置区域把PLLI2SN和PLLI2SR设置成合适的值使得PLLI2SCLK 50MHz。然后到MCO1配置区域时钟源选PLLI2SCLK分频选1。如果你用的是STM32H7系列时钟树更复杂但思路是一样的找到一个能产生精确50MHz的时钟源路由到MCO1再输出给PHY。提示MCO输出的时钟精度和抖动会影响RMII通信的稳定性。如果MCO输出的50MHz抖动太大可能会出现丢包或者协商速率不稳定的情况。建议用示波器实测一下PA8引脚的时钟波形确认频率和幅值都正常。另外如果你选择的是普通RMII模式PHY提供50MHz给STM32那STM32这边的ETH_REF_CLK引脚通常是PA1需要配置成正确的复用功能并且在CubeMX里确认ETH外设的时钟源是外部REF_CLK。这个配置在CubeMX里通常是自动的但如果你手动改了引脚分配可能会被覆盖掉需要检查一下。3. 完整配置流程与实操记录3.1 从零开始CubeMX工程配置步骤我以STM32F407LAN8720的方案为例把完整的配置流程走一遍。你可以跟着这个步骤操作也可以对照检查自己已有的工程。第一步新建工程选好芯片型号。在Pinout界面找到ETH外设选择RMII模式。这时候CubeMX会自动分配一批引脚PA1REF_CLK、PA2MDIO、PA7CRS_DV、PC1MDC、PC4RXD0、PC5RXD1、PB11TX_EN、PB12TXD0、PB13TXD1。这些引脚是固定的不能随便改。第二步在ETH的参数配置里找到“Advanced Parameters”或者“Ethernet Parameters”选项卡。这里有几个关键设置PHY Address填0或者你实际硬件上的地址、MDC Clock Range根据HCLK选择合适的分频、RMII Clock Mode选择“Output”还是“Input”。如果你用STM32输出50MHz就选Output。第三步配置时钟树。先把系统时钟配到168MHz然后找到MCO1的配置时钟源选PLLI2SCLK分频1。再到PLLI2S配置区域设置PLLI2SN和PLLI2SR使得输出为50MHz。具体值需要根据你的晶振频率计算。假设外部晶振是8MHzPLLI2S的输入是8MHz要得到50MHz可以设置PLLI2SN125PLLI2SR2这样VCO频率是8×125/2500MHz再除以2得到250MHz不对这里需要仔细算。实际上STM32F4的PLLI2S配置公式是PLLI2SCLK (HSE / PLLM) × PLLI2SN / PLLI2SR。假设HSE8MHzPLLM8那PLLI2S输入就是1MHz。要得到50MHz可以设PLLI2SN100PLLI2SR2这样1×100/250MHz。对这样就可以了。第四步配置GPIO。PA8会自动被配置为MCO1输出确认一下它的模式是“Alternate Function Push-Pull”速度设为“Very High”。另外如果你有PHY复位引脚把它配置成GPIO Output初始电平设为高。第五步生成代码。在Project Manager里设置好工具链和代码生成选项点击生成。3.2 代码层面的关键修改CubeMX生成的代码框架是好的但有几个地方需要手动补充。首先是PHY复位。在main.c的MX_ETH_Init()调用之前加上PHY硬件复位的代码。如果你在CubeMX里配置了复位引脚可以直接用HAL_GPIO_WritePin操作。其次是PHY地址确认。在MX_ETH_Init()之后加一段PHY ID读取的代码确认通信正常。如果读不到正确的ID后面的一切都是白搭。uint32_t phy_id 0; HAL_ETH_ReadPHYRegister(heth, 0, 2, phy_id); printf(PHY ID1: 0x%04X\r\n, phy_id 0xFFFF); HAL_ETH_ReadPHYRegister(heth, 0, 3, phy_id); printf(PHY ID2: 0x%04X\r\n, phy_id 0xFFFF);正常的话你应该看到PHY ID1是0x0007PHY ID2是0xC0F1。如果读到0xFFFF或者0x0000说明MDIO通信有问题回去检查MDC分频和PHY地址。然后是PHY初始化和自协商。CubeMX生成的代码里HAL_ETH_Init()会自动调用PHY的初始化流程包括自协商。但有时候自协商会失败尤其是网线质量不好的时候。你可以在初始化之后手动检查一下自协商结果uint32_t bsr 0; HAL_ETH_ReadPHYRegister(heth, 0, 1, bsr); if (bsr (1 5)) { printf(Auto-negotiation complete\r\n); uint32_t bmsr 0; HAL_ETH_ReadPHYRegister(heth, 0, 1, bmsr); if (bmsr (1 2)) { printf(Link up\r\n); } }这段代码可以帮你快速判断PHY是否完成了自协商和链路建立。3.3 实测验证ping测试与吞吐量测试配置完成后最基本的验证就是ping。把板子和电脑接到同一个交换机或者直连给板子配一个同网段的IP然后从电脑ping板子。如果ping不通先别急着改代码。按这个顺序排查第一看PHY的Link灯和Speed灯是否亮起。如果Link灯不亮说明物理链路有问题检查网线、变压器、RJ45座子。第二如果Link灯亮但ping不通用前面说的PHY ID读取代码确认MDIO通信正常。第三如果MDIO正常但ping不通检查STM32的MAC地址配置和IP地址配置。ping通了之后建议做一个简单的吞吐量测试。用iperf或者自己写一个TCP/UDP回环测试看看实际能跑多少Mbps。RMII接口理论上是100Mbps实际能跑到80Mbps以上就算正常。如果只有几Mbps那可能是REF_CLK抖动太大或者MDC分频不合适。我实测过一个F407LAN8720的方案用STM32输出50MHz给PHYTCP吞吐量稳定在92Mbps左右。另一个方案用外部有源晶振给PHY供50MHz吞吐量也差不多。但有一个方案因为MDC分频设得太快4MHz吞吐量只有60Mbps左右而且偶尔丢包。把MDC降到1.5MHz之后吞吐量恢复到90Mbps以上。4. 常见问题速查与排查技巧4.1 初始化超时HAL_ETH_Init返回HAL_TIMEOUT这是最常见的问题。HAL_ETH_Init()内部会等待PHY自协商完成如果超时就会返回HAL_TIMEOUT。原因通常有三个PHY地址不对、MDIO通信失败、PHY没有正常复位。排查顺序先用PHY ID读取代码确认MDIO通信。如果读不到ID检查MDC分频和PHY地址。如果能读到ID但初始化还是超时检查PHY的复位时序确保nRST引脚在初始化前被正确拉低再拉高。还有一个容易被忽略的点LAN8720的nINT/REFCLKO引脚配置。如果你用的是外部晶振模式这个引脚可能被配置成中断输出导致REF_CLK没有输出。检查一下PHY的MODE引脚和寄存器配置。4.2 ping通但丢包严重ping能通说明基本通信是正常的但丢包严重说明链路质量有问题。可能的原因REF_CLK抖动太大、MDC时钟太快、网线质量差、变压器匹配不好。先换一根网线试试排除线缆问题。然后用示波器测PA8MCO输出的50MHz波形看频率是否准确、幅值是否足够、抖动是否在合理范围内。如果MCO输出正常再测MDC时钟确认频率在1MHz到2.5MHz之间。如果这些都正常那可能是硬件布线问题检查RMII的几根数据线是否等长、是否远离干扰源。4.3 热复位后网络失效这个问题前面提过根源是PHY复位时序。STM32复位后GPIO处于浮空状态如果PHY的nRST没有外部上拉PHY可能处于不确定状态。解决方案是在STM32的初始化代码里尽早配置nRST引脚为输出高电平或者在硬件上给nRST加一个10k上拉电阻。还有一种情况是STM32复位后MCO输出还没有稳定PHY就开始了自协商导致协商失败。解决方法是先配置MCO输出延时一段时间等时钟稳定再初始化ETH。4.4 自协商速率不对百兆变十兆有时候PHY自协商的结果是10Mbps而不是100Mbps。原因可能是网线只有两对线百兆需要四对线全通或者变压器不支持百兆或者PHY的某个配置寄存器被错误修改。先换一根确认支持百兆的网线。如果还是10Mbps读一下PHY的PHYSR寄存器地址0x1F看看具体是什么原因。LAN8720的PHYSR寄存器里有一位是“Speed Indication”可以告诉你当前协商的速率。如果硬件支持百兆但协商成十兆可以尝试强制PHY工作在百兆全双工模式通过写BCR寄存器实现。// 强制百兆全双工 uint32_t bcr 0; HAL_ETH_ReadPHYRegister(heth, 0, 0, bcr); bcr ~(1 12); // 清除自协商使能 bcr | (1 13) | (1 8); // 百兆、全双工 HAL_ETH_WritePHYRegister(heth, 0, 0, bcr);注意强制速率模式需要两边都强制如果一边自协商一边强制可能会出问题。建议只在调试阶段用正式产品还是用自协商。4.5 常见问题速查表现象可能原因排查方法解决方案ETH初始化超时PHY地址错误扫描PHY地址读ID修改CubeMX中的PHY地址ETH初始化超时MDIO通信失败测MDC时钟、查上拉电阻调整MDC分频、加4.7k上拉ping不通REF_CLK方向错误测PA8有无50MHz改RMII模式为Outputping通但丢包MDC太快测MDC频率降低MDC到1-2MHz热复位后失效PHY复位时序测nRST引脚电平加外部上拉、软件复位速率只有10M网线或变压器换网线、查变压器换百兆网线、强制百兆吞吐量低REF_CLK抖动示波器测抖动改用PLLI2S输出5. 几个容易被忽略的硬件配合细节5.1 LAN8720的MODE引脚配置LAN8720有几个MODE引脚用来配置它的工作模式。其中最重要的是MODE[2:0]决定了PHY上电后的默认行为。如果你用的是RMII模式并且REF_CLK由外部提供那MODE引脚需要配置成特定的值。具体来说MODE2和MODE1的组合决定了PHY是输出50MHz还是输入50MHz。很多开发板为了省事直接把MODE引脚固定接高或接低。但如果你自己画板子一定要查数据手册确认MODE引脚的配置。配错了的话PHY可能根本不工作或者工作模式不对。5.2 25MHz晶振与50MHz输出的关系有些板子设计的时候给LAN8720接了一个25MHz晶振然后利用LAN8720内部的PLL倍频到50MHz从nINT/REFCLKO引脚输出给STM32。这种方案下CubeMX里应该选普通RMII模式REF_CLK方向是Input。但这里有一个细节LAN8720的REFCLKO输出使能需要通过寄存器配置。上电默认情况下这个输出可能是关闭的。你需要在初始化代码里先通过MDIO写PHY寄存器使能REFCLKO输出然后再初始化ETH。如果顺序反了STM32等不到REF_CLK初始化就会超时。5.3 变压器和RJ45的选型这个虽然不属于CubeMX配置的范畴但实际调试中经常被忽略。RMII接口的100Mbps以太网对变压器的要求比较高。如果用了劣质变压器或者变压器的中心抽头电容选得不对会导致信号完整性下降表现为丢包或者协商速率降级。我一般推荐用集成变压器的RJ45座子比如HR911105A省事而且匹配性好。如果分离设计变压器的中心抽头要接一个0.1uF的电容到地这个电容不能省。5.4 PCB布线对RMII的影响RMII的几根数据线TXD0、TXD1、RXD0、RXD1和时钟线REF_CLK对走线长度比较敏感。理想情况下这几根线应该等长并且远离高频干扰源。如果走线长度差异太大会导致数据采样时序偏移表现为丢包或者完全不通。我见过一个案例RXD0和RXD1的走线长度差了将近5厘米结果就是ping通但丢包率30%以上。把走线改等长之后丢包率降到0.1%以下。所以如果你是自己画板子RMII的这几根线尽量走等长误差控制在5毫米以内。6. 我的实操心得与最后几条建议折腾LAN8720这些年最大的体会就是CubeMX是个好工具但它不能替你理解硬件。它生成的代码框架能帮你省掉很多重复劳动但PHY地址、时钟方向、复位时序这些跟硬件强相关的配置必须你自己去确认。我现在的习惯是每次新建以太网工程都会先做三件事第一翻原理图确认PHY地址和REF_CLK方向第二在代码里加PHY ID读取和打印第三用示波器测一下MCO输出和MDC时钟。这三件事花不了十分钟但能省下后面几个小时的调试时间。还有一个小技巧如果你手头没有示波器可以用STM32的定时器输入捕获功能来测MCO输出的频率。把PA8的MCO输出接到另一个定时器的输入捕获引脚上通过计算捕获到的周期数就能算出实际频率。虽然精度不如示波器但判断有没有输出、频率大概对不对足够了。最后再说一个关于CubeMX版本的问题。不同版本的CubeMXETH外设的配置界面和生成的代码可能有差异。我遇到过在某个版本里RMII Clock Mode的选项名称变了导致我找了半天没找到。所以如果你照着网上的教程配置发现界面跟教程里不一样先确认一下CubeMX的版本。建议用比较新的版本比如6.x系列对H7系列的支持更好F4系列的配置也更稳定。以太网调试这件事说难不难说简单也不简单。关键是把几个核心细节确认到位剩下的就是耐心排查。希望这篇内容能帮你少走一些弯路。
返回列表